aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorcbdev <cb@cbcdn.com>2019-12-31 10:58:34 +0100
committercbdev <cb@cbcdn.com>2019-12-31 10:58:34 +0100
commit3b4a2f9f3bbe97c5b77eb74ba9c0163b52d33206 (patch)
tree1defeeb5b081b8c951d920298fa10858191d6beb
parent7360766777a7969b76fa306f7381d2d51efa1ebe (diff)
parent87fe68ef7d929b2f79e695df649b374fe0e2c572 (diff)
downloadmidimonster-3b4a2f9f3bbe97c5b77eb74ba9c0163b52d33206.tar.gz
midimonster-3b4a2f9f3bbe97c5b77eb74ba9c0163b52d33206.tar.bz2
midimonster-3b4a2f9f3bbe97c5b77eb74ba9c0163b52d33206.zip
Merge branch 'master' into debian/master
-rw-r--r--.travis-ci.sh30
-rw-r--r--.travis.yml16
-rw-r--r--Makefile24
-rw-r--r--README.md23
-rw-r--r--TODO16
-rw-r--r--backend.c44
-rw-r--r--backends/Makefile19
-rw-r--r--backends/artnet.c109
-rw-r--r--backends/artnet.h6
-rw-r--r--backends/evdev.c114
-rw-r--r--backends/evdev.h6
-rw-r--r--backends/jack.c143
-rw-r--r--backends/jack.h6
-rw-r--r--backends/libmmbackend.c33
-rw-r--r--backends/libmmbackend.h8
-rw-r--r--backends/loopback.c30
-rw-r--r--backends/loopback.h6
-rw-r--r--backends/lua.c69
-rw-r--r--backends/lua.h6
-rw-r--r--backends/maweb.c134
-rw-r--r--backends/maweb.h6
-rw-r--r--backends/midi.c82
-rw-r--r--backends/midi.h6
-rw-r--r--backends/ola.cpp66
-rw-r--r--backends/ola.h6
-rw-r--r--backends/osc.c148
-rw-r--r--backends/osc.h6
-rw-r--r--backends/sacn.c142
-rw-r--r--backends/sacn.h7
-rw-r--r--backends/sacn.md6
-rw-r--r--backends/winmidi.c206
-rw-r--r--backends/winmidi.h16
-rw-r--r--config.c11
-rw-r--r--configs/launchctl-sacn.cfg1
-rw-r--r--configs/layering.cfg23
-rw-r--r--configs/layering.lua76
-rwxr-xr-xinstaller.sh213
-rw-r--r--midimonster.118
-rw-r--r--midimonster.c27
-rw-r--r--midimonster.h37
-rw-r--r--plugin.c7
41 files changed, 1116 insertions, 836 deletions
diff --git a/.travis-ci.sh b/.travis-ci.sh
index da36c17..1475dea 100644
--- a/.travis-ci.sh
+++ b/.travis-ci.sh
@@ -76,9 +76,39 @@ elif [[ $TASK = 'windows' ]]; then
travis_fold start "make_windows"
make windows;
travis_fold end "make_windows"
+ travis_fold start "deploy_windows"
+ if [ "$(git describe)" == "$(git describe --abbrev=0)" ]; then
+ mkdir ./deployment
+ mkdir ./deployment/backends
+ mkdir ./deployment/docs
+ cp ./midimonster.exe ./deployment/
+ cp ./backends/*.dll ./deployment/backends/
+ cp ./monster.cfg ./deployment/monster.cfg
+ cp ./backends/*.md ./deployment/docs/
+ cp -r ./configs ./deployment/
+ cd ./deployment
+ zip -r "./midimonster-$(git describe)-windows.zip" "./"
+ find . ! -iname '*.zip' -delete
+ fi
+ travis_fold end "deploy_windows"
else
# Otherwise compile as normal
travis_fold start "make"
make full;
travis_fold end "make"
+ travis_fold start "deploy_unix"
+ if [ "$(git describe)" == "$(git describe --abbrev=0)" ]; then
+ mkdir ./deployment
+ mkdir ./deployment/backends
+ mkdir ./deployment/docs
+ cp ./midimonster ./deployment/
+ cp ./backends/*.so ./deployment/backends/
+ cp ./monster.cfg ./deployment/monster.cfg
+ cp ./backends/*.md ./deployment/docs/
+ cp -r ./configs ./deployment/
+ cd ./deployment
+ tar czf "midimonster-$(git describe)-$TRAVIS_OS_NAME.tgz" *
+ find . ! -iname '*.tgz' -delete
+ fi
+ travis_fold end "deploy_unix"
fi
diff --git a/.travis.yml b/.travis.yml
index bdaf63a..21c2a40 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -34,7 +34,6 @@ addons:
- *core_build
- mingw-w64
-
matrix:
fast_finish: true
include:
@@ -179,8 +178,11 @@ install:
- if [ "$TASK" = "codespell" ]; then pip install --user git+https://github.com/codespell-project/codespell.git; fi
before_install:
+# Travis clones with --branch, which omits tags. Since we use them for the version string at build time, fetch them
+ - git pull --tags
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update; fi
- - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install ccache ola lua openssl jack; fi
+# 'brew install' sometimes returns non-zero for some arcane reason. Executing 'true' resets the exit code and allows Travis to continue building...
+ - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install ccache ola lua openssl jack; true; fi
# OpenSSL is not a proper install due to some Apple bull, so provide additional locations via the environment...
# Additionally, newer versions of this "recipe" seem to use the name 'openssl@1.1' instead of plain 'openssl' and there seems to be
# no way to programmatically get the link and include paths. Genius! Hardcoding the new version for the time being...
@@ -199,3 +201,13 @@ before_install:
after_script:
- if [ "$TASK" = "coverity" ]; then tail -n 10000 ${TRAVIS_BUILD_DIR}/cov-int/build-log.txt; cat ${TRAVIS_BUILD_DIR}/cov-int/scm_log.txt; fi
+
+deploy:
+ provider: releases
+ file_glob: true
+ api_key: $GITHUB_TOKEN
+ file: ./deployment/*
+ skip_cleanup: true
+ draft: true
+ on:
+ tags: true
diff --git a/Makefile b/Makefile
index 8b2d7ec..990aec3 100644
--- a/Makefile
+++ b/Makefile
@@ -3,14 +3,22 @@ OBJS = config.o backend.o plugin.o
PREFIX ?= /usr
PLUGIN_INSTALL = $(PREFIX)/lib/midimonster
+EXAMPLES ?= $(PREFIX)/share/midimonster
SYSTEM := $(shell uname -s)
+GITVERSION = $(shell git describe)
+# Default compilation CFLAGS
CFLAGS ?= -g -Wall -Wpedantic
+#CFLAGS += -DDEBUG
# Hide all non-API symbols for export
CFLAGS += -fvisibility=hidden
-#CFLAGS += -DDEBUG
midimonster: LDLIBS = -ldl
+# Replace version string with current git-describe if possible
+ifneq "$(GITVERSION)" ""
+midimonster: CFLAGS += -DMIDIMONSTER_VERSION=\"$(GITVERSION)\"
+midimonster.exe: CFLAGS += -DMIDIMONSTER_VERSION=\"$(GITVERSION)\"
+endif
# Work around strange linker passing convention differences in Linux and OSX
ifeq ($(SYSTEM),Linux)
@@ -64,14 +72,14 @@ run:
valgrind --leak-check=full --show-leak-kinds=all ./midimonster
install:
- install -d "$(DESTDIR)$(PREFIX)/bin"
- install -m 0755 midimonster "$(DESTDIR)$(PREFIX)/bin"
- install -d "$(DESTDIR)$(PLUGIN_INSTALL)"
- install -m 0755 backends/*.so "$(DESTDIR)$(PLUGIN_INSTALL)"
- install -d "$(DESTDIR)$(PREFIX)/share/midimonster"
- install -m 0644 configs/* "$(DESTDIR)$(PREFIX)/share/midimonster"
+ install -d "$(DESTDIR)$(PREFIX)/bin"
+ install -m 0755 midimonster "$(DESTDIR)$(PREFIX)/bin"
+ install -d "$(DESTDIR)$(PLUGIN_INSTALL)"
+ install -m 0755 backends/*.so "$(DESTDIR)$(PLUGIN_INSTALL)"
+ install -d "$(DESTDIR)$(EXAMPLES)"
+ install -m 0644 configs/* "$(DESTDIR)$(EXAMPLES)"
ifdef DEFAULT_CFG
- install -Dm 0644 monster.cfg "$(DESTDIR)$(DEFAULT_CFG)"
+ install -Dm 0644 monster.cfg "$(DESTDIR)$(DEFAULT_CFG)"
endif
sanitize: export CC = clang
diff --git a/README.md b/README.md
index bcae9dd..e955d53 100644
--- a/README.md
+++ b/README.md
@@ -163,8 +163,15 @@ as arguments to the `make` invocation:
|---------------|-----------------------|-------------------------------|-------------------------------|
| build targets | `DEFAULT_CFG` | `monster.cfg` | Default configuration file |
| build targets | `PLUGINS` | Linux/OSX: `./backends/`, Windows: `backends\` | Backend plugin library path |
-| `install` | `DESTDIR` | empty | Destination directory for packaging builds |
| `install` | `PREFIX` | `/usr` | Install prefix for binaries |
+| `install` | `DESTDIR` | empty | Destination directory for packaging builds |
+| `install` | `DEFAULT_CFG` | empty | Install path for default configuration file |
+| `install` | `PLUGINS` | `$(PREFIX)/lib/midimonster` | Install path for backend shared objects |
+| `install` | `EXAMPLES` | `$(PREFIX)/share/midimonster` | Install path for example configurations |
+
+Note that the same variables may have different default values depending on the target. This implies that
+builds that are destined to be installed require those variables to be set to the same value for the
+build and `install` targets.
Some backends have been marked as optional as they require rather large additional software to be installed,
for example the `ola` backend. To create a build including these, run `make full`.
@@ -175,6 +182,17 @@ for example
```
make jack.so
```
+#### Using the installer
+
+For easy installation on Linux, the [installer script](installer.sh) can be used:
+
+```
+wget https://raw.githubusercontent.com/cbdevnet/midimonster/master/installer.sh ./
+chmod +x ./installer.sh
+./installer.sh
+```
+This tool can also update MIDImonster automatically using a configuration file generated by the installer.
+To do so, run `midimonster-updater` as root on your system after using the installer.
#### Building for packaging or installation
@@ -192,6 +210,9 @@ make install
Depending on your configuration of `DESTDIR`, the `make install` step may require root privileges to
install the binaries to the appropriate destinations.
+To create Debian packages, use the debianization and `git-buildpackage` configuration on the `debian/master`
+branch. Simply running `gbp buildpackage` should build a package for the last tagged release.
+
#### Building for Windows
To build for Windows, you still need to compile on a Linux machine (virtual machines work well for this).
diff --git a/TODO b/TODO
index cba1c15..ee58777 100644
--- a/TODO
+++ b/TODO
@@ -1,15 +1,7 @@
-winmidi
-rename
-release
-
-MIDI NRPN
keepalive channels per backend?
-mm_backend_start might get some arguments so they don't have to fetch them all the time
-mm_channel_resolver might get additional info about the mapping direction
Note source in channel value struct
Optimize core channel search (store backend offset)
-
-mm_managed_fd.impl is not freed currently
-
-rtpmidi mode=peer
- mode=initiator
+udp backends may ignore MTU
+mm_managed_fd.impl is not freed currently (and is heaped most of the time anyway) -> documentation
+make event collectors threadsafe to stop marshalling data...
+collect & check backend API version
diff --git a/backend.c b/backend.c
index 3a18f41..a2b120f 100644
--- a/backend.c
+++ b/backend.c
@@ -150,6 +150,12 @@ MM_API int mm_backend_instances(char* name, size_t* ninst, instance*** inst){
}
*ninst = n;
+
+ if(!n){
+ *inst = NULL;
+ return 0;
+ }
+
*inst = calloc(n, sizeof(instance*));
if(!*inst){
fprintf(stderr, "Failed to allocate memory\n");
@@ -183,7 +189,7 @@ void channels_free(){
size_t u;
for(u = 0; u < nchannels; u++){
DBGPF("Destroying channel %lu on instance %s\n", channels[u]->ident, channels[u]->instance->name);
- if(channels[u]->impl){
+ if(channels[u]->impl && channels[u]->instance->backend->channel_free){
channels[u]->instance->backend->channel_free(channels[u]);
}
free(channels[u]);
@@ -256,30 +262,56 @@ MM_API int mm_backend_register(backend b){
int backends_start(){
int rv = 0, current;
- size_t u, p;
+ size_t n, u, p;
+ instance** inst = NULL;
+
for(u = 0; u < nbackends; u++){
//only start backends that have instances
for(p = 0; p < ninstances && instances[p]->backend != backends + u; p++){
}
+
+ //backend has no instances, skip the start call
if(p == ninstances){
- fprintf(stderr, "Skipping start of backend %s\n", backends[u].name);
continue;
}
+
+ //fetch list of instances
+ if(mm_backend_instances(backends[u].name, &n, &inst)){
+ fprintf(stderr, "Failed to fetch instance list for initialization of backend %s\n", backends[u].name);
+ return 1;
+ }
- current = backends[u].start();
+ //start the backend
+ current = backends[u].start(n, inst);
if(current){
fprintf(stderr, "Failed to start backend %s\n", backends[u].name);
}
+
+ //clean up
+ free(inst);
+ inst = NULL;
rv |= current;
}
return rv;
}
int backends_stop(){
- size_t u;
+ size_t u, n;
+ instance** inst = NULL;
+
for(u = 0; u < nbackends; u++){
- backends[u].shutdown();
+ //fetch list of instances
+ if(mm_backend_instances(backends[u].name, &n, &inst)){
+ fprintf(stderr, "Failed to fetch instance list for shutdown of backend %s\n", backends[u].name);
+ n = 0;
+ inst = NULL;
+ }
+
+ backends[u].shutdown(n, inst);
+ free(inst);
+ inst = NULL;
}
+
free(backends);
nbackends = 0;
return 0;
diff --git a/backends/Makefile b/backends/Makefile
index feefd7b..656e6b6 100644
--- a/backends/Makefile
+++ b/backends/Makefile
@@ -7,8 +7,12 @@ BACKEND_LIB = libmmbackend.o
SYSTEM := $(shell uname -s)
-CFLAGS += -g -fPIC -I../ -Wall -Wpedantic
-CPPFLAGS += -g -fPIC -I../
+# Generate debug symbols unless overridden
+CFLAGS ?= -g
+CPPFLAGS ?= -g
+
+CFLAGS += -fPIC -I../ -Wall -Wpedantic
+CPPFLAGS += -fPIC -I../
LDFLAGS += -shared
# Build Linux backends if possible
@@ -47,10 +51,13 @@ evdev.so: CFLAGS += $(shell pkg-config --cflags libevdev || echo "-DBUILD_ERROR=
evdev.so: LDLIBS = $(shell pkg-config --libs libevdev || echo "-DBUILD_ERROR=\"Missing pkg-config data for libevdev\"")
ola.so: LDLIBS = -lola
ola.so: CPPFLAGS += -Wno-write-strings
+
# The pkg-config name for liblua5.3 is subject to discussion. I prefer 'lua5.3' (which works on Debian and OSX),
# but Arch requires 'lua53' which works on Debian, too, but breaks on OSX.
lua.so: CFLAGS += $(shell pkg-config --cflags lua53 || pkg-config --cflags lua5.3 || echo "-DBUILD_ERROR=\"Missing pkg-config data for lua53\"")
lua.so: LDLIBS += $(shell pkg-config --libs lua53 || pkg-config --libs lua5.3 || echo "-DBUILD_ERROR=\"Missing pkg-config data for lua53\"")
+lua.dll: CFLAGS += $(shell pkg-config --cflags lua53 || pkg-config --cflags lua5.3 || echo "-DBUILD_ERROR=\"Missing pkg-config data for lua53\"")
+lua.dll: LDLIBS += -L../libs -llua53
%.so :: %.c %.h $(BACKEND_LIB)
$(CC) $(CFLAGS) $(LDLIBS) $< $(ADDITIONAL_OBJS) -o $@ $(LDFLAGS)
@@ -66,10 +73,12 @@ all: $(BACKEND_LIB) $(BACKENDS)
../libmmapi.a:
$(MAKE) -C ../ midimonster.exe
-windows: export CC = x86_64-w64-mingw32-gcc
-windows: LDLIBS += -lmmapi
-windows: LDFLAGS += -L../
+%.dll: export CC = x86_64-w64-mingw32-gcc
+%.dll: LDLIBS += -lmmapi
+%.dll: LDFLAGS += -L../
+%.dll: CFLAGS += -Wno-format -Wno-pointer-sign
windows: CFLAGS += -Wno-format -Wno-pointer-sign
+windows: export CC = x86_64-w64-mingw32-gcc
windows: ../libmmapi.a $(BACKEND_LIB) $(WINDOWS_BACKENDS)
full: $(BACKEND_LIB) $(BACKENDS) $(OPTIONAL_BACKENDS)
diff --git a/backends/artnet.c b/backends/artnet.c
index 8a62a43..0bd1a32 100644
--- a/backends/artnet.c
+++ b/backends/artnet.c
@@ -1,3 +1,5 @@
+#define BACKEND_NAME "artnet"
+
#include <string.h>
#include <ctype.h>
#include <errno.h>
@@ -6,7 +8,6 @@
#include "artnet.h"
#define MAX_FDS 255
-#define BACKEND_NAME "artnet"
static uint8_t default_net = 0;
static size_t artnet_fds = 0;
@@ -15,7 +16,7 @@ static artnet_descriptor* artnet_fd = NULL;
static int artnet_listener(char* host, char* port){
int fd;
if(artnet_fds >= MAX_FDS){
- fprintf(stderr, "ArtNet backend descriptor limit reached\n");
+ LOG("Backend descriptor limit reached");
return -1;
}
@@ -28,11 +29,11 @@ static int artnet_listener(char* host, char* port){
artnet_fd = realloc(artnet_fd, (artnet_fds + 1) * sizeof(artnet_descriptor));
if(!artnet_fd){
close(fd);
- fprintf(stderr, "Failed to allocate memory\n");
+ LOG("Failed to allocate memory");
return -1;
}
- fprintf(stderr, "ArtNet backend interface %" PRIsize_t " bound to %s port %s\n", artnet_fds, host, port);
+ LOGPF("Interface %" PRIsize_t " bound to %s port %s", artnet_fds, host, port);
artnet_fd[artnet_fds].fd = fd;
artnet_fd[artnet_fds].output_instances = 0;
artnet_fd[artnet_fds].output_instance = NULL;
@@ -55,45 +56,41 @@ MM_PLUGIN_API int init(){
};
if(sizeof(artnet_instance_id) != sizeof(uint64_t)){
- fprintf(stderr, "ArtNet instance identification union out of bounds\n");
+ LOG("Instance identification union out of bounds");
return 1;
}
//register backend
if(mm_backend_register(artnet)){
- fprintf(stderr, "Failed to register ArtNet backend\n");
+ LOG("Failed to register backend");
return 1;
}
return 0;
}
static int artnet_configure(char* option, char* value){
- char* host = NULL, *port = NULL;
+ char* host = NULL, *port = NULL, *fd_opts = NULL;
if(!strcmp(option, "net")){
//configure default net
default_net = strtoul(value, NULL, 0);
return 0;
}
else if(!strcmp(option, "bind")){
- mmbackend_parse_hostspec(value, &host, &port);
-
- if(!port){
- port = ARTNET_PORT;
- }
+ mmbackend_parse_hostspec(value, &host, &port, &fd_opts);
if(!host){
- fprintf(stderr, "Not valid ArtNet bind address given\n");
+ LOGPF("%s is not a valid bind address", value);
return 1;
}
- if(artnet_listener(host, port)){
- fprintf(stderr, "Failed to bind ArtNet descriptor: %s\n", value);
+ if(artnet_listener(host, (port ? port : ARTNET_PORT))){
+ LOGPF("Failed to bind descriptor: %s", value);
return 1;
}
return 0;
}
- fprintf(stderr, "Unknown ArtNet backend option %s\n", option);
+ LOGPF("Unknown backend option %s", option);
return 1;
}
@@ -106,7 +103,7 @@ static instance* artnet_instance(){
data = calloc(1, sizeof(artnet_instance_data));
if(!data){
- fprintf(stderr, "Failed to allocate memory\n");
+ LOG("Failed to allocate memory");
return NULL;
}
@@ -132,31 +129,27 @@ static int artnet_configure_instance(instance* inst, char* option, char* value){
data->fd_index = strtoul(value, NULL, 0);
if(data->fd_index >= artnet_fds){
- fprintf(stderr, "Invalid interface configured for ArtNet instance %s\n", inst->name);
+ LOGPF("Invalid interface configured for instance %s", inst->name);
return 1;
}
return 0;
}
else if(!strcmp(option, "dest") || !strcmp(option, "destination")){
- mmbackend_parse_hostspec(value, &host, &port);
-
- if(!port){
- port = ARTNET_PORT;
- }
+ mmbackend_parse_hostspec(value, &host, &port, NULL);
if(!host){
- fprintf(stderr, "Not a valid ArtNet destination for instance %s\n", inst->name);
+ LOGPF("Not a valid destination for instance %s: %s", inst->name, value);
return 1;
}
- return mmbackend_parse_sockaddr(host, port, &data->dest_addr, &data->dest_len);
+ return mmbackend_parse_sockaddr(host, port ? port : ARTNET_PORT, &data->dest_addr, &data->dest_len);
}
- fprintf(stderr, "Unknown ArtNet option %s for instance %s\n", option, inst->name);
+ LOGPF("Unknown instance option %s for instance %s", option, inst->name);
return 1;
}
-static channel* artnet_channel(instance* inst, char* spec){
+static channel* artnet_channel(instance* inst, char* spec, uint8_t flags){
artnet_instance_data* data = (artnet_instance_data*) inst->impl;
char* spec_next = spec;
unsigned chan_a = strtoul(spec, &spec_next, 10);
@@ -164,7 +157,7 @@ static channel* artnet_channel(instance* inst, char* spec){
//primary channel sanity check
if(!chan_a || chan_a > 512){
- fprintf(stderr, "Invalid ArtNet channel specification %s\n", spec);
+ LOGPF("Invalid channel specification %s", spec);
return NULL;
}
chan_a--;
@@ -173,14 +166,14 @@ static channel* artnet_channel(instance* inst, char* spec){
if(*spec_next == '+'){
chan_b = strtoul(spec_next + 1, NULL, 10);
if(!chan_b || chan_b > 512){
- fprintf(stderr, "Invalid wide-channel spec %s\n", spec);
+ LOGPF("Invalid wide-channel specification %s", spec);
return NULL;
}
chan_b--;
//if mapped mode differs, bail
if(IS_ACTIVE(data->data.map[chan_b]) && data->data.map[chan_b] != (MAP_FINE | chan_a)){
- fprintf(stderr, "Fine channel already mapped for ArtNet spec %s\n", spec);
+ LOGPF("Fine channel already mapped for spec %s", spec);
return NULL;
}
@@ -191,7 +184,7 @@ static channel* artnet_channel(instance* inst, char* spec){
if(IS_ACTIVE(data->data.map[chan_a])){
if((*spec_next == '+' && data->data.map[chan_a] != (MAP_COARSE | chan_b))
|| (*spec_next != '+' && data->data.map[chan_a] != (MAP_SINGLE | chan_a))){
- fprintf(stderr, "Primary ArtNet channel already mapped at differing mode: %s\n", spec);
+ LOGPF("Primary channel already mapped at differing mode: %s", spec);
return NULL;
}
}
@@ -218,7 +211,7 @@ static int artnet_transmit(instance* inst){
memcpy(frame.data, data->data.out, 512);
if(sendto(artnet_fd[data->fd_index].fd, (uint8_t*) &frame, sizeof(frame), 0, (struct sockaddr*) &data->dest_addr, data->dest_len) < 0){
- fprintf(stderr, "Failed to output ArtNet frame for instance %s: %s\n", inst->name, strerror(errno));
+ LOGPF("Failed to output frame for instance %s: %s", inst->name, strerror(errno));
}
//update last frame timestamp
@@ -235,7 +228,7 @@ static int artnet_set(instance* inst, size_t num, channel** c, channel_value* v)
artnet_instance_data* data = (artnet_instance_data*) inst->impl;
if(!data->dest_len){
- fprintf(stderr, "ArtNet instance %s not enabled for output (%" PRIsize_t " channel events)\n", inst->name, num);
+ LOGPF("Instance %s not enabled for output (%" PRIsize_t " channel events)", inst->name, num);
return 0;
}
@@ -275,7 +268,7 @@ static inline int artnet_process_frame(instance* inst, artnet_pkt* frame){
artnet_instance_data* data = (artnet_instance_data*) inst->impl;
if(be16toh(frame->length) > 512){
- fprintf(stderr, "Invalid ArtNet frame channel count\n");
+ LOGPF("Invalid frame channel count: %d", be16toh(frame->length));
return 1;
}
@@ -300,7 +293,7 @@ static inline int artnet_process_frame(instance* inst, artnet_pkt* frame){
}
if(!chan){
- fprintf(stderr, "Active channel %" PRIsize_t " on %s not known to core\n", p, inst->name);
+ LOGPF("Active channel %" PRIsize_t " on %s not known to core", p, inst->name);
return 1;
}
@@ -319,7 +312,7 @@ static inline int artnet_process_frame(instance* inst, artnet_pkt* frame){
}
if(mm_channel_event(chan, val)){
- fprintf(stderr, "Failed to push ArtNet channel event to core\n");
+ LOG("Failed to push channel event to core");
return 1;
}
}
@@ -366,7 +359,7 @@ static int artnet_handle(size_t num, managed_fd* fds){
inst_id.fields.uni = frame->universe;
inst = mm_instance_find(BACKEND_NAME, inst_id.label);
if(inst && artnet_process_frame(inst, frame)){
- fprintf(stderr, "Failed to process ArtNet frame\n");
+ LOG("Failed to process frame");
}
}
}
@@ -377,11 +370,11 @@ static int artnet_handle(size_t num, managed_fd* fds){
#else
if(bytes_read < 0 && errno != EAGAIN){
#endif
- fprintf(stderr, "ArtNet failed to receive data: %s\n", strerror(errno));
+ LOGPF("Failed to receive data: %s", strerror(errno));
}
if(bytes_read == 0){
- fprintf(stderr, "ArtNet listener closed\n");
+ LOG("Listener closed");
return 1;
}
}
@@ -389,29 +382,16 @@ static int artnet_handle(size_t num, managed_fd* fds){
return 0;
}
-static int artnet_start(){
- size_t n, u, p;
+static int artnet_start(size_t n, instance** inst){
+ size_t u, p;
int rv = 1;
- instance** inst = NULL;
artnet_instance_data* data = NULL;
artnet_instance_id id = {
.label = 0
};
- //fetch all defined instances
- if(mm_backend_instances(BACKEND_NAME, &n, &inst)){
- fprintf(stderr, "Failed to fetch instance list\n");
- return 1;
- }
-
- if(!n){
- free(inst);
- return 0;
- }
-
if(!artnet_fds){
- free(inst);
- fprintf(stderr, "Failed to start ArtNet backend: no descriptors bound\n");
+ LOG("Failed to start backend: no descriptors bound");
return 1;
}
@@ -426,7 +406,7 @@ static int artnet_start(){
//check for duplicates
for(p = 0; p < u; p++){
if(inst[u]->ident == inst[p]->ident){
- fprintf(stderr, "Universe specified multiple times, use one instance: %s - %s\n", inst[u]->name, inst[p]->name);
+ LOGPF("Universe specified multiple times, use one instance: %s - %s", inst[u]->name, inst[p]->name);
goto bail;
}
}
@@ -437,7 +417,7 @@ static int artnet_start(){
artnet_fd[data->fd_index].last_frame = realloc(artnet_fd[data->fd_index].last_frame, (artnet_fd[data->fd_index].output_instances + 1) * sizeof(uint64_t));
if(!artnet_fd[data->fd_index].output_instance || !artnet_fd[data->fd_index].last_frame){
- fprintf(stderr, "Failed to allocate memory\n");
+ LOG("Failed to allocate memory");
goto bail;
}
artnet_fd[data->fd_index].output_instance[artnet_fd[data->fd_index].output_instances] = id;
@@ -447,7 +427,7 @@ static int artnet_start(){
}
}
- fprintf(stderr, "ArtNet backend registering %" PRIsize_t " descriptors to core\n", artnet_fds);
+ LOGPF("Registering %" PRIsize_t " descriptors to core", artnet_fds);
for(u = 0; u < artnet_fds; u++){
if(mm_manage_fd(artnet_fd[u].fd, BACKEND_NAME, 1, (void*) u)){
goto bail;
@@ -456,22 +436,15 @@ static int artnet_start(){
rv = 0;
bail:
- free(inst);
return rv;
}
-static int artnet_shutdown(){
- size_t n, p;
- instance** inst = NULL;
- if(mm_backend_instances(BACKEND_NAME, &n, &inst)){
- fprintf(stderr, "Failed to fetch instance list\n");
- return 1;
- }
+static int artnet_shutdown(size_t n, instance** inst){
+ size_t p;
for(p = 0; p < n; p++){
free(inst[p]->impl);
}
- free(inst);
for(p = 0; p < artnet_fds; p++){
close(artnet_fd[p].fd);
@@ -480,6 +453,6 @@ static int artnet_shutdown(){
}
free(artnet_fd);
- fprintf(stderr, "ArtNet backend shut down\n");
+ LOG("Backend shut down");
return 0;
}
diff --git a/backends/artnet.h b/backends/artnet.h
index cce11d1..59bd53f 100644
--- a/backends/artnet.h
+++ b/backends/artnet.h
@@ -7,11 +7,11 @@ MM_PLUGIN_API int init();
static int artnet_configure(char* option, char* value);
static int artnet_configure_instance(instance* instance, char* option, char* value);
static instance* artnet_instance();
-static channel* artnet_channel(instance* instance, char* spec);
+static channel* artnet_channel(instance* instance, char* spec, uint8_t flags);
static int artnet_set(instance* inst, size_t num, channel** c, channel_value* v);
static int artnet_handle(size_t num, managed_fd* fds);
-static int artnet_start();
-static int artnet_shutdown();
+static int artnet_start(size_t n, instance** inst);
+static int artnet_shutdown(size_t n, instance** inst);
#define ARTNET_PORT "6454"
#define ARTNET_VERSION 14
diff --git a/backends/evdev.c b/backends/evdev.c
index dd2231b..4725ef7 100644
--- a/backends/evdev.c
+++ b/backends/evdev.c
@@ -1,3 +1,5 @@
+#define BACKEND_NAME "evdev"
+
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
@@ -16,8 +18,6 @@
#include "midimonster.h"
#include "evdev.h"
-#define BACKEND_NAME "evdev"
-
static struct {
uint8_t detect;
} evdev_config = {
@@ -38,12 +38,12 @@ MM_PLUGIN_API int init(){
};
if(sizeof(evdev_channel_ident) != sizeof(uint64_t)){
- fprintf(stderr, "evdev channel identification union out of bounds\n");
+ LOG("Channel identification union out of bounds");
return 1;
}
if(mm_backend_register(evdev)){
- fprintf(stderr, "Failed to register evdev backend\n");
+ LOG("Failed to register backend");
return 1;
}
@@ -59,7 +59,7 @@ static int evdev_configure(char* option, char* value) {
return 0;
}
- fprintf(stderr, "Unknown configuration option %s for evdev backend\n", option);
+ LOGPF("Unknown backend configuration option %s", option);
return 1;
}
@@ -71,7 +71,7 @@ static instance* evdev_instance(){
evdev_instance_data* data = calloc(1, sizeof(evdev_instance_data));
if(!data){
- fprintf(stderr, "Failed to allocate memory\n");
+ LOG("Failed to allocate memory");
return NULL;
}
@@ -79,7 +79,7 @@ static instance* evdev_instance(){
#ifndef EVDEV_NO_UINPUT
data->output_proto = libevdev_new();
if(!data->output_proto){
- fprintf(stderr, "Failed to initialize libevdev output prototype device\n");
+ LOG("Failed to initialize libevdev output prototype device");
free(data);
return NULL;
}
@@ -91,25 +91,25 @@ static instance* evdev_instance(){
static int evdev_attach(instance* inst, evdev_instance_data* data, char* node){
if(data->input_fd >= 0){
- fprintf(stderr, "Instance %s already was assigned an input device\n", inst->name);
+ LOGPF("Instance %s already assigned an input device", inst->name);
return 1;
}
data->input_fd = open(node, O_RDONLY | O_NONBLOCK);
if(data->input_fd < 0){
- fprintf(stderr, "Failed to open evdev input device node %s: %s\n", node, strerror(errno));
+ LOGPF("Failed to open input device node %s: %s", node, strerror(errno));
return 1;
}
if(libevdev_new_from_fd(data->input_fd, &data->input_ev)){
- fprintf(stderr, "Failed to initialize libevdev for %s\n", node);
+ LOGPF("Failed to initialize libevdev for %s", node);
close(data->input_fd);
data->input_fd = -1;
return 1;
}
if(data->exclusive && libevdev_grab(data->input_ev, LIBEVDEV_GRAB)){
- fprintf(stderr, "Failed to obtain exclusive device access on %s\n", node);
+ LOGPF("Failed to obtain exclusive device access on %s", node);
}
return 0;
@@ -123,7 +123,7 @@ static char* evdev_find(char* name){
char device_name[UINPUT_MAX_NAME_SIZE], *result = NULL;
if(!nodes){
- fprintf(stderr, "Failed to query input device nodes in %s: %s", INPUT_NODES, strerror(errno));
+ LOGPF("Failed to query input device nodes in %s: %s", INPUT_NODES, strerror(errno));
return NULL;
}
@@ -133,12 +133,12 @@ static char* evdev_find(char* name){
fd = open(file_path, O_RDONLY);
if(fd < 0){
- fprintf(stderr, "Failed to access %s: %s\n", file_path, strerror(errno));
+ LOGPF("Failed to access %s: %s", file_path, strerror(errno));
continue;
}
if(ioctl(fd, EVIOCGNAME(sizeof(device_name)), device_name) < 0){
- fprintf(stderr, "Failed to read name for %s: %s\n", file_path, strerror(errno));
+ LOGPF("Failed to read name for %s: %s", file_path, strerror(errno));
close(fd);
continue;
}
@@ -146,7 +146,7 @@ static char* evdev_find(char* name){
close(fd);
if(!strncmp(device_name, name, strlen(name))){
- fprintf(stderr, "Matched name %s for %s: %s\n", device_name, name, file_path);
+ LOGPF("Matched name %s for %s: %s", device_name, name, file_path);
break;
}
}
@@ -178,7 +178,7 @@ static int evdev_configure_instance(instance* inst, char* option, char* value) {
else if(!strcmp(option, "input")){
next_token = evdev_find(value);
if(!next_token){
- fprintf(stderr, "Failed to find evdev input device with name %s for instance %s\n", value, inst->name);
+ LOGPF("Failed to match input device with name %s for instance %s", value, inst->name);
return 1;
}
if(evdev_attach(inst, data, next_token)){
@@ -190,7 +190,7 @@ static int evdev_configure_instance(instance* inst, char* option, char* value) {
}
else if(!strcmp(option, "exclusive")){
if(data->input_fd >= 0 && libevdev_grab(data->input_ev, LIBEVDEV_GRAB)){
- fprintf(stderr, "Failed to obtain exclusive device access on %s\n", inst->name);
+ LOGPF("Failed to obtain exclusive device access on %s", inst->name);
}
data->exclusive = 1;
return 0;
@@ -198,7 +198,7 @@ static int evdev_configure_instance(instance* inst, char* option, char* value) {
else if(!strncmp(option, "relaxis.", 8)){
data->relative_axis = realloc(data->relative_axis, (data->relative_axes + 1) * sizeof(evdev_relaxis_config));
if(!data->relative_axis){
- fprintf(stderr, "Failed to allocate memory\n");
+ LOG("Failed to allocate memory");
return 1;
}
data->relative_axis[data->relative_axes].inverted = 0;
@@ -208,9 +208,12 @@ static int evdev_configure_instance(instance* inst, char* option, char* value) {
data->relative_axis[data->relative_axes].max *= -1;
data->relative_axis[data->relative_axes].inverted = 1;
}
+ else if(data->relative_axis[data->relative_axes].max == 0){
+ LOGPF("Relative axis configuration for %s.%s has invalid range", inst->name, option + 8);
+ }
data->relative_axis[data->relative_axes].current = strtoul(next_token, NULL, 0);
if(data->relative_axis[data->relative_axes].code < 0){
- fprintf(stderr, "Failed to configure relative axis extents for %s.%s\n", inst->name, option + 8);
+ LOGPF("Failed to configure relative axis extents for %s.%s", inst->name, option + 8);
return 1;
}
data->relative_axes++;
@@ -239,17 +242,17 @@ static int evdev_configure_instance(instance* inst, char* option, char* value) {
abs_info.flat = strtol(next_token, &next_token, 0);
abs_info.resolution = strtol(next_token, &next_token, 0);
if(libevdev_enable_event_code(data->output_proto, EV_ABS, libevdev_event_code_from_name(EV_ABS, option + 5), &abs_info)){
- fprintf(stderr, "Failed to enable absolute axis %s for output\n", option + 5);
+ LOGPF("Failed to enable absolute axis %s.%s for output", inst->name, option + 5);
return 1;
}
return 0;
}
#endif
- fprintf(stderr, "Unknown instance configuration parameter %s for evdev instance %s\n", option, inst->name);
+ LOGPF("Unknown instance configuration parameter %s for instance %s", option, inst->name);
return 1;
}
-static channel* evdev_channel(instance* inst, char* spec){
+static channel* evdev_channel(instance* inst, char* spec, uint8_t flags){
#ifndef EVDEV_NO_UINPUT
evdev_instance_data* data = (evdev_instance_data*) inst->impl;
#endif
@@ -259,14 +262,14 @@ static channel* evdev_channel(instance* inst, char* spec){
};
if(!separator){
- fprintf(stderr, "Invalid evdev channel specification %s\n", spec);
+ LOGPF("Invalid channel specification %s", spec);
return NULL;
}
*(separator++) = 0;
if(libevdev_event_type_from_name(spec) < 0){
- fprintf(stderr, "Invalid evdev type specification: %s", spec);
+ LOGPF("Invalid type specification: %s", spec);
return NULL;
}
ident.fields.type = libevdev_event_type_from_name(spec);
@@ -275,7 +278,7 @@ static channel* evdev_channel(instance* inst, char* spec){
ident.fields.code = libevdev_event_code_from_name(ident.fields.type, separator);
}
else{
- fprintf(stderr, "evdev Code name not recognized, using as number: %s\n", separator);
+ LOGPF("Code name not recognized, using as number: %s.%s", inst->name, separator);
ident.fields.code = strtoul(separator, NULL, 10);
}
@@ -285,7 +288,7 @@ static channel* evdev_channel(instance* inst, char* spec){
//enable the event on the device
//the previous check is necessary to not fail while enabling axes, which require additional information
if(libevdev_enable_event_code(data->output_proto, ident.fields.type, ident.fields.code, NULL)){
- fprintf(stderr, "Failed to enable output event %s.%s%s\n",
+ LOGPF("Failed to enable output event %s.%s%s",
libevdev_event_type_get_name(ident.fields.type),
libevdev_event_code_get_name(ident.fields.type, ident.fields.code),
(ident.fields.type == EV_ABS) ? ": To output absolute axes, specify their details in the configuration":"");
@@ -339,13 +342,13 @@ static int evdev_push_event(instance* inst, evdev_instance_data* data, struct in
}
if(mm_channel_event(chan, val)){
- fprintf(stderr, "Failed to push evdev channel event to core\n");
+ LOG("Failed to push channel event to core");
return 1;
}
}
if(evdev_config.detect){
- fprintf(stderr, "Incoming evdev data for channel %s.%s.%s\n", inst->name, libevdev_event_type_get_name(event.type), libevdev_event_code_get_name(event.type, event.code));
+ LOGPF("Incoming data for channel %s.%s.%s", inst->name, libevdev_event_type_get_name(event.type), libevdev_event_code_get_name(event.type, event.code));
}
return 0;
@@ -366,7 +369,7 @@ static int evdev_handle(size_t num, managed_fd* fds){
for(fd = 0; fd < num; fd++){
inst = (instance*) fds[fd].impl;
if(!inst){
- fprintf(stderr, "evdev backend signaled for unknown fd\n");
+ LOG("Signaled for unknown FD");
continue;
}
@@ -393,53 +396,39 @@ static int evdev_handle(size_t num, managed_fd* fds){
return 0;
}
-static int evdev_start(){
- size_t n, u, fds = 0;
- instance** inst = NULL;
+static int evdev_start(size_t n, instance** inst){
+ size_t u, fds = 0;
evdev_instance_data* data = NULL;
- if(mm_backend_instances(BACKEND_NAME, &n, &inst)){
- fprintf(stderr, "Failed to fetch instance list\n");
- return 1;
- }
-
- if(!n){
- free(inst);
- return 0;
- }
-
for(u = 0; u < n; u++){
data = (evdev_instance_data*) inst[u]->impl;
#ifndef EVDEV_NO_UINPUT
if(data->output_enabled){
if(libevdev_uinput_create_from_device(data->output_proto, LIBEVDEV_UINPUT_OPEN_MANAGED, &data->output_ev)){
- fprintf(stderr, "Failed to create evdev output device: %s\n", strerror(errno));
- free(inst);
+ LOGPF("Failed to create output device: %s", strerror(errno));
return 1;
}
- fprintf(stderr, "Created device node %s for instance %s\n", libevdev_uinput_get_devnode(data->output_ev), inst[u]->name);
+ LOGPF("Created device node %s for instance %s", libevdev_uinput_get_devnode(data->output_ev), inst[u]->name);
}
#endif
inst[u]->ident = data->input_fd;
if(data->input_fd >= 0){
if(mm_manage_fd(data->input_fd, BACKEND_NAME, 1, inst[u])){
- fprintf(stderr, "Failed to register event input descriptor for instance %s\n", inst[u]->name);
- free(inst);
+ LOGPF("Failed to register input descriptor for instance %s", inst[u]->name);
return 1;
}
fds++;
}
if(data->input_fd <= 0 && !data->output_ev){
- fprintf(stderr, "Instance %s has neither input nor output device set up\n", inst[u]->name);
+ LOGPF("Instance %s has neither input nor output device set up", inst[u]->name);
}
}
- fprintf(stderr, "evdev backend registered %zu descriptors to core\n", fds);
- free(inst);
+ LOGPF("Registered %zu descriptors to core", fds);
return 0;
}
@@ -458,7 +447,7 @@ static int evdev_set(instance* inst, size_t num, channel** c, channel_value* v)
}
if(!data->output_enabled){
- fprintf(stderr, "Instance %s not enabled for output\n", inst->name);
+ LOGPF("Instance %s not enabled for output (%" PRIsize_t " channel events)", inst->name, num);
return 0;
}
@@ -494,36 +483,30 @@ static int evdev_set(instance* inst, size_t num, channel** c, channel_value* v)
}
if(libevdev_uinput_write_event(data->output_ev, ident.fields.type, ident.fields.code, value)){
- fprintf(stderr, "Failed to output event on instance %s\n", inst->name);
+ LOGPF("Failed to output event on instance %s", inst->name);
return 1;
}
}
//send syn event to publish all events
if(libevdev_uinput_write_event(data->output_ev, EV_SYN, SYN_REPORT, 0)){
- fprintf(stderr, "Failed to output sync event on instance %s\n", inst->name);
+ LOGPF("Failed to output sync event on instance %s", inst->name);
return 1;
}
return 0;
#else
- fprintf(stderr, "The evdev backend does not support output on this platform\n");
+ LOG("No output support on this platform");
return 1;
#endif
}
-static int evdev_shutdown(){
+static int evdev_shutdown(size_t n, instance** inst){
evdev_instance_data* data = NULL;
- instance** instances = NULL;
- size_t n, u;
-
- if(mm_backend_instances(BACKEND_NAME, &n, &instances)){
- fprintf(stderr, "Failed to fetch instance list\n");
- return 1;
- }
+ size_t u;
for(u = 0; u < n; u++){
- data = (evdev_instance_data*) instances[u]->impl;
+ data = (evdev_instance_data*) inst[u]->impl;
if(data->input_fd >= 0){
libevdev_free(data->input_ev);
@@ -539,10 +522,9 @@ static int evdev_shutdown(){
#endif
data->relative_axes = 0;
free(data->relative_axis);
- free(data);
+ free(inst[u]->impl);
}
- free(instances);
- fprintf(stderr, "evdev backend shut down\n");
+ LOG("Backend shut down");
return 0;
}
diff --git a/backends/evdev.h b/backends/evdev.h
index 30ce892..0c877fc 100644
--- a/backends/evdev.h
+++ b/backends/evdev.h
@@ -12,11 +12,11 @@ MM_PLUGIN_API int init();
static int evdev_configure(char* option, char* value);
static int evdev_configure_instance(instance* instance, char* option, char* value);
static instance* evdev_instance();
-static channel* evdev_channel(instance* instance, char* spec);
+static channel* evdev_channel(instance* instance, char* spec, uint8_t flags);
static int evdev_set(instance* inst, size_t num, channel** c, channel_value* v);
static int evdev_handle(size_t num, managed_fd* fds);
-static int evdev_start();
-static int evdev_shutdown();
+static int evdev_start(size_t n, instance** inst);
+static int evdev_shutdown(size_t n, instance** inst);
#define INPUT_NODES "/dev/input"
#define INPUT_PREFIX "event"
diff --git a/backends/jack.c b/backends/jack.c
index 192aab2..d7f68c4 100644
--- a/backends/jack.c
+++ b/backends/jack.c
@@ -1,3 +1,5 @@
+#define BACKEND_NAME "jack"
+
#include <string.h>
#include <signal.h>
#include <sys/types.h>
@@ -8,7 +10,6 @@
#include <jack/midiport.h>
#include <jack/metadata.h>
-#define BACKEND_NAME "jack"
#define JACKEY_SIGNAL_TYPE "http://jackaudio.org/metadata/signal-type"
#ifdef __APPLE__
@@ -41,20 +42,20 @@ MM_PLUGIN_API int init(){
};
if(sizeof(mmjack_channel_ident) != sizeof(uint64_t)){
- fprintf(stderr, "jack channel identification union out of bounds\n");
+ LOG("Channel identification union out of bounds");
return 1;
}
//register backend
if(mm_backend_register(mmjack)){
- fprintf(stderr, "Failed to register jack backend\n");
+ LOG("Failed to register backend");
return 1;
}
return 0;
}
static void mmjack_message_print(const char* msg){
- fprintf(stderr, "JACK message: %s\n", msg);
+ LOGPF("JACK message: %s", msg);
}
static void mmjack_message_ignore(const char* msg){
@@ -66,7 +67,7 @@ static int mmjack_midiqueue_append(mmjack_port* port, mmjack_channel_ident ident
//extend the queue
port->queue = realloc(port->queue, (port->queue_len + JACK_MIDIQUEUE_CHUNK) * sizeof(mmjack_midiqueue));
if(!port->queue){
- fprintf(stderr, "Failed to allocate memory\n");
+ LOG("Failed to allocate memory");
return 1;
}
port->queue_alloc += JACK_MIDIQUEUE_CHUNK;
@@ -75,7 +76,7 @@ static int mmjack_midiqueue_append(mmjack_port* port, mmjack_channel_ident ident
port->queue[port->queue_len].ident.label = ident.label;
port->queue[port->queue_len].raw = value;
port->queue_len++;
- DBGPF("Appended event to queue for %s, now at %" PRIsize_t " entries\n", port->name, port->queue_len);
+ DBGPF("Appended event to queue for %s, now at %" PRIsize_t " entries", port->name, port->queue_len);
return 0;
}
@@ -90,7 +91,7 @@ static int mmjack_process_midi(instance* inst, mmjack_port* port, size_t nframes
if(port->input){
if(event_count){
- DBGPF("Reading %u MIDI events from jack port %s\n", event_count, port->name);
+ DBGPF("Reading %u MIDI events from port %s", event_count, port->name);
for(u = 0; u < event_count; u++){
ident.label = 0;
//read midi data from stream
@@ -98,20 +99,20 @@ static int mmjack_process_midi(instance* inst, mmjack_port* port, size_t nframes
//ident.fields.port set on output in mmjack_handle_midi
ident.fields.sub_channel = event.buffer[0] & 0x0F;
ident.fields.sub_type = event.buffer[0] & 0xF0;
+ ident.fields.sub_control = event.buffer[1];
+ value = event.buffer[2];
if(ident.fields.sub_type == 0x80){
ident.fields.sub_type = midi_note;
value = 0;
}
else if(ident.fields.sub_type == midi_pitchbend){
+ ident.fields.sub_control = 0;
value = event.buffer[1] | (event.buffer[2] << 7);
}
else if(ident.fields.sub_type == midi_aftertouch){
+ ident.fields.sub_control = 0;
value = event.buffer[1];
}
- else{
- ident.fields.sub_control = event.buffer[1];
- value = event.buffer[2];
- }
//append midi data
mmjack_midiqueue_append(port, ident, value);
}
@@ -128,7 +129,7 @@ static int mmjack_process_midi(instance* inst, mmjack_port* port, size_t nframes
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){
- fprintf(stderr, "Failed to reserve MIDI stream data\n");
+ LOG("Failed to reserve MIDI stream data");
return 1;
}
event_data[0] = ident.fields.sub_channel | ident.fields.sub_type;
@@ -146,7 +147,7 @@ static int mmjack_process_midi(instance* inst, mmjack_port* port, size_t nframes
}
if(port->queue_len){
- DBGPF("Wrote %" PRIsize_t " MIDI events to jack port %s\n", port->queue_len, port->name);
+ DBGPF("Wrote %" PRIsize_t " MIDI events to port %s", port->queue_len, port->name);
}
port->queue_len = 0;
}
@@ -180,21 +181,21 @@ static int mmjack_process(jack_nframes_t nframes, void* instp){
size_t p, mark = 0;
int rv = 0;
- //DBGPF("jack callback for %d frames on %s\n", nframes, inst->name);
+ //DBGPF("jack callback for %d frames on %s", nframes, inst->name);
for(p = 0; p < data->ports; p++){
pthread_mutex_lock(&data->port[p].lock);
switch(data->port[p].type){
case port_midi:
- //DBGPF("Handling MIDI port %s.%s\n", inst->name, data->port[p].name);
+ //DBGPF("Handling MIDI port %s.%s", inst->name, data->port[p].name);
rv |= mmjack_process_midi(inst, data->port + p, nframes, &mark);
break;
case port_cv:
- //DBGPF("Handling CV port %s.%s\n", inst->name, data->port[p].name);
+ //DBGPF("Handling CV port %s.%s", inst->name, data->port[p].name);
rv |= mmjack_process_cv(inst, data->port + p, nframes, &mark);
break;
default:
- fprintf(stderr, "Unhandled jack port type in processing callback\n");
+ LOG("Unhandled port type in processing callback");
pthread_mutex_unlock(&data->port[p].lock);
return 1;
}
@@ -203,14 +204,14 @@ static int mmjack_process(jack_nframes_t nframes, void* instp){
//notify the main thread
if(mark){
- DBGPF("Notifying handler thread for jack instance %s\n", inst->name);
+ DBGPF("Notifying handler thread for instance %s", inst->name);
send(data->fd, "c", 1, 0);
}
return rv;
}
static void mmjack_server_shutdown(void* inst){
- fprintf(stderr, "jack server shutdown notification\n");
+ LOG("Server shut down");
config.jack_shutdown = 1;
}
@@ -232,7 +233,7 @@ static int mmjack_configure(char* option, char* value){
return 0;
}
- fprintf(stderr, "Unknown jack backend option %s\n", option);
+ LOGPF("Unknown backend option %s", option);
return 1;
}
@@ -258,7 +259,7 @@ static int mmjack_parse_portconfig(mmjack_port* port, char* spec){
else if(!strcmp(token, "max")){
token = strtok(NULL, " ");
if(!token){
- fprintf(stderr, "jack port %s configuration missing argument\n", port->name);
+ LOGPF("Port %s configuration missing argument", port->name);
return 1;
}
port->max = strtod(token, NULL);
@@ -266,19 +267,19 @@ static int mmjack_parse_portconfig(mmjack_port* port, char* spec){
else if(!strcmp(token, "min")){
token = strtok(NULL, " ");
if(!token){
- fprintf(stderr, "jack port %s configuration missing argument\n", port->name);
+ LOGPF("Port %s configuration missing argument", port->name);
return 1;
}
port->min = strtod(token, NULL);
}
else{
- fprintf(stderr, "Unknown jack channel configuration token %s on port %s\n", token, port->name);
+ LOGPF("Unknown channel configuration token %s on port %s", token, port->name);
return 1;
}
}
if(port->type == port_none){
- fprintf(stderr, "jack channel %s assigned no port type\n", port->name);
+ LOGPF("Channel %s assigned no port type", port->name);
return 1;
}
return 0;
@@ -306,24 +307,24 @@ static int mmjack_configure_instance(instance* inst, char* option, char* value){
//register new port, first check for unique name
for(p = 0; p < data->ports; p++){
if(!strcmp(data->port[p].name, option)){
- fprintf(stderr, "jack instance %s has duplicate port %s\n", inst->name, option);
+ LOGPF("Instance %s has duplicate port %s", inst->name, option);
return 1;
}
}
if(strchr(option, '.')){
- fprintf(stderr, "Invalid jack channel spec %s.%s\n", inst->name, option);
+ LOGPF("Invalid channel spec %s.%s", inst->name, option);
}
//add port to registry
//TODO for OSC ports we need to configure subchannels for each message
data->port = realloc(data->port, (data->ports + 1) * sizeof(mmjack_port));
if(!data->port){
- fprintf(stderr, "Failed to allocate memory\n");
+ LOG("Failed to allocate memory");
return 1;
}
data->port[data->ports].name = strdup(option);
if(!data->port[data->ports].name){
- fprintf(stderr, "Failed to allocate memory\n");
+ LOG("Failed to allocate memory");
return 1;
}
if(mmjack_parse_portconfig(data->port + p, value)){
@@ -341,7 +342,7 @@ static instance* mmjack_instance(){
inst->impl = calloc(1, sizeof(mmjack_instance_data));
if(!inst->impl){
- fprintf(stderr, "Failed to allocate memory\n");
+ LOG("Failed to allocate memory");
return NULL;
}
@@ -359,18 +360,18 @@ static int mmjack_parse_midispec(mmjack_channel_ident* ident, char* spec){
}
if(!next_token){
- fprintf(stderr, "Invalid jack MIDI spec %s\n", spec);
+ LOGPF("Invalid MIDI spec %s", spec);
return 1;
}
ident->fields.sub_channel = strtoul(next_token, &next_token, 10);
if(ident->fields.sub_channel > 15){
- fprintf(stderr, "Invalid jack MIDI spec %s, channel out of range\n", spec);
+ LOGPF("Invalid MIDI spec %s, channel out of range", spec);
return 1;
}
if(*next_token != '.'){
- fprintf(stderr, "Invalid jack MIDI spec %s\n", spec);
+ LOGPF("Invalid MIDI spec %s", spec);
return 1;
}
@@ -395,7 +396,7 @@ static int mmjack_parse_midispec(mmjack_channel_ident* ident, char* spec){
ident->fields.sub_type = midi_aftertouch;
}
else{
- fprintf(stderr, "Unknown jack MIDI control type in spec %s\n", spec);
+ LOGPF("Unknown MIDI control type in spec %s", spec);
return 1;
}
@@ -403,13 +404,13 @@ static int mmjack_parse_midispec(mmjack_channel_ident* ident, char* spec){
if(ident->fields.sub_type == midi_none
|| ident->fields.sub_control > 127){
- fprintf(stderr, "Invalid jack MIDI spec %s\n", spec);
+ LOGPF("Invalid MIDI spec %s", spec);
return 1;
}
return 0;
}
-static channel* mmjack_channel(instance* inst, char* spec){
+static channel* mmjack_channel(instance* inst, char* spec, uint8_t flags){
mmjack_instance_data* data = (mmjack_instance_data*) inst->impl;
mmjack_channel_ident ident = {
.label = 0
@@ -425,7 +426,7 @@ static channel* mmjack_channel(instance* inst, char* spec){
}
if(u == data->ports){
- fprintf(stderr, "jack port %s.%s not found\n", inst->name, spec);
+ LOGPF("Tried to map unknown port %s.%s", inst->name, spec);
return NULL;
}
@@ -456,7 +457,7 @@ static int mmjack_set(instance* inst, size_t num, channel** c, channel_value* v)
ident.label = c[u]->ident;
if(data->port[ident.fields.port].input){
- fprintf(stderr, "jack port %s.%s is an input port, no output is possible\n", inst->name, data->port[ident.fields.port].name);
+ LOGPF("Port %s.%s is an input port, no output is possible", inst->name, data->port[ident.fields.port].name);
continue;
}
range = data->port[ident.fields.port].max - data->port[ident.fields.port].min;
@@ -466,7 +467,7 @@ static int mmjack_set(instance* inst, size_t num, channel** c, channel_value* v)
case port_cv:
//scale value to given range
data->port[ident.fields.port].last = (range * v[u].normalised) + data->port[ident.fields.port].min;
- DBGPF("CV port %s updated to %f\n", data->port[ident.fields.port].name, data->port[ident.fields.port].last);
+ DBGPF("CV port %s updated to %f", data->port[ident.fields.port].name, data->port[ident.fields.port].last);
break;
case port_midi:
value = v[u].normalised * 127.0;
@@ -479,7 +480,7 @@ static int mmjack_set(instance* inst, size_t num, channel** c, channel_value* v)
}
break;
default:
- fprintf(stderr, "No handler implemented for jack port type %s.%s\n", inst->name, data->port[ident.fields.port].name);
+ LOGPF("No handler implemented for port type %s.%s", inst->name, data->port[ident.fields.port].name);
break;
}
pthread_mutex_unlock(&data->port[ident.fields.port].lock);
@@ -503,7 +504,7 @@ static void mmjack_handle_midi(instance* inst, size_t index, mmjack_port* port){
else{
val.normalised = ((double)port->queue[u].raw) / 127.0;
}
- DBGPF("Pushing MIDI channel %d type %02X control %d value %f raw %d label %" PRIu64 "\n",
+ DBGPF("Pushing MIDI channel %d type %02X control %d value %f raw %d label %" PRIu64,
port->queue[u].ident.fields.sub_channel,
port->queue[u].ident.fields.sub_type,
port->queue[u].ident.fields.sub_control,
@@ -511,13 +512,13 @@ static void mmjack_handle_midi(instance* inst, size_t index, mmjack_port* port){
port->queue[u].raw,
port->queue[u].ident.label);
if(mm_channel_event(chan, val)){
- fprintf(stderr, "Failed to push MIDI event to core on jack port %s.%s\n", inst->name, port->name);
+ LOGPF("Failed to push MIDI event to core on port %s.%s", inst->name, port->name);
}
}
}
if(port->queue_len){
- DBGPF("Pushed %" PRIsize_t " MIDI events to core for jack port %s.%s\n", port->queue_len, inst->name, port->name);
+ DBGPF("Pushed %" PRIsize_t " MIDI events to core for port %s.%s", port->queue_len, inst->name, port->name);
}
port->queue_len = 0;
}
@@ -532,7 +533,7 @@ static void mmjack_handle_cv(instance* inst, size_t index, mmjack_port* port){
channel* chan = mm_channel(inst, ident.label, 0);
if(!chan){
//this might happen if a channel is registered but not mapped
- DBGPF("Failed to match jack CV channel %s.%s to core channel\n", inst->name, port->name);
+ DBGPF("Failed to match CV channel %s.%s to core channel", inst->name, port->name);
return;
}
@@ -541,9 +542,9 @@ static void mmjack_handle_cv(instance* inst, size_t index, mmjack_port* port){
val.normalised = port->last - port->min;
val.normalised /= range;
val.normalised = clamp(val.normalised, 1.0, 0.0);
- DBGPF("Pushing CV channel %s value %f raw %f min %f max %f\n", port->name, val.normalised, port->last, port->min, port->max);
+ DBGPF("Pushing CV channel %s value %f raw %f min %f max %f", port->name, val.normalised, port->last, port->min, port->max);
if(mm_channel_event(chan, val)){
- fprintf(stderr, "Failed to push CV event to core for %s.%s\n", inst->name, port->name);
+ LOGPF("Failed to push CV event to core for %s.%s", inst->name, port->name);
}
}
@@ -560,7 +561,7 @@ static int mmjack_handle(size_t num, managed_fd* fds){
data = (mmjack_instance_data*) inst->impl;
bytes = recv(fds[u].fd, recv_buf, sizeof(recv_buf), 0);
if(bytes < 0){
- fprintf(stderr, "Failed to receive on feedback socket for instance %s\n", inst->name);
+ LOGPF("Failed to receive on feedback socket for instance %s", inst->name);
return 1;
}
@@ -575,7 +576,7 @@ static int mmjack_handle(size_t num, managed_fd* fds){
mmjack_handle_midi(inst, p, data->port + p);
break;
default:
- fprintf(stderr, "Output handler not implemented for unknown jack channel type on %s.%s\n", inst->name, data->port[p].name);
+ LOGPF("Output handler not implemented for unknown channel type on %s.%s", inst->name, data->port[p].name);
break;
}
@@ -587,16 +588,15 @@ static int mmjack_handle(size_t num, managed_fd* fds){
}
if(config.jack_shutdown){
- fprintf(stderr, "JACK server disconnected\n");
+ LOG("Server disconnected");
return 1;
}
return 0;
}
-static int mmjack_start(){
+static int mmjack_start(size_t n, instance** inst){
int rv = 1, feedback_fd[2];
- size_t n, u, p;
- instance** inst = NULL;
+ size_t u, p;
pthread_mutexattr_t mutex_attr;
mmjack_instance_data* data = NULL;
jack_status_t error;
@@ -614,13 +614,7 @@ static int mmjack_start(){
//prepare mutex attributes because the initializer macro for adaptive mutexes is a GNU extension...
if(pthread_mutexattr_init(&mutex_attr)
|| pthread_mutexattr_settype(&mutex_attr, PTHREAD_MUTEX_ADAPTIVE_NP)){
- fprintf(stderr, "Failed to initialize mutex attributes\n");
- goto bail;
- }
-
- //fetch all instances
- if(mm_backend_instances(BACKEND_NAME, &n, &inst)){
- fprintf(stderr, "Failed to fetch instance list\n");
+ LOG("Failed to initialize mutex attributes");
goto bail;
}
@@ -635,19 +629,19 @@ static int mmjack_start(){
if(!data->client){
//TODO pretty-print failures
- fprintf(stderr, "jack backend failed to connect to server, return status %u\n", error);
+ LOGPF("Failed to connect to server, return status %u", error);
goto bail;
}
//set up the feedback fd
if(socketpair(AF_LOCAL, SOCK_DGRAM, 0, feedback_fd)){
- fprintf(stderr, "Failed to create feedback socket pair\n");
+ LOG("Failed to create feedback socket pair");
goto bail;
}
data->fd = feedback_fd[0];
if(mm_manage_fd(feedback_fd[1], BACKEND_NAME, 1, inst[u])){
- fprintf(stderr, "jack backend failed to register feedback fd with core\n");
+ LOG("Failed to register feedback FD with core");
goto bail;
}
@@ -655,12 +649,12 @@ static int mmjack_start(){
jack_set_process_callback(data->client, mmjack_process, inst[u]);
jack_on_shutdown(data->client, mmjack_server_shutdown, inst[u]);
- fprintf(stderr, "jack instance %s assigned client name %s\n", inst[u]->name, jack_get_client_name(data->client));
+ LOGPF("Instance %s assigned client name %s", inst[u]->name, jack_get_client_name(data->client));
//create and initialize jack ports
for(p = 0; p < data->ports; p++){
if(pthread_mutex_init(&(data->port[p].lock), &mutex_attr)){
- fprintf(stderr, "Failed to create port mutex\n");
+ LOG("Failed to create port mutex");
goto bail;
}
@@ -673,36 +667,29 @@ static int mmjack_start(){
jack_set_property(data->client, jack_port_uuid(data->port[p].port), JACKEY_SIGNAL_TYPE, "CV", "text/plain");
if(!data->port[p].port){
- fprintf(stderr, "Failed to create jack port %s.%s\n", inst[u]->name, data->port[p].name);
+ LOGPF("Failed to create port %s.%s", inst[u]->name, data->port[p].name);
goto bail;
}
}
//do the thing
if(jack_activate(data->client)){
- fprintf(stderr, "Failed to activate jack client for instance %s\n", inst[u]->name);
+ LOGPF("Failed to activate client for instance %s", inst[u]->name);
goto bail;
}
}
- fprintf(stderr, "jack backend registered %" PRIsize_t " descriptors to core\n", n);
+ LOGPF("Registered %" PRIsize_t " descriptors to core", n);
rv = 0;
bail:
pthread_mutexattr_destroy(&mutex_attr);
- free(inst);
return rv;
}
-static int mmjack_shutdown(){
- size_t n, u, p;
- instance** inst = NULL;
+static int mmjack_shutdown(size_t n, instance** inst){
+ size_t u, p;
mmjack_instance_data* data = NULL;
- if(mm_backend_instances(BACKEND_NAME, &n, &inst)){
- fprintf(stderr, "Failed to fetch instance list\n");
- return 1;
- }
-
for(u = 0; u < n; u++){
data = (mmjack_instance_data*) inst[u]->impl;
@@ -739,10 +726,10 @@ static int mmjack_shutdown(){
data->client_name = NULL;
close(data->fd);
data->fd = -1;
- }
- free(inst);
+ free(inst[u]->impl);
+ }
- fprintf(stderr, "jack backend shut down\n");
+ LOG("Backend shut down");
return 0;
}
diff --git a/backends/jack.h b/backends/jack.h
index 5598042..66c66db 100644
--- a/backends/jack.h
+++ b/backends/jack.h
@@ -6,11 +6,11 @@ MM_PLUGIN_API int init();
static int mmjack_configure(char* option, char* value);
static int mmjack_configure_instance(instance* inst, char* option, char* value);
static instance* mmjack_instance();
-static channel* mmjack_channel(instance* inst, char* spec);
+static channel* mmjack_channel(instance* inst, char* spec, uint8_t flags);
static int mmjack_set(instance* inst, size_t num, channel** c, channel_value* v);
static int mmjack_handle(size_t num, managed_fd* fds);
-static int mmjack_start();
-static int mmjack_shutdown();
+static int mmjack_start(size_t n, instance** inst);
+static int mmjack_shutdown(size_t n, instance** inst);
#define JACK_DEFAULT_CLIENT_NAME "MIDIMonster"
#define JACK_DEFAULT_SERVER_NAME "default"
diff --git a/backends/libmmbackend.c b/backends/libmmbackend.c
index ccbeb52..ffa403b 100644
--- a/backends/libmmbackend.c
+++ b/backends/libmmbackend.c
@@ -1,6 +1,8 @@
#include "libmmbackend.h"
-void mmbackend_parse_hostspec(char* spec, char** host, char** port){
+#define LOGPF(format, ...) fprintf(stderr, "libmmbe\t" format "\n", __VA_ARGS__)
+
+void mmbackend_parse_hostspec(char* spec, char** host, char** port, char** options){
size_t u = 0;
if(!spec || !host || !port){
@@ -29,6 +31,19 @@ void mmbackend_parse_hostspec(char* spec, char** host, char** port){
spec[u] = 0;
*port = spec + u + 1;
}
+
+ if(options){
+ *options = NULL;
+ if(*port){
+ //scan for space after port
+ for(u = 0; (*port)[u] && !isspace((*port)[u]); u++){
+ }
+ if(isspace((*port)[u])){
+ (*port)[u] = 0;
+ *options = (*port) + u + 1;
+ }
+ }
+ }
}
int mmbackend_parse_sockaddr(char* host, char* port, struct sockaddr_storage* addr, socklen_t* len){
@@ -39,7 +54,7 @@ int mmbackend_parse_sockaddr(char* host, char* port, struct sockaddr_storage* ad
int error = getaddrinfo(host, port, &hints, &head);
if(error || !head){
- fprintf(stderr, "Failed to parse address %s port %s: %s\n", host, port, gai_strerror(error));
+ LOGPF("Failed to parse address %s port %s: %s", host, port, gai_strerror(error));
return 1;
}
@@ -63,7 +78,7 @@ int mmbackend_socket(char* host, char* port, int socktype, uint8_t listener, uin
status = getaddrinfo(host, port, &hints, &info);
if(status){
- fprintf(stderr, "Failed to parse address %s port %s: %s\n", host, port, gai_strerror(status));
+ LOGPF("Failed to parse address %s port %s: %s", host, port, gai_strerror(status));
return -1;
}
@@ -77,18 +92,18 @@ int mmbackend_socket(char* host, char* port, int socktype, uint8_t listener, uin
//set required socket options
yes = 1;
if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void*)&yes, sizeof(yes)) < 0){
- fprintf(stderr, "Failed to enable SO_REUSEADDR on socket\n");
+ LOGPF("Failed to enable SO_REUSEADDR on socket: %s", strerror(errno));
}
if(mcast){
yes = 1;
if(setsockopt(fd, SOL_SOCKET, SO_BROADCAST, (void*)&yes, sizeof(yes)) < 0){
- fprintf(stderr, "Failed to enable SO_BROADCAST on socket\n");
+ LOGPF("Failed to enable SO_BROADCAST on socket: %s", strerror(errno));
}
yes = 0;
if(setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, (void*)&yes, sizeof(yes)) < 0){
- fprintf(stderr, "Failed to disable IP_MULTICAST_LOOP on socket: %s\n", strerror(errno));
+ LOGPF("Failed to disable IP_MULTICAST_LOOP on socket: %s", strerror(errno));
}
}
@@ -112,7 +127,7 @@ int mmbackend_socket(char* host, char* port, int socktype, uint8_t listener, uin
freeaddrinfo(info);
if(!addr_it){
- fprintf(stderr, "Failed to create socket for %s port %s\n", host, port);
+ LOGPF("Failed to create socket for %s port %s", host, port);
return -1;
}
@@ -126,7 +141,7 @@ int mmbackend_socket(char* host, char* port, int socktype, uint8_t listener, uin
#else
int flags = fcntl(fd, F_GETFL, 0);
if(fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0){
- fprintf(stderr, "Failed to set socket nonblocking\n");
+ LOGPF("Failed to set socket nonblocking: %s", strerror(errno));
close(fd);
return -1;
}
@@ -140,7 +155,7 @@ int mmbackend_send(int fd, uint8_t* data, size_t length){
while(total < length){
sent = send(fd, data + total, length - total, 0);
if(sent < 0){
- fprintf(stderr, "Failed to send: %s\n", strerror(errno));
+ LOGPF("Failed to send: %s", strerror(errno));
return 1;
}
total += sent;
diff --git a/backends/libmmbackend.h b/backends/libmmbackend.h
index 5749119..aa0d0f0 100644
--- a/backends/libmmbackend.h
+++ b/backends/libmmbackend.h
@@ -22,13 +22,15 @@
/*
* Parse spec as host specification in the form
- * host port
+ * host port [options]
* into its constituent parts.
* Returns offsets into the original string and modifies it.
* Returns NULL in *port if none given.
* Returns NULL in both *port and *host if spec was an empty string.
+ * Returns a pointer after the port in *options if options is non-NULL
+ * and the port was not followed by \0
*/
-void mmbackend_parse_hostspec(char* spec, char** host, char** port);
+void mmbackend_parse_hostspec(char* spec, char** host, char** port, char** options);
/*
* Parse a given host / port combination into a sockaddr_storage
@@ -92,7 +94,7 @@ size_t json_obj_offset(char* json, char* key);
size_t json_array_offset(char* json, uint64_t key);
/*
- * Check for for a key within a JSON object / index within an array
+ * Check for a key within a JSON object / index within an array
* Assumes a zero-terminated, validated JSON object / array as input
* Returns type of value
*/
diff --git a/backends/loopback.c b/backends/loopback.c
index 0a45bde..085d1df 100644
--- a/backends/loopback.c
+++ b/backends/loopback.c
@@ -1,8 +1,8 @@
+#define BACKEND_NAME "loopback"
+
#include <string.h>
#include "loopback.h"
-#define BACKEND_NAME "loopback"
-
MM_PLUGIN_API int init(){
backend loopback = {
.name = BACKEND_NAME,
@@ -18,7 +18,7 @@ MM_PLUGIN_API int init(){
//register backend
if(mm_backend_register(loopback)){
- fprintf(stderr, "Failed to register loopback backend\n");
+ LOG("Failed to register backend");
return 1;
}
return 0;
@@ -42,14 +42,14 @@ static instance* loopback_instance(){
i->impl = calloc(1, sizeof(loopback_instance_data));
if(!i->impl){
- fprintf(stderr, "Failed to allocate memory\n");
+ LOG("Failed to allocate memory");
return NULL;
}
return i;
}
-static channel* loopback_channel(instance* inst, char* spec){
+static channel* loopback_channel(instance* inst, char* spec, uint8_t flags){
size_t u;
loopback_instance_data* data = (loopback_instance_data*) inst->impl;
@@ -64,13 +64,13 @@ static channel* loopback_channel(instance* inst, char* spec){
if(u == data->n){
data->name = realloc(data->name, (u + 1) * sizeof(char*));
if(!data->name){
- fprintf(stderr, "Failed to allocate memory\n");
+ LOG("Failed to allocate memory");
return NULL;
}
data->name[u] = strdup(spec);
if(!data->name[u]){
- fprintf(stderr, "Failed to allocate memory\n");
+ LOG("Failed to allocate memory");
return NULL;
}
data->n++;
@@ -92,20 +92,14 @@ static int loopback_handle(size_t num, managed_fd* fds){
return 0;
}
-static int loopback_start(){
+static int loopback_start(size_t n, instance** inst){
return 0;
}
-static int loopback_shutdown(){
- size_t n, u, p;
- instance** inst = NULL;
+static int loopback_shutdown(size_t n, instance** inst){
+ size_t u, p;
loopback_instance_data* data = NULL;
- if(mm_backend_instances(BACKEND_NAME, &n, &inst)){
- fprintf(stderr, "Failed to fetch instance list\n");
- return 1;
- }
-
for(u = 0; u < n; u++){
data = (loopback_instance_data*) inst[u]->impl;
for(p = 0; p < data->n; p++){
@@ -115,8 +109,6 @@ static int loopback_shutdown(){
free(inst[u]->impl);
}
- free(inst);
-
- fprintf(stderr, "Loopback backend shut down\n");
+ LOG("Backend shut down");
return 0;
}
diff --git a/backends/loopback.h b/backends/loopback.h
index a08417b..c508d72 100644
--- a/backends/loopback.h
+++ b/backends/loopback.h
@@ -4,11 +4,11 @@ MM_PLUGIN_API int init();
static int loopback_configure(char* option, char* value);
static int loopback_configure_instance(instance* inst, char* option, char* value);
static instance* loopback_instance();
-static channel* loopback_channel(instance* inst, char* spec);
+static channel* loopback_channel(instance* inst, char* spec, uint8_t flags);
static int loopback_set(instance* inst, size_t num, channel** c, channel_value* v);
static int loopback_handle(size_t num, managed_fd* fds);
-static int loopback_start();
-static int loopback_shutdown();
+static int loopback_start(size_t n, instance** inst);
+static int loopback_shutdown(size_t n, instance** inst);
typedef struct /*_loopback_instance_data*/ {
size_t n;
diff --git a/backends/lua.c b/backends/lua.c
index 0b47b2c..ee9e03f 100644
--- a/backends/lua.c
+++ b/backends/lua.c
@@ -1,5 +1,6 @@
-#include "lua.h"
+#define BACKEND_NAME "lua"
+#include "lua.h"
#include <string.h>
#include <unistd.h>
#include <errno.h>
@@ -7,7 +8,6 @@
#include <sys/timerfd.h>
#endif
-#define BACKEND_NAME "lua"
#define LUA_REGISTRY_KEY "_midimonster_lua_instance"
static size_t timers = 0;
@@ -37,7 +37,7 @@ MM_PLUGIN_API int init(){
//register backend
if(mm_backend_register(lua)){
- fprintf(stderr, "Failed to register lua backend\n");
+ LOG("Failed to register backend");
return 1;
}
@@ -45,7 +45,7 @@ MM_PLUGIN_API int init(){
//create the timer to expire intervals
timer_fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK);
if(timer_fd < 0){
- fprintf(stderr, "Failed to create timer for Lua backend\n");
+ LOG("Failed to create timer");
return 1;
}
#endif
@@ -130,7 +130,7 @@ static int lua_callback_output(lua_State* interpreter){
lua_instance_data* data = NULL;
if(lua_gettop(interpreter) != 2){
- fprintf(stderr, "Lua output function called with %d arguments, expected 2 (string, number)\n", lua_gettop(interpreter));
+ LOGPF("Output function called with %d arguments, expected 2 (string, number)", lua_gettop(interpreter));
return 0;
}
@@ -157,7 +157,7 @@ static int lua_callback_output(lua_State* interpreter){
}
}
- fprintf(stderr, "Tried to set unknown channel %s.%s\n", inst->name, channel_name);
+ LOGPF("Tried to set unknown channel %s.%s", inst->name, channel_name);
return 0;
}
@@ -167,7 +167,7 @@ static int lua_callback_interval(lua_State* interpreter){
int reference = LUA_NOREF;
if(lua_gettop(interpreter) != 2){
- fprintf(stderr, "Lua output function called with %d arguments, expected 2 (string, number)\n", lua_gettop(interpreter));
+ LOGPF("Interval function called with %d arguments, expected 2 (function, number)", lua_gettop(interpreter));
return 0;
}
@@ -217,7 +217,7 @@ static int lua_callback_interval(lua_State* interpreter){
//append new timer
timer = realloc(timer, (timers + 1) * sizeof(lua_timer));
if(!timer){
- fprintf(stderr, "Failed to allocate memory\n");
+ LOG("Failed to allocate memory");
timers = 0;
return 0;
}
@@ -240,7 +240,7 @@ static int lua_callback_value(lua_State* interpreter, uint8_t input){
const char* channel_name = NULL;
if(lua_gettop(interpreter) != 1){
- fprintf(stderr, "Lua get_value function called with %d arguments, expected 1 (string)\n", lua_gettop(interpreter));
+ LOGPF("get_value function called with %d arguments, expected 1 (string)", lua_gettop(interpreter));
return 0;
}
@@ -261,7 +261,7 @@ static int lua_callback_value(lua_State* interpreter, uint8_t input){
}
}
- fprintf(stderr, "Tried to get unknown channel %s.%s\n", inst->name, channel_name);
+ LOGPF("Tried to get unknown channel %s.%s", inst->name, channel_name);
return 0;
}
@@ -274,7 +274,7 @@ static int lua_callback_output_value(lua_State* interpreter){
}
static int lua_configure(char* option, char* value){
- fprintf(stderr, "The lua backend does not take any global configuration\n");
+ LOG("No backend configuration possible");
return 1;
}
@@ -284,13 +284,13 @@ static int lua_configure_instance(instance* inst, char* option, char* value){
//load a lua file into the interpreter
if(!strcmp(option, "script") || !strcmp(option, "source")){
if(luaL_dofile(data->interpreter, value)){
- fprintf(stderr, "Failed to load lua source file %s for instance %s: %s\n", value, inst->name, lua_tostring(data->interpreter, -1));
+ LOGPF("Failed to load source file %s for instance %s: %s", value, inst->name, lua_tostring(data->interpreter, -1));
return 1;
}
return 0;
}
- fprintf(stderr, "Unknown configuration parameter %s for lua instance %s\n", option, inst->name);
+ LOGPF("Unknown instance configuration parameter %s for instance %s", option, inst->name);
return 1;
}
@@ -302,14 +302,14 @@ static instance* lua_instance(){
lua_instance_data* data = calloc(1, sizeof(lua_instance_data));
if(!data){
- fprintf(stderr, "Failed to allocate memory\n");
+ LOG("Failed to allocate memory");
return NULL;
}
//load the interpreter
data->interpreter = luaL_newstate();
if(!data->interpreter){
- fprintf(stderr, "Failed to initialize LUA\n");
+ LOG("Failed to initialize interpreter");
free(data);
return NULL;
}
@@ -330,7 +330,7 @@ static instance* lua_instance(){
return inst;
}
-static channel* lua_channel(instance* inst, char* spec){
+static channel* lua_channel(instance* inst, char* spec, uint8_t flags){
size_t u;
lua_instance_data* data = (lua_instance_data*) inst->impl;
@@ -348,7 +348,7 @@ static channel* lua_channel(instance* inst, char* spec){
data->input = realloc(data->input, (u + 1) * sizeof(double));
data->output = realloc(data->output, (u + 1) * sizeof(double));
if(!data->channel_name || !data->reference || !data->input || !data->output){
- fprintf(stderr, "Failed to allocate memory\n");
+ LOG("Failed to allocate memory");
return NULL;
}
@@ -356,7 +356,7 @@ static channel* lua_channel(instance* inst, char* spec){
data->input[u] = data->output[u] = 0.0;
data->channel_name[u] = strdup(spec);
if(!data->channel_name[u]){
- fprintf(stderr, "Failed to allocate memory\n");
+ LOG("Failed to allocate memory");
return NULL;
}
data->channels++;
@@ -377,7 +377,7 @@ static int lua_set(instance* inst, size_t num, channel** c, channel_value* v){
lua_rawgeti(data->interpreter, LUA_REGISTRYINDEX, data->reference[c[n]->ident]);
lua_pushnumber(data->interpreter, v[n].normalised);
if(lua_pcall(data->interpreter, 1, 0, 0) != LUA_OK){
- fprintf(stderr, "Failed to call handler for %s.%s: %s\n", inst->name, data->channel_name[c[n]->ident], lua_tostring(data->interpreter, -1));
+ LOGPF("Failed to call handler for %s.%s: %s", inst->name, data->channel_name[c[n]->ident], lua_tostring(data->interpreter, -1));
lua_pop(data->interpreter, 1);
}
}
@@ -397,7 +397,7 @@ static int lua_handle(size_t num, managed_fd* fds){
//read the timer iteration to acknowledge the fd
if(read(timer_fd, read_buffer, sizeof(read_buffer)) < 0){
- fprintf(stderr, "Failed to read from Lua timer: %s\n", strerror(errno));
+ LOGPF("Failed to read timer: %s", strerror(errno));
return 1;
}
#else
@@ -428,17 +428,10 @@ static int lua_handle(size_t num, managed_fd* fds){
return 0;
}
-static int lua_start(){
- size_t n, u, p;
- instance** inst = NULL;
+static int lua_start(size_t n, instance** inst){
+ size_t u, p;
lua_instance_data* data = NULL;
- //fetch all defined instances
- if(mm_backend_instances(BACKEND_NAME, &n, &inst)){
- fprintf(stderr, "Failed to fetch instance list\n");
- return 1;
- }
-
//resolve channels to their handler functions
for(u = 0; u < n; u++){
data = (lua_instance_data*) inst[u]->impl;
@@ -457,11 +450,9 @@ static int lua_start(){
}
}
- free(inst);
-
#ifdef MMBACKEND_LUA_TIMERFD
//register the timer with the core
- fprintf(stderr, "Lua backend registering 1 descriptor to core\n");
+ LOG("Registering 1 descriptor to core");
if(mm_manage_fd(timer_fd, BACKEND_NAME, 1, NULL)){
return 1;
}
@@ -469,17 +460,10 @@ static int lua_start(){
return 0;
}
-static int lua_shutdown(){
- size_t n, u, p;
- instance** inst = NULL;
+static int lua_shutdown(size_t n, instance** inst){
+ size_t u, p;
lua_instance_data* data = NULL;
- //fetch all instances
- if(mm_backend_instances(BACKEND_NAME, &n, &inst)){
- fprintf(stderr, "Failed to fetch instance list\n");
- return 1;
- }
-
for(u = 0; u < n; u++){
data = (lua_instance_data*) inst[u]->impl;
//stop the interpreter
@@ -495,7 +479,6 @@ static int lua_shutdown(){
free(inst[u]->impl);
}
- free(inst);
//free module-global data
free(timer);
timer = NULL;
@@ -505,6 +488,6 @@ static int lua_shutdown(){
timer_fd = -1;
#endif
- fprintf(stderr, "Lua backend shut down\n");
+ LOG("Backend shut down");
return 0;
}
diff --git a/backends/lua.h b/backends/lua.h
index e187a8e..75f03c4 100644
--- a/backends/lua.h
+++ b/backends/lua.h
@@ -13,11 +13,11 @@ MM_PLUGIN_API int init();
static int lua_configure(char* option, char* value);
static int lua_configure_instance(instance* inst, char* option, char* value);
static instance* lua_instance();
-static channel* lua_channel(instance* inst, char* spec);
+static channel* lua_channel(instance* inst, char* spec, uint8_t flags);
static int lua_set(instance* inst, size_t num, channel** c, channel_value* v);
static int lua_handle(size_t num, managed_fd* fds);
-static int lua_start();
-static int lua_shutdown();
+static int lua_start(size_t n, instance** inst);
+static int lua_shutdown(size_t n, instance** inst);
#ifndef MMBACKEND_LUA_TIMERFD
static uint32_t lua_interval();
#endif
diff --git a/backends/maweb.c b/backends/maweb.c
index 08156f2..f81ab46 100644
--- a/backends/maweb.c
+++ b/backends/maweb.c
@@ -1,3 +1,5 @@
+#define BACKEND_NAME "maweb"
+
#include <string.h>
#include <unistd.h>
#include <errno.h>
@@ -8,7 +10,6 @@
#include "libmmbackend.h"
#include "maweb.h"
-#define BACKEND_NAME "maweb"
#define WS_LEN(a) ((a) & 0x7F)
#define WS_OP(a) ((a) & 0x0F)
#define WS_FLAG_FIN 0x80
@@ -88,7 +89,7 @@ MM_PLUGIN_API int init(){
//register backend
if(mm_backend_register(maweb)){
- fprintf(stderr, "Failed to register maweb backend\n");
+ LOG("Failed to register backend");
return 1;
}
return 0;
@@ -139,18 +140,18 @@ static int maweb_configure(char* option, char* value){
return 0;
}
- fprintf(stderr, "Unknown maweb backend configuration option %s\n", option);
+ LOGPF("Unknown backend configuration option %s", option);
return 1;
}
static int maweb_configure_instance(instance* inst, char* option, char* value){
maweb_instance_data* data = (maweb_instance_data*) inst->impl;
- char* host = NULL, *port = NULL;
+ char* host = NULL, *port = NULL, *fd_opts = NULL;
if(!strcmp(option, "host")){
- mmbackend_parse_hostspec(value, &host, &port);
+ mmbackend_parse_hostspec(value, &host, &port, &fd_opts);
if(!host){
- fprintf(stderr, "Invalid host specified for maweb instance %s\n", inst->name);
+ LOGPF("Invalid host specified for instance %s", inst->name);
return 1;
}
free(data->host);
@@ -179,7 +180,7 @@ static int maweb_configure_instance(instance* inst, char* option, char* value){
}
return 0;
#else
- fprintf(stderr, "This build of the maweb backend only supports the default password\n");
+ LOG("This build only supports the default password");
return 1;
#endif
}
@@ -194,13 +195,13 @@ static int maweb_configure_instance(instance* inst, char* option, char* value){
data->cmdline = cmd_downgrade;
}
else{
- fprintf(stderr, "Unknown maweb commandline mode %s for instance %s\n", value, inst->name);
+ LOGPF("Unknown commandline mode %s for instance %s", value, inst->name);
return 1;
}
return 0;
}
- fprintf(stderr, "Unknown configuration parameter %s for maweb instance %s\n", option, inst->name);
+ LOGPF("Unknown instance configuration parameter %s for instance %s", option, inst->name);
return 1;
}
@@ -212,14 +213,14 @@ static instance* maweb_instance(){
maweb_instance_data* data = calloc(1, sizeof(maweb_instance_data));
if(!data){
- fprintf(stderr, "Failed to allocate memory\n");
+ LOG("Failed to allocate memory");
return NULL;
}
data->fd = -1;
data->buffer = calloc(MAWEB_RECV_CHUNK, sizeof(uint8_t));
if(!data->buffer){
- fprintf(stderr, "Failed to allocate memory\n");
+ LOG("Failed to allocate memory");
free(data);
return NULL;
}
@@ -229,7 +230,7 @@ static instance* maweb_instance(){
return inst;
}
-static channel* maweb_channel(instance* inst, char* spec){
+static channel* maweb_channel(instance* inst, char* spec, uint8_t flags){
maweb_instance_data* data = (maweb_instance_data*) inst->impl;
maweb_channel_data chan = {
0
@@ -241,7 +242,7 @@ static channel* maweb_channel(instance* inst, char* spec){
if(!strncmp(spec, "page", 4)){
chan.page = strtoul(spec + 4, &next_token, 10);
if(*next_token != '.'){
- fprintf(stderr, "Failed to parse maweb channel spec %s: Missing separator\n", spec);
+ LOGPF("Failed to parse channel spec %s: Missing separator", spec);
return NULL;
}
@@ -273,7 +274,7 @@ static channel* maweb_channel(instance* inst, char* spec){
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);
+ LOGPF("Key %s does not work with the current commandline mode for instance %s", spec, inst->name);
return NULL;
}
@@ -293,7 +294,7 @@ static channel* maweb_channel(instance* inst, char* spec){
if(maweb_channel_index(data, chan.type, chan.page, chan.index) == -1){
data->channel = realloc(data->channel, (data->channels + 1) * sizeof(maweb_channel_data));
if(!data->channel){
- fprintf(stderr, "Failed to allocate memory\n");
+ LOG("Failed to allocate memory");
return NULL;
}
data->channel[data->channels] = chan;
@@ -305,7 +306,7 @@ static channel* maweb_channel(instance* inst, char* spec){
return channel_ref;
}
- fprintf(stderr, "Failed to parse maweb channel spec %s\n", spec);
+ LOGPF("Failed to parse channel spec %s", spec);
return NULL;
}
@@ -353,7 +354,7 @@ static int maweb_process_playback(instance* inst, int64_t page, maweb_channel_ty
//ignore unused buttons
return 0;
}
- fprintf(stderr, "maweb missing exec block data on exec %" PRIu64 ".%" PRIu64 "\n", page, exec_index);
+ LOGPF("Missing exec block data on exec %" PRIu64 ".%" PRIu64, page, exec_index);
return 1;
}
@@ -395,7 +396,7 @@ static int maweb_process_playback(instance* inst, int64_t page, maweb_channel_ty
}
}
- DBGPF("maweb page %" PRIu64 " exec %" PRIu64 " value %f running %" PRIu64 "\n", page, exec_index, json_obj_double(payload + control, "v", 0.0), json_obj_int(payload, "isRun", 0));
+ DBGPF("Page %" PRIu64 " exec %" PRIu64 " value %f running %" PRIu64, page, exec_index, json_obj_double(payload + control, "v", 0.0), json_obj_int(payload, "isRun", 0));
exec_index++;
block++;
}
@@ -408,12 +409,12 @@ static int maweb_process_playbacks(instance* inst, int64_t page, char* payload,
uint64_t group = 0, subgroup, item, metatype;
if(!page){
- fprintf(stderr, "maweb received playbacks for invalid page\n");
+ LOG("Received playbacks for invalid page");
return 0;
}
if(!base_offset){
- fprintf(stderr, "maweb playback data missing item key\n");
+ LOG("Playback data missing item key");
return 0;
}
@@ -448,7 +449,7 @@ static int maweb_process_playbacks(instance* inst, int64_t page, char* payload,
group++;
}
updates_inflight--;
- DBGPF("maweb playback message processing done, %" PRIu64 " updates inflight\n", updates_inflight);
+ DBGPF("Playback message processing done, %" PRIu64 " updates inflight", updates_inflight);
return 0;
}
@@ -461,7 +462,7 @@ static int maweb_request_playbacks(instance* inst){
size_t page_index = 0, view = 3, channel = 0, offsets[3], channel_offset, channels;
if(updates_inflight){
- fprintf(stderr, "maweb skipping update request, %" PRIu64 " updates still inflight\n", updates_inflight);
+ LOGPF("Skipping update request, %" PRIu64 " updates still inflight", updates_inflight);
return 0;
}
@@ -522,7 +523,7 @@ static int maweb_request_playbacks(instance* inst){
snprintf(item_counts, sizeof(item_indices), "[%" PRIsize_t "]", ((channels / 5) * 5 + 5));
}
- DBGPF("maweb poll range first %d: %d.%d last %d: %d.%d next %d: %d.%d\n",
+ DBGPF("Poll range first %d: %d.%d last %d: %d.%d next %d: %d.%d",
data->channel[channel].type, data->channel[channel].page, data->channel[channel].index,
data->channel[channel + channel_offset - 1].type, data->channel[channel + channel_offset - 1].page, data->channel[channel + channel_offset - 1].index,
data->channel[channel + channel_offset].type, data->channel[channel + channel_offset].page, data->channel[channel + channel_offset].index);
@@ -550,11 +551,11 @@ static int maweb_request_playbacks(instance* inst){
view,
data->session);
rv |= maweb_send_frame(inst, ws_text, (uint8_t*) xmit_buffer, strlen(xmit_buffer));
- DBGPF("maweb poll request: %s\n", xmit_buffer);
+ DBGPF("Poll request: %s", xmit_buffer);
updates_inflight++;
}
- DBGPF("maweb poll request handling done, %" PRIu64 " updates requested\n", updates_inflight);
+ DBGPF("Poll request handling done, %" PRIu64 " updates requested", updates_inflight);
return rv;
}
@@ -568,42 +569,42 @@ static int maweb_handle_message(instance* inst, char* payload, size_t payload_le
field = json_obj_str(payload, "responseType", NULL);
if(!strncmp(field, "login", 5)){
if(json_obj_bool(payload, "result", 0)){
- fprintf(stderr, "maweb login successful\n");
+ LOG("Login successful");
data->login = 1;
}
else{
- fprintf(stderr, "maweb login failed\n");
+ LOG("Login failed");
data->login = 0;
}
}
if(!strncmp(field, "playbacks", 9)){
if(maweb_process_playbacks(inst, json_obj_int(payload, "iPage", 0), payload, payload_length)){
- fprintf(stderr, "maweb failed to handle/request input data\n");
+ LOG("Failed to handle/request input data");
}
return 0;
}
}
- DBGPF("maweb message (%" PRIsize_t "): %s\n", payload_length, payload);
+ DBGPF("Incoming message (%" PRIsize_t "): %s", payload_length, payload);
if(json_obj(payload, "session") == JSON_NUMBER){
data->session = json_obj_int(payload, "session", data->session);
if(data->session < 0){
- fprintf(stderr, "maweb login failed\n");
+ LOG("Login failed");
data->login = 0;
return 0;
}
- fprintf(stderr, "maweb session id is now %" PRId64 "\n", data->session);
+ LOGPF("Session id is now %" PRId64, data->session);
}
if(json_obj_bool(payload, "forceLogin", 0)){
- fprintf(stderr, "maweb sending user credentials\n");
+ LOG("Sending user credentials");
snprintf(xmit_buffer, sizeof(xmit_buffer),
"{\"requestType\":\"login\",\"username\":\"%s\",\"password\":\"%s\",\"session\":%" PRIu64 "}",
(data->peer_type == peer_dot2) ? "remote" : data->user, data->pass ? data->pass : MAWEB_DEFAULT_PASSWORD, data->session);
maweb_send_frame(inst, ws_text, (uint8_t*) xmit_buffer, strlen(xmit_buffer));
}
if(json_obj(payload, "status") && json_obj(payload, "appType")){
- fprintf(stderr, "maweb connection established\n");
+ LOG("Connection established");
field = json_obj_str(payload, "appType", NULL);
if(!strncmp(field, "dot2", 4)){
data->peer_type = peer_dot2;
@@ -644,13 +645,13 @@ static int maweb_connect(instance* inst){
//and the whole websocket 'accept key' dance is plenty stupid as it is
|| mmbackend_send_str(data->fd, "Sec-WebSocket-Key: rbEQrXMEvCm4ZUjkj6juBQ==\r\n")
|| mmbackend_send_str(data->fd, "\r\n")){
- fprintf(stderr, "maweb backend failed to communicate with peer\n");
+ LOG("Failed to communicate with peer");
return 1;
}
//register new fd
if(mm_manage_fd(data->fd, BACKEND_NAME, 1, (void*) inst)){
- fprintf(stderr, "maweb backend failed to register fd\n");
+ LOG("Failed to register FD");
return 1;
}
return 0;
@@ -667,7 +668,7 @@ static ssize_t maweb_handle_lines(instance* inst, ssize_t bytes_read){
data->state = ws_http;
}
else{
- fprintf(stderr, "maweb received invalid HTTP response for instance %s\n", inst->name);
+ LOGPF("Invalid HTTP response for instance %s", inst->name);
return -1;
}
}
@@ -737,11 +738,11 @@ static ssize_t maweb_handle_ws(instance* inst, ssize_t bytes_read){
case ws_ping:
//answer server ping with a pong
if(maweb_send_frame(inst, ws_pong, payload, payload_length)){
- fprintf(stderr, "maweb failed to send pong\n");
+ LOG("Failed to send pong");
}
return header_length + payload_length;
default:
- fprintf(stderr, "maweb encountered unhandled frame type %02X\n", WS_OP(data->buffer[0]));
+ LOGPF("Unhandled frame type %02X", WS_OP(data->buffer[0]));
//this is somewhat dicey, it might be better to handle only header + payload length for known but unhandled types
return data->offset + bytes_read;
}
@@ -751,12 +752,12 @@ static ssize_t maweb_handle_ws(instance* inst, ssize_t bytes_read){
static int maweb_handle_fd(instance* inst){
maweb_instance_data* data = (maweb_instance_data*) inst->impl;
- ssize_t bytes_read, bytes_left = data->allocated - data->offset, bytes_handled;
+ ssize_t bytes_read, bytes_left = data->allocated - data->offset, bytes_handled = 0;
if(bytes_left < 3){
data->buffer = realloc(data->buffer, (data->allocated + MAWEB_RECV_CHUNK) * sizeof(uint8_t));
if(!data->buffer){
- fprintf(stderr, "Failed to allocate memory\n");
+ LOG("Failed to allocate memory");
return 1;
}
data->allocated += MAWEB_RECV_CHUNK;
@@ -765,7 +766,7 @@ static int maweb_handle_fd(instance* inst){
bytes_read = recv(data->fd, data->buffer + data->offset, bytes_left - 1, 0);
if(bytes_read < 0){
- fprintf(stderr, "maweb backend failed to receive: %s\n", strerror(errno));
+ LOGPF("Failed to receive: %s", strerror(errno));
//TODO close, reopen
return 1;
}
@@ -793,7 +794,7 @@ static int maweb_handle_fd(instance* inst){
bytes_handled = data->offset + bytes_read;
data->offset = 0;
//TODO close, reopen
- fprintf(stderr, "maweb failed to handle incoming data\n");
+ LOG("Failed to handle incoming data");
return 1;
}
else if(bytes_handled == 0){
@@ -818,7 +819,7 @@ static int maweb_set(instance* inst, size_t num, channel** c, channel_value* v){
size_t n;
if(num && !data->login){
- fprintf(stderr, "maweb instance %s can not send output, not logged in\n", inst->name);
+ LOGPF("Instance %s can not send output, not logged in", inst->name);
return 0;
}
@@ -904,16 +905,16 @@ static int maweb_set(instance* inst, size_t num, channel** c, channel_value* v){
}
}
else{
- fprintf(stderr, "maweb commandline key %s not executed on %s due to mode mismatch\n",
+ LOGPF("Key %s not executed on %s due to mode mismatch",
cmdline_keys[chan->index].name, inst->name);
continue;
}
break;
default:
- fprintf(stderr, "maweb control not yet implemented\n");
+ LOG("Control not yet implemented");
return 1;
}
- DBGPF("maweb command out %s\n", xmit_buffer);
+ DBGPF("Command out %s", xmit_buffer);
maweb_send_frame(inst, ws_text, (uint8_t*) xmit_buffer, strlen(xmit_buffer));
}
return 0;
@@ -927,7 +928,7 @@ static int maweb_keepalive(){
//fetch all defined instances
if(mm_backend_instances(BACKEND_NAME, &n, &inst)){
- fprintf(stderr, "Failed to fetch instance list\n");
+ LOG("Failed to fetch instance list");
return 1;
}
@@ -951,7 +952,7 @@ static int maweb_poll(){
//fetch all defined instances
if(mm_backend_instances(BACKEND_NAME, &n, &inst)){
- fprintf(stderr, "Failed to fetch instance list\n");
+ LOG( "Failed to fetch instance list");
return 1;
}
@@ -989,17 +990,10 @@ static int maweb_handle(size_t num, managed_fd* fds){
return rv;
}
-static int maweb_start(){
- size_t n, u, p;
- instance** inst = NULL;
+static int maweb_start(size_t n, instance** inst){
+ size_t u, p;
maweb_instance_data* data = NULL;
- //fetch all defined instances
- if(mm_backend_instances(BACKEND_NAME, &n, &inst)){
- fprintf(stderr, "Failed to fetch instance list\n");
- return 1;
- }
-
for(u = 0; u < n; u++){
//sort channels
data = (maweb_instance_data*) inst[u]->impl;
@@ -1011,35 +1005,23 @@ static int maweb_start(){
}
if(maweb_connect(inst[u])){
- fprintf(stderr, "Failed to open connection to MA Web Remote for instance %s\n", inst[u]->name);
+ LOGPF("Failed to open connection for instance %s", inst[u]->name);
free(inst);
return 1;
}
}
- free(inst);
- if(!n){
- return 0;
- }
-
- fprintf(stderr, "maweb backend registering %" PRIsize_t " descriptors to core\n", n);
+ LOGPF("Registering %" PRIsize_t " descriptors to core", n);
//initialize timeouts
last_keepalive = last_update = mm_timestamp();
return 0;
}
-static int maweb_shutdown(){
- size_t n, u;
- instance** inst = NULL;
+static int maweb_shutdown(size_t n, instance** inst){
+ size_t u;
maweb_instance_data* data = NULL;
- //fetch all instances
- if(mm_backend_instances(BACKEND_NAME, &n, &inst)){
- fprintf(stderr, "Failed to fetch instance list\n");
- return 1;
- }
-
for(u = 0; u < n; u++){
data = (maweb_instance_data*) inst[u]->impl;
free(data->host);
@@ -1063,10 +1045,10 @@ static int maweb_shutdown(){
free(data->channel);
data->channel = NULL;
data->channels = 0;
- }
- free(inst);
+ free(inst[u]->impl);
+ }
- fprintf(stderr, "maweb backend shut down\n");
+ LOG("Backend shut down");
return 0;
}
diff --git a/backends/maweb.h b/backends/maweb.h
index 9091cda..50b777a 100644
--- a/backends/maweb.h
+++ b/backends/maweb.h
@@ -4,11 +4,11 @@ MM_PLUGIN_API int init();
static int maweb_configure(char* option, char* value);
static int maweb_configure_instance(instance* inst, char* option, char* value);
static instance* maweb_instance();
-static channel* maweb_channel(instance* inst, char* spec);
+static channel* maweb_channel(instance* inst, char* spec, uint8_t flags);
static int maweb_set(instance* inst, size_t num, channel** c, channel_value* v);
static int maweb_handle(size_t num, managed_fd* fds);
-static int maweb_start();
-static int maweb_shutdown();
+static int maweb_start(size_t n, instance** inst);
+static int maweb_shutdown(size_t n, instance** inst);
static uint32_t maweb_interval();
//Default login password: MD5("midimonster")
diff --git a/backends/midi.c b/backends/midi.c
index f380f59..11d759d 100644
--- a/backends/midi.c
+++ b/backends/midi.c
@@ -1,8 +1,9 @@
+#define BACKEND_NAME "midi"
+
#include <string.h>
#include <alsa/asoundlib.h>
#include "midi.h"
-#define BACKEND_NAME "midi"
static char* sequencer_name = NULL;
static snd_seq_t* sequencer = NULL;
@@ -37,13 +38,13 @@ MM_PLUGIN_API int init(){
};
if(sizeof(midi_channel_ident) != sizeof(uint64_t)){
- fprintf(stderr, "MIDI channel identification union out of bounds\n");
+ LOG("Channel identification union out of bounds");
return 1;
}
//register backend
if(mm_backend_register(midi)){
- fprintf(stderr, "Failed to register MIDI backend\n");
+ LOG("Failed to register backend");
return 1;
}
@@ -64,7 +65,7 @@ static int midi_configure(char* option, char* value){
return 0;
}
- fprintf(stderr, "Unknown MIDI backend option %s\n", option);
+ LOGPF("Unknown backend option %s", option);
return 1;
}
@@ -76,7 +77,7 @@ static instance* midi_instance(){
inst->impl = calloc(1, sizeof(midi_instance_data));
if(!inst->impl){
- fprintf(stderr, "Failed to allocate memory\n");
+ LOG("Failed to allocate memory");
return NULL;
}
@@ -90,7 +91,7 @@ static int midi_configure_instance(instance* inst, char* option, char* value){
if(!strcmp(option, "read")){
//connect input device
if(data->read){
- fprintf(stderr, "MIDI instance %s was already connected to an input device\n", inst->name);
+ LOGPF("Instance %s was already connected to an input device", inst->name);
return 1;
}
data->read = strdup(value);
@@ -99,18 +100,18 @@ static int midi_configure_instance(instance* inst, char* option, char* value){
else if(!strcmp(option, "write")){
//connect output device
if(data->write){
- fprintf(stderr, "MIDI instance %s was already connected to an output device\n", inst->name);
+ LOGPF("Instance %s was already connected to an output device", inst->name);
return 1;
}
data->write = strdup(value);
return 0;
}
- fprintf(stderr, "Unknown MIDI instance option %s\n", option);
+ LOGPF("Unknown instance option %s", option);
return 1;
}
-static channel* midi_channel(instance* inst, char* spec){
+static channel* midi_channel(instance* inst, char* spec, uint8_t flags){
midi_channel_ident ident = {
.label = 0
};
@@ -141,18 +142,18 @@ static channel* midi_channel(instance* inst, char* spec){
old_syntax = 1;
}
else{
- fprintf(stderr, "Unknown MIDI channel control type in %s\n", spec);
+ LOGPF("Unknown control type in %s", spec);
return NULL;
}
ident.fields.channel = strtoul(channel, &channel, 10);
if(ident.fields.channel > 15){
- fprintf(stderr, "MIDI channel out of range in midi channel spec %s\n", spec);
+ LOGPF("Channel out of range in spec %s", spec);
return NULL;
}
if(*channel != '.'){
- fprintf(stderr, "Need MIDI channel specification of form channel<X>.<control><Y>, had %s\n", spec);
+ LOGPF("Need specification of form channel<X>.<control><Y>, had %s", spec);
return NULL;
}
//skip the period
@@ -182,7 +183,7 @@ static channel* midi_channel(instance* inst, char* spec){
ident.fields.type = aftertouch;
}
else{
- fprintf(stderr, "Unknown MIDI channel control type in %s\n", spec);
+ LOGPF("Unknown control type in %s", spec);
return NULL;
}
}
@@ -306,14 +307,14 @@ static int midi_handle(size_t num, managed_fd* fds){
ident.fields.control = ev->data.control.param;
break;
default:
- fprintf(stderr, "Ignored MIDI event of unsupported type\n");
+ LOG("Ignored event of unsupported type");
continue;
}
inst = mm_instance_find(BACKEND_NAME, ev->dest.port);
if(!inst){
//FIXME might want to return failure
- fprintf(stderr, "Delivered MIDI event did not match any instance\n");
+ LOG("Delivered event did not match any instance");
continue;
}
@@ -327,10 +328,10 @@ 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){
- fprintf(stderr, "Incoming MIDI data on channel %s.ch%d.%s\n", inst->name, ident.fields.channel, event_type);
+ LOGPF("Incoming data on channel %s.ch%d.%s", inst->name, ident.fields.channel, event_type);
}
else{
- fprintf(stderr, "Incoming MIDI data on channel %s.ch%d.%s%d\n", inst->name, ident.fields.channel, event_type, ident.fields.control);
+ LOGPF("Incoming data on channel %s.ch%d.%s%d", inst->name, ident.fields.channel, event_type, ident.fields.control);
}
}
}
@@ -338,37 +339,25 @@ static int midi_handle(size_t num, managed_fd* fds){
return 0;
}
-static int midi_start(){
- size_t n = 0, p;
+static int midi_start(size_t n, instance** inst){
+ size_t p;
int nfds, rv = 1;
struct pollfd* pfds = NULL;
- instance** inst = NULL;
midi_instance_data* data = NULL;
snd_seq_addr_t addr;
- if(mm_backend_instances(BACKEND_NAME, &n, &inst)){
- fprintf(stderr, "Failed to fetch instance list\n");
- return 1;
- }
-
- //if there are no ports, do nothing
- if(!n){
- free(inst);
- return 0;
- }
-
//connect to the sequencer
if(snd_seq_open(&sequencer, "default", SND_SEQ_OPEN_DUPLEX, 0) < 0){
- fprintf(stderr, "Failed to open ALSA sequencer\n");
+ LOG("Failed to open ALSA sequencer");
goto bail;
}
snd_seq_nonblock(sequencer, 1);
- fprintf(stderr, "MIDI client ID is %d\n", snd_seq_client_id(sequencer));
+ LOGPF("Client ID is %d", snd_seq_client_id(sequencer));
//update the sequencer client name
if(snd_seq_set_client_name(sequencer, sequencer_name ? sequencer_name : "MIDIMonster") < 0){
- fprintf(stderr, "Failed to set MIDI client name to %s\n", sequencer_name);
+ LOGPF("Failed to set client name to %s", sequencer_name);
goto bail;
}
@@ -381,11 +370,11 @@ static int midi_start(){
//make connections
if(data->write){
if(snd_seq_parse_address(sequencer, &addr, data->write) == 0){
- fprintf(stderr, "Connecting output of instance %s to MIDI device %s (%d:%d)\n", inst[p]->name, data->write, addr.client, addr.port);
+ LOGPF("Connecting output of instance %s to device %s (%d:%d)", inst[p]->name, data->write, addr.client, addr.port);
snd_seq_connect_to(sequencer, data->port, addr.client, addr.port);
}
else{
- fprintf(stderr, "Failed to get destination MIDI device address: %s\n", data->write);
+ LOGPF("Failed to get destination device address: %s", data->write);
}
free(data->write);
data->write = NULL;
@@ -393,11 +382,11 @@ static int midi_start(){
if(data->read){
if(snd_seq_parse_address(sequencer, &addr, data->read) == 0){
- fprintf(stderr, "Connecting input from MIDI device %s to instance %s (%d:%d)\n", data->read, inst[p]->name, addr.client, addr.port);
+ LOGPF("Connecting input from device %s to instance %s (%d:%d)", data->read, inst[p]->name, addr.client, addr.port);
snd_seq_connect_from(sequencer, data->port, addr.client, addr.port);
}
else{
- fprintf(stderr, "Failed to get source MIDI device address: %s\n", data->read);
+ LOGPF("Failed to get source device address: %s", data->read);
}
free(data->read);
data->read = NULL;
@@ -408,12 +397,12 @@ static int midi_start(){
nfds = snd_seq_poll_descriptors_count(sequencer, POLLIN | POLLOUT);
pfds = calloc(nfds, sizeof(struct pollfd));
if(!pfds){
- fprintf(stderr, "Failed to allocate memory\n");
+ LOG("Failed to allocate memory");
goto bail;
}
nfds = snd_seq_poll_descriptors(sequencer, pfds, nfds, POLLIN | POLLOUT);
- fprintf(stderr, "MIDI backend registering %d descriptors to core\n", nfds);
+ LOGPF("Registering %d descriptors to core", nfds);
for(p = 0; p < nfds; p++){
if(mm_manage_fd(pfds[p].fd, BACKEND_NAME, 1, NULL)){
goto bail;
@@ -424,18 +413,12 @@ static int midi_start(){
bail:
free(pfds);
- free(inst);
return rv;
}
-static int midi_shutdown(){
- size_t n, p;
- instance** inst = NULL;
+static int midi_shutdown(size_t n, instance** inst){
+ size_t p;
midi_instance_data* data = NULL;
- if(mm_backend_instances(BACKEND_NAME, &n, &inst)){
- fprintf(stderr, "Failed to fetch instance list\n");
- return 1;
- }
for(p = 0; p < n; p++){
data = (midi_instance_data*) inst[p]->impl;
@@ -445,7 +428,6 @@ static int midi_shutdown(){
data->write = NULL;
free(inst[p]->impl);
}
- free(inst);
//close midi
if(sequencer){
@@ -459,6 +441,6 @@ static int midi_shutdown(){
free(sequencer_name);
sequencer_name = NULL;
- fprintf(stderr, "MIDI backend shut down\n");
+ LOG("Backend shut down");
return 0;
}
diff --git a/backends/midi.h b/backends/midi.h
index b9934f1..66a02bc 100644
--- a/backends/midi.h
+++ b/backends/midi.h
@@ -4,11 +4,11 @@ MM_PLUGIN_API int init();
static int midi_configure(char* option, char* value);
static int midi_configure_instance(instance* instance, char* option, char* value);
static instance* midi_instance();
-static channel* midi_channel(instance* instance, char* spec);
+static channel* midi_channel(instance* instance, char* spec, uint8_t flags);
static int midi_set(instance* inst, size_t num, channel** c, channel_value* v);
static int midi_handle(size_t num, managed_fd* fds);
-static int midi_start();
-static int midi_shutdown();
+static int midi_start(size_t n, instance** inst);
+static int midi_shutdown(size_t n, instance** inst);
typedef struct /*_midi_instance_data*/ {
int port;
diff --git a/backends/ola.cpp b/backends/ola.cpp
index d069a8c..09d68c9 100644
--- a/backends/ola.cpp
+++ b/backends/ola.cpp
@@ -1,3 +1,5 @@
+#define BACKEND_NAME "ola"
+
#include "ola.h"
#include <cstring>
#include <ola/DmxBuffer.h>
@@ -7,7 +9,6 @@
#include <ola/io/SelectServer.h>
#include <ola/network/Socket.h>
-#define BACKEND_NAME "ola"
static ola::io::SelectServer* ola_select = NULL;
static ola::OlaCallbackClient* ola_client = NULL;
@@ -26,7 +27,7 @@ MM_PLUGIN_API int init(){
//register backend
if(mm_backend_register(ola)){
- fprintf(stderr, "Failed to register OLA backend\n");
+ LOG("Failed to register backend");
return 1;
}
@@ -35,7 +36,7 @@ MM_PLUGIN_API int init(){
}
static int ola_configure(char* option, char* value){
- fprintf(stderr, "Unknown OLA backend option %s\n", option);
+ LOGPF("Unknown backend option %s", option);
return 1;
}
@@ -48,7 +49,7 @@ static instance* ola_instance(){
data = (ola_instance_data*)calloc(1, sizeof(ola_instance_data));
if(!data){
- fprintf(stderr, "Failed to allocate memory\n");
+ LOG("Failed to allocate memory");
return NULL;
}
@@ -64,11 +65,11 @@ static int ola_configure_instance(instance* inst, char* option, char* value){
return 0;
}
- fprintf(stderr, "Unknown OLA option %s for instance %s\n", option, inst->name);
+ LOGPF("Unknown instance configuration option %s for instance %s", option, inst->name);
return 1;
}
-static channel* ola_channel(instance* inst, char* spec){
+static channel* ola_channel(instance* inst, char* spec, uint8_t flags){
ola_instance_data* data = (ola_instance_data*) inst->impl;
char* spec_next = spec;
unsigned chan_a = strtoul(spec, &spec_next, 10);
@@ -76,7 +77,7 @@ static channel* ola_channel(instance* inst, char* spec){
//primary channel sanity check
if(!chan_a || chan_a > 512){
- fprintf(stderr, "Invalid OLA channel specification %s\n", spec);
+ LOGPF("Invalid channel specification %s", spec);
return NULL;
}
chan_a--;
@@ -85,14 +86,14 @@ static channel* ola_channel(instance* inst, char* spec){
if(*spec_next == '+'){
chan_b = strtoul(spec_next + 1, NULL, 10);
if(!chan_b || chan_b > 512){
- fprintf(stderr, "Invalid wide-channel spec %s\n", spec);
+ LOGPF("Invalid wide-channel spec %s", spec);
return NULL;
}
chan_b--;
//if mapped mode differs, bail
if(IS_ACTIVE(data->data.map[chan_b]) && data->data.map[chan_b] != (MAP_FINE | chan_a)){
- fprintf(stderr, "Fine channel already mapped for OLA spec %s\n", spec);
+ LOGPF("Fine channel already mapped for spec %s", spec);
return NULL;
}
@@ -103,7 +104,7 @@ static channel* ola_channel(instance* inst, char* spec){
if(IS_ACTIVE(data->data.map[chan_a])){
if((*spec_next == '+' && data->data.map[chan_a] != (MAP_COARSE | chan_b))
|| (*spec_next != '+' && data->data.map[chan_a] != (MAP_SINGLE | chan_a))){
- fprintf(stderr, "Primary OLA channel already mapped at differing mode: %s\n", spec);
+ LOGPF("Primary channel already mapped at differing mode: %s", spec);
return NULL;
}
}
@@ -189,7 +190,7 @@ static void ola_data_receive(unsigned int universe, const ola::DmxBuffer& ola_dm
}
if(!chan){
- fprintf(stderr, "Active channel %zu on %s not known to core\n", p, inst->name);
+ LOGPF("Active channel %" PRIsize_t " on %s not known to core", p, inst->name);
return;
}
@@ -207,7 +208,7 @@ static void ola_data_receive(unsigned int universe, const ola::DmxBuffer& ola_dm
}
if(mm_channel_event(chan, val)){
- fprintf(stderr, "Failed to push OLA channel event to core\n");
+ LOG("Failed to push event to core");
return;
}
}
@@ -216,51 +217,38 @@ static void ola_data_receive(unsigned int universe, const ola::DmxBuffer& ola_dm
static void ola_register_callback(const std::string &error) {
if(!error.empty()){
- fprintf(stderr, "OLA backend failed to register for universe: %s\n", error.c_str());
+ LOGPF("Failed to register for universe: %s", error.c_str());
}
}
-static int ola_start(){
- size_t n, u, p;
- instance** inst = NULL;
+static int ola_start(size_t n, instance** inst){
+ size_t u, p;
ola_instance_data* data = NULL;
ola_select = new ola::io::SelectServer();
ola::network::IPV4SocketAddress ola_server(ola::network::IPV4Address::Loopback(), ola::OLA_DEFAULT_PORT);
ola::network::TCPSocket* ola_socket = ola::network::TCPSocket::Connect(ola_server);
if(!ola_socket){
- fprintf(stderr, "Failed to connect to OLA server\n");
+ LOG("Failed to connect to server");
return 1;
}
ola_client = new ola::OlaCallbackClient(ola_socket);
if(!ola_client->Setup()){
- fprintf(stderr, "Failed to start OLA client\n");
+ LOG("Failed to start client");
goto bail;
}
ola_select->AddReadDescriptor(ola_socket);
- fprintf(stderr, "OLA backend registering connection descriptor to core\n");
+ LOG("Registering connection descriptor to core");
if(mm_manage_fd(ola_socket->ReadDescriptor(), BACKEND_NAME, 1, NULL)){
goto bail;
}
ola_client->SetDmxCallback(ola::NewCallback(&ola_data_receive));
- //fetch all defined instances
- if(mm_backend_instances(BACKEND_NAME, &n, &inst)){
- fprintf(stderr, "Failed to fetch instance list\n");
- goto bail;
- }
-
- //this should not happen anymore (backends without instances are not started anymore)
- if(!n){
- free(inst);
- return 0;
- }
-
for(u = 0; u < n; u++){
data = (ola_instance_data*) inst[u]->impl;
inst[u]->ident = data->universe_id;
@@ -268,7 +256,7 @@ static int ola_start(){
//check for duplicate instances (using the same universe)
for(p = 0; p < u; p++){
if(inst[u]->ident == inst[p]->ident){
- fprintf(stderr, "OLA universe used in multiple instances, use one instance: %s - %s\n", inst[u]->name, inst[p]->name);
+ LOGPF("Universe used in multiple instances, use one instance: %s - %s", inst[u]->name, inst[p]->name);
goto bail;
}
}
@@ -277,10 +265,8 @@ static int ola_start(){
//run the ola select implementation to run all commands
ola_select->RunOnce();
- free(inst);
return 0;
bail:
- free(inst);
delete ola_client;
ola_client = NULL;
delete ola_select;
@@ -288,18 +274,12 @@ bail:
return 1;
}
-static int ola_shutdown(){
- size_t n, p;
- instance** inst = NULL;
- if(mm_backend_instances(BACKEND_NAME, &n, &inst)){
- fprintf(stderr, "Failed to fetch instance list\n");
- return 1;
- }
+static int ola_shutdown(size_t n, instance** inst){
+ size_t p;
for(p = 0; p < n; p++){
free(inst[p]->impl);
}
- free(inst);
if(ola_client){
ola_client->Stop();
@@ -313,6 +293,6 @@ static int ola_shutdown(){
ola_select = NULL;
}
- fprintf(stderr, "OLA backend shut down\n");
+ LOG("Backend shut down");
return 0;
}
diff --git a/backends/ola.h b/backends/ola.h
index 1637495..083e971 100644
--- a/backends/ola.h
+++ b/backends/ola.h
@@ -8,11 +8,11 @@ extern "C" {
static int ola_configure(char* option, char* value);
static int ola_configure_instance(instance* instance, char* option, char* value);
static instance* ola_instance();
- static channel* ola_channel(instance* instance, char* spec);
+ static channel* ola_channel(instance* instance, char* spec, uint8_t flags);
static int ola_set(instance* inst, size_t num, channel** c, channel_value* v);
static int ola_handle(size_t num, managed_fd* fds);
- static int ola_start();
- static int ola_shutdown();
+ static int ola_start(size_t n, instance** inst);
+ static int ola_shutdown(size_t n, instance** inst);
}
#define MAP_COARSE 0x0200
diff --git a/backends/osc.c b/backends/osc.c
index d9f9139..7b9a5a5 100644
--- a/backends/osc.c
+++ b/backends/osc.c
@@ -1,3 +1,5 @@
+#define BACKEND_NAME "osc"
+
#include <string.h>
#include <ctype.h>
#include <errno.h>
@@ -11,7 +13,6 @@
*/
#define osc_align(a) ((((a) / 4) + (((a) % 4) ? 1 : 0)) * 4)
-#define BACKEND_NAME "osc"
static struct {
uint8_t detect;
@@ -33,13 +34,13 @@ MM_PLUGIN_API int init(){
};
if(sizeof(osc_channel_ident) != sizeof(uint64_t)){
- fprintf(stderr, "OSC channel identification union out of bounds\n");
+ LOG("Channel identification union out of bounds");
return 1;
}
//register backend
if(mm_backend_register(osc)){
- fprintf(stderr, "Failed to register OSC backend\n");
+ LOG("Failed to register backend");
return 1;
}
return 0;
@@ -55,7 +56,7 @@ static size_t osc_data_length(osc_parameter_type t){
case double64:
return 8;
default:
- fprintf(stderr, "Invalid OSC format specified %c\n", t);
+ LOGPF("Invalid OSC format specifier %c", t);
return 0;
}
}
@@ -78,7 +79,7 @@ static inline void osc_defaults(osc_parameter_type t, osc_parameter_value* max,
max->d = 1.0;
return;
default:
- fprintf(stderr, "Invalid OSC type, not setting any sane defaults\n");
+ LOG("Invalid OSC type, not setting any sane defaults");
return;
}
}
@@ -96,7 +97,7 @@ static inline osc_parameter_value osc_parse(osc_parameter_type t, uint8_t* data)
v.i64 = be64toh(*((int64_t*) data));
break;
default:
- fprintf(stderr, "Invalid OSC type passed to parsing routine\n");
+ LOG("Invalid OSC type passed to parsing routine");
}
return v;
}
@@ -117,7 +118,7 @@ static inline int osc_deparse(osc_parameter_type t, osc_parameter_value v, uint8
memcpy(data, &u64, sizeof(u64));
break;
default:
- fprintf(stderr, "Invalid OSC type passed to parsing routine\n");
+ LOG("Invalid OSC type passed to parsing routine");
return 1;
}
return 0;
@@ -140,7 +141,7 @@ static inline osc_parameter_value osc_parse_value_spec(osc_parameter_type t, cha
v.d = strtod(value, NULL);
break;
default:
- fprintf(stderr, "Invalid OSC type passed to value parser\n");
+ LOG("Invalid OSC type passed to value parser");
}
return v;
}
@@ -181,7 +182,7 @@ static inline channel_value osc_parameter_normalise(osc_parameter_type t, osc_pa
v.normalised = v.raw.dbl / range.d64;
break;
default:
- fprintf(stderr, "Invalid OSC type passed to interpolation routine\n");
+ LOG("Invalid OSC type passed to interpolation routine (normalise)");
}
//clamp to range
@@ -218,7 +219,7 @@ static inline osc_parameter_value osc_parameter_denormalise(osc_parameter_type t
v.d = (range.d64 * cur.normalised) + min.d;
break;
default:
- fprintf(stderr, "Invalid OSC type passed to interpolation routine\n");
+ LOG("Invalid OSC type passed to interpolation routine (denormalise)");
}
return v;
@@ -232,27 +233,27 @@ static int osc_path_validate(char* path, uint8_t allow_patterns){
uint8_t square_open = 0, curly_open = 0;
if(path[0] != '/'){
- fprintf(stderr, "%s is not a valid OSC path: Missing root /\n", path);
+ LOGPF("%s is not a valid OSC path: Missing root /", path);
return 1;
}
for(u = 0; u < strlen(path); u++){
for(c = 0; c < sizeof(illegal_chars); c++){
if(path[u] == illegal_chars[c]){
- fprintf(stderr, "%s is not a valid OSC path: Illegal '%c' at %" PRIsize_t "\n", path, illegal_chars[c], u);
+ LOGPF("%s is not a valid OSC path: Illegal '%c' at %" PRIsize_t, path, illegal_chars[c], u);
return 1;
}
}
if(!isgraph(path[u])){
- fprintf(stderr, "%s is not a valid OSC path: Illegal '%c' at %" PRIsize_t "\n", path, pattern_chars[c], u);
+ LOGPF("%s is not a valid OSC path: Illegal '%c' at %" PRIsize_t, path, pattern_chars[c], u);
return 1;
}
if(!allow_patterns){
for(c = 0; c < sizeof(pattern_chars); c++){
if(path[u] == pattern_chars[c]){
- fprintf(stderr, "%s is not a valid OSC path: Illegal '%c' at %" PRIsize_t "\n", path, pattern_chars[c], u);
+ LOGPF("%s is not a valid OSC path: Illegal '%c' at %" PRIsize_t, path, pattern_chars[c], u);
return 1;
}
}
@@ -261,14 +262,14 @@ static int osc_path_validate(char* path, uint8_t allow_patterns){
switch(path[u]){
case '{':
if(square_open || curly_open){
- fprintf(stderr, "%s is not a valid OSC path: Illegal '%c' at %" PRIsize_t "\n", path, pattern_chars[c], u);
+ LOGPF("%s is not a valid OSC path: Illegal '%c' at %" PRIsize_t, path, pattern_chars[c], u);
return 1;
}
curly_open = 1;
break;
case '[':
if(square_open || curly_open){
- fprintf(stderr, "%s is not a valid OSC path: Illegal '%c' at %" PRIsize_t "\n", path, pattern_chars[c], u);
+ LOGPF("%s is not a valid OSC path: Illegal '%c' at %" PRIsize_t, path, pattern_chars[c], u);
return 1;
}
square_open = 1;
@@ -281,14 +282,14 @@ static int osc_path_validate(char* path, uint8_t allow_patterns){
break;
case '/':
if(square_open || curly_open){
- fprintf(stderr, "%s is not a valid OSC path: Pattern across part boundaries\n", path);
+ LOGPF("%s is not a valid OSC path: Pattern across part boundaries", path);
return 1;
}
}
}
if(square_open || curly_open){
- fprintf(stderr, "%s is not a valid OSC path: Unterminated pattern expression\n", path);
+ LOGPF("%s is not a valid OSC path: Unterminated pattern expression", path);
return 1;
}
return 0;
@@ -421,7 +422,7 @@ static int osc_configure(char* option, char* value){
return 0;
}
- fprintf(stderr, "Unknown configuration parameter %s for OSC backend\n", option);
+ LOGPF("Unknown backend configuration parameter %s", option);
return 1;
}
@@ -430,21 +431,21 @@ static int osc_register_pattern(osc_instance_data* data, char* pattern_path, cha
char* format = NULL, *token = NULL;
if(osc_path_validate(pattern_path, 1)){
- fprintf(stderr, "Not a valid OSC pattern: %s\n", pattern_path);
+ LOGPF("Not a valid OSC pattern: %s", pattern_path);
return 1;
}
//tokenize configuration
format = strtok(configuration, " ");
if(!format || strlen(format) < 1){
- fprintf(stderr, "Not a valid format specification for OSC pattern %s\n", pattern_path);
+ LOGPF("Not a valid format specification for OSC pattern %s", pattern_path);
return 1;
}
//create pattern
data->pattern = realloc(data->pattern, (data->patterns + 1) * sizeof(osc_channel));
if(!data->pattern){
- fprintf(stderr, "Failed to allocate memory\n");
+ LOG("Failed to allocate memory");
return 1;
}
pattern = data->patterns;
@@ -461,14 +462,14 @@ static int osc_register_pattern(osc_instance_data* data, char* pattern_path, cha
|| !data->pattern[pattern].min){
//this should fail config parsing and thus call the shutdown function,
//which should properly free the rest of the data
- fprintf(stderr, "Failed to allocate memory\n");
+ LOG("Failed to allocate memory");
return 1;
}
//check format validity and store min/max values
for(u = 0; u < strlen(format); u++){
if(!osc_data_length(format[u])){
- fprintf(stderr, "Invalid format specifier %c for pattern %s\n", format[u], pattern_path);
+ LOGPF("Invalid format specifier %c for pattern %s", format[u], pattern_path);
return 1;
}
@@ -477,14 +478,14 @@ static int osc_register_pattern(osc_instance_data* data, char* pattern_path, cha
//parse min/max values
token = strtok(NULL, " ");
if(!token){
- fprintf(stderr, "Missing minimum specification for parameter %" PRIsize_t " of OSC pattern %s\n", u, pattern_path);
+ LOGPF("Missing minimum specification for parameter %" PRIsize_t " of OSC pattern %s", u, pattern_path);
return 1;
}
data->pattern[pattern].min[u] = osc_parse_value_spec(format[u], token);
token = strtok(NULL, " ");
if(!token){
- fprintf(stderr, "Missing maximum specification for parameter %" PRIsize_t " of OSC pattern %s\n", u, pattern_path);
+ LOGPF("Missing maximum specification for parameter %" PRIsize_t " of OSC pattern %s", u, pattern_path);
return 1;
}
data->pattern[pattern].max[u] = osc_parse_value_spec(format[u], token);
@@ -496,11 +497,11 @@ static int osc_register_pattern(osc_instance_data* data, char* pattern_path, cha
static int osc_configure_instance(instance* inst, char* option, char* value){
osc_instance_data* data = (osc_instance_data*) inst->impl;
- char* host = NULL, *port = NULL;
+ char* host = NULL, *port = NULL, *fd_opts = NULL;
if(!strcmp(option, "root")){
if(osc_path_validate(value, 0)){
- fprintf(stderr, "Not a valid OSC root: %s\n", value);
+ LOGPF("Not a valid OSC root: %s", value);
return 1;
}
@@ -510,21 +511,22 @@ static int osc_configure_instance(instance* inst, char* option, char* value){
data->root = strdup(value);
if(!data->root){
- fprintf(stderr, "Failed to allocate memory\n");
+ LOG("Failed to allocate memory");
return 1;
}
return 0;
}
else if(!strcmp(option, "bind")){
- mmbackend_parse_hostspec(value, &host, &port);
+ mmbackend_parse_hostspec(value, &host, &port, &fd_opts);
if(!host || !port){
- fprintf(stderr, "Invalid bind address for instance %s\n", inst->name);
+ LOGPF("Invalid bind address for instance %s", inst->name);
return 1;
}
+ //this requests a socket with SO_BROADCAST set, whether this is useful functionality for OSC is up for debate
data->fd = mmbackend_socket(host, port, SOCK_DGRAM, 1, 1);
if(data->fd < 0){
- fprintf(stderr, "Failed to bind for instance %s\n", inst->name);
+ LOGPF("Failed to bind for instance %s", inst->name);
return 1;
}
return 0;
@@ -540,14 +542,14 @@ static int osc_configure_instance(instance* inst, char* option, char* value){
return 0;
}
- mmbackend_parse_hostspec(value, &host, &port);
+ mmbackend_parse_hostspec(value, &host, &port, NULL);
if(!host || !port){
- fprintf(stderr, "Invalid destination address for instance %s\n", inst->name);
+ LOGPF("Invalid destination address for instance %s", inst->name);
return 1;
}
if(mmbackend_parse_sockaddr(host, port, &data->dest, &data->dest_len)){
- fprintf(stderr, "Failed to parse destination address for instance %s\n", inst->name);
+ LOGPF("Failed to parse destination address for instance %s", inst->name);
return 1;
}
return 0;
@@ -556,7 +558,7 @@ static int osc_configure_instance(instance* inst, char* option, char* value){
return osc_register_pattern(data, option, value);
}
- fprintf(stderr, "Unknown configuration parameter %s for OSC instance %s\n", option, inst->name);
+ LOGPF("Unknown instance configuration parameter %s for instance %s", option, inst->name);
return 1;
}
@@ -568,7 +570,7 @@ static instance* osc_instance(){
osc_instance_data* data = calloc(1, sizeof(osc_instance_data));
if(!data){
- fprintf(stderr, "Failed to allocate memory\n");
+ LOG("Failed to allocate memory");
return NULL;
}
@@ -577,7 +579,7 @@ static instance* osc_instance(){
return inst;
}
-static channel* osc_map_channel(instance* inst, char* spec){
+static channel* osc_map_channel(instance* inst, char* spec, uint8_t flags){
size_t u, p;
osc_instance_data* data = (osc_instance_data*) inst->impl;
osc_channel_ident ident = {
@@ -612,14 +614,14 @@ static channel* osc_map_channel(instance* inst, char* spec){
data->channel = realloc(data->channel, (u + 1) * sizeof(osc_channel));
if(!data->channel){
- fprintf(stderr, "Failed to allocate memory\n");
+ LOG("Failed to allocate memory");
return NULL;
}
memset(data->channel + u, 0, sizeof(osc_channel));
data->channel[u].path = strdup(spec);
if(p != data->patterns){
- fprintf(stderr, "Matched pattern %s for %s\n", data->pattern[p].path, spec);
+ LOGPF("Matched pattern %s for %s", data->pattern[p].path, spec);
data->channel[u].params = data->pattern[p].params;
//just reuse the pointers from the pattern
data->channel[u].type = data->pattern[p].type;
@@ -631,12 +633,12 @@ static channel* osc_map_channel(instance* inst, char* spec){
data->channel[u].out = calloc(data->channel[u].params, sizeof(osc_parameter_value));
}
else if(data->patterns){
- fprintf(stderr, "No pattern match found for %s\n", spec);
+ LOGPF("No pattern match found for %s", spec);
}
if(!data->channel[u].path
|| (data->channel[u].params && (!data->channel[u].in || !data->channel[u].out))){
- fprintf(stderr, "Failed to allocate memory\n");
+ LOG("Failed to allocate memory");
return NULL;
}
data->channels++;
@@ -660,7 +662,7 @@ static int osc_output_channel(instance* inst, size_t channel){
//determine minimum packet size
if(osc_align((data->root ? strlen(data->root) : 0) + strlen(data->channel[channel].path) + 1) + osc_align(data->channel[channel].params + 2) >= sizeof(xmit_buf)){
- fprintf(stderr, "Insufficient buffer size for OSC transmitting channel %s.%s\n", inst->name, data->channel[channel].path);
+ LOGPF("Insufficient buffer size for transmitting channel %s.%s", inst->name, data->channel[channel].path);
return 1;
}
@@ -686,7 +688,7 @@ static int osc_output_channel(instance* inst, size_t channel){
//write data
if(offset + osc_data_length(data->channel[channel].type[p]) >= sizeof(xmit_buf)){
- fprintf(stderr, "Insufficient buffer size for OSC transmitting channel %s.%s at parameter %" PRIsize_t "\n", inst->name, data->channel[channel].path, p);
+ LOGPF("Insufficient buffer size for transmitting channel %s.%s at parameter %" PRIsize_t, inst->name, data->channel[channel].path, p);
return 1;
}
@@ -698,7 +700,7 @@ static int osc_output_channel(instance* inst, size_t channel){
//output packet
if(sendto(data->fd, xmit_buf, offset, 0, (struct sockaddr*) &(data->dest), data->dest_len) < 0){
- fprintf(stderr, "Failed to transmit OSC packet: %s\n", strerror(errno));
+ LOGPF("Failed to transmit packet: %s", strerror(errno));
}
return 0;
}
@@ -717,7 +719,7 @@ static int osc_set(instance* inst, size_t num, channel** c, channel_value* v){
osc_instance_data* data = (osc_instance_data*) inst->impl;
if(!data->dest_len){
- fprintf(stderr, "OSC instance %s does not have a destination, output is disabled (%" PRIsize_t " channels)\n", inst->name, num);
+ LOGPF("Instance %s does not have a destination, output is disabled (%" PRIsize_t " channels)", inst->name, num);
return 0;
}
@@ -727,13 +729,13 @@ static int osc_set(instance* inst, size_t num, channel** c, channel_value* v){
//sanity check
if(ident.fields.channel >= data->channels
|| ident.fields.parameter >= data->channel[ident.fields.channel].params){
- fprintf(stderr, "OSC channel identifier out of range\n");
+ LOG("Channel identifier out of range");
return 1;
}
//if the format is unknown, don't output
if(!data->channel[ident.fields.channel].params){
- fprintf(stderr, "OSC channel %s.%s requires format specification for output\n", inst->name, data->channel[ident.fields.channel].path);
+ LOGPF("Channel %s.%s requires format specification for output", inst->name, data->channel[ident.fields.channel].path);
continue;
}
@@ -775,7 +777,7 @@ static int osc_process_packet(instance* inst, char* local_path, char* format, ui
channel* chan = NULL;
if(payload_len % 4){
- fprintf(stderr, "Invalid OSC packet, data length %" PRIsize_t "\n", payload_len);
+ LOGPF("Invalid packet, data length %" PRIsize_t, payload_len);
return 0;
}
@@ -784,7 +786,7 @@ static int osc_process_packet(instance* inst, char* local_path, char* format, ui
ident.fields.channel = c;
//unconfigured input should work without errors (using default limits)
if(data->channel[c].params && strlen(format) != data->channel[c].params){
- fprintf(stderr, "OSC message %s.%s had format %s, internal representation has %" PRIsize_t " parameters\n", inst->name, local_path, format, data->channel[c].params);
+ LOGPF("Message %s.%s had format %s, internal representation has %" PRIsize_t " parameters", inst->name, local_path, format, data->channel[c].params);
continue;
}
@@ -829,7 +831,7 @@ static int osc_handle(size_t num, managed_fd* fds){
for(fd = 0; fd < num; fd++){
inst = (instance*) fds[fd].impl;
if(!inst){
- fprintf(stderr, "OSC backend signaled for unknown fd\n");
+ LOG("Signaled for unknown FD");
continue;
}
@@ -857,13 +859,13 @@ static int osc_handle(size_t num, managed_fd* fds){
osc_fmt = recv_buf + osc_align(strlen(recv_buf) + 1);
if(*osc_fmt != ','){
//invalid format string
- fprintf(stderr, "Invalid OSC format string in packet\n");
+ LOGPF("Invalid format string in packet for instance %s", inst->name);
continue;
}
osc_fmt++;
if(osc_global_config.detect){
- fprintf(stderr, "Incoming OSC data: Path %s.%s Format %s\n", inst->name, osc_local, osc_fmt);
+ LOGPF("Incoming data: Path %s.%s Format %s", inst->name, osc_local, osc_fmt);
}
//FIXME check supplied data length
@@ -879,11 +881,11 @@ static int osc_handle(size_t num, managed_fd* fds){
#else
if(bytes_read < 0 && errno != EAGAIN){
#endif
- fprintf(stderr, "OSC failed to receive data for instance %s: %s\n", inst->name, strerror(errno));
+ LOGPF("Failed to receive data for instance %s: %s", inst->name, strerror(errno));
}
if(bytes_read == 0){
- fprintf(stderr, "OSC descriptor for instance %s closed\n", inst->name);
+ LOGPF("Descriptor for instance %s closed", inst->name);
return 1;
}
}
@@ -891,22 +893,10 @@ static int osc_handle(size_t num, managed_fd* fds){
return 0;
}
-static int osc_start(){
- size_t n, u, fds = 0;
- instance** inst = NULL;
+static int osc_start(size_t n, instance** inst){
+ size_t u, fds = 0;
osc_instance_data* data = NULL;
- //fetch all instances
- if(mm_backend_instances(BACKEND_NAME, &n, &inst)){
- fprintf(stderr, "Failed to fetch instance list\n");
- return 1;
- }
-
- if(!n){
- free(inst);
- return 0;
- }
-
//update instance identifiers
for(u = 0; u < n; u++){
data = (osc_instance_data*) inst[u]->impl;
@@ -914,8 +904,7 @@ static int osc_start(){
if(data->fd >= 0){
inst[u]->ident = data->fd;
if(mm_manage_fd(data->fd, BACKEND_NAME, 1, inst[u])){
- fprintf(stderr, "Failed to register OSC descriptor for instance %s\n", inst[u]->name);
- free(inst);
+ LOGPF("Failed to register descriptor for instance %s", inst[u]->name);
return 1;
}
fds++;
@@ -925,22 +914,14 @@ static int osc_start(){
}
}
- fprintf(stderr, "OSC backend registered %" PRIsize_t " descriptors to core\n", fds);
-
- free(inst);
+ LOGPF("Registered %" PRIsize_t " descriptors to core", fds);
return 0;
}
-static int osc_shutdown(){
- size_t n, u, c;
- instance** inst = NULL;
+static int osc_shutdown(size_t n, instance** inst){
+ size_t u, c;
osc_instance_data* data = NULL;
- if(mm_backend_instances(BACKEND_NAME, &n, &inst)){
- fprintf(stderr, "Failed to fetch instance list\n");
- return 1;
- }
-
for(u = 0; u < n; u++){
data = (osc_instance_data*) inst[u]->impl;
for(c = 0; c < data->channels; c++){
@@ -967,7 +948,6 @@ static int osc_shutdown(){
free(inst[u]->impl);
}
- free(inst);
- fprintf(stderr, "OSC backend shut down\n");
+ LOG("Backend shut down");
return 0;
}
diff --git a/backends/osc.h b/backends/osc.h
index 86be285..f8ff3ff 100644
--- a/backends/osc.h
+++ b/backends/osc.h
@@ -11,11 +11,11 @@ MM_PLUGIN_API int init();
static int osc_configure(char* option, char* value);
static int osc_configure_instance(instance* inst, char* option, char* value);
static instance* osc_instance();
-static channel* osc_map_channel(instance* inst, char* spec);
+static channel* osc_map_channel(instance* inst, char* spec, uint8_t flags);
static int osc_set(instance* inst, size_t num, channel** c, channel_value* v);
static int osc_handle(size_t num, managed_fd* fds);
-static int osc_start();
-static int osc_shutdown();
+static int osc_start(size_t n, instance** inst);
+static int osc_shutdown(size_t n, instance** inst);
typedef enum {
not_set = 0,
diff --git a/backends/sacn.c b/backends/sacn.c
index d8b3eb3..ff2b61e 100644
--- a/backends/sacn.c
+++ b/backends/sacn.c
@@ -1,3 +1,5 @@
+#define BACKEND_NAME "sacn"
+
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
@@ -15,7 +17,10 @@
//upper limit imposed by using the fd index as 16-bit part of the instance id
#define MAX_FDS 4096
-#define BACKEND_NAME "sacn"
+
+enum /*_sacn_fd_flags*/ {
+ mcast_loop = 1
+};
static struct /*_sacn_global_config*/ {
uint8_t source_name[64];
@@ -45,23 +50,23 @@ MM_PLUGIN_API int init(){
};
if(sizeof(sacn_instance_id) != sizeof(uint64_t)){
- fprintf(stderr, "sACN instance identification union out of bounds\n");
+ LOG("Instance identification union out of bounds");
return 1;
}
//register the backend
if(mm_backend_register(sacn)){
- fprintf(stderr, "Failed to register sACN backend\n");
+ LOG("Failed to register backend");
return 1;
}
return 0;
}
-static int sacn_listener(char* host, char* port, uint8_t fd_flags){
- int fd = -1;
+static int sacn_listener(char* host, char* port, uint8_t flags){
+ int fd = -1, yes = 1;
if(global_cfg.fds >= MAX_FDS){
- fprintf(stderr, "sACN backend descriptor limit reached\n");
+ LOG("Descriptor limit reached");
return -1;
}
@@ -74,16 +79,23 @@ static int sacn_listener(char* host, char* port, uint8_t fd_flags){
global_cfg.fd = realloc(global_cfg.fd, (global_cfg.fds + 1) * sizeof(sacn_fd));
if(!global_cfg.fd){
close(fd);
- fprintf(stderr, "Failed to allocate memory\n");
+ LOG("Failed to allocate memory");
return -1;
}
- fprintf(stderr, "sACN backend interface %" PRIsize_t " bound to %s port %s\n", global_cfg.fds, host, port);
+ LOGPF("Interface %" PRIsize_t " bound to %s port %s", global_cfg.fds, host, port);
global_cfg.fd[global_cfg.fds].fd = fd;
- global_cfg.fd[global_cfg.fds].flags = fd_flags;
global_cfg.fd[global_cfg.fds].universes = 0;
global_cfg.fd[global_cfg.fds].universe = NULL;
global_cfg.fd[global_cfg.fds].last_frame = NULL;
+
+ if(flags & mcast_loop){
+ //set IP_MCAST_LOOP to allow local applications to receive output
+ if(setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, (void*)&yes, sizeof(yes)) < 0){
+ LOGPF("Failed to re-enable IP_MULTICAST_LOOP on socket: %s", strerror(errno));
+ }
+ }
+
global_cfg.fds++;
return 0;
}
@@ -95,7 +107,7 @@ static int sacn_configure(char* option, char* value){
if(!strcmp(option, "name")){
if(strlen(value) > 63){
- fprintf(stderr, "Invalid sACN source name %s, limit is 63 characters\n", value);
+ LOGPF("Invalid source name %s, limit is 63 characters", value);
return 1;
}
@@ -110,24 +122,26 @@ static int sacn_configure(char* option, char* value){
}
}
else if(!strcmp(option, "bind")){
- mmbackend_parse_hostspec(value, &host, &port);
- if(!port){
- port = SACN_PORT;
- }
+ mmbackend_parse_hostspec(value, &host, &port, &next);
if(!host){
- fprintf(stderr, "No valid sACN bind address provided\n");
+ LOG("No valid bind address provided");
return 1;
}
- if(sacn_listener(host, port, flags)){
- fprintf(stderr, "Failed to bind sACN descriptor: %s\n", value);
+ if(next && !strncmp(next, "local", 5)){
+ flags = mcast_loop;
+ }
+
+ if(sacn_listener(host, port ? port : SACN_PORT, flags)){
+ LOGPF("Failed to bind descriptor: %s", value);
return 1;
}
+
return 0;
}
- fprintf(stderr, "Unknown sACN backend option %s\n", option);
+ LOGPF("Unknown backend configuration option %s", option);
return 1;
}
@@ -144,7 +158,7 @@ static int sacn_configure_instance(instance* inst, char* option, char* value){
data->fd_index = strtoul(value, NULL, 10);
if(data->fd_index >= global_cfg.fds){
- fprintf(stderr, "Configured sACN interface index is out of range on instance %s\n", inst->name);
+ LOGPF("Configured interface index is out of range on instance %s", inst->name);
return 1;
}
return 0;
@@ -154,17 +168,14 @@ static int sacn_configure_instance(instance* inst, char* option, char* value){
return 0;
}
else if(!strcmp(option, "destination")){
- mmbackend_parse_hostspec(value, &host, &port);
- if(!port){
- port = SACN_PORT;
- }
+ mmbackend_parse_hostspec(value, &host, &port, NULL);
if(!host){
- fprintf(stderr, "No valid sACN destination for instance %s\n", inst->name);
+ LOGPF("No valid destination for instance %s", inst->name);
return 1;
}
- return mmbackend_parse_sockaddr(host, port, &data->dest_addr, &data->dest_len);
+ return mmbackend_parse_sockaddr(host, port ? port : SACN_PORT, &data->dest_addr, &data->dest_len);
}
else if(!strcmp(option, "from")){
next = value;
@@ -172,14 +183,15 @@ static int sacn_configure_instance(instance* inst, char* option, char* value){
for(u = 0; u < sizeof(data->cid_filter); u++){
data->cid_filter[u] = (strtoul(next, &next, 0) & 0xFF);
}
- fprintf(stderr, "Enabled source CID filter for instance %s\n", inst->name);
+ LOGPF("Enabled source CID filter for instance %s", inst->name);
return 0;
}
else if(!strcmp(option, "unicast")){
data->unicast_input = strtoul(value, NULL, 10);
+ return 0;
}
- fprintf(stderr, "Unknown configuration option %s for sACN backend\n", option);
+ LOGPF("Unknown instance configuration option %s for instance %s", option, inst->name);
return 1;
}
@@ -191,14 +203,14 @@ static instance* sacn_instance(){
inst->impl = calloc(1, sizeof(sacn_instance_data));
if(!inst->impl){
- fprintf(stderr, "Failed to allocate memory");
+ LOG("Failed to allocate memory");
return NULL;
}
return inst;
}
-static channel* sacn_channel(instance* inst, char* spec){
+static channel* sacn_channel(instance* inst, char* spec, uint8_t flags){
sacn_instance_data* data = (sacn_instance_data*) inst->impl;
char* spec_next = spec;
@@ -206,7 +218,7 @@ static channel* sacn_channel(instance* inst, char* spec){
//range check
if(!chan_a || chan_a > 512){
- fprintf(stderr, "sACN channel out of range on instance %s: %s\n", inst->name, spec);
+ LOGPF("Channel out of range on instance %s: %s", inst->name, spec);
return NULL;
}
chan_a--;
@@ -215,14 +227,14 @@ static channel* sacn_channel(instance* inst, char* spec){
if(*spec_next == '+'){
chan_b = strtoul(spec_next + 1, NULL, 10);
if(!chan_b || chan_b > 512){
- fprintf(stderr, "Invalid wide-channel spec on instance %s: %s\n", inst->name, spec);
+ LOGPF("Invalid wide-channel spec on instance %s: %s", inst->name, spec);
return NULL;
}
chan_b--;
//if already mapped, bail
if(IS_ACTIVE(data->data.map[chan_b]) && data->data.map[chan_b] != (MAP_FINE | chan_a)){
- fprintf(stderr, "Fine channel %u already mapped on instance %s\n", chan_b, inst->name);
+ LOGPF("Fine channel %u already mapped on instance %s", chan_b, inst->name);
return NULL;
}
@@ -233,7 +245,7 @@ static channel* sacn_channel(instance* inst, char* spec){
if(IS_ACTIVE(data->data.map[chan_a])){
if((*spec_next == '+' && data->data.map[chan_a] != (MAP_COARSE | chan_b))
|| (*spec_next != '+' && data->data.map[chan_a] != (MAP_SINGLE | chan_a))){
- fprintf(stderr, "Primary sACN channel %u already mapped in another mode on instance %s\n", chan_a, inst->name);
+ LOGPF("Primary channel %u already mapped in another mode on instance %s", chan_a, inst->name);
return NULL;
}
}
@@ -279,7 +291,7 @@ static int sacn_transmit(instance* inst){
memcpy((((uint8_t*)pdu.data.data) + 1), data->data.out, 512);
if(sendto(global_cfg.fd[data->fd_index].fd, (uint8_t*) &pdu, sizeof(pdu), 0, (struct sockaddr*) &data->dest_addr, data->dest_len) < 0){
- fprintf(stderr, "Failed to output sACN frame for instance %s: %s\n", inst->name, strerror(errno));
+ LOGPF("Failed to output frame for instance %s: %s", inst->name, strerror(errno));
}
//update last transmit timestamp
@@ -300,7 +312,7 @@ static int sacn_set(instance* inst, size_t num, channel** c, channel_value* v){
}
if(!data->xmit_prio){
- fprintf(stderr, "sACN instance %s not enabled for output (%" PRIsize_t " channel events)\n", inst->name, num);
+ LOGPF("Instance %s not enabled for output (%" PRIsize_t " channel events)", inst->name, num);
return 0;
}
@@ -346,12 +358,12 @@ static int sacn_process_frame(instance* inst, sacn_frame_root* frame, sacn_frame
if(data->format != 0xa1
|| data->startcode_offset
|| be16toh(data->address_increment) != 1){
- fprintf(stderr, "sACN framing not supported\n");
+ LOGPF("Framing not supported for incoming data on instance %s\n", inst->name);
return 1;
}
if(be16toh(data->channels) > 513){
- fprintf(stderr, "Invalid sACN frame channel count\n");
+ LOGPF("Invalid frame channel count %d on instance %s", be16toh(data->channels), inst->name);
return 1;
}
@@ -385,7 +397,7 @@ static int sacn_process_frame(instance* inst, sacn_frame_root* frame, sacn_frame
}
if(!chan){
- fprintf(stderr, "Active channel %" PRIsize_t " on %s not known to core", u, inst->name);
+ LOGPF("Active channel %" PRIsize_t " on %s not known to core", u, inst->name);
return 1;
}
@@ -402,7 +414,7 @@ static int sacn_process_frame(instance* inst, sacn_frame_root* frame, sacn_frame
}
if(mm_channel_event(chan, val)){
- fprintf(stderr, "Failed to push sACN channel event to core\n");
+ LOG("Failed to push event to core");
return 1;
}
}
@@ -453,7 +465,7 @@ static void sacn_discovery(size_t fd){
memcpy(pdu.data.data, global_cfg.fd[fd].universe + page * 512, universes * sizeof(uint16_t));
if(sendto(global_cfg.fd[fd].fd, (uint8_t*) &pdu, sizeof(pdu) - (512 - universes) * sizeof(uint16_t), 0, (struct sockaddr*) &discovery_dest, sizeof(discovery_dest)) < 0){
- fprintf(stderr, "Failed to output sACN universe discovery frame for interface %" PRIsize_t ": %s\n", fd, strerror(errno));
+ LOGPF("Failed to output universe discovery frame for interface %" PRIsize_t ": %s", fd, strerror(errno));
}
}
}
@@ -513,7 +525,7 @@ static int sacn_handle(size_t num, managed_fd* fds){
instance_id.fields.uni = be16toh(data->universe);
inst = mm_instance_find(BACKEND_NAME, instance_id.label);
if(inst && sacn_process_frame(inst, frame, data)){
- fprintf(stderr, "Failed to process sACN frame\n");
+ LOG("Failed to process frame");
}
}
}
@@ -524,11 +536,11 @@ static int sacn_handle(size_t num, managed_fd* fds){
#else
if(bytes_read < 0 && errno != EAGAIN){
#endif
- fprintf(stderr, "sACN failed to receive data: %s\n", strerror(errno));
+ LOGPF("Failed to receive data: %s", strerror(errno));
}
if(bytes_read == 0){
- fprintf(stderr, "sACN listener closed\n");
+ LOG("Listener closed");
return 1;
}
}
@@ -536,10 +548,9 @@ static int sacn_handle(size_t num, managed_fd* fds){
return 0;
}
-static int sacn_start(){
- size_t n, u, p;
+static int sacn_start(size_t n, instance** inst){
+ size_t u, p;
int rv = 1;
- instance** inst = NULL;
sacn_instance_data* data = NULL;
sacn_instance_id id = {
.label = 0
@@ -549,19 +560,8 @@ static int sacn_start(){
};
struct sockaddr_in* dest_v4 = NULL;
- //fetch all instances
- if(mm_backend_instances(BACKEND_NAME, &n, &inst)){
- fprintf(stderr, "Failed to fetch instance list\n");
- return 1;
- }
-
- if(!n){
- free(inst);
- return 0;
- }
-
if(!global_cfg.fds){
- fprintf(stderr, "Failed to start sACN backend: no descriptors bound\n");
+ LOG("Failed to start, no descriptors bound");
free(inst);
return 1;
}
@@ -574,14 +574,14 @@ static int sacn_start(){
inst[u]->ident = id.label;
if(!data->uni){
- fprintf(stderr, "Please specify a universe on instance %s\n", inst[u]->name);
+ LOGPF("Please specify a universe on instance %s", inst[u]->name);
goto bail;
}
//find duplicates
for(p = 0; p < u; p++){
if(inst[u]->ident == inst[p]->ident){
- fprintf(stderr, "Colliding sACN instances, use one: %s - %s\n", inst[u]->name, inst[p]->name);
+ LOGPF("Colliding instances, use one: %s - %s", inst[u]->name, inst[p]->name);
goto bail;
}
}
@@ -589,7 +589,7 @@ static int sacn_start(){
if(!data->unicast_input){
mcast_req.imr_multiaddr.s_addr = htobe32(((uint32_t) 0xefff0000) | ((uint32_t) data->uni));
if(setsockopt(global_cfg.fd[data->fd_index].fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (uint8_t*) &mcast_req, sizeof(mcast_req))){
- fprintf(stderr, "Failed to join Multicast group for sACN universe %u on instance %s: %s\n", data->uni, inst[u]->name, strerror(errno));
+ LOGPF("Failed to join Multicast group for universe %u on instance %s: %s", data->uni, inst[u]->name, strerror(errno));
}
}
@@ -597,7 +597,7 @@ static int sacn_start(){
//add to list of advertised universes for this fd
global_cfg.fd[data->fd_index].universe = realloc(global_cfg.fd[data->fd_index].universe, (global_cfg.fd[data->fd_index].universes + 1) * sizeof(uint16_t));
if(!global_cfg.fd[data->fd_index].universe){
- fprintf(stderr, "Failed to allocate memory\n");
+ LOG("Failed to allocate memory");
goto bail;
}
@@ -615,12 +615,12 @@ static int sacn_start(){
}
}
- fprintf(stderr, "sACN backend registering %" PRIsize_t " descriptors to core\n", global_cfg.fds);
+ LOGPF("Registering %" PRIsize_t " descriptors to core", global_cfg.fds);
for(u = 0; u < global_cfg.fds; u++){
//allocate memory for storing last frame transmission timestamp
global_cfg.fd[u].last_frame = calloc(global_cfg.fd[u].universes, sizeof(uint64_t));
if(!global_cfg.fd[u].last_frame){
- fprintf(stderr, "Failed to allocate memory\n");
+ LOG("Failed to allocate memory");
goto bail;
}
if(mm_manage_fd(global_cfg.fd[u].fd, BACKEND_NAME, 1, (void*) u)){
@@ -630,23 +630,15 @@ static int sacn_start(){
rv = 0;
bail:
- free(inst);
return rv;
}
-static int sacn_shutdown(){
- size_t n, p;
- instance** inst = NULL;
-
- if(mm_backend_instances(BACKEND_NAME, &n, &inst)){
- fprintf(stderr, "Failed to fetch instance list\n");
- return 1;
- }
+static int sacn_shutdown(size_t n, instance** inst){
+ size_t p;
for(p = 0; p < n; p++){
free(inst[p]->impl);
}
- free(inst);
for(p = 0; p < global_cfg.fds; p++){
close(global_cfg.fd[p].fd);
@@ -654,6 +646,6 @@ static int sacn_shutdown(){
free(global_cfg.fd[p].last_frame);
}
free(global_cfg.fd);
- fprintf(stderr, "sACN backend shut down\n");
+ LOG("Backend shut down");
return 0;
}
diff --git a/backends/sacn.h b/backends/sacn.h
index 631d3a4..c8d11e9 100644
--- a/backends/sacn.h
+++ b/backends/sacn.h
@@ -4,11 +4,11 @@ MM_PLUGIN_API int init();
static int sacn_configure(char* option, char* value);
static int sacn_configure_instance(instance* instance, char* option, char* value);
static instance* sacn_instance();
-static channel* sacn_channel(instance* instance, char* spec);
+static channel* sacn_channel(instance* instance, char* spec, uint8_t flags);
static int sacn_set(instance* inst, size_t num, channel** c, channel_value* v);
static int sacn_handle(size_t num, managed_fd* fds);
-static int sacn_start();
-static int sacn_shutdown();
+static int sacn_start(size_t n, instance** inst);
+static int sacn_shutdown(size_t n, instance** inst);
#define SACN_PORT "5568"
#define SACN_RECV_BUF 8192
@@ -56,7 +56,6 @@ typedef union /*_sacn_instance_id*/ {
typedef struct /*_sacn_socket*/ {
int fd;
- uint8_t flags;
size_t universes;
uint16_t* universe;
uint64_t* last_frame;
diff --git a/backends/sacn.md b/backends/sacn.md
index 434beeb..f5f1db4 100644
--- a/backends/sacn.md
+++ b/backends/sacn.md
@@ -10,7 +10,11 @@ containing all write-enabled universes.
|---------------|-----------------------|-----------------------|-----------------------|
| `name` | `sACN source` | `MIDIMonster` | sACN source name |
| `cid` | `0xAA 0xBB 0xCC` ... | `MIDIMonster` | Source CID (16 bytes) |
-| `bind` | `0.0.0.0 5568` | none | Binds a network address to listen for data. This option may be set multiple times, with each descriptor being assigned an index starting from 0 to be used with the `interface` instance configuration option. At least one descriptor is required for transmission. |
+| `bind` | `0.0.0.0 5568` | none | Binds a network address to listen for data. This option may be set multiple times, with each descriptor being assigned an index starting from 0 to be used with the `interface` instance configuration option. At least one descriptor is required for operation. |
+
+The `bind` configuration value can be extended by the keyword `local` to allow software on the
+local host to process the sACN output frames from the MIDIMonster (e.g. `bind = 0.0.0.0 5568 local`).
+This has the side effect of mirroring the output of instances on those descriptors to their input.
#### Instance configuration
diff --git a/backends/winmidi.c b/backends/winmidi.c
index bda5401..0722ca2 100644
--- a/backends/winmidi.c
+++ b/backends/winmidi.c
@@ -1,3 +1,5 @@
+#define BACKEND_NAME "winmidi"
+
#include <string.h>
#include "libmmbackend.h"
@@ -5,8 +7,6 @@
#include "winmidi.h"
-#define BACKEND_NAME "winmidi"
-
static struct {
uint8_t list_devices;
uint8_t detect;
@@ -37,13 +37,13 @@ MM_PLUGIN_API int init(){
};
if(sizeof(winmidi_channel_ident) != sizeof(uint64_t)){
- fprintf(stderr, "winmidi channel identification union out of bounds\n");
+ LOG("Channel identification union out of bounds");
return 1;
}
//register backend
if(mm_backend_register(winmidi)){
- fprintf(stderr, "Failed to register winmidi backend\n");
+ LOG("Failed to register backend");
return 1;
}
@@ -68,7 +68,7 @@ static int winmidi_configure(char* option, char* value){
return 0;
}
- fprintf(stderr, "Unknown winmidi backend option %s\n", option);
+ LOGPF("Unknown backend option %s", option);
return 1;
}
@@ -76,7 +76,7 @@ static int winmidi_configure_instance(instance* inst, char* option, char* value)
winmidi_instance_data* data = (winmidi_instance_data*) inst->impl;
if(!strcmp(option, "read")){
if(data->read){
- fprintf(stderr, "winmidi instance %s already connected to an input device\n", inst->name);
+ LOGPF("Instance %s already connected to an input device", inst->name);
return 1;
}
data->read = strdup(value);
@@ -84,14 +84,14 @@ static int winmidi_configure_instance(instance* inst, char* option, char* value)
}
if(!strcmp(option, "write")){
if(data->write){
- fprintf(stderr, "winmidi instance %s already connected to an output device\n", inst->name);
+ LOGPF("Instance %s already connected to an output device", inst->name);
return 1;
}
data->write = strdup(value);
return 0;
}
- fprintf(stderr, "Unknown winmidi instance option %s\n", option);
+ LOGPF("Unknown instance configuration option %s on instance %s", option, inst->name);
return 1;
}
@@ -103,14 +103,14 @@ static instance* winmidi_instance(){
i->impl = calloc(1, sizeof(winmidi_instance_data));
if(!i->impl){
- fprintf(stderr, "Failed to allocate memory\n");
+ LOG("Failed to allocate memory");
return NULL;
}
return i;
}
-static channel* winmidi_channel(instance* inst, char* spec){
+static channel* winmidi_channel(instance* inst, char* spec, uint8_t flags){
char* next_token = NULL;
winmidi_channel_ident ident = {
.label = 0
@@ -124,18 +124,18 @@ static channel* winmidi_channel(instance* inst, char* spec){
}
if(!next_token){
- fprintf(stderr, "Invalid winmidi channel specification %s\n", spec);
+ LOGPF("Invalid channel specification %s", spec);
return NULL;
}
ident.fields.channel = strtoul(next_token, &next_token, 10);
if(ident.fields.channel > 15){
- fprintf(stderr, "MIDI channel out of range in winmidi channel spec %s\n", spec);
+ LOGPF("MIDI channel out of range in spec %s", spec);
return NULL;
}
if(*next_token != '.'){
- fprintf(stderr, "winmidi channel specification %s does not conform to channel<X>.<control><Y>\n", spec);
+ LOGPF("Channel specification %s does not conform to channel<X>.<control><Y>", spec);
return NULL;
}
@@ -160,7 +160,7 @@ static channel* winmidi_channel(instance* inst, char* spec){
ident.fields.type = aftertouch;
}
else{
- fprintf(stderr, "Unknown winmidi channel control type in %s\n", spec);
+ LOGPF("Unknown control type in %s", spec);
return NULL;
}
@@ -196,42 +196,24 @@ static int winmidi_set(instance* inst, size_t num, channel** c, channel_value* v
}
if(!data->device_out){
- fprintf(stderr, "winmidi instance %s has no output device\n", inst->name);
+ LOGPF("Instance %s has no output device", inst->name);
return 0;
}
for(u = 0; u < num; u++){
ident.label = c[u]->ident;
- switch(ident.fields.type){
- case note:
- output.components.status = 0x90 | ident.fields.channel;
- output.components.data1 = ident.fields.control;
- output.components.data2 = v[u].normalised * 127.0;
- break;
- case cc:
- output.components.status = 0xB0 | ident.fields.channel;
- output.components.data1 = ident.fields.control;
- output.components.data2 = v[u].normalised * 127.0;
- break;
- case pressure:
- output.components.status = 0xA0 | ident.fields.channel;
- output.components.data1 = ident.fields.control;
- output.components.data2 = v[u].normalised * 127.0;
- break;
- case aftertouch:
- output.components.status = 0xD0 | ident.fields.channel;
- output.components.data1 = v[u].normalised * 127.0;
- output.components.data2 = 0;
- break;
- case pitchbend:
- output.components.status = 0xE0 | ident.fields.channel;
- output.components.data1 = ((int)(v[u].normalised * 16384.0)) & 0x7F;
- output.components.data2 = (((int)(v[u].normalised * 16384.0)) >> 7) & 0x7F;
- break;
- default:
- fprintf(stderr, "Unknown winmidi channel type %d\n", ident.fields.type);
- continue;
+ //build output message
+ output.components.status = ident.fields.type | ident.fields.channel;
+ output.components.data1 = ident.fields.control;
+ output.components.data2 = v[u].normalised * 127.0;
+ if(ident.fields.type == pitchbend){
+ output.components.data1 = ((int)(v[u].normalised * 16384.0)) & 0x7F;
+ output.components.data2 = (((int)(v[u].normalised * 16384.0)) >> 7) & 0x7F;
+ }
+ else if(ident.fields.type == aftertouch){
+ output.components.data1 = v[u].normalised * 127.0;
+ output.components.data2 = 0;
}
midiOutShortMsg(data->device_out, output.dword);
@@ -277,14 +259,14 @@ static int winmidi_handle(size_t num, managed_fd* fds){
//pretty-print channel-wide events
if(backend_config.event[u].channel.fields.type == pitchbend
|| backend_config.event[u].channel.fields.type == aftertouch){
- fprintf(stderr, "Incoming MIDI data on channel %s.ch%d.%s, value %f\n",
+ LOGPF("Incoming data on channel %s.ch%d.%s, value %f",
backend_config.event[u].inst->name,
backend_config.event[u].channel.fields.channel,
winmidi_type_name(backend_config.event[u].channel.fields.type),
backend_config.event[u].value);
}
else{
- fprintf(stderr, "Incoming MIDI data on channel %s.ch%d.%s%d, value %f\n",
+ LOGPF("Incoming data on channel %s.ch%d.%s%d, value %f",
backend_config.event[u].inst->name,
backend_config.event[u].channel.fields.channel,
winmidi_type_name(backend_config.event[u].channel.fields.type),
@@ -297,7 +279,7 @@ static int winmidi_handle(size_t num, managed_fd* fds){
mm_channel_event(chan, backend_config.event[u].value);
}
}
- DBGPF("winmidi flushed %" PRIsize_t " wakeups, handled %" PRIsize_t " events\n", bytes, backend_config.events_active);
+ DBGPF("Flushed %" PRIsize_t " wakeups, handled %" PRIsize_t " events", bytes, backend_config.events_active);
backend_config.events_active = 0;
LeaveCriticalSection(&backend_config.push_events);
return 0;
@@ -321,7 +303,7 @@ static void CALLBACK winmidi_input_callback(HMIDIIN device, unsigned message, DW
};
//callbacks may run on different threads, so we queue all events and alert the main thread via the feedback socket
- DBGPF("winmidi input callback on thread %ld\n", GetCurrentThreadId());
+ DBGPF("Input callback on thread %ld", GetCurrentThreadId());
switch(message){
case MIM_MOREDATA:
@@ -330,40 +312,21 @@ static void CALLBACK winmidi_input_callback(HMIDIIN device, unsigned message, DW
//param1 has the message
input.dword = param1;
ident.fields.channel = input.components.status & 0x0F;
- switch(input.components.status & 0xF0){
- case 0x80:
- ident.fields.type = note;
- ident.fields.control = input.components.data1;
- val.normalised = 0.0;
- break;
- case 0x90:
- ident.fields.type = note;
- ident.fields.control = input.components.data1;
- val.normalised = (double) input.components.data2 / 127.0;
- break;
- case 0xA0:
- ident.fields.type = pressure;
- ident.fields.control = input.components.data1;
- val.normalised = (double) input.components.data2 / 127.0;
- break;
- case 0xB0:
- ident.fields.type = cc;
- ident.fields.control = input.components.data1;
- val.normalised = (double) input.components.data2 / 127.0;
- break;
- case 0xD0:
- ident.fields.type = aftertouch;
- ident.fields.control = 0;
- val.normalised = (double) input.components.data1 / 127.0;
- break;
- case 0xE0:
- ident.fields.type = pitchbend;
- ident.fields.control = 0;
- val.normalised = (double)((input.components.data2 << 7) | input.components.data1) / 16384.0;
- break;
- default:
- fprintf(stderr, "winmidi unhandled status byte %02X\n", input.components.status);
- return;
+ ident.fields.type = input.components.status & 0xF0;
+ ident.fields.control = input.components.data1;
+ val.normalised = (double) input.components.data2 / 127.0;
+
+ if(ident.fields.type == 0x80){
+ ident.fields.type = note;
+ val.normalised = 0;
+ }
+ else if(ident.fields.type == pitchbend){
+ ident.fields.control = 0;
+ val.normalised = (double)((input.components.data2 << 7) | input.components.data1) / 16384.0;
+ }
+ else if(ident.fields.type == aftertouch){
+ ident.fields.control = 0;
+ val.normalised = (double) input.components.data1 / 127.0;
}
break;
case MIM_LONGDATA:
@@ -371,7 +334,7 @@ static void CALLBACK winmidi_input_callback(HMIDIIN device, unsigned message, DW
return;
case MIM_ERROR:
//error in input stream
- fprintf(stderr, "winmidi warning: error in input stream\n");
+ LOG("Error in input stream");
return;
case MIM_OPEN:
case MIM_CLOSE:
@@ -380,14 +343,14 @@ static void CALLBACK winmidi_input_callback(HMIDIIN device, unsigned message, DW
}
- DBGPF("winmidi incoming message type %d channel %d control %d value %f\n",
+ DBGPF("Incoming message type %d channel %d control %d value %f",
ident.fields.type, ident.fields.channel, ident.fields.control, val.normalised);
EnterCriticalSection(&backend_config.push_events);
if(backend_config.events_alloc <= backend_config.events_active){
backend_config.event = realloc((void*) backend_config.event, (backend_config.events_alloc + 1) * sizeof(winmidi_event));
if(!backend_config.event){
- fprintf(stderr, "Failed to allocate memory\n");
+ LOG("Failed to allocate memory");
backend_config.events_alloc = 0;
backend_config.events_active = 0;
LeaveCriticalSection(&backend_config.push_events);
@@ -408,7 +371,7 @@ static void CALLBACK winmidi_input_callback(HMIDIIN device, unsigned message, DW
}
static void CALLBACK winmidi_output_callback(HMIDIOUT device, unsigned message, DWORD_PTR inst, DWORD param1, DWORD param2){
- DBGPF("winmidi output callback on thread %ld\n", GetCurrentThreadId());
+ DBGPF("Output callback on thread %ld", GetCurrentThreadId());
}
static int winmidi_match_input(char* prefix){
@@ -418,13 +381,13 @@ static int winmidi_match_input(char* prefix){
size_t n;
if(!prefix){
- fprintf(stderr, "winmidi detected %u input devices\n", inputs);
+ LOGPF("Detected %u input devices", inputs);
}
else{
n = strtoul(prefix, &next_token, 10);
if(!(*next_token) && n < inputs){
midiInGetDevCaps(n, &input_caps, sizeof(MIDIINCAPS));
- fprintf(stderr, "winmidi selected input device %s for ID %d\n", input_caps.szPname, n);
+ LOGPF("Selected input device %s for ID %d", input_caps.szPname, n);
return n;
}
}
@@ -433,10 +396,10 @@ static int winmidi_match_input(char* prefix){
for(n = 0; n < inputs; n++){
midiInGetDevCaps(n, &input_caps, sizeof(MIDIINCAPS));
if(!prefix){
- printf("\tID %d: %s\n", n, input_caps.szPname);
+ printf("\tID %d: %s", n, input_caps.szPname);
}
else if(!strncmp(input_caps.szPname, prefix, strlen(prefix))){
- fprintf(stderr, "winmidi selected input device %s (ID %" PRIsize_t ") for name %s\n", input_caps.szPname, n, prefix);
+ LOGPF("Selected input device %s (ID %" PRIsize_t ") for name %s", input_caps.szPname, n, prefix);
return n;
}
}
@@ -451,13 +414,13 @@ static int winmidi_match_output(char* prefix){
size_t n;
if(!prefix){
- fprintf(stderr, "winmidi detected %u output devices\n", outputs);
+ LOGPF("Detected %u output devices", outputs);
}
else{
n = strtoul(prefix, &next_token, 10);
if(!(*next_token) && n < outputs){
midiOutGetDevCaps(n, &output_caps, sizeof(MIDIOUTCAPS));
- fprintf(stderr, "winmidi selected output device %s for ID %d\n", output_caps.szPname, n);
+ LOGPF("Selected output device %s for ID %d", output_caps.szPname, n);
return n;
}
}
@@ -466,10 +429,10 @@ static int winmidi_match_output(char* prefix){
for(n = 0; n < outputs; n++){
midiOutGetDevCaps(n, &output_caps, sizeof(MIDIOUTCAPS));
if(!prefix){
- printf("\tID %d: %s\n", n, output_caps.szPname);
+ printf("\tID %d: %s", n, output_caps.szPname);
}
else if(!strncmp(output_caps.szPname, prefix, strlen(prefix))){
- fprintf(stderr, "winmidi selected output device %s (ID %" PRIsize_t " for name %s\n", output_caps.szPname, n, prefix);
+ LOGPF("Selected output device %s (ID %" PRIsize_t " for name %s", output_caps.szPname, n, prefix);
return n;
}
}
@@ -477,10 +440,9 @@ static int winmidi_match_output(char* prefix){
return -1;
}
-static int winmidi_start(){
- size_t n = 0, p;
+static int winmidi_start(size_t n, instance** inst){
+ size_t p;
int device, rv = -1;
- instance** inst = NULL;
winmidi_instance_data* data = NULL;
struct sockaddr_storage sockadd = {
0
@@ -488,19 +450,7 @@ static int winmidi_start(){
//this really should be a size_t but getsockname specifies int* for some reason
int sockadd_len = sizeof(sockadd);
char* error = NULL;
- DBGPF("winmidi main thread ID is %ld\n", GetCurrentThreadId());
-
- //fetch all instances
- if(mm_backend_instances(BACKEND_NAME, &n, &inst)){
- fprintf(stderr, "Failed to fetch instance list\n");
- return 1;
- }
-
- //no instances, we're done
- if(!n){
- free(inst);
- return 0;
- }
+ DBGPF("Main thread ID is %ld", GetCurrentThreadId());
//output device list if requested
if(backend_config.list_devices){
@@ -512,13 +462,13 @@ static int winmidi_start(){
//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");
+ LOG("Failed to open feedback socket");
return 1;
}
if(getsockname(backend_config.socket_pair[0], (struct sockaddr*) &sockadd, &sockadd_len)){
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, WSAGetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &error, 0, NULL);
- fprintf(stderr, "winmidi failed to query feedback socket information: %s\n", error);
+ LOGPF("Failed to query feedback socket information: %s", error);
LocalFree(error);
return 1;
}
@@ -534,15 +484,15 @@ static int winmidi_start(){
// ((struct sockaddr_in6*) &sockadd)->sin6_addr = in6addr_any;
// break;
default:
- fprintf(stderr, "winmidi invalid feedback socket family\n");
+ LOG("Invalid feedback socket family");
return 1;
}
- DBGPF("winmidi feedback socket family %d port %d\n", sockadd.ss_family, be16toh(((struct sockaddr_in*)&sockadd)->sin_port));
+ DBGPF("Feedback socket family %d port %d", sockadd.ss_family, be16toh(((struct sockaddr_in*)&sockadd)->sin_port));
backend_config.socket_pair[1] = socket(sockadd.ss_family, SOCK_DGRAM, IPPROTO_UDP);
if(backend_config.socket_pair[1] < 0 || connect(backend_config.socket_pair[1], (struct sockaddr*) &sockadd, sockadd_len)){
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, WSAGetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &error, 0, NULL);
- fprintf(stderr, "winmidi failed to connect to feedback socket: %s\n", error);
+ LOGPF("Failed to connect to feedback socket: %s", error);
LocalFree(error);
return 1;
}
@@ -556,11 +506,11 @@ static int winmidi_start(){
if(data->read){
device = winmidi_match_input(data->read);
if(device < 0){
- fprintf(stderr, "Failed to match input device %s for instance %s\n", data->read, inst[p]->name);
+ LOGPF("Failed to match input device %s for instance %s", data->read, inst[p]->name);
goto bail;
}
if(midiInOpen(&(data->device_in), device, (DWORD_PTR) winmidi_input_callback, (DWORD_PTR) inst[p], CALLBACK_FUNCTION | MIDI_IO_STATUS) != MMSYSERR_NOERROR){
- fprintf(stderr, "Failed to open input device for instance %s\n", inst[p]->name);
+ LOGPF("Failed to open input device for instance %s", inst[p]->name);
goto bail;
}
//start midi input callbacks
@@ -571,38 +521,31 @@ static int winmidi_start(){
if(data->write){
device = winmidi_match_output(data->write);
if(device < 0){
- fprintf(stderr, "Failed to match output device %s for instance %s\n", data->read, inst[p]->name);
+ LOGPF("Failed to match output device %s for instance %s", data->read, inst[p]->name);
goto bail;
}
if(midiOutOpen(&(data->device_out), device, (DWORD_PTR) winmidi_output_callback, (DWORD_PTR) inst[p], CALLBACK_FUNCTION) != MMSYSERR_NOERROR){
- fprintf(stderr, "Failed to open output device for instance %s\n", inst[p]->name);
+ LOGPF("Failed to open output device for instance %s", inst[p]->name);
goto bail;
}
}
}
//register the feedback socket to the core
- fprintf(stderr, "winmidi backend registering 1 descriptor to core\n");
+ LOG("Registering 1 descriptor to core");
if(mm_manage_fd(backend_config.socket_pair[0], BACKEND_NAME, 1, NULL)){
goto bail;
}
rv = 0;
bail:
- free(inst);
return rv;
}
-static int winmidi_shutdown(){
- size_t n, u;
- instance** inst = NULL;
+static int winmidi_shutdown(size_t n, instance** inst){
+ size_t u;
winmidi_instance_data* data = NULL;
- if(mm_backend_instances(BACKEND_NAME, &n, &inst)){
- fprintf(stderr, "Failed to fetch instance list\n");
- return 1;
- }
-
for(u = 0; u < n; u++){
data = (winmidi_instance_data*) inst[u]->impl;
free(data->read);
@@ -621,9 +564,10 @@ static int winmidi_shutdown(){
midiOutClose(data->device_out);
data->device_out = NULL;
}
+
+ free(inst[u]->impl);
}
- free(inst);
closesocket(backend_config.socket_pair[0]);
closesocket(backend_config.socket_pair[1]);
@@ -635,6 +579,6 @@ static int winmidi_shutdown(){
LeaveCriticalSection(&backend_config.push_events);
DeleteCriticalSection(&backend_config.push_events);
- fprintf(stderr, "winmidi backend shut down\n");
+ LOG("Backend shut down");
return 0;
}
diff --git a/backends/winmidi.h b/backends/winmidi.h
index ffa6a26..81e7439 100644
--- a/backends/winmidi.h
+++ b/backends/winmidi.h
@@ -4,11 +4,11 @@ MM_PLUGIN_API int init();
static int winmidi_configure(char* option, char* value);
static int winmidi_configure_instance(instance* inst, char* option, char* value);
static instance* winmidi_instance();
-static channel* winmidi_channel(instance* inst, char* spec);
+static channel* winmidi_channel(instance* inst, char* spec, uint8_t flags);
static int winmidi_set(instance* inst, size_t num, channel** c, channel_value* v);
static int winmidi_handle(size_t num, managed_fd* fds);
-static int winmidi_start();
-static int winmidi_shutdown();
+static int winmidi_start(size_t n, instance** inst);
+static int winmidi_shutdown(size_t n, instance** inst);
typedef struct /*_winmidi_instance_data*/ {
char* read;
@@ -19,11 +19,11 @@ typedef struct /*_winmidi_instance_data*/ {
enum /*_winmidi_channel_type*/ {
none = 0,
- note,
- cc,
- pressure,
- aftertouch,
- pitchbend
+ note = 0x90,
+ cc = 0xB0,
+ pressure = 0xA0,
+ aftertouch = 0xD0,
+ pitchbend = 0xE0
};
typedef union {
diff --git a/config.c b/config.c
index 8e7e581..c7e2f7e 100644
--- a/config.c
+++ b/config.c
@@ -175,7 +175,7 @@ static int config_glob_scan(instance* inst, channel_spec* spec){
return 0;
}
-static channel* config_glob_resolve(instance* inst, channel_spec* spec, uint64_t n){
+static channel* config_glob_resolve(instance* inst, channel_spec* spec, uint64_t n, uint8_t map_direction){
size_t glob = 0, glob_length;
ssize_t bytes = 0;
uint64_t current_value = 0;
@@ -216,7 +216,7 @@ static channel* config_glob_resolve(instance* inst, channel_spec* spec, uint64_t
}
}
- result = inst->backend->channel(inst, resolved_spec);
+ result = inst->backend->channel(inst, resolved_spec, map_direction);
if(spec->globs && !result){
fprintf(stderr, "Failed to match multichannel evaluation %s to a channel\n", resolved_spec);
}
@@ -294,8 +294,8 @@ static int config_map(char* to_raw, char* from_raw){
//iterate, resolve globs and map
rv = 0;
for(n = 0; !rv && n < max(spec_from.channels, spec_to.channels); n++){
- channel_from = config_glob_resolve(instance_from, &spec_from, min(n, spec_from.channels));
- channel_to = config_glob_resolve(instance_to, &spec_to, min(n, spec_to.channels));
+ channel_from = config_glob_resolve(instance_from, &spec_from, min(n, spec_from.channels), mmchannel_input);
+ channel_to = config_glob_resolve(instance_to, &spec_to, min(n, spec_to.channels), mmchannel_output);
if(!channel_from || !channel_to){
rv = 1;
@@ -317,6 +317,7 @@ int config_read(char* cfg_filepath){
size_t line_alloc = 0;
ssize_t status;
map_type mapping_type = map_rtl;
+ FILE* source = NULL;
char* line_raw = NULL, *line, *separator;
//create heap copy of file name because original might be in readonly memory
@@ -346,7 +347,7 @@ int config_read(char* cfg_filepath){
source_file = source_dir;
}
- FILE* source = fopen(source_file, "r");
+ source = fopen(source_file, "r");
if(!source){
fprintf(stderr, "Failed to open configuration file for reading\n");
diff --git a/configs/launchctl-sacn.cfg b/configs/launchctl-sacn.cfg
index 0f4a19b..dedfc0f 100644
--- a/configs/launchctl-sacn.cfg
+++ b/configs/launchctl-sacn.cfg
@@ -14,6 +14,7 @@ read = Launch Control
[sacn out]
universe = 1
+priority = 100
[map]
diff --git a/configs/layering.cfg b/configs/layering.cfg
new file mode 100644
index 0000000..7adcd6f
--- /dev/null
+++ b/configs/layering.cfg
@@ -0,0 +1,23 @@
+; This configuration demonstrates how to create a "layered" mapping
+; using the Lua backend. The 'control' channel on the layers instance
+; selects the offset to which the 16 input channels (mapped from
+; the rotaries of a Launch Control) are mapped on the output instance
+
+[backend artnet]
+bind = 0.0.0.0
+
+[midi in]
+read = Launch Control
+
+[artnet out]
+destination = 255.255.255.255
+universe = 1
+
+[lua layers]
+script = layering.lua
+
+[map]
+in.ch0.cc{0..15} > layers.in{0..15}
+layers.out{0..511} > out.{1..512}
+
+in.ch0.note0 > layers.control
diff --git a/configs/layering.lua b/configs/layering.lua
new file mode 100644
index 0000000..5d9458d
--- /dev/null
+++ b/configs/layering.lua
@@ -0,0 +1,76 @@
+-- This global variable has the current base offset for the input channels.
+-- We want to map 16 input channels (from MIDI) to 512 output channels (ArtNet),
+-- so we have 32 possible offsets (32 * 16 = 512)
+current_layer = 0
+
+-- Set the current_layer based on the control input channel
+function control(value)
+ current_layer = math.floor(value * 31.99);
+end
+
+-- Handler functions for the input channels
+-- Calculate the channel offset and just output the value the input channel provides
+function in0(value)
+ output("out"..((current_layer * 16)), value)
+ print("Output on out"..(current_layer * 16))
+end
+
+function in1(value)
+ output("out"..((current_layer * 16) + 1), value)
+end
+
+function in2(value)
+ output("out"..((current_layer * 16) + 2), value)
+end
+
+function in3(value)
+ output("out"..((current_layer * 16) + 3), value)
+end
+
+function in4(value)
+ output("out"..((current_layer * 16) + 4), value)
+end
+
+function in5(value)
+ output("out"..((current_layer * 16) + 5), value)
+end
+
+function in6(value)
+ output("out"..((current_layer * 16) + 6), value)
+end
+
+function in7(value)
+ output("out"..((current_layer * 16) + 7), value)
+end
+
+function in8(value)
+ output("out"..((current_layer * 16) + 8), value)
+end
+
+function in9(value)
+ output("out"..((current_layer * 16) + 9), value)
+end
+
+function in10(value)
+ output("out"..((current_layer * 16) + 10), value)
+end
+
+function in11(value)
+ output("out"..((current_layer * 16) + 11), value)
+end
+
+function in12(value)
+ output("out"..((current_layer * 16) + 12), value)
+end
+
+function in13(value)
+ output("out"..((current_layer * 16) + 13), value)
+end
+
+function in14(value)
+ output("out"..((current_layer * 16) + 14), value)
+end
+
+function in15(value)
+ output("out"..((current_layer * 16) + 15), value)
+end
diff --git a/installer.sh b/installer.sh
new file mode 100755
index 0000000..15ad203
--- /dev/null
+++ b/installer.sh
@@ -0,0 +1,213 @@
+#!/bin/bash
+
+################################################ SETUP ################################################
+deps=(libasound2-dev libevdev-dev liblua5.3-dev libjack-jackd2-dev pkg-config libssl-dev gcc make wget git)
+user=$(whoami) # for bypassing user check replace "$(whoami)" with "root".
+
+tmp_path=$(mktemp -d) # Repo download path
+updater_dir=/etc/midimonster-updater # Updater download + config path
+updater_file=$updater_dir/updater.conf
+
+latest_version=$(curl --silent "https://api.github.com/repos/cbdevnet/midimonster/releases/latest" | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/')
+
+makeargs=all # Build args
+
+VAR_DESTDIR="" # Unused
+VAR_PREFIX="/usr"
+VAR_PLUGINS="$VAR_PREFIX/lib/midimonster"
+VAR_DEFAULT_CFG="/etc/midimonster/midimonster.cfg"
+VAR_EXAMPLE_CFGS="$VAR_PREFIX/share/midimonster"
+
+bold=$(tput bold)
+normal=$(tput sgr0)
+
+################################################ SETUP ################################################
+
+############################################## FUNCTIONS ##############################################
+
+INSTALL-DEPS () { ##Install deps from array "$deps"
+ for t in ${deps[@]}; do
+ if [ $(dpkg-query -W -f='${Status}' $t 2>/dev/null | grep -c "ok installed") -eq 0 ]; then
+ printf "Installing %s\n" "$t"
+ apt-get install $t;
+ printf "Done\n";
+ else
+ printf "%s already installed!\n" "$t"
+ fi
+ done
+ printf "\n"
+}
+
+NIGHTLY_CHECK () {
+ #Asks for nightly version
+ read -p "Do you want to install the latest development version? (y/n)? " magic
+ case "$magic" in
+ y|Y)
+ printf "OK! You´re a risky person ;D\n"
+ NIGHTLY=1
+ ;;
+ n|N)
+ printf "That´s OK - installing the latest stable version for you ;-)\n"
+ NIGHTLY=0
+ ;;
+ *)
+ printf "Invalid input\n"
+ ERROR
+ ;;
+ esac
+
+ # Roll back to last tag if we're not on a nightly build
+ if [ "$NIGHTLY" != 1 ]; then
+ printf "Finding latest stable version...\n"
+ Iversion=$(git describe --abbrev=0)
+ printf "Starting Git checkout to %s...\n" "$Iversion"
+ git checkout -f -q $Iversion
+ fi
+}
+
+INSTALL-PREP () {
+ (
+ printf "Starting download...\n"
+ git clone https://github.com/cbdevnet/midimonster.git "$tmp_path" # Gets Midimonster
+ printf "\nInitializing repository...\n"
+ cd $tmp_path
+ git init $tmp_path
+ printf "\n"
+ )
+ NIGHTLY_CHECK
+ printf "Preparation successful\n\n"
+ printf "${bold}If you don't know what you're doing, just hit enter 4 times.${normal}\n"
+
+ read -e -i "$VAR_PREFIX" -p "PREFIX (Install root directory): " input # Reads VAR_PREFIX
+ VAR_PREFIX="${input:-$VAR_PREFIX}"
+
+ read -e -i "$VAR_PLUGINS" -p "PLUGINS (Plugin directory): " input # Reads VAR_PLUGINS
+ VAR_PLUGINS="${input:-$VAR_PLUGINS}"
+
+ read -e -i "$VAR_DEFAULT_CFG" -p "Default config path: " input # Reads VAR_DEFAULT_CFG
+ VAR_DEFAULT_CFG="${input:-$VAR_DEFAULT_CFG}"
+
+ read -e -i "$VAR_EXAMPLE_CFGS" -p "Example config directory: " input # Reads VAR_EXAMPLE_CFGS
+ VAR_EXAMPLE_CFGS="${input:-$VAR_EXAMPLE_CFGS}"
+
+ UPDATER_SAVE
+
+ export PREFIX=$VAR_PREFIX
+ export PLUGINS=$VAR_PLUGINS
+ export DEFAULT_CFG=$VAR_DEFAULT_CFG
+ export DESTDIR=$VAR_DESTDIR
+ export EXAMPLES=$VAR_EXAMPLE_CFGS
+}
+
+UPDATER-PREP () {
+ (
+ printf "Starting download...\n"
+ git clone https://github.com/cbdevnet/midimonster.git "$tmp_path" # Gets Midimonster
+ printf "\nInitializing repository...\n"
+ cd $tmp_path
+ git init $tmp_path
+ printf "Successfully imported settings from %s\n" "$updater_file"
+ )
+ NIGHTLY_CHECK
+ printf "Preparation successful\n\n"
+
+ rm -f "$VAR_PREFIX/bin/midimonster"
+ rm -rf "$VAR_PLUGINS/"
+
+ UPDATER_SAVE
+
+ export PREFIX=$VAR_PREFIX
+ export PLUGINS=$VAR_PLUGINS
+ export DEFAULT_CFG=$VAR_DEFAULT_CFG
+ export DESTDIR=$VAR_DESTDIR
+ export EXAMPLES=$VAR_EXAMPLE_CFGS
+}
+
+UPDATER () {
+ installed_version="$(midimonster --version)"
+ #installed_version="MIDIMonster v0.3-40-gafed325" # FOR TESTING ONLY! (or bypassing updater version check)
+ if [[ "$installed_version" =~ "$latest_version" ]]; then
+ printf "Newest Version is already installed! ${bold}($installed_version)${normal}\n\n"
+ ERROR
+ else
+ printf "The installed Version ${bold}´$installed_version´${normal} equals not the newest stable version ${bold}´$latest_version´${normal} (Maybe you are running a development version?)\n\n"
+ fi
+
+ UPDATER-PREP
+ INSTALL-RUN
+ DONE
+}
+
+INSTALL-RUN () { # Build
+ cd "$tmp_path"
+ make clean
+ make $makeargs
+ make install
+}
+
+UPDATER_SAVE () { # Saves file for the auto updater in this script
+ rm -rf $updater_dir
+ printf "Saving updater to %s/updater.sh\n" "$update_dir"
+ mkdir -p "$updater_dir"
+ wget https://raw.githubusercontent.com/cbdevnet/midimonster/master/installer.sh -O $updater_dir/updater.sh
+ printf "Creating symlink to updater in /usr/bin/midimonster-updater\n"
+ ln -s "$updater_dir/updater.sh" "/usr/bin/midimonster-updater"
+ chmod +x "$updater_dir/updater.sh"
+ printf "Exporting updater config to %s\n" "$updater_file"
+ printf "VAR_PREFIX=%s\nVAR_PLUGINS=%s\nVAR_DEFAULT_CFG=%s\nVAR_DESTDIR=%s\nVAR_EXAMPLE_CFGS=%s\n" "$VAR_PREFIX" "$VAR_PLUGINS" "$VAR_DEFAULT_CFG" "$VAR_DESTDIR" "$VAR_EXAMPLE_CFGS" > $updater_file
+}
+
+ERROR () {
+ printf "\nAborting...\n"
+ CLEAN
+ printf "Exiting...\n"
+ exit 1
+}
+
+DONE () {
+ printf "\nDone.\n"
+ CLEAN
+ exit 0
+}
+
+CLEAN () {
+ printf "\nCleaning...\n"
+ rm -rf $tmp_path
+}
+
+############################################## FUNCTIONS ##############################################
+
+
+################################################ Main #################################################
+trap ERROR SIGINT SIGTERM SIGKILL
+clear
+
+# Check if $user = root!
+if [ "$user" != "root" ]; then
+ printf "Installer must be run as root\n"
+ ERROR
+fi
+
+if [ $(wget -q --spider http://github.com) $? -eq 1 ]; then
+ printf "You need connection to the internet\n"
+ ERROR
+fi
+
+# Check if updater config file exist and import it (overwrites default values!)
+if [ -f $updater_file ]; then
+ printf "Starting updater...\n\n"
+ . $updater_file
+
+ # Check if binary $updater/bin/midimonster exist. If yes start updater else skip.
+ if [ -x "$VAR_PREFIX/bin/midimonster" ]; then
+ UPDATER
+ else
+ printf "midimonster binary not found, skipping updater.\n"
+ fi
+fi
+
+INSTALL-DEPS
+INSTALL-PREP
+printf "\n"
+INSTALL-RUN
+DONE
diff --git a/midimonster.1 b/midimonster.1
new file mode 100644
index 0000000..131ed44
--- /dev/null
+++ b/midimonster.1
@@ -0,0 +1,18 @@
+.TH MIDIMONSTER 1 "December 2019"
+.SH NAME
+midimonster \- Multi-protocol translation tool
+.SH SYNOPSIS
+.B midimonster
+.I config-file
+.SH DESCRIPTION
+.B MIDIMonster
+allows the user to translate any channel on one supported protocol into channel(s)
+on any other (or the same) supported protocol.
+.SH OPTIONS
+.TP
+.I config-file
+The configuration file to read. If not specified, a default configuration file is read.
+.SH "SEE ALSO"
+Online documentation and repository at https://github.com/cbdevnet/midimonster
+.SH AUTHOR
+Fabian "cbdev" Stumpf <fjs at fabianstumpf dot de>
diff --git a/midimonster.c b/midimonster.c
index eb64974..2ec165b 100644
--- a/midimonster.c
+++ b/midimonster.c
@@ -223,8 +223,12 @@ static void event_free(){
}
}
+static void version(){
+ printf("MIDIMonster %s\n", MIDIMONSTER_VERSION);
+}
+
static int usage(char* fn){
- fprintf(stderr, "MIDIMonster v0.2\n");
+ version();
fprintf(stderr, "Usage:\n");
fprintf(stderr, "\t%s <configfile>\n", fn);
return EXIT_FAILURE;
@@ -263,6 +267,21 @@ static int platform_initialize(){
return 0;
}
+static int args_parse(int argc, char** argv, char** cfg_file){
+ size_t u;
+ for(u = 1; u < argc; u++){
+ if(!strcmp(argv[u], "-v") || !strcmp(argv[u], "--version")){
+ version();
+ return 1;
+ }
+
+ //if nothing else matches, it's probably the configuration file
+ *cfg_file = argv[u];
+ }
+
+ return 0;
+}
+
int main(int argc, char** argv){
fd_set all_fds, read_fds;
event_collection* secondary = NULL;
@@ -271,8 +290,10 @@ int main(int argc, char** argv){
managed_fd* signaled_fds = NULL;
int rv = EXIT_FAILURE, error, maxfd = -1;
char* cfg_file = DEFAULT_CFG;
- if(argc > 1){
- cfg_file = argv[1];
+
+ //parse commandline arguments
+ if(args_parse(argc, argv, &cfg_file)){
+ return EXIT_FAILURE;
}
if(platform_initialize()){
diff --git a/midimonster.h b/midimonster.h
index 1192d6a..5844bb9 100644
--- a/midimonster.h
+++ b/midimonster.h
@@ -5,6 +5,16 @@
#include <stdint.h>
#include <inttypes.h>
+/* Core version unless set by the build process */
+#ifndef MIDIMONSTER_VERSION
+ #define MIDIMONSTER_VERSION "v0.3-dist"
+#endif
+
+/* Set backend name if unset */
+#ifndef BACKEND_NAME
+ #define BACKEND_NAME "unspec"
+#endif
+
/* API call attributes and visibilities */
#ifndef MM_API
#ifdef _WIN32
@@ -32,13 +42,15 @@
/* Debug messages only compile in when DEBUG is set */
#ifdef DEBUG
- #define DBGPF(format, ...) fprintf(stderr, (format), __VA_ARGS__)
- #define DBG(message) fprintf(stderr, "%s", (message))
+ #define DBGPF(format, ...) fprintf(stderr, "debug/%s\t" format "\n", (BACKEND_NAME), __VA_ARGS__)
#else
#define DBGPF(format, ...)
- #define DBG(message)
#endif
+/* Log messages should be routed through these macros to ensure interoperability with different core implementations */
+#define LOGPF(format, ...) fprintf(stderr, "%s\t" format "\n", (BACKEND_NAME), __VA_ARGS__)
+#define LOG(message) fprintf(stderr, "%s\t%s\n", (BACKEND_NAME), (message))
+
/* Stop compilation if the build system reports an error */
#ifdef BUILD_ERROR
#error The build system reported an error, compilation stopped. Refer to the invocation for this compilation unit for more information.
@@ -87,8 +99,11 @@ struct _managed_fd;
* Parse instance configuration from the user-supplied configuration
* file. Returning a non-zero value fails config parsing.
* * mmbackend_channel
- * Parse a channel-spec to be mapped to/from. Returning NULL signals an
- * out-of-memory condition and terminates the program.
+ * Parse a channel-spec to be mapped to/from. The `flags` parameter supplies
+ * additional information to the parser, such as whether the channel is being
+ * queried for use as input (to the MIDIMonster core) and/or output
+ * (from the MIDIMonster core) channel (on a per-query basis).
+ * Returning NULL signals an out-of-memory condition and terminates the program.
* * mmbackend_start
* Called after all instances have been created and all mappings
* have been set up. Only backends for which instances have been configured
@@ -121,14 +136,20 @@ struct _managed_fd;
*/
typedef int (*mmbackend_handle_event)(struct _backend_instance* inst, size_t channels, struct _backend_channel** c, struct _channel_value* v);
typedef struct _backend_instance* (*mmbackend_create_instance)();
-typedef struct _backend_channel* (*mmbackend_parse_channel)(struct _backend_instance* instance, char* spec);
+typedef struct _backend_channel* (*mmbackend_parse_channel)(struct _backend_instance* instance, char* spec, uint8_t flags);
typedef void (*mmbackend_free_channel)(struct _backend_channel* c);
typedef int (*mmbackend_configure)(char* option, char* value);
typedef int (*mmbackend_configure_instance)(struct _backend_instance* instance, char* option, char* value);
typedef int (*mmbackend_process_fd)(size_t nfds, struct _managed_fd* fds);
-typedef int (*mmbackend_start)();
+typedef int (*mmbackend_start)(size_t ninstances, struct _backend_instance** inst);
typedef uint32_t (*mmbackend_interval)();
-typedef int (*mmbackend_shutdown)();
+typedef int (*mmbackend_shutdown)(size_t ninstances, struct _backend_instance** inst);
+
+/* Bit masks for the `flags` parameter to mmbackend_parse_channel */
+typedef enum {
+ mmchannel_input = 0x1,
+ mmchannel_output = 0x2
+} mmbe_channel_flags;
/* Channel event value, .normalised is used by backends to determine channel values */
typedef struct _channel_value {
diff --git a/plugin.c b/plugin.c
index c7c9812..56d6651 100644
--- a/plugin.c
+++ b/plugin.c
@@ -30,6 +30,11 @@ static int plugin_attach(char* path, char* file){
char* path_separator = "/";
#endif
+ if(!path || !file || !strlen(path)){
+ fprintf(stderr, "Invalid plugin loader path\n");
+ return 1;
+ }
+
lib = calloc(strlen(path) + strlen(file) + 2, sizeof(char));
if(!lib){
fprintf(stderr, "Failed to allocate memory\n");
@@ -37,7 +42,7 @@ static int plugin_attach(char* path, char* file){
}
snprintf(lib, strlen(path) + strlen(file) + 2, "%s%s%s",
path,
- (path[strlen(path)] == path_separator[0]) ? "" : path_separator,
+ (path[strlen(path) - 1] == path_separator[0]) ? "" : path_separator,
file);
handle = dlopen(lib, RTLD_NOW);