12. Hardware Listener

All hardware peripherals (monome, griffin) are polled inside of a single listener thread. Anytime a message is received by a hardware interface, it is immediately handled by the corresponding virtual interface.

12.1. Thread hardware listener callback

The hardware listener runs as a POSIX thread, so the pthread header is included.

<<system_includes>>=
#ifdef MONOLITH_OSX
#include <pthread/pthread.h>
#endif

#ifdef MONOLITH_LINUX
#include <pthread.h>
#endif

#include <unistd.h>

The function usleep is always implicitely declared when enforcing c89 C mode. To remove this warning, the function prototype is explicitely written.

This delcaration seems to cause problems when cross compiling for norns because it can't seem to find the useconds_t. For now, I am using a ifndef macro as a hack for now.

<<system_includes>>=
#ifndef MONOLITH_NORNS
int usleep(useconds_t usec);
#endif

The listener itself is contained in a static pthread function called listener. Contained inside is a while loop which periodically polls the peripherals for any new events. Any new events found will be immediately processed with the virtual hardware interface.

<<static_function_declarations>>=
static void* listener(void *data);
<<functions>>=
void monolith_poll(monolith_d *m)
{
<<poll_the_monome>>
<<poll_the_griffin>>
<<poll_the_g430>>
<<poll_the_norns>>
}
static void* listener(void *data)
{
    monolith_d *m;
    m = data;
    while (m->listener_running) {
        monolith_poll(m);
        usleep(800);
    }
    pthread_exit(NULL);
}

12.2. Monolith listener data

The listener thread has a pthread data type called listener_thread

<<struct_contents>>=
pthread_t listener_thread;

On init, this is set to be NULL.

<<init>>=
m->listener_thread = 0;

There is also a flag that is used to break out of the while loop called listener_running.

<<struct_contents>>=
int listener_running;
<<init>>=
m->listener_running = 0;

By default, it's set to be 0.

12.3. Starting/Stopping the hardware listener

The hardware listener can be started with the callback monolith_listener_start. This will start the pthread listener.

<<function_declarations>>=
void monolith_listener_start(monolith_d *m);
<<functions>>=
void monolith_listener_start(monolith_d *m)
{
    if(m->listener_running) {
        fprintf(stderr, "Hardware listener seems to be running already\n");
        return;
    }
    m->listener_running = 1;
    pthread_create(&m->listener_thread, NULL, listener, m);
}

The hardware listener can be started from scheme using the function monolith:listener-start.

<<primitive_entries>>=
{"monolith:listener-start", pp_listener_start, 0, 0, {CHR,___,___}},
<<scheme_functions>>=
static cell pp_listener_start(cell x) {
    monolith_listener_start(monolith_data_get());
    return UNSPECIFIC;
}
<<core_janet_entries>>=
{
"monolith/listener-start",
j_listener_start,
"Starts the main listener."
},
<<janet_functions>>=
static Janet j_listener_start(int32_t argc, Janet *argv)
{
    monolith_d *m;
    janet_fixarity(argc, 0);
    m = monolith_data_get();
    monolith_listener_start(m);
    return janet_wrap_nil();
}

The hardware listern can be stopped with the callback monolith_listener_stop.

<<function_declarations>>=
void monolith_listener_stop(monolith_d *m);
<<functions>>=
void monolith_listener_stop(monolith_d *m)
{
    if(m->listener_running == 0) return;
    m->listener_running = 0;
    pthread_join(m->listener_thread, NULL);
}

12.4. Stopping the listener at cleanup

At cleanup, monolith_listener_stop is stopped. To prevent segfaults, hardware must be explicitely stopped after that.

<<cleanup>>=
monolith_listener_stop(m);
<<hardware_cleanup>>

12.5. Single-threaded event polling

This will create a blocking event loop. On the norns, this should be able to allow Janet functions to be called by the norns aux menu.

SIGINT will be mapped so that a command line test will cleanly quit out of the listener loop.

<<function_declarations>>=
void monolith_listener_loop(monolith_d *m);
<<system_includes>>=
#include <signal.h>
<<functions>>=
static int loop_running = 0;
static void close_eventloop(int dummy)
{
    fprintf(stderr, "Stopping event loop.\n");
    loop_running = 0;
}

void monolith_listener_loop(monolith_d *m)
{
    signal(SIGINT, close_eventloop);
    loop_running = 1;
    m->listener_running = 1;

#ifdef MONOLITH_NORNS
    while (loop_running && m->norns_running) {
        monolith_poll(m);
        usleep(800);
    }
#else
    while (loop_running) {
        monolith_poll(m);
        usleep(800);
    }
#endif

    m->listener_running = 0;
}

Call this from Janet with monolith/event-loop.

<<core_janet_entries>>=
{
"monolith/event-loop",
j_event_loop,
"A single-threaded event loop.\n"
},
<<janet_functions>>=
static Janet j_event_loop(int32_t argc, Janet *argv)
{
    janet_fixarity(argc, 0);
    monolith_listener_loop(monolith_data_get());
    return janet_wrap_nil();
}



prev | home | next