7. Audio Render Thread

This section outlines all the components needed to run Monolith as a realtime audio service.

7.1. JACK audio

At the time of writing, JACK will the API used to handle realtime audio. The API is pretty sane, and it also works on OSX and Linux.

The JACK simple client example code will be used as reference for building these parts.

7.1.1. JACK data

Required JACK data needed to be held on to is the jack_port_t types for the stereo outputs, and the client struct jack_client_t.

<<struct_contents>>=
#ifndef MONOLITH_SIMPLE
jack_port_t *in[2];
jack_port_t *out[2];
jack_client_t *client;
#endif

On init, these are all set to NULL. Any programs wishing to access these items should probably check for NULL first!

<<init>>=
#ifndef MONOLITH_SIMPLE
m->out[0] = NULL;
m->out[1] = NULL;
m->client = NULL;
#endif

7.1.2. JACK callbacks

The actual audio callback for JACK is done with a static function called jack_process.

<<static_function_declarations>>=
#ifndef MONOLITH_SIMPLE
static int jack_process(jack_nframes_t nframes, void *arg);
#endif
<<functions>>=
#ifndef MONOLITH_SIMPLE
static int jack_process(jack_nframes_t nframes, void *arg)
{
    jack_default_audio_sample_t *out[2];
    monolith_d *m;

    m = (monolith_d *)arg;
    out[0] = (jack_default_audio_sample_t*)jack_port_get_buffer(m->out[0], nframes);
    out[1] = (jack_default_audio_sample_t*)jack_port_get_buffer(m->out[1], nframes);

    monolith_compute(m, nframes, out);
    return 0;
}
#endif

7.1.3. Starting Audio

The realtime audio thread is started with the function monolith_audio_start. In this case, it starts the JACK audio server. The internal samplerate should be set here.

7.1.3.1. In C

<<function_declarations>>=
void monolith_audio_start(monolith_d *m);
<<functions>>=
void monolith_audio_start(monolith_d *m)
{
#ifndef MONOLITH_SIMPLE
    const char **ports;
    const char *client_name;
    const char *server_name;
    jack_options_t options;
    jack_status_t status;

    if(m->client != NULL) {
        fprintf(stderr, "JACK audio server seems to be started already\n");
        return;
    }

    options = JackNullOption;

    server_name=NULL;
    client_name="Monolith"; /* set the client name */

    m->client = jack_client_open(client_name, options, &status, server_name);

    if(m->client == NULL) {
        fprintf(stderr, "JACK has failed you.\n");
        if (status & JackServerFailed) {
            fprintf (stderr, "It was unable to connect to the JACK server\n");
        }
        return;
    }

    if (status & JackServerStarted) {

    }

    if (status & JackNameNotUnique) {
        client_name = jack_get_client_name(m->client);
        fprintf(stderr, "unique name `%s' assigned\n", client_name);
    }

    jack_set_process_callback (m->client, jack_process, m);

    m->out[0] = jack_port_register(m->client, "output1",
                                JACK_DEFAULT_AUDIO_TYPE,
                                JackPortIsOutput, 0);
    m->out[1] = jack_port_register (m->client, "output2",
                                    JACK_DEFAULT_AUDIO_TYPE,
                                    JackPortIsOutput, 0);

    if((m->out[0] == NULL) || (m->out[1] == NULL)) {
        fprintf(stderr, "no more JACK ports available\n");
        return;
    }

    m->in[0] = jack_port_register(m->client, "input1",
                                  JACK_DEFAULT_AUDIO_TYPE,
                                  JackPortIsInput, 0);

    if(jack_activate(m->client)) {
        fprintf(stderr, "cannot activate client\n");
        return;
    }

    ports = jack_get_ports (m->client, NULL, NULL,
                            JackPortIsPhysical|JackPortIsInput);

    if (ports == NULL) {
        fprintf(stderr, "no physical playback ports\n");
        return;
    }

    if (jack_connect(m->client, jack_port_name(m->out[0]), ports[0])) {
        fprintf (stderr, "cannot connect output ports\n");
    }

    if (jack_connect(m->client, jack_port_name(m->out[1]), ports[1])) {
        fprintf (stderr, "cannot connect output ports\n");
    }


    monolith_sr_set(m, jack_get_sample_rate(m->client));
    jack_free (ports);
#endif
}

The scheme function for starting the audio can be done with monolith:audio-start.

7.1.3.2. In Scheme

<<primitive_entries>>=
{"monolith:audio-start", pp_audio_start, 0, 0, {CHR,___,___}},
<<scheme_functions>>=
static cell pp_audio_start(cell x) {
    monolith_audio_start(monolith_data_get());
    return UNSPECIFIC;
}
7.1.3.3. In Janet

<<core_janet_entries>>=
{
"monolith/audio-start",
j_audio_start,
"Starts audio."
},
<<janet_functions>>=
static Janet j_audio_start(int32_t argc, Janet *argv)
{
    monolith_d *m;
    janet_fixarity(argc, 0);
    m = monolith_data_get();
    monolith_audio_start(m);
    return janet_wrap_nil();
}

7.1.4. Stopping Audio

The jack audio thread is stopped with the function called monolith_audio_stop.

<<function_declarations>>=
void monolith_audio_stop(monolith_d *m);
<<functions>>=
void monolith_audio_stop(monolith_d *m)
{
#ifndef MONOLITH_SIMPLE
    if(m->client != NULL) {
        jack_client_close (m->client);
        m->client = NULL;
    }
#endif
}

The scheme function for starting the audio can be done with monolith:audio-stop.

<<primitive_entries>>=
{"monolith:audio-stop", pp_audio_stop, 0, 0, {CHR,___,___}},
<<scheme_functions>>=
static cell pp_audio_stop(cell x) {
    monolith_audio_stop(monolith_data_get());
    return UNSPECIFIC;
}

Audio is stopped at cleanup as well, if the jack client has been allocated.

<<cleanup>>=
monolith_audio_stop(m);



prev | home | next