diff options
author | Robin Haberkorn <robin.haberkorn@googlemail.com> | 2015-11-04 05:25:10 +0100 |
---|---|---|
committer | Robin Haberkorn <robin.haberkorn@googlemail.com> | 2015-11-04 05:25:10 +0100 |
commit | f5e8f70a7aaab571e1136e2eb789e3c1ebdc26f6 (patch) | |
tree | 827abc2ccf52626a418d271c32fbac802667f85b | |
parent | 4f800f22dcf18830a53bc8e736c51e4e7707b4a2 (diff) | |
download | applause2-f5e8f70a7aaab571e1136e2eb789e3c1ebdc26f6.tar.gz |
added simple support for MIDI CC commands
* MIDICCStream provides a stream of CC values as if
polled from the controller (this is emulated in
applause.c)
-rw-r--r-- | Makefile | 3 | ||||
-rw-r--r-- | applause.c | 123 | ||||
-rw-r--r-- | applause.lua | 21 |
3 files changed, 144 insertions, 3 deletions
@@ -19,7 +19,8 @@ JACK_CFLAGS := $(shell pkg-config --cflags jack) JACK_LDFLAGS := $(shell pkg-config --libs jack) CFLAGS += $(LUA_CFLAGS) $(READLINE_CFLAGS) $(JACK_CFLAGS) -LDFLAGS += $(LUA_LDFLAGS) $(READLINE_LDFLAGS) $(JACK_LDFLAGS) +LDFLAGS += $(LUA_LDFLAGS) $(READLINE_LDFLAGS) $(JACK_LDFLAGS) \ + -lpthread all : applause @@ -1,7 +1,9 @@ #define _XOPEN_SOURCE +#define _POSIX_C_SOURCE 199506L #include <stdio.h> #include <stdlib.h> +#include <stdint.h> #include <signal.h> #include <errno.h> @@ -9,6 +11,8 @@ #include <sys/ipc.h> #include <sys/sem.h> +#include <pthread.h> + #include <lua.h> #include <lauxlib.h> #include <lualib.h> @@ -17,6 +21,7 @@ #include <readline/history.h> #include <jack/jack.h> +#include <jack/midiport.h> #include <jack/ringbuffer.h> #define LUA_MODULE "applause.lua" @@ -29,10 +34,31 @@ static jack_client_t *client = NULL; #define DEFAULT_BUFFER_SIZE 100 /* milliseconds */ static jack_ringbuffer_t *buffer = NULL; static int buffer_sem; +/** + * True if a buffer underrun occurs. + * FIXME: sig_atomic_t is probably the wrong type. + * Perhaps use the Mintomic types. + */ static sig_atomic_t buffer_xrun = 0; static sig_atomic_t interrupted = 0; +static jack_port_t *midi_port; +/** + * State of all the MIDI controls as updated by + * CC commands. + * Access must be synchronized with `midi_mutex`. + * Perhaps this should use atomic operations instead + * (wasting a few kilobytes). + */ +static uint8_t midi_controls[16][127]; +/** + * Mutex for synchronizing access to `midi_controls`. + * This MUST have the PTHREAD_PRIO_INHERIT protocol + * since it will be locked from a realtime thread. + */ +static pthread_mutex_t midi_mutex; + static int svsem_init(size_t value) { @@ -113,6 +139,9 @@ jack_process(jack_nframes_t nframes, void *arg) size_t len = sizeof(*out)*nframes; size_t r; + void *midi_in; + jack_nframes_t midi_events; + out = (jack_default_audio_sample_t *) jack_port_get_buffer(output_port, nframes); @@ -138,6 +167,29 @@ jack_process(jack_nframes_t nframes, void *arg) memset((char *)out + r, 0, len - r); buffer_xrun |= len - r > 0; + /* + * MIDI processing. + * NOTE: This uses a priority inheriting mutex to + * remain realtime capable. + */ + midi_in = jack_port_get_buffer(midi_port, nframes); + midi_events = jack_midi_get_event_count(midi_in); + + for (int i = 0; i < midi_events; i++) { + jack_midi_event_t event; + + jack_midi_event_get(&event, midi_in, i); + + if ((event.buffer[0] & 0xF0) != 0xB0) + /* not a control command */ + continue; + + pthread_mutex_lock(&midi_mutex); + midi_controls[event.buffer[0] & 0x0F] + [event.buffer[1]] = event.buffer[2]; + pthread_mutex_unlock(&midi_mutex); + } + return 0; } @@ -193,8 +245,9 @@ init_audio(int buffer_size) jack_on_shutdown (client, jack_shutdown, 0); - /* create two ports */ - + /* + * Create output ports + */ output_port = jack_port_register (client, "output", JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); @@ -204,6 +257,27 @@ init_audio(int buffer_size) } /* + * Create MIDI input port + */ + midi_port = jack_port_register(client, "midi_input", + JACK_DEFAULT_MIDI_TYPE, + JackPortIsInput, 0); + if (midi_port == NULL) { + fprintf(stderr, "no more JACK ports available\n"); + return 1; + } + + memset(&midi_controls, 0, sizeof(midi_controls)); + + pthread_mutexattr_t prioinherit; + if (pthread_mutexattr_setprotocol(&prioinherit, PTHREAD_PRIO_INHERIT)) { + fprintf(stderr, "Error initializing MIDI mutex with priority inheritance!\n"); + return 1; + } + + pthread_mutex_init(&midi_mutex, &prioinherit); + + /* * Calculate the buffer size in bytes given the `buffer_size` * in milliseconds. * Make sure it is at least the Jack server's buffer size, @@ -268,6 +342,39 @@ init_audio(int buffer_size) } static int +l_MIDICCStream_getValue(lua_State *L) +{ + int top = lua_gettop(L); + lua_Integer channel, control, value; + + luaL_argcheck(L, top == 2, top, "Control and channel number expected"); + luaL_checktype(L, 1, LUA_TNUMBER); + luaL_checktype(L, 2, LUA_TNUMBER); + + control = lua_tointeger(L, 1); + luaL_argcheck(L, 0 <= control && control <= 127, control, + "Invalid control number range (0 <= x <= 127)"); + channel = lua_tointeger(L, 2); + luaL_argcheck(L, 0 <= channel && channel <= 15, channel, + "Invalid channel range (0 <= x <= 15)"); + + pthread_mutex_lock(&midi_mutex); + /* + * This thread might be lifted to realtime priority + * since this is a priority inheritance mutex. + * We will block a realtime thread while we're in the + * critical section. + * Therefore it is crucial that the following code + * is realtime-safe. + */ + value = midi_controls[channel][control]; + pthread_mutex_unlock(&midi_mutex); + + lua_pushinteger(L, value); + return 1; +} + +static int l_Stream_play(lua_State *L) { int top = lua_gettop(L); @@ -363,6 +470,11 @@ main(int argc, char **argv) {NULL, NULL} }; + static const luaL_Reg midiccstream_methods[] = { + {"getValue", l_MIDICCStream_getValue}, + {NULL, NULL} + }; + lua_State *L; /* @@ -407,6 +519,13 @@ main(int argc, char **argv) lua_pop(L, 1); /* + * Register C functions in the `MIDICCStream` class + */ + lua_getglobal(L, "MIDICCStream"); + luaL_register(L, NULL, midiccstream_methods); + lua_pop(L, 1); + + /* * Set global `samplerate` */ lua_pushinteger(L, (lua_Integer)jack_get_sample_rate(client)); diff --git a/applause.lua b/applause.lua index 5d19ad8..e5a9b42 100644 --- a/applause.lua +++ b/applause.lua @@ -884,6 +884,27 @@ function NoiseStream:tick() end end +MIDICCStream = DeriveClass(Stream, function(self, control, channel) + self.control = control + self.channel = channel or 0 +end) + +-- implemented in applause.c, private! +function MIDICCStream.getValue(control, channel) + error("C function not registered!") +end + +-- FIXME: Perhaps implement tick() directly in C? +function MIDICCStream:tick() + local control = self.control + local channel = self.channel + local getValue = self.getValue + + return function() + return getValue(control, channel) + end +end + -- primitives function tostream(v) |