aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorcbdev <cb@cbcdn.com>2017-06-06 00:08:06 +0200
committercbdev <cb@cbcdn.com>2017-06-06 00:08:06 +0200
commit1cbf63996a51a67bdd221257d74adec61f03ea32 (patch)
tree6d9c06e3ca13bf47b83eadc37d2b3f989e2575f4
parentddb185aaa2a5ef6a70c130675826c33f90057591 (diff)
downloadmidimonster-1cbf63996a51a67bdd221257d74adec61f03ea32.tar.gz
midimonster-1cbf63996a51a67bdd221257d74adec61f03ea32.tar.bz2
midimonster-1cbf63996a51a67bdd221257d74adec61f03ea32.zip
Implement MIDI backend event fetching
-rw-r--r--artnet.c6
-rw-r--r--artnet.h4
-rw-r--r--backend.c66
-rw-r--r--backend.h3
-rw-r--r--midi.c98
-rw-r--r--midi.h4
-rw-r--r--midimonster.c37
-rw-r--r--midimonster.h27
-rw-r--r--monster.cfg17
9 files changed, 223 insertions, 39 deletions
diff --git a/artnet.c b/artnet.c
index 598fed8..f2a2fa5 100644
--- a/artnet.c
+++ b/artnet.c
@@ -107,14 +107,14 @@ static channel* artnet_channel(instance* instance, char* spec){
return mm_channel(instance, channel, 1);
}
-static int artnet_set(size_t num, channel* c, channel_value* v){
+static int artnet_set(instance* inst, size_t num, channel* c, channel_value* v){
//TODO
return 1;
}
-static int artnet_handle(size_t num, int* fd, void** data){
+static int artnet_handle(size_t num, managed_fd* fds){
//TODO
- return 1;
+ return 0;
}
static int artnet_start(){
diff --git a/artnet.h b/artnet.h
index 4976938..00a75a7 100644
--- a/artnet.h
+++ b/artnet.h
@@ -5,8 +5,8 @@ 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 int artnet_set(size_t num, channel* c, channel_value* v);
-static int artnet_handle(size_t num, int* fd, void** data);
+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();
diff --git a/backend.c b/backend.c
index 2eb6fcf..1b297d4 100644
--- a/backend.c
+++ b/backend.c
@@ -9,10 +9,37 @@ static instance** instances = NULL;
static size_t nchannels = 0;
static channel** channels = NULL;
+int backends_handle(size_t nfds, managed_fd* fds){
+ size_t u, p, n;
+ int rv = 0;
+ managed_fd xchg;
+
+ for(u = 0; u < nbackends && !rv; u++){
+ n = 0;
+
+ for(p = 0; p < nfds; p++){
+ if(fds[p].backend == backends + u){
+ xchg = fds[n];
+ fds[n] = fds[p];
+ fds[p] = xchg;
+ n++;
+ }
+ }
+
+ rv |= backends[u].process(n, fds);
+ }
+ return rv;
+}
+
+int backends_notify(size_t nev, channel* c, channel_value* v){
+ //TODO
+ return 1;
+}
+
channel* mm_channel(instance* i, uint64_t ident, uint8_t create){
size_t u;
for(u = 0; u < nchannels; u++){
- if(channels[u]->instance == 0 && channels[u]->ident == ident){
+ if(channels[u]->instance == i && channels[u]->ident == ident){
return channels[u];
}
}
@@ -58,6 +85,22 @@ instance* mm_instance(){
return instances[ninstances++];
}
+instance* mm_instance_find(char* name, uint64_t ident){
+ size_t u;
+ backend* b = backend_match(name);
+ if(!b){
+ return NULL;
+ }
+
+ for(u = 0; u < ninstances; u++){
+ if(instances[u]->backend == b && instances[u]->ident == ident){
+ return instances[u];
+ }
+ }
+
+ return NULL;
+}
+
int mm_backend_instances(char* name, size_t* ninst, instance*** inst){
backend* b = backend_match(name);
size_t n = 0, u;
@@ -132,12 +175,25 @@ instance* instance_match(char* name){
}
struct timeval backend_timeout(){
- //TODO fetch minimum poll interval from backends
+ size_t u;
+ uint32_t res, secs = 1, msecs = 0;
+
+ for(u = 0; u < nbackends; u++){
+ if(backends[u].interval){
+ res = backends[u].interval();
+ if((res / 1000) <= secs){
+ secs = res / 1000;
+ if((res % 1000) < msecs){
+ msecs = res % 1000;
+ }
+ }
+ }
+ }
+
struct timeval tv = {
- 0,
- 10000
+ secs,
+ msecs
};
-
return tv;
}
diff --git a/backend.h b/backend.h
index 16cf77e..e2eaac5 100644
--- a/backend.h
+++ b/backend.h
@@ -1,5 +1,8 @@
#include <sys/types.h>
+int backends_handle(size_t nfds, managed_fd* fds);
+int backends_notify(size_t nev, channel* c, channel_value* v);
+
backend* backend_match(char* name);
instance* instance_match(char* name);
struct timeval backend_timeout();
diff --git a/midi.c b/midi.c
index ca1f66b..857460f 100644
--- a/midi.c
+++ b/midi.c
@@ -14,6 +14,7 @@ enum /*_midi_channel_type*/ {
none = 0,
note,
cc,
+ nrpn,
sysmsg
};
@@ -125,6 +126,10 @@ static channel* midi_channel(instance* instance, char* spec){
ident.fields.type = note;
channel = spec + 4;
}
+ else if(!strncmp(spec, "nrpn", 4)){
+ ident.fields.type = nrpn;
+ channel = spec + 4;
+ }
else{
fprintf(stderr, "Unknown MIDI channel specification %s\n", spec);
return NULL;
@@ -153,14 +158,84 @@ static channel* midi_channel(instance* instance, char* spec){
return NULL;
}
-static int midi_set(size_t num, channel* c, channel_value* v){
- //TODO
- return 1;
+static int midi_set(instance* inst, size_t num, channel* c, channel_value* v){
+ size_t u;
+ midi_instance_data* data;
+
+ for(u = 0; u < num; u++){
+ data = (midi_instance_data*) c[u].instance->impl;
+ //TODO write out event
+ }
+
+ return 0;
}
-static int midi_handle(size_t num, int* fd, void** data){
- //TODO
- return 1;
+static int midi_handle(size_t num, managed_fd* fds){
+ snd_seq_event_t* ev = NULL;
+ instance* inst = NULL;
+ channel* changed = NULL;
+ channel_value val;
+ union {
+ struct {
+ uint8_t pad[5];
+ uint8_t type;
+ uint8_t channel;
+ uint8_t control;
+ } fields;
+ uint64_t label;
+ } ident = {
+ .label = 0
+ };
+
+ while(snd_seq_event_input(sequencer, &ev) > 0){
+ ident.label = 0;
+ switch(ev->type){
+ case SND_SEQ_EVENT_NOTEON:
+ case SND_SEQ_EVENT_NOTEOFF:
+ case SND_SEQ_EVENT_KEYPRESS:
+ case SND_SEQ_EVENT_NOTE:
+ ident.fields.type = note;
+ ident.fields.channel = ev->data.note.channel;
+ ident.fields.control = ev->data.note.note;
+ val.normalised = (double)ev->data.note.velocity / 127.0;
+ break;
+ case SND_SEQ_EVENT_CONTROLLER:
+ ident.fields.type = cc;
+ ident.fields.channel = ev->data.control.channel;
+ ident.fields.control = ev->data.control.param;
+ val.raw.u64 = ev->data.control.value;
+ val.normalised = (double)ev->data.control.value / 127.0;
+ break;
+ case SND_SEQ_EVENT_CONTROL14:
+ case SND_SEQ_EVENT_NONREGPARAM:
+ case SND_SEQ_EVENT_REGPARAM:
+ //FIXME value calculation
+ ident.fields.type = nrpn;
+ ident.fields.channel = ev->data.control.channel;
+ ident.fields.control = ev->data.control.param;
+ break;
+ default:
+ fprintf(stderr, "Ignored MIDI event of unsupported type\n");
+ 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");
+ continue;
+ }
+
+ changed = mm_channel(inst, ident.label, 0);
+ if(changed){
+ if(mm_channel_event(changed, val)){
+ free(ev);
+ return 1;
+ }
+ }
+ }
+ free(ev);
+ return 0;
}
static int midi_start(){
@@ -180,6 +255,7 @@ static int midi_start(){
for(p = 0; p < n; p++){
data = (midi_instance_data*) inst[p]->impl;
data->port = snd_seq_create_simple_port(sequencer, inst[p]->name, SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE | SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ, SND_SEQ_PORT_TYPE_MIDI_GENERIC);
+ inst[p]->ident = data->port;
//make connections
if(data->write){
@@ -218,7 +294,9 @@ static int midi_start(){
fprintf(stderr, "Registering %d descriptors to core\n", nfds);
for(p = 0; p < nfds; p++){
- mm_manage_fd(pfds[p].fd, BACKEND_NAME, 1, NULL);
+ if(mm_manage_fd(pfds[p].fd, BACKEND_NAME, 1, NULL)){
+ goto bail;
+ }
}
rv = 0;
@@ -232,12 +310,18 @@ bail:
static int midi_shutdown(){
size_t n, p;
instance** inst = NULL;
+ 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;
+ free(data->read);
+ free(data->write);
+ data->read = NULL;
+ data->write = NULL;
free(inst[p]->impl);
}
free(inst);
diff --git a/midi.h b/midi.h
index 7a13f7c..dc997bc 100644
--- a/midi.h
+++ b/midi.h
@@ -5,8 +5,8 @@ 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 int midi_set(size_t num, channel* c, channel_value* v);
-static int midi_handle(size_t num, int* fd, void** data);
+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();
diff --git a/midimonster.c b/midimonster.c
index 2c3013d..33858fd 100644
--- a/midimonster.c
+++ b/midimonster.c
@@ -133,6 +133,12 @@ void fds_free(){
fd = NULL;
}
+int mm_channel_event(channel* c, channel_value v){
+ //TODO
+ fprintf(stderr, "Stub implementation: Channel on %s at value %f\n", c->instance->name, v.normalised);
+ return 0;
+}
+
int usage(char* fn){
fprintf(stderr, "MIDIMonster v0.1\n");
fprintf(stderr, "Usage:\n");
@@ -143,7 +149,8 @@ int usage(char* fn){
int main(int argc, char** argv){
fd_set all_fds, read_fds;
struct timeval tv;
- size_t u;
+ size_t u, n;
+ managed_fd* signaled_fds;
int rv = EXIT_FAILURE, error, maxfd = -1;
char* cfg_file = DEFAULT_CFG;
if(argc > 1){
@@ -176,6 +183,13 @@ int main(int argc, char** argv){
signal(SIGINT, signal_handler);
+ //allocate data buffers
+ signaled_fds = calloc(fds, sizeof(managed_fd));
+ if(!signaled_fds){
+ fprintf(stderr, "Failed to allocate memory\n");
+ goto bail;
+ }
+
//create initial fd set
FD_ZERO(&all_fds);
for(u = 0; u < fds; u++){
@@ -195,14 +209,29 @@ int main(int argc, char** argv){
fprintf(stderr, "select failed: %s\n", strerror(errno));
break;
}
- //TODO process all backends, collect events
- //TODO push all events to backends
- //FIXME notify non-fd backends
+
+ //find all signaled fds
+ n = 0;
+ for(u = 0; u < fds; u++){
+ if(fd[u].fd >= 0 && FD_ISSET(fd[u].fd, &read_fds)){
+ signaled_fds[n] = fd[u];
+ n++;
+ }
+ }
+
+ //run backend processing, collect events
+ if(backends_handle(n, signaled_fds)){
+ fprintf(stderr, "Backends failed to handle input\n");
+ goto bail;
+ }
+
+ //TODO push collected events to target backends
}
rv = EXIT_SUCCESS;
bail:
//free all data
+ free(signaled_fds);
backends_stop();
channels_free();
instances_free();
diff --git a/midimonster.h b/midimonster.h
index d548528..bd42d8f 100644
--- a/midimonster.h
+++ b/midimonster.h
@@ -10,21 +10,24 @@
struct _channel_value;
struct _backend_channel;
struct _backend_instance;
+struct _managed_fd;
-typedef int (*mmbackend_handle_event)(size_t channels, struct _backend_channel* c, struct _channel_value* v);
+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 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 fds, int* fd, void** impl);
+typedef int (*mmbackend_process_fd)(size_t nfds, struct _managed_fd* fds);
typedef int (*mmbackend_start)();
-typedef uint32_t (*mmbackend_max_interval)();
+typedef uint32_t (*mmbackend_interval)();
typedef int (*mmbackend_shutdown)();
typedef struct _channel_value {
- double raw_double;
- uint64_t raw_u64;
+ union {
+ double dbl;
+ uint64_t u64;
+ } raw;
double normalised;
} channel_value;
@@ -39,11 +42,12 @@ typedef struct /*_mm_backend*/ {
mmbackend_start start;
mmbackend_shutdown shutdown;
mmbackend_free_channel channel_free;
+ mmbackend_interval interval;
} backend;
typedef struct _backend_instance {
backend* backend;
- uint64_t ident; /*FIXME needed? identification provided by name*/
+ uint64_t ident;
void* impl;
char* name;
} instance;
@@ -56,7 +60,7 @@ typedef struct _backend_channel {
//FIXME might be replaced by struct pollfd
//FIXME who frees impl
-typedef struct /*_mm_managed_fd*/ {
+typedef struct _managed_fd {
int fd;
backend* backend;
void* impl;
@@ -87,6 +91,14 @@ int mm_backend_register(backend b);
*/
instance* mm_instance();
/*
+ * Finds an instance matching the specified backend and identifier.
+ * Since setting an identifier for an instance is optional,
+ * this may not work depending on the backend.
+ * Instance identifiers may for example be set in the backends
+ * mmbackend_start call.
+ */
+instance* mm_instance_find(char* backend, uint64_t ident);
+/*
* Provides a pointer to a channel structure, pre-filled with
* the provided instance reference and identifier.
* Will return previous allocations if the provided fields
@@ -109,6 +121,7 @@ int mm_manage_fd(int fd, char* backend, int manage, void* impl);
int mm_channel_event(channel* c, channel_value v);
/*
* Query all active instances for a given backend.
+ * *i will need to be freed by the caller.
*/
int mm_backend_instances(char* backend, size_t* n, instance*** i);
int mm_map_channel(channel* from, channel* to);
diff --git a/monster.cfg b/monster.cfg
index eb65a07..3ff2e3f 100644
--- a/monster.cfg
+++ b/monster.cfg
@@ -7,17 +7,13 @@ net = 0
;[backend osc]
-[midi indisc]
-port = MIDIMonster Input 1
-;mode = input,output,create
+[midi bcf]
+read = BCF
[midi lc1]
-device = 20:0
-;mode = input,output
+read = Launch Control
[midi xlate]
-port = Translation Output 1
-;mode = input,output,create
[artnet net1]
net = 1
@@ -28,6 +24,9 @@ output = true
net = 0
[map]
-net1.231 = foo.1
-net1.255 = lc1.cc1.1
+net1.255 = lc1.cc0.1
+net1.256 = lc1.note0.9
+net1.257 = bcf.cc0.81
+;net1.231 = foo.1
+;net1.255 = lc1.cc0.1
;net1.231 = osc.f/channel5/ toggle=127