Jack

Jack is intended for professional audio, connecting many devices in arbitrary configurations. All software components run synchronously, with effectively zero latency.

Introduction

The article Knowing Jack gives a gentle introduction to Jack.

Resources

Starting Jack

The Jack server is jackd. It has one required parameter which is the sound backend such as ALSA. The minimal command is

	
jackd -dalsa
	
      

if you are using a normal Linux distro such as Fedora or Ubuntu, this will quite likely fail if the PulseAudio system is running. This may need to be stopped, or at least paused while you run Jack. See the previous chapter for stopping PulseAudio, To pause it, I usually run this in a terminal window:

	
pasuspender cat
	
      

This will pause PulseAudio until cat terminates, which it will do when you enter ctrl-d.

jackd will try to start using the Linux real-time scheduler. If you want to run without it, use the option

	
jackd --no-realtime -dalsa
	
      

If you want to run with the realtime scheduler, there are several ways:

Note that if you run the server as the root user, then you will not be able to connect to it from clients that are not in the jackuser group.

No apparent systemd or upstart scripts exist for Jack, but there are instructions about starting Jack at boot time from Gentoo jack :

	
#!/sbin/runscript
 # This programm will be used by init in order to launch jackd with the privileges
 # and id of the user defined into /etc/conf.d/jackd

 depend() {
	need alsasound
 }

 start() {
	if ! test -f "${JACKDHOME}/.jackdrc"; then
		eerror "You must start and configure jackd before launch it. Sorry."
		eerror "You can use qjackctl for that."
		return 1
	else JACKDOPTS=$(cat "${JACKDHOME}/.jackdrc"|sed -e 's\/usr/bin/jackd \\')
	fi

	if [ -e /var/run/jackd.pid ]; then
		 rm /var/run/jackd.pid
	fi

	ebegin "Starting JACK Daemon"
	env HOME="${JACKDHOME}" start-stop-daemon --start \
		--quiet --background \
		--make-pidfile --pidfile /var/run/jackd.pid \
		-c ${JACKDUSER} \
		-x /usr/bin/jackd -- ${JACKDOPTS} >${LOG}
	
	sleep 2
	if ! pgrep -u ${JACKDUSER} jackd > /dev/null; then
 		eerror "JACK daemon can't be started! Check logfile: ${LOG}"
 	fi
 	eend $?
 }

 stop() {
 	ebegin "Stopping JACK daemon -- please wait"
 	start-stop-daemon --stop --pidfile /var/run/jackd.pid &>/dev/null
 	eend $?
 }

 restart() {
 	svc_stop
 	while `pgrep -u ${JACKDUSER} jackd >/dev/null`; do
 		sleep 1
 	done
 	svc_start
 }
	
      

File: /etc/conf.d/jackd:

  


 # owner of jackd process (Must be an existing user.)
 JACKDUSER="dom"

 # .jackdrc location for that user (Must be existing, JACKDUSER can use
 # qjackctl in order to create it.) 
 JACKDHOME="/home/${JACKDUSER}" 

 # logfile (/dev/null for nowhere)
 LOG=/var/log/jackd.log


Create and save those 2 files. Don't forget to adjust JACKDUSER to the wanted user name (the same as yours I guess). We need to make /etc/init.d/jackd executable:



# chmod +x /etc/init.d/jackd


Adding the script into the default run-level:


# rc-update add jackd default


Before restarting your system or starting this script, you must be sure that jackd is configured for $JACKUSER or jackd will fail. This is because the script will read /home/${USER}/.jackdrc. If this file doesn't exist, the easiest way to create it is to run QJackCtl as explained above.

Note on Realtime: Due to a limitation in the implementation of start-stop-daemon, it is not possible to start jackd in realtime mode as a non-root user by this method if using pam_limits. start-stop-daemon does not implement support for pam_sessions, meaning that changes to limits.conf have no effect in this context.

User tools

There is really only one tool that you need to use with Jack: qjackctl. This gives a graphical view of which Jack applications are playing and allows you to link inputs and outputs.

A simple tutorial on using qjackctl is HowToQjackCtlConnections It is actually amazingly simple to use: click on a source, link it to a destination by clicking on that. A line will be shown linking them - and that's all you have to do. Many Jack applications will do this for you, so you just observe the results.

Applications using Jack

There are many pieces of software using Jack, described in Applications using JACK

mplayer

To run mplayer using Jack, add the option -ao jack:

	
mplayer -ao jack 54154.mp3
	
      

mplayer used in this way will connect to the Jack system output device. To output to another Jack application such as jack-rack, append the output application to the audio output command

	
mplayer -ao jack:port=jack_rack 54154.mp3
	
      

VLC

VLC will play to Jack output if the Jack module is included. This is available as a downloadable Debian package vlc-plugin-jack. You can check if you have it by seeing if jack is listed as a module in vlc --list shows ALSA but not Jack.

Play a file using Jack by e.g.

	
vlc --aout jack 54154.mp3
	
      

You should be able to connect to a particular Jack application using the option --jack-connect-regex <string>.

TiMidity

TiMidity is a MIDI player discussed later. It can play to Jack output devices by

	
timidity -Oj 54154.mid
	
      

Jack-supplied programs

Jack comes with a large number of clients:

	
jack_alias                  jack_midisine
jack_bufsize                jack_monitor_client
jack_connect                jack_multiple_metro
jack_control                jack_net_master
jack_cpu                    jack_net_slave
jack_cpu_load               jack_netsource
jackd                       jack_rec
jackdbus                    jack_samplerate
jack_disconnect             jack_server_control
jack_evmon                  jack_session_notify
jack_freewheel              jack_showtime
jack_iodelay                jack_simple_client
jack_latent_client          jack_simple_session_client
jack_load                   jack_test
jack_lsp                    jack_thru
jack_metro                  jack_transport
jack_midi_dump              jack_unload
jack_midi_latency_test      jack_wait
jack_midiseq                jack_zombie
	
      

For many of these the source code is available in the Jack source code distribution and there is a man page for each one.

Running, say, jack_thru connects the system capture ports to the jack_thru input ports and the jack_thru output ports to the system playback ports. You can then do things such as disconnect ports using "client:port" for the port name as in

	
jack_disconnect jack_thru:output_1 system:playback_1
	
      

These command line tools allow you to do the same kind of actions as qjackctl

Other Jack programs

The page Applications using JACK lists many applications using Jack.

The page Jack MIDI Apps at linuxaudio.org lists many MIDI applications using Jack

Using a different soundcard

The default ALSA device for Jack will be hw:0. If you wish to use a different soundcard then you can spcecify this when starting Jack as in

	
jackd -dalsa -dhw:0
	
      

I have a USB Sound Blaster card, which requires some extra parameters

	
jackd -dalsa -dhw:2 -r 48000 -S
	
      

This doesn't work great - I get a regular "ticking" sound.

Without the -S (16-bit) flag I just get the cryptic

	
ALSA: cannot set hardware parameters for playback
	
      

Alternatively, I can run

	
jackd -dalsa -dplughw:2 -r 48000
	
      

When I start it this way, Jack advises against using ALSA plug devices but it works best so far.

How can I use multiple soundcards with JACK?

See How can I use multiple soundcards with JACK?

Mixing audio

If two output ports from two different sources are connected to the same input port, then Jack will mix them for you. This allows you to sing along to your favourite MP3 file with no effort:

Of course, there is no volume control on each source. You can insert a mixer such as jack_mixer (maybe in your distro too), and then use that to control the volume of each source, as shown in this qjackctl screen:

Writing Audio Applications With JACK

See Writing Audio Applications With JACK - A tutorial/journal

The design of Jack is discussed at The JACK Audio Connection Kit by its primary author Paul Davis. The goals are

To pick the eyes out of this, the principal goals are

The second is guaranteed by the Jack framework. The first is supplied by the Jack framework - as long as the applications are coded correctly.

Under the hood Jack uses fast Linux (Unix) pipelines to stream data from one aplication to another. Within each Jack application is a realtime loop which takes data off the input pipe and sends data to the output pipe. To avoid latency delays, there should essentially be no (or as little as possible) processing between reading and writing data - the ideal would be to pass pointer data from input to output, or at most to just do a memcpy.

So how can processing be done? Copy the data read to another data structure and pass processing off to another thread, or copy data processed in another thread to the ouput pipe. Anything else will cause latency which may become noticeable. In particular, certain system calls are essentially banned: malloc can cause swapping; sleep is an obvious no-no; read/write etc can cause disk I/O; pthread_cond_wait will ... wait.

Jack applications are inherently multi-threaded. In a Linux world this means Posix threads, and fortunately there is a book PThreads Primer by Bil Lewis and Daniel J Berg to tell you all about Posix threads!

There are mechanisms to set up a Jack application:

Libraries

The following examples need to be linked to various libraries. So far, they need to be

	
 LIBS = -ljack -ljackserver -lasound -lm -lpthread -lsndfile
	
      

Port information

Jack uses ports which carry mono 32-bit data. Each port has a name as a string, and poperties such as input and output. Once a connection to a Jack server has been made, queries for ports known to the server can be made using jack_get_ports. If the arguments are NULL or zero then al ports are returned, or patterns can be used to restrict the port names returned. Once a port name is found it can be turned into a jack_port_t and its properties can be queried.

A program to do this is listports.c:

/** @file delay.c
 *
 * @brief This client delays one channel by 4096 framse.
 */

#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <signal.h>
#ifndef WIN32
#include <unistd.h>
#endif
#include <jack/jack.h>

jack_client_t *client;

void print_port_info(char *name) {
    printf("Port name is %s\n", name);
    jack_port_t *port =	jack_port_by_name (client, name);
    if (port == NULL) {
	printf("No port by name %s\n", name);
	return;
    }
    printf("  Type is %s\n", jack_port_type(port));

    int flags = jack_port_flags(port);
    if (flags & JackPortIsInput) 
	printf("  Is an input port\n");
    else
	printf("  Is an output port\n");
    char **connections = jack_port_get_connections(port);
    char **c = connections;
    printf("  Connected to:\n");
    while ((c != NULL) && (*c != NULL)) {
	printf("    %s\n", *c++);
    }
    if (connections != NULL)
	jack_free(connections);
}

int
main ( int argc, char *argv[] )
{
    int i;
    const char **ports;
    const char *client_name;
    const char *server_name = NULL;
    jack_options_t options = JackNullOption;
    jack_status_t status;

    if ( argc >= 2 )        /* client name specified? */
    {
        client_name = argv[1];
        if ( argc >= 3 )    /* server name specified? */
        {
            server_name = argv[2];
            options |= JackServerName;
        }
    }
    else              /* use basename of argv[0] */
    {
        client_name = strrchr ( argv[0], '/' );
        if ( client_name == 0 )
        {
            client_name = argv[0];
        }
        else
        {
            client_name++;
        }
    }

    /* open a client connection to the JACK server */

    client = jack_client_open ( client_name, options, &status, server_name );
    if ( client == NULL )
    {
        fprintf ( stderr, "jack_client_open() failed, "
                  "status = 0x%2.0x\n", status );
        if ( status & JackServerFailed )
        {
            fprintf ( stderr, "Unable to connect to JACK server\n" );
        }
        exit ( 1 );
    }
    if ( status & JackServerStarted )
    {
        fprintf ( stderr, "JACK server started\n" );
    }
    if ( status & JackNameNotUnique )
    {
        client_name = jack_get_client_name ( client );
        fprintf ( stderr, "unique name `%s' assigned\n", client_name );
    }

    if ( jack_activate ( client ) )
    {
        fprintf ( stderr, "cannot activate client" );
        exit ( 1 );
    }

     ports = jack_get_ports ( client, NULL, NULL, 0 );
    if ( ports == NULL )
    {
        fprintf ( stderr, "no ports\n" );
        exit ( 1 );
    }
    char **p = ports;
    while (*p != NULL)
	print_port_info(*p++);
    jack_free(ports);

    jack_client_close ( client );
    exit ( 0 );
}

      

Copy input to output

The Jack source code distribution has an "example clients" subdirectory. Included in there is a client thru_client.c which just copies input to output. The processing heart of this example is the function process. This takes a number of frames available on both input and output as parameter. This loops through the (stereo) channels, gets corresponding input and output buffers (for input and output pipelines) and copies data from input to corresponding output.

The code is

/** @file thru_client.c
 *
 * @brief This simple through client demonstrates the basic features of JACK
 * as they would be used by many applications.
 */

#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <signal.h>
#ifndef WIN32
#include <unistd.h>
#endif
#include <jack/jack.h>

jack_port_t **input_ports;
jack_port_t **output_ports;
jack_client_t *client;

static void signal_handler ( int sig )
{
    jack_client_close ( client );
    fprintf ( stderr, "signal received, exiting ...\n" );
    exit ( 0 );
}

/**
 * The process callback for this JACK application is called in a
 * special realtime thread once for each audio cycle.
 *
 * This client follows a simple rule: when the JACK transport is
 * running, copy the input port to the output.  When it stops, exit.
 */

int
process ( jack_nframes_t nframes, void *arg )
{
    int i;
    jack_default_audio_sample_t *in, *out;
    for ( i = 0; i < 2; i++ )
    {
        in = jack_port_get_buffer ( input_ports[i], nframes );
        out = jack_port_get_buffer ( output_ports[i], nframes );
        memcpy ( out, in, nframes * sizeof ( jack_default_audio_sample_t ) );
    }
    return 0;
}

/**
 * JACK calls this shutdown_callback if the server ever shuts down or
 * decides to disconnect the client.
 */
void
jack_shutdown ( void *arg )
{
    free ( input_ports );
    free ( output_ports );
    exit ( 1 );
}

int
main ( int argc, char *argv[] )
{
    int i;
    const char **ports;
    const char *client_name;
    const char *server_name = NULL;
    jack_options_t options = JackNullOption;
    jack_status_t status;

    if ( argc >= 2 )        /* client name specified? */
    {
        client_name = argv[1];
        if ( argc >= 3 )    /* server name specified? */
        {
            server_name = argv[2];
            options |= JackServerName;
        }
    }
    else              /* use basename of argv[0] */
    {
        client_name = strrchr ( argv[0], '/' );
        if ( client_name == 0 )
        {
            client_name = argv[0];
        }
        else
        {
            client_name++;
        }
    }

    /* open a client connection to the JACK server */

    client = jack_client_open ( client_name, options, &status, server_name );
    if ( client == NULL )
    {
        fprintf ( stderr, "jack_client_open() failed, "
                  "status = 0x%2.0x\n", status );
        if ( status & JackServerFailed )
        {
            fprintf ( stderr, "Unable to connect to JACK server\n" );
        }
        exit ( 1 );
    }
    if ( status & JackServerStarted )
    {
        fprintf ( stderr, "JACK server started\n" );
    }
    if ( status & JackNameNotUnique )
    {
        client_name = jack_get_client_name ( client );
        fprintf ( stderr, "unique name `%s' assigned\n", client_name );
    }

    /* tell the JACK server to call `process()' whenever
       there is work to be done.
    */

    jack_set_process_callback ( client, process, 0 );

    /* tell the JACK server to call `jack_shutdown()' if
       it ever shuts down, either entirely, or if it
       just decides to stop calling us.
    */

    jack_on_shutdown ( client, jack_shutdown, 0 );

    /* create two ports pairs*/
    input_ports = ( jack_port_t** ) calloc ( 2, sizeof ( jack_port_t* ) );
    output_ports = ( jack_port_t** ) calloc ( 2, sizeof ( jack_port_t* ) );

    char port_name[16];
    for ( i = 0; i < 2; i++ )
    {
        sprintf ( port_name, "input_%d", i + 1 );
        input_ports[i] = jack_port_register ( client, port_name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0 );
        sprintf ( port_name, "output_%d", i + 1 );
        output_ports[i] = jack_port_register ( client, port_name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0 );
        if ( ( input_ports[i] == NULL ) || ( output_ports[i] == NULL ) )
        {
            fprintf ( stderr, "no more JACK ports available\n" );
            exit ( 1 );
        }
    }

    /* Tell the JACK server that we are ready to roll.  Our
     * process() callback will start running now. */

    if ( jack_activate ( client ) )
    {
        fprintf ( stderr, "cannot activate client" );
        exit ( 1 );
    }

    /* Connect the ports.  You can't do this before the client is
     * activated, because we can't make connections to clients
     * that aren't running.  Note the confusing (but necessary)
     * orientation of the driver backend ports: playback ports are
     * "input" to the backend, and capture ports are "output" from
     * it.
     */

    ports = jack_get_ports ( client, NULL, NULL, JackPortIsPhysical|JackPortIsOutput );
    if ( ports == NULL )
    {
        fprintf ( stderr, "no physical capture ports\n" );
        exit ( 1 );
    }

    for ( i = 0; i < 2; i++ )
        if ( jack_connect ( client, ports[i], jack_port_name ( input_ports[i] ) ) )
            fprintf ( stderr, "cannot connect input ports\n" );

    free ( ports );

    ports = jack_get_ports ( client, NULL, NULL, JackPortIsPhysical|JackPortIsInput );
    if ( ports == NULL )
    {
        fprintf ( stderr, "no physical playback ports\n" );
        exit ( 1 );
    }

    for ( i = 0; i < 2; i++ )
        if ( jack_connect ( client, jack_port_name ( output_ports[i] ), ports[i] ) )
            fprintf ( stderr, "cannot connect input ports\n" );

    free ( ports );

    /* install a signal handler to properly quits jack client */
#ifdef WIN32
    signal ( SIGINT, signal_handler );
    signal ( SIGABRT, signal_handler );
    signal ( SIGTERM, signal_handler );
#else
    signal ( SIGQUIT, signal_handler );
    signal ( SIGTERM, signal_handler );
    signal ( SIGHUP, signal_handler );
    signal ( SIGINT, signal_handler );
#endif

    /* keep running until the transport stops */

    while (1)
    {
#ifdef WIN32
        Sleep ( 1000 );
#else
        sleep ( 1 );
#endif
    }

    jack_client_close ( client );
    exit ( 0 );
}

      

Delaying audio

While this book is not about audio effects, we can easily introduce one effect - latency - by just delaying sounds. Now this - and any time consuming actions - are against the spirit (and implementation!) of Jack, so it can only be done in co-operation with the Jack model.

The simplest idea is just to throw in sleep commands at the right places. This would assume that calls to the process callback happen asynchronously but they don't - they happen synchronously within the Jack processing thread. Activities which cost time aren't allowed. If you try it, you will will end up with lots of xruns at best, seizures of Jack at worst.

In this case the solution is straightforward: keep a buffer in which previous inputs are kept, and read older entries out of this buffer when output is requested. A "big enough" wrap-around array will do this, where old entries are read out and new entries read in.

The following program delay.c will copy the left channel in real time, but delay the left channel by 4096 samples:

/** @file delay.c
 *
 * @brief This client delays one channel by 4096 framse.
 */

#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <signal.h>
#ifndef WIN32
#include <unistd.h>
#endif
#include <jack/jack.h>

jack_port_t **input_ports;
jack_port_t **output_ports;
jack_client_t *client;

#define SIZE 8192
#define DELAY 4096
jack_default_audio_sample_t buffer[SIZE];
int idx, delay_idx;

static void signal_handler ( int sig )
{
    jack_client_close ( client );
    fprintf ( stderr, "signal received, exiting ...\n" );
    exit ( 0 );
}

static void copy2out( jack_default_audio_sample_t *out, 
		      jack_nframes_t nframes) {
    if (delay_idx + nframes < SIZE) {
	memcpy(out, buffer + delay_idx, 
	       nframes * sizeof ( jack_default_audio_sample_t ) );
    } else {
	int frames_to_end = SIZE - delay_idx;
	int overflow = delay_idx + nframes - SIZE;
	memcpy(out, buffer + delay_idx, 
	       frames_to_end * sizeof ( jack_default_audio_sample_t ) );
	memcpy(out, buffer, overflow * sizeof(jack_default_audio_sample_t));
    }
    delay_idx = (delay_idx + nframes) % SIZE;
}

static void copy2buffer( jack_default_audio_sample_t *in, 
		      jack_nframes_t nframes) {
    if (idx + nframes < SIZE) {
	memcpy(buffer + idx, in,
	       nframes * sizeof ( jack_default_audio_sample_t ) );
    } else {
	int frames_to_end = SIZE - idx;
	int overflow = idx + nframes - SIZE;
	memcpy(buffer + idx, in,
	       frames_to_end * sizeof ( jack_default_audio_sample_t ) );
	memcpy(buffer, in, overflow * sizeof(jack_default_audio_sample_t));
    }
    idx = (idx + nframes) % SIZE;
}

/**
 * The process callback for this JACK application is called in a
 * special realtime thread once for each audio cycle.
 *
 * This client follows a simple rule: when the JACK transport is
 * running, copy the input port to the output.  When it stops, exit.
 */

int
process ( jack_nframes_t nframes, void *arg )
{
    int i;
    jack_default_audio_sample_t *in, *out;

    in = jack_port_get_buffer ( input_ports[0], nframes );
    out = jack_port_get_buffer ( output_ports[0], nframes );
    memcpy ( out, in, nframes * sizeof ( jack_default_audio_sample_t ) );

    in = jack_port_get_buffer ( input_ports[1], nframes );
    out = jack_port_get_buffer ( output_ports[1], nframes );
    copy2out(out, nframes);
    copy2buffer(in, nframes);

    return 0;
}

/**
 * JACK calls this shutdown_callback if the server ever shuts down or
 * decides to disconnect the client.
 */
void
jack_shutdown ( void *arg )
{
    free ( input_ports );
    free ( output_ports );
    exit ( 1 );
}

int
main ( int argc, char *argv[] )
{
    int i;
    const char **ports;
    const char *client_name;
    const char *server_name = NULL;
    jack_options_t options = JackNullOption;
    jack_status_t status;

    if ( argc >= 2 )        /* client name specified? */
    {
        client_name = argv[1];
        if ( argc >= 3 )    /* server name specified? */
        {
            server_name = argv[2];
            options |= JackServerName;
        }
    }
    else              /* use basename of argv[0] */
    {
        client_name = strrchr ( argv[0], '/' );
        if ( client_name == 0 )
        {
            client_name = argv[0];
        }
        else
        {
            client_name++;
        }
    }

    /* open a client connection to the JACK server */

    client = jack_client_open ( client_name, options, &status, server_name );
    if ( client == NULL )
    {
        fprintf ( stderr, "jack_client_open() failed, "
                  "status = 0x%2.0x\n", status );
        if ( status & JackServerFailed )
        {
            fprintf ( stderr, "Unable to connect to JACK server\n" );
        }
        exit ( 1 );
    }
    if ( status & JackServerStarted )
    {
        fprintf ( stderr, "JACK server started\n" );
    }
    if ( status & JackNameNotUnique )
    {
        client_name = jack_get_client_name ( client );
        fprintf ( stderr, "unique name `%s' assigned\n", client_name );
    }

    /* tell the JACK server to call `process()' whenever
       there is work to be done.
    */

    jack_set_process_callback ( client, process, 0 );

    /* tell the JACK server to call `jack_shutdown()' if
       it ever shuts down, either entirely, or if it
       just decides to stop calling us.
    */

    jack_on_shutdown ( client, jack_shutdown, 0 );

    /* create two ports pairs*/
    input_ports = ( jack_port_t** ) calloc ( 2, sizeof ( jack_port_t* ) );
    output_ports = ( jack_port_t** ) calloc ( 2, sizeof ( jack_port_t* ) );

    char port_name[16];
    for ( i = 0; i < 2; i++ )
    {
        sprintf ( port_name, "input_%d", i + 1 );
        input_ports[i] = jack_port_register ( client, port_name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0 );
        sprintf ( port_name, "output_%d", i + 1 );
        output_ports[i] = jack_port_register ( client, port_name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0 );
        if ( ( input_ports[i] == NULL ) || ( output_ports[i] == NULL ) )
        {
            fprintf ( stderr, "no more JACK ports available\n" );
            exit ( 1 );
        }
    }

    bzero(buffer, SIZE * sizeof ( jack_default_audio_sample_t ));
    delay_idx = 0;
    idx = DELAY;

    /* Tell the JACK server that we are ready to roll.  Our
     * process() callback will start running now. */

    if ( jack_activate ( client ) )
    {
        fprintf ( stderr, "cannot activate client" );
        exit ( 1 );
    }

    /* Connect the ports.  You can't do this before the client is
     * activated, because we can't make connections to clients
     * that aren't running.  Note the confusing (but necessary)
     * orientation of the driver backend ports: playback ports are
     * "input" to the backend, and capture ports are "output" from
     * it.
     */

    ports = jack_get_ports ( client, NULL, NULL, JackPortIsPhysical|JackPortIsOutput );
    if ( ports == NULL )
    {
        fprintf ( stderr, "no physical capture ports\n" );
        exit ( 1 );
    }

    for ( i = 0; i < 2; i++ )
        if ( jack_connect ( client, ports[i], jack_port_name ( input_ports[i] ) ) )
            fprintf ( stderr, "cannot connect input ports\n" );

    free ( ports );

    ports = jack_get_ports ( client, NULL, NULL, JackPortIsPhysical|JackPortIsInput );
    if ( ports == NULL )
    {
        fprintf ( stderr, "no physical playback ports\n" );
        exit ( 1 );
    }

    for ( i = 0; i < 2; i++ )
        if ( jack_connect ( client, jack_port_name ( output_ports[i] ), ports[i] ) )
            fprintf ( stderr, "cannot connect input ports\n" );

    free ( ports );

    /* install a signal handler to properly quits jack client */
#ifdef WIN32
    signal ( SIGINT, signal_handler );
    signal ( SIGABRT, signal_handler );
    signal ( SIGTERM, signal_handler );
#else
    signal ( SIGQUIT, signal_handler );
    signal ( SIGTERM, signal_handler );
    signal ( SIGHUP, signal_handler );
    signal ( SIGINT, signal_handler );
#endif

    /* keep running until the transport stops */

    while (1)
    {
#ifdef WIN32
        Sleep ( 1000 );
#else
        sleep ( 1 );
#endif
    }

    jack_client_close ( client );
    exit ( 0 );
}

      

Audacity with Jack

Audacity is Jack aware. You can use it to capture and display Jack streams. But that doesn't mean that for the user it plays in a nice way! With a running Jack system, starting Audacity registers it with Jack - but there are no input nor output ports. These only show up when you start a record session with Audacity. It then establishes its own links within Jack.

For example, with thru_client as the only client within Jack, qjackctl shows the connections

In this, the capture devices are connected to the thru_client inputs, and the thru_client outputs are connected to the playback outputs.

Just starting Audacity but not recording anything makes no changes to this connection graph.

But when Audacity starts recording with thru_client already running, qjackctl shows the links established as in this figure:

This is a lot messier: Audacity shows as Port Audio devices and the capture devices are linked to the Port Audio inputs while the Port Audio outputs are linked to the playback devices. The existing thru_client links are basically discarded. To set up your desired situation, these have to be relinked as needed.

To demonstrate the effects of delaying one channel, start Jack, start delay and then start Audacity. Relink the ports according to the following qjackctl figure:

That is, capture ports are linked to delay input ports, delay output ports are linked to Port Audio (Audacity) input ports and Port Audio output ports are linked to playback ports.

The waveforms captured by Audacity clearly show the delay on the left channel compared to the right:

Play a sine wave

The copy example does not show the detail of what is in the buffers: the contents are jack_default_audio_sample_t's. What these are is described in the macro JACK_DEFAULT_AUDIO_TYPE with default value "32 bit float mono audio"

To do anything more than simply pass audio through, you need to handle the data in this format. The example program simple_client.c fills an array with 32-bit floating point sine curve values. On each call to process it copies data from the sine curve array into the output buffers. The increment into the sine curve array is different for left and right channels to give a different note on each channel.

Note that the calculation of the sine curve array is not done within the process function - that would be too slow and would cause latency.

The program is

/** @file simple_client.c
 *
 * @brief This simple client demonstrates the basic features of JACK
 * as they would be used by many applications.
 */

#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <signal.h>
#ifndef WIN32
#include <unistd.h>
#endif
#include <jack/jack.h>

jack_port_t *output_port1, *output_port2;
jack_client_t *client;

#ifndef M_PI
#define M_PI  (3.14159265)
#endif

#define TABLE_SIZE   (200)
typedef struct
{
    float sine[TABLE_SIZE];
    int left_phase;
    int right_phase;
}
paTestData;

static void signal_handler(int sig)
{
	jack_client_close(client);
	fprintf(stderr, "signal received, exiting ...\n");
	exit(0);
}

/**
 * The process callback for this JACK application is called in a
 * special realtime thread once for each audio cycle.
 *
 * This client follows a simple rule: when the JACK transport is
 * running, copy the input port to the output.  When it stops, exit.
 */

int
process (jack_nframes_t nframes, void *arg)
{
	jack_default_audio_sample_t *out1, *out2;
	paTestData *data = (paTestData*)arg;
	int i;

	out1 = (jack_default_audio_sample_t*)jack_port_get_buffer (output_port1, nframes);
	out2 = (jack_default_audio_sample_t*)jack_port_get_buffer (output_port2, nframes);

	for( i=0; i<nframes; i++ )
    {
        out1[i] = data->sine[data->left_phase];  /* left */
        out2[i] = data->sine[data->right_phase];  /* right */
        data->left_phase += 1;
        if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE;
        data->right_phase += 3; /* higher pitch so we can distinguish left and right. */
        if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE;
    }
    
	return 0;      
}

/**
 * JACK calls this shutdown_callback if the server ever shuts down or
 * decides to disconnect the client.
 */
void
jack_shutdown (void *arg)
{
	exit (1);
}

int
main (int argc, char *argv[])
{
	const char **ports;
	const char *client_name;
	const char *server_name = NULL;
	jack_options_t options = JackNullOption;
	jack_status_t status;
	paTestData data;
	int i;

	if (argc >= 2) {		/* client name specified? */
		client_name = argv[1];
		if (argc >= 3) {	/* server name specified? */
			server_name = argv[2];
            int my_option = JackNullOption | JackServerName;
			options = (jack_options_t)my_option;
		}
	} else {			/* use basename of argv[0] */
		client_name = strrchr(argv[0], '/');
		if (client_name == 0) {
			client_name = argv[0];
		} else {
			client_name++;
		}
	}

	for( i=0; i<TABLE_SIZE; i++ )
    {
        data.sine[i] = 0.2 * (float) sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. );
    }
    data.left_phase = data.right_phase = 0;
  

	/* open a client connection to the JACK server */

	client = jack_client_open (client_name, options, &status, server_name);
	if (client == NULL) {
		fprintf (stderr, "jack_client_open() failed, "
			 "status = 0x%2.0x\n", status);
		if (status & JackServerFailed) {
			fprintf (stderr, "Unable to connect to JACK server\n");
		}
		exit (1);
	}
	if (status & JackServerStarted) {
		fprintf (stderr, "JACK server started\n");
	}
	if (status & JackNameNotUnique) {
		client_name = jack_get_client_name(client);
		fprintf (stderr, "unique name `%s' assigned\n", client_name);
	}

	/* tell the JACK server to call `process()' whenever
	   there is work to be done.
	*/

	jack_set_process_callback (client, process, &data);

	/* tell the JACK server to call `jack_shutdown()' if
	   it ever shuts down, either entirely, or if it
	   just decides to stop calling us.
	*/

	jack_on_shutdown (client, jack_shutdown, 0);

	/* create two ports */

	output_port1 = jack_port_register (client, "output1",
					  JACK_DEFAULT_AUDIO_TYPE,
					  JackPortIsOutput, 0);

	output_port2 = jack_port_register (client, "output2",
					  JACK_DEFAULT_AUDIO_TYPE,
					  JackPortIsOutput, 0);

	if ((output_port1 == NULL) || (output_port2 == NULL)) {
		fprintf(stderr, "no more JACK ports available\n");
		exit (1);
	}

	/* Tell the JACK server that we are ready to roll.  Our
	 * process() callback will start running now. */

	if (jack_activate (client)) {
		fprintf (stderr, "cannot activate client");
		exit (1);
	}

	/* Connect the ports.  You can't do this before the client is
	 * activated, because we can't make connections to clients
	 * that aren't running.  Note the confusing (but necessary)
	 * orientation of the driver backend ports: playback ports are
	 * "input" to the backend, and capture ports are "output" from
	 * it.
	 */
 	
	ports = jack_get_ports (client, NULL, NULL,
				JackPortIsPhysical|JackPortIsInput);
	if (ports == NULL) {
		fprintf(stderr, "no physical playback ports\n");
		exit (1);
	}

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

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

	free (ports);
    
    /* install a signal handler to properly quits jack client */
#ifdef WIN32
	signal(SIGINT, signal_handler);
    signal(SIGABRT, signal_handler);
	signal(SIGTERM, signal_handler);
#else
	signal(SIGQUIT, signal_handler);
	signal(SIGTERM, signal_handler);
	signal(SIGHUP, signal_handler);
	signal(SIGINT, signal_handler);
#endif

	/* keep running until the Ctrl+C */

	while (1) {
	#ifdef WIN32 
		Sleep(1000);
	#else
		sleep (1);
	#endif
	}

	jack_client_close (client);
	exit (0);
}

      

Saving input to disk

Disk I/O cannot be performed within the Jack processing loop: it is just too slow. To save input to file requires use of a separate thread to manage disk I/O and pass control between the Jack and disk threads.

The program capture_client.c from the examples does this:

/*
    Copyright (C) 2001 Paul Davis
    Copyright (C) 2003 Jack O'Quin

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

    * 2002/08/23 - modify for libsndfile 1.0.0 <andy@alsaplayer.org>
    * 2003/05/26 - use ringbuffers - joq
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sndfile.h>
#include <pthread.h>
#include <signal.h>
#include <getopt.h>
#include <jack/jack.h>
#include <jack/ringbuffer.h>

typedef struct _thread_info {
    pthread_t thread_id;
    SNDFILE *sf;
    jack_nframes_t duration;
    jack_nframes_t rb_size;
    jack_client_t *client;
    unsigned int channels;
    int bitdepth;
    char *path;
    volatile int can_capture;
    volatile int can_process;
    volatile int status;
} jack_thread_info_t;

/* JACK data */
unsigned int nports;
jack_port_t **ports;
jack_default_audio_sample_t **in;
jack_nframes_t nframes;
const size_t sample_size = sizeof(jack_default_audio_sample_t);

/* Synchronization between process thread and disk thread. */
#define DEFAULT_RB_SIZE 16384		/* ringbuffer size in frames */
jack_ringbuffer_t *rb;
pthread_mutex_t disk_thread_lock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t  data_ready = PTHREAD_COND_INITIALIZER;
long overruns = 0;
jack_client_t *client;

static void signal_handler(int sig)
{
	jack_client_close(client);
	fprintf(stderr, "signal received, exiting ...\n");
	exit(0);
}

static void *
disk_thread (void *arg)
{
	jack_thread_info_t *info = (jack_thread_info_t *) arg;
	static jack_nframes_t total_captured = 0;
	jack_nframes_t samples_per_frame = info->channels;
	size_t bytes_per_frame = samples_per_frame * sample_size;
	void *framebuf = malloc (bytes_per_frame);

	pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
	pthread_mutex_lock (&disk_thread_lock);

	info->status = 0;

	while (1) {

		/* Write the data one frame at a time.  This is
		 * inefficient, but makes things simpler. */
		while (info->can_capture &&
		       (jack_ringbuffer_read_space (rb) >= bytes_per_frame)) {

			jack_ringbuffer_read (rb, framebuf, bytes_per_frame);

			if (sf_writef_float (info->sf, framebuf, 1) != 1) {
				char errstr[256];
				sf_error_str (0, errstr, sizeof (errstr) - 1);
				fprintf (stderr,
					 "cannot write sndfile (%s)\n",
					 errstr);
				info->status = EIO; /* write failed */
				goto done;
			}

			if (++total_captured >= info->duration) {
				printf ("disk thread finished\n");
				goto done;
			}
		}

		/* wait until process() signals more data */
		pthread_cond_wait (&data_ready, &disk_thread_lock);
	}

 done:
	pthread_mutex_unlock (&disk_thread_lock);
	free (framebuf);
	return 0;
}

static int
process (jack_nframes_t nframes, void *arg)
{
	int chn;
	size_t i;
	jack_thread_info_t *info = (jack_thread_info_t *) arg;

	/* Do nothing until we're ready to begin. */
	if ((!info->can_process) || (!info->can_capture))
		return 0;

	for (chn = 0; chn < nports; chn++)
		in[chn] = jack_port_get_buffer (ports[chn], nframes);

	/* Sndfile requires interleaved data.  It is simpler here to
	 * just queue interleaved samples to a single ringbuffer. */
	for (i = 0; i < nframes; i++) {
		for (chn = 0; chn < nports; chn++) {
			if (jack_ringbuffer_write (rb, (void *) (in[chn]+i),
					      sample_size)
			    < sample_size)
				overruns++;
		}
	}

	/* Tell the disk thread there is work to do.  If it is already
	 * running, the lock will not be available.  We can't wait
	 * here in the process() thread, but we don't need to signal
	 * in that case, because the disk thread will read all the
	 * data queued before waiting again. */
	if (pthread_mutex_trylock (&disk_thread_lock) == 0) {
	    pthread_cond_signal (&data_ready);
	    pthread_mutex_unlock (&disk_thread_lock);
	}

	return 0;
}

static void
jack_shutdown (void *arg)
{
	fprintf(stderr, "JACK shut down, exiting ...\n");
	exit(1);
}

static void
setup_disk_thread (jack_thread_info_t *info)
{
	SF_INFO sf_info;
	int short_mask;

	sf_info.samplerate = jack_get_sample_rate (info->client);
	sf_info.channels = info->channels;

	switch (info->bitdepth) {
		case 8: short_mask = SF_FORMAT_PCM_U8;
		  	break;
		case 16: short_mask = SF_FORMAT_PCM_16;
			 break;
		case 24: short_mask = SF_FORMAT_PCM_24;
			 break;
		case 32: short_mask = SF_FORMAT_PCM_32;
			 break;
		default: short_mask = SF_FORMAT_PCM_16;
			 break;
	}
	sf_info.format = SF_FORMAT_WAV|short_mask;

	if ((info->sf = sf_open (info->path, SFM_WRITE, &sf_info)) == NULL) {
		char errstr[256];
		sf_error_str (0, errstr, sizeof (errstr) - 1);
		fprintf (stderr, "cannot open sndfile \"%s\" for output (%s)\n", info->path, errstr);
		jack_client_close (info->client);
		exit (1);
	}

	info->duration *= sf_info.samplerate;
	info->can_capture = 0;

	pthread_create (&info->thread_id, NULL, disk_thread, info);
}

static void
run_disk_thread (jack_thread_info_t *info)
{
	info->can_capture = 1;
	pthread_join (info->thread_id, NULL);
	sf_close (info->sf);
	if (overruns > 0) {
		fprintf (stderr,
			 "jackrec failed with %ld overruns.\n", overruns);
		fprintf (stderr, " try a bigger buffer than -B %"
			 PRIu32 ".\n", info->rb_size);
		info->status = EPIPE;
	}
}

static void
setup_ports (int sources, char *source_names[], jack_thread_info_t *info)
{
	unsigned int i;
	size_t in_size;

	/* Allocate data structures that depend on the number of ports. */
	nports = sources;
	ports = (jack_port_t **) malloc (sizeof (jack_port_t *) * nports);
	in_size =  nports * sizeof (jack_default_audio_sample_t *);
	in = (jack_default_audio_sample_t **) malloc (in_size);
	rb = jack_ringbuffer_create (nports * sample_size * info->rb_size);

	/* When JACK is running realtime, jack_activate() will have
	 * called mlockall() to lock our pages into memory.  But, we
	 * still need to touch any newly allocated pages before
	 * process() starts using them.  Otherwise, a page fault could
	 * create a delay that would force JACK to shut us down. */
	memset(in, 0, in_size);
	memset(rb->buf, 0, rb->size);

	for (i = 0; i < nports; i++) {
		char name[64];

		sprintf (name, "input%d", i+1);

		if ((ports[i] = jack_port_register (info->client, name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0)) == 0) {
			fprintf (stderr, "cannot register input port \"%s\"!\n", name);
			jack_client_close (info->client);
			exit (1);
		}
	}

	for (i = 0; i < nports; i++) {
		if (jack_connect (info->client, source_names[i], jack_port_name (ports[i]))) {
			fprintf (stderr, "cannot connect input port %s to %s\n", jack_port_name (ports[i]), source_names[i]);
			jack_client_close (info->client);
			exit (1);
		}
	}

	info->can_process = 1;		/* process() can start, now */
}

int
main (int argc, char *argv[])
{
    jack_thread_info_t thread_info;
	int c;
	int longopt_index = 0;
	extern int optind, opterr;
	int show_usage = 0;
	char *optstring = "d:f:b:B:h";
	struct option long_options[] = {
		{ "help", 0, 0, 'h' },
		{ "duration", 1, 0, 'd' },
		{ "file", 1, 0, 'f' },
		{ "bitdepth", 1, 0, 'b' },
		{ "bufsize", 1, 0, 'B' },
		{ 0, 0, 0, 0 }
	};

	memset (&thread_info, 0, sizeof (thread_info));
	thread_info.rb_size = DEFAULT_RB_SIZE;
	opterr = 0;

	while ((c = getopt_long (argc, argv, optstring, long_options, &longopt_index)) != -1) {
		switch (c) {
		case 1:
			/* getopt signals end of '-' options */
			break;

		case 'h':
			show_usage++;
			break;
		case 'd':
			thread_info.duration = atoi (optarg);
			break;
		case 'f':
			thread_info.path = optarg;
			break;
		case 'b':
			thread_info.bitdepth = atoi (optarg);
			break;
		case 'B':
			thread_info.rb_size = atoi (optarg);
			break;
		default:
			fprintf (stderr, "error\n");
			show_usage++;
			break;
		}
	}

	if (show_usage || thread_info.path == NULL || optind == argc) {
		fprintf (stderr, "usage: jackrec -f filename [ -d second ] [ -b bitdepth ] [ -B bufsize ] port1 [ port2 ... ]\n");
		exit (1);
	}

	if ((client = jack_client_open ("jackrec", JackNullOption, NULL)) == 0) {
		fprintf (stderr, "JACK server not running?\n");
		exit (1);
	}

	thread_info.client = client;
	thread_info.channels = argc - optind;
	thread_info.can_process = 0;

	setup_disk_thread (&thread_info);

	jack_set_process_callback (client, process, &thread_info);
	jack_on_shutdown (client, jack_shutdown, &thread_info);

	if (jack_activate (client)) {
		fprintf (stderr, "cannot activate client");
	}

	setup_ports (argc - optind, &argv[optind], &thread_info);

     /* install a signal handler to properly quits jack client */
    signal(SIGQUIT, signal_handler);
	signal(SIGTERM, signal_handler);
	signal(SIGHUP, signal_handler);
	signal(SIGINT, signal_handler);

	run_disk_thread (&thread_info);

	jack_client_close (client);

	jack_ringbuffer_free (rb);

	exit (0);
}

      

Interacting with ALSA devices

Jack will eventually get its input from, and send its output to, devices. Currently, they are most likely to be ALSA devices. Consequently there must be a bridge between Jack processing and ALSA input and output. This will involve all the complexity of ALSA programming.

Fortunately there are Jack clients that do this. The Jack framework will talk to these as specified on starting the Jack server

	
jackd -dalsa
	
      
so you don't need to worry about that.

If you do want to worry, the examples directory contains ALSA examples. The program alsa_in.c.ok brings an ALSA input device into the Jack world

/** @file simple_client.c
 *
 * @brief This simple client demonstrates the basic features of JACK
 * as they would be used by many applications.
 */

#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>

#include <math.h>

#include <jack/jack.h>
#include <jack/jslist.h>
#include "memops.h"

#include "alsa/asoundlib.h"

#include <samplerate.h>

// Here are the lists of the jack ports...

JSList	   *capture_ports = NULL;
JSList	   *capture_srcs = NULL;
JSList	   *playback_ports = NULL;
JSList	   *playback_srcs = NULL;
jack_client_t *client;

snd_pcm_t *alsa_handle;

int jack_sample_rate;
int jack_buffer_size;

int quit = 0;
double resample_mean = 1.0;
double static_resample_factor = 1.0;
double resample_lower_limit = 0.25;
double resample_upper_limit = 4.0;

double *offset_array;
double *window_array;
int offset_differential_index = 0;

double offset_integral = 0;

// ------------------------------------------------------ commandline parameters

int sample_rate = 0;				 /* stream rate */
int num_channels = 2;				 /* count of channels */
int period_size = 1024;
int num_periods = 2;

int target_delay = 0;	    /* the delay which the program should try to approach. */
int max_diff = 0;	    /* the diff value, when a hard readpointer skip should occur */
int catch_factor = 100000;
int catch_factor2 = 10000;
double pclamp = 15.0;
double controlquant = 10000.0;
int smooth_size = 256;
int good_window=0;
int verbose = 0;
int instrument = 0;
int samplerate_quality = 2;

// Debug stuff:

volatile float output_resampling_factor = 1.0;
volatile int output_new_delay = 0;
volatile float output_offset = 0.0;
volatile float output_integral = 0.0;
volatile float output_diff = 0.0;

snd_pcm_uframes_t real_buffer_size;
snd_pcm_uframes_t real_period_size;

// buffers

char *tmpbuf;
char *outbuf;
float *resampbuf;

// format selection, and corresponding functions from memops in a nice set of structs.

typedef struct alsa_format {
	snd_pcm_format_t format_id;
	size_t sample_size;
	void (*jack_to_soundcard) (char *dst, jack_default_audio_sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state);
	void (*soundcard_to_jack) (jack_default_audio_sample_t *dst, char *src, unsigned long nsamples, unsigned long src_skip);
	const char *name;
} alsa_format_t;

alsa_format_t formats[] = {
	{ SND_PCM_FORMAT_FLOAT_LE, 4, sample_move_dS_floatLE, sample_move_floatLE_sSs, "float" },
	{ SND_PCM_FORMAT_S32, 4, sample_move_d32u24_sS, sample_move_dS_s32u24, "32bit" },
	{ SND_PCM_FORMAT_S24_3LE, 3, sample_move_d24_sS, sample_move_dS_s24, "24bit - real" },
	{ SND_PCM_FORMAT_S24, 4, sample_move_d24_sS, sample_move_dS_s24, "24bit" },
	{ SND_PCM_FORMAT_S16, 2, sample_move_d16_sS, sample_move_dS_s16, "16bit" }
};
#define NUMFORMATS (sizeof(formats)/sizeof(formats[0]))
int format=0;

// Alsa stuff... i dont want to touch this bullshit in the next years.... please...

static int xrun_recovery(snd_pcm_t *handle, int err) {
//    printf( "xrun !!!.... %d\n", err );
	if (err == -EPIPE) {	/* under-run */
		err = snd_pcm_prepare(handle);
		if (err < 0)
			printf("Can't recovery from underrun, prepare failed: %s\n", snd_strerror(err));
		return 0;
	} else if (err == -EAGAIN) {
		while ((err = snd_pcm_resume(handle)) == -EAGAIN)
			usleep(100);	/* wait until the suspend flag is released */
		if (err < 0) {
			err = snd_pcm_prepare(handle);
			if (err < 0)
				printf("Can't recovery from suspend, prepare failed: %s\n", snd_strerror(err));
		}
		return 0;
	}
	return err;
}

static int set_hwformat( snd_pcm_t *handle, snd_pcm_hw_params_t *params )
{
	int i;
	int err;

	for( i=0; i<NUMFORMATS; i++ ) {
		/* set the sample format */
		err = snd_pcm_hw_params_set_format(handle, params, formats[i].format_id);
		if (err == 0) {
			format = i;
			return 0;
		}
	}

	return err;
}

static int set_hwparams(snd_pcm_t *handle, snd_pcm_hw_params_t *params, snd_pcm_access_t access, int rate, int channels, int period, int nperiods ) {
	int err, dir=0;
	unsigned int buffer_time;
	unsigned int period_time;
	unsigned int rrate;
	unsigned int rchannels;

	/* choose all parameters */
	err = snd_pcm_hw_params_any(handle, params);
	if (err < 0) {
		printf("Broken configuration for playback: no configurations available: %s\n", snd_strerror(err));
		return err;
	}
	/* set the interleaved read/write format */
	err = snd_pcm_hw_params_set_access(handle, params, access);
	if (err < 0) {
		printf("Access type not available for playback: %s\n", snd_strerror(err));
		return err;
	}

	/* set the sample format */
	err = set_hwformat(handle, params);
	if (err < 0) {
		printf("Sample format not available for playback: %s\n", snd_strerror(err));
		return err;
	}
	/* set the count of channels */
	rchannels = channels;
	err = snd_pcm_hw_params_set_channels_near(handle, params, &rchannels);
	if (err < 0) {
		printf("Channels count (%i) not available for record: %s\n", channels, snd_strerror(err));
		return err;
	}
	if (rchannels != channels) {
		printf("WARNING: chennel count does not match (requested %d got %d)\n", channels, rchannels);
		num_channels = rchannels;
	}
	/* set the stream rate */
	rrate = rate;
	err = snd_pcm_hw_params_set_rate_near(handle, params, &rrate, 0);
	if (err < 0) {
		printf("Rate %iHz not available for playback: %s\n", rate, snd_strerror(err));
		return err;
	}
	if (rrate != rate) {
		printf("WARNING: Rate doesn't match (requested %iHz, get %iHz)\n", rate, rrate);
		sample_rate = rrate;
	}
	/* set the buffer time */

	buffer_time = 1000000*(uint64_t)period*nperiods/rate;
	err = snd_pcm_hw_params_set_buffer_time_near(handle, params, &buffer_time, &dir);
	if (err < 0) {
		printf("Unable to set buffer time %i for playback: %s\n",  1000000*period*nperiods/rate, snd_strerror(err));
		return err;
	}
	err = snd_pcm_hw_params_get_buffer_size( params, &real_buffer_size );
	if (err < 0) {
		printf("Unable to get buffer size back: %s\n", snd_strerror(err));
		return err;
	}
	if( real_buffer_size != nperiods * period ) {
	    printf( "WARNING: buffer size does not match: (requested %d, got %d)\n", nperiods * period, (int) real_buffer_size );
	}
	/* set the period time */
	period_time = 1000000*(uint64_t)period/rate;
	err = snd_pcm_hw_params_set_period_time_near(handle, params, &period_time, &dir);
	if (err < 0) {
		printf("Unable to set period time %i for playback: %s\n", 1000000*period/rate, snd_strerror(err));
		return err;
	}
	err = snd_pcm_hw_params_get_period_size(params, &real_period_size, NULL );
	if (err < 0) {
		printf("Unable to get period size back: %s\n", snd_strerror(err));
		return err;
	}
	if( real_period_size != period ) {
	    printf( "WARNING: period size does not match: (requested %i, got %i)\n", period, (int)real_period_size );
	}
	/* write the parameters to device */
	err = snd_pcm_hw_params(handle, params);
	if (err < 0) {
		printf("Unable to set hw params for playback: %s\n", snd_strerror(err));
		return err;
	}
	return 0;
}

static int set_swparams(snd_pcm_t *handle, snd_pcm_sw_params_t *swparams, int period) {
	int err;

	/* get the current swparams */
	err = snd_pcm_sw_params_current(handle, swparams);
	if (err < 0) {
		printf("Unable to determine current swparams for capture: %s\n", snd_strerror(err));
		return err;
	}
	/* start the transfer when the buffer is full */
	err = snd_pcm_sw_params_set_start_threshold(handle, swparams, period );
	if (err < 0) {
		printf("Unable to set start threshold mode for capture: %s\n", snd_strerror(err));
		return err;
	}
	err = snd_pcm_sw_params_set_stop_threshold(handle, swparams, -1 );
	if (err < 0) {
		printf("Unable to set start threshold mode for capture: %s\n", snd_strerror(err));
		return err;
	}
	/* allow the transfer when at least period_size samples can be processed */
	err = snd_pcm_sw_params_set_avail_min(handle, swparams, 2*period );
	if (err < 0) {
		printf("Unable to set avail min for capture: %s\n", snd_strerror(err));
		return err;
	}
	/* align all transfers to 1 sample */
	err = snd_pcm_sw_params_set_xfer_align(handle, swparams, 1);
	if (err < 0) {
		printf("Unable to set transfer align for capture: %s\n", snd_strerror(err));
		return err;
	}
	/* write the parameters to the playback device */
	err = snd_pcm_sw_params(handle, swparams);
	if (err < 0) {
		printf("Unable to set sw params for capture: %s\n", snd_strerror(err));
		return err;
	}
	return 0;
}

// ok... i only need this function to communicate with the alsa bloat api...

static snd_pcm_t *open_audiofd( char *device_name, int capture, int rate, int channels, int period, int nperiods ) {
  int err;
  snd_pcm_t *handle;
  snd_pcm_hw_params_t *hwparams;
  snd_pcm_sw_params_t *swparams;

  snd_pcm_hw_params_alloca(&hwparams);
  snd_pcm_sw_params_alloca(&swparams);

  if ((err = snd_pcm_open(&(handle), device_name, capture ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK )) < 0) {
      printf("Capture open error: %s\n", snd_strerror(err));
      return NULL;
  }

  if ((err = set_hwparams(handle, hwparams,SND_PCM_ACCESS_RW_INTERLEAVED, rate, channels, period, nperiods )) < 0) {
      printf("Setting of hwparams failed: %s\n", snd_strerror(err));
      return NULL;
  }
  if ((err = set_swparams(handle, swparams, period)) < 0) {
      printf("Setting of swparams failed: %s\n", snd_strerror(err));
      return NULL;
  }

  snd_pcm_start( handle );
  snd_pcm_wait( handle, 200 );

  return handle;
}

double hann( double x )
{
	return 0.5 * (1.0 - cos( 2*M_PI * x ) );
}

/**
 * The process callback for this JACK application.
 * It is called by JACK at the appropriate times.
 */
int process (jack_nframes_t nframes, void *arg) {

    int rlen;
    int err;
    snd_pcm_sframes_t delay = target_delay;
    int put_back_samples=0;
    int i;

    delay = snd_pcm_avail( alsa_handle );

    delay -= jack_frames_since_cycle_start( client );
    // Do it the hard way.
    // this is for compensating xruns etc...

    if( delay > (target_delay+max_diff) ) {

	output_new_delay = (int) delay;

	while ((delay-target_delay) > 0) {
	    snd_pcm_uframes_t to_read = ((delay-target_delay) > 512) ? 512 : (delay-target_delay);
	    snd_pcm_readi( alsa_handle, tmpbuf, to_read );
	    delay -= to_read;
	}

	delay = target_delay;

	// Set the resample_rate... we need to adjust the offset integral, to do this.
	// first look at the PI controller, this code is just a special case, which should never execute once
	// everything is swung in. 
	offset_integral = - (resample_mean - static_resample_factor) * catch_factor * catch_factor2;
	// Also clear the array. we are beginning a new control cycle.
	for( i=0; i<smooth_size; i++ )
		offset_array[i] = 0.0;
    }
    if( delay < (target_delay-max_diff) ) {
	snd_pcm_rewind( alsa_handle, target_delay - delay );
	output_new_delay = (int) delay;
	delay = target_delay;

	// Set the resample_rate... we need to adjust the offset integral, to do this.
	offset_integral = - (resample_mean - static_resample_factor) * catch_factor * catch_factor2;
	// Also clear the array. we are beginning a new control cycle.
	for( i=0; i<smooth_size; i++ )
		offset_array[i] = 0.0;
    }
    /* ok... now we should have target_delay +- max_diff on the alsa side.
     *
     * calculate the number of frames, we want to get.
     */

    double offset = delay - target_delay;

    // Save offset.
    offset_array[(offset_differential_index++)% smooth_size ] = offset;

    // Build the mean of the windowed offset array
    // basically fir lowpassing.
    double smooth_offset = 0.0;
    for( i=0; i<smooth_size; i++ )
	    smooth_offset +=
		    offset_array[ (i + offset_differential_index-1) % smooth_size] * window_array[i];
    smooth_offset /= (double) smooth_size;

    // this is the integral of the smoothed_offset
    offset_integral += smooth_offset;

    // Clamp offset.
    // the smooth offset still contains unwanted noise
    // which would go straigth onto the resample coeff.
    // it only used in the P component and the I component is used for the fine tuning anyways.
    if( fabs( smooth_offset ) < pclamp )
	    smooth_offset = 0.0;

    // ok. now this is the PI controller. 
    // u(t) = K * ( e(t) + 1/T \int e(t') dt' )
    // K = 1/catch_factor and T = catch_factor2
    double current_resample_factor = static_resample_factor - smooth_offset / (double) catch_factor - offset_integral / (double) catch_factor / (double)catch_factor2;

    // now quantize this value around resample_mean, so that the noise which is in the integral component doesnt hurt.
    current_resample_factor = floor( (current_resample_factor - resample_mean) * controlquant + 0.5 ) / controlquant + resample_mean;

    // Output "instrumentatio" gonna change that to real instrumentation in a few.
    output_resampling_factor = (float) current_resample_factor;
    output_diff = (float) smooth_offset;
    output_integral = (float) offset_integral;
    output_offset = (float) offset;

    // Clamp a bit.
    if( current_resample_factor < resample_lower_limit ) current_resample_factor = resample_lower_limit;
    if( current_resample_factor > resample_upper_limit ) current_resample_factor = resample_upper_limit;

    // Now Calculate how many samples we need.
    rlen = ceil( ((double)nframes) / current_resample_factor )+2;
    assert( rlen > 2 );

    // Calculate resample_mean so we can init ourselves to saner values.
    resample_mean = 0.9999 * resample_mean + 0.0001 * current_resample_factor;

    // get the data...
again:
    err = snd_pcm_readi(alsa_handle, outbuf, rlen);
    if( err < 0 ) {
	printf( "err = %d\n", err );
	if (xrun_recovery(alsa_handle, err) < 0) {
	    //printf("Write error: %s\n", snd_strerror(err));
	    //exit(EXIT_FAILURE);
	}
	goto again;
    }
    if( err != rlen ) {
	//printf( "read = %d\n", rlen );
    }

    /*
     * render jack ports to the outbuf...
     */

    int chn = 0;
    JSList *node = capture_ports;
    JSList *src_node = capture_srcs;
    SRC_DATA src;

    while ( node != NULL)
    {
	jack_port_t *port = (jack_port_t *) node->data;
	float *buf = jack_port_get_buffer (port, nframes);

	SRC_STATE *src_state = src_node->data;

	formats[format].soundcard_to_jack( resampbuf, outbuf + format[formats].sample_size * chn, rlen, num_channels*format[formats].sample_size );

	src.data_in = resampbuf;
	src.input_frames = rlen;

	src.data_out = buf;
	src.output_frames = nframes;
	src.end_of_input = 0;

	src.src_ratio = current_resample_factor;

	src_process( src_state, &src );

	put_back_samples = rlen-src.input_frames_used;

	src_node = jack_slist_next (src_node);
	node = jack_slist_next (node);
	chn++;
    }

    // Put back the samples libsamplerate did not consume.
    //printf( "putback = %d\n", put_back_samples );
    snd_pcm_rewind( alsa_handle, put_back_samples );

    return 0;      
}

/**
 * the latency callback.
 * sets up the latencies on the ports.
 */

void
latency_cb (jack_latency_callback_mode_t mode, void *arg)
{
	jack_latency_range_t range;
	JSList *node;

	range.min = range.max = target_delay;

	if (mode == JackCaptureLatency) {
		for (node = capture_ports; node; node = jack_slist_next (node)) {
			jack_port_t *port = node->data;
			jack_port_set_latency_range (port, mode, &range);
		}
	} else {
		for (node = playback_ports; node; node = jack_slist_next (node)) {
			jack_port_t *port = node->data;
			jack_port_set_latency_range (port, mode, &range);
		}
	}
}


/**
 * Allocate the necessary jack ports...
 */

void alloc_ports( int n_capture, int n_playback ) {

    int port_flags = JackPortIsOutput;
    int chn;
    jack_port_t *port;
    char buf[32];

    capture_ports = NULL;
    for (chn = 0; chn < n_capture; chn++)
    {
	snprintf (buf, sizeof(buf) - 1, "capture_%u", chn+1);

	port = jack_port_register (client, buf,
		JACK_DEFAULT_AUDIO_TYPE,
		port_flags, 0);

	if (!port)
	{
	    printf( "jacknet_client: cannot register port for %s", buf);
	    break;
	}

	capture_srcs = jack_slist_append( capture_srcs, src_new( 4-samplerate_quality, 1, NULL ) );
	capture_ports = jack_slist_append (capture_ports, port);
    }

    port_flags = JackPortIsInput;

    playback_ports = NULL;
    for (chn = 0; chn < n_playback; chn++)
    {
	snprintf (buf, sizeof(buf) - 1, "playback_%u", chn+1);

	port = jack_port_register (client, buf,
		JACK_DEFAULT_AUDIO_TYPE,
		port_flags, 0);

	if (!port)
	{
	    printf( "jacknet_client: cannot register port for %s", buf);
	    break;
	}

	playback_srcs = jack_slist_append( playback_srcs, src_new( 4-samplerate_quality, 1, NULL ) );
	playback_ports = jack_slist_append (playback_ports, port);
    }
}

/**
 * This is the shutdown callback for this JACK application.
 * It is called by JACK if the server ever shuts down or
 * decides to disconnect the client.
 */

void jack_shutdown (void *arg) {

	exit (1);
}

/**
 * be user friendly.
 * be user friendly.
 * be user friendly.
 */

void printUsage() {
fprintf(stderr, "usage: alsa_out [options]\n"
		"\n"
		"  -j <jack name> - client name\n"
		"  -d <alsa_device> \n"
		"  -c <channels> \n"
		"  -p <period_size> \n"
		"  -n <num_period> \n"
		"  -r <sample_rate> \n"
		"  -q <sample_rate quality [0..4]\n"
		"  -m <max_diff> \n"
		"  -t <target_delay> \n"
		"  -i  turns on instrumentation\n"
		"  -v  turns on printouts\n"
		"\n");
}


/**
 * the main function....
 */

void
sigterm_handler( int signal )
{
	quit = 1;
}


int main (int argc, char *argv[]) {
    char jack_name[30] = "alsa_in";
    char alsa_device[30] = "hw:0";

    extern char *optarg;
    extern int optind, optopt;
    int errflg=0;
    int c;

    while ((c = getopt(argc, argv, "ivj:r:c:p:n:d:q:m:t:f:F:C:Q:s:")) != -1) {
	switch(c) {
	    case 'j':
		strcpy(jack_name,optarg);
		break;
	    case 'r':
		sample_rate = atoi(optarg);
		break;
	    case 'c':
		num_channels = atoi(optarg);
		break;
	    case 'p':
		period_size = atoi(optarg);
		break;
	    case 'n':
		num_periods = atoi(optarg);
		break;
	    case 'd':
		strcpy(alsa_device,optarg);
		break;
	    case 't':
		target_delay = atoi(optarg);
		break;
	    case 'q':
		samplerate_quality = atoi(optarg);
		break;
	    case 'm':
		max_diff = atoi(optarg);
		break;
	    case 'f':
		catch_factor = atoi(optarg);
		break;
	    case 'F':
		catch_factor2 = atoi(optarg);
		break;
	    case 'C':
		pclamp = (double) atoi(optarg);
		break;
	    case 'Q':
		controlquant = (double) atoi(optarg);
		break;
	    case 'v':
		verbose = 1;
		break;
	    case 'i':
		instrument = 1;
		break;
	    case 's':
		smooth_size = atoi(optarg);
		break;
	    case ':':
		fprintf(stderr,
			"Option -%c requires an operand\n", optopt);
		errflg++;
		break;
	    case '?':
		fprintf(stderr,
			"Unrecognized option: -%c\n", optopt);
		errflg++;
	}
    }
    if (errflg) {
	printUsage();
	exit(2);
    }

    if( (samplerate_quality < 0) || (samplerate_quality > 4) ) {
	fprintf (stderr, "invalid samplerate quality\n");
	return 1;
    }
    if ((client = jack_client_open (jack_name, 0, NULL)) == 0) {
	fprintf (stderr, "jack server not running?\n");
	return 1;
    }

    /* tell the JACK server to call `process()' whenever
       there is work to be done.
       */

    jack_set_process_callback (client, process, 0);

    /* tell the JACK server to call `jack_shutdown()' if
       it ever shuts down, either entirely, or if it
       just decides to stop calling us.
       */

    jack_on_shutdown (client, jack_shutdown, 0);

    if (jack_set_latency_callback)
	    jack_set_latency_callback (client, latency_cb, 0);

    // get jack sample_rate
    
    jack_sample_rate = jack_get_sample_rate( client );

    if( !sample_rate )
	sample_rate = jack_sample_rate;

    // now open the alsa fd...
    alsa_handle = open_audiofd( alsa_device, 1, sample_rate, num_channels, period_size, num_periods);
    if( alsa_handle == 0 )
	exit(20);

    printf( "selected sample format: %s\n", formats[format].name );

    static_resample_factor = (double) jack_sample_rate / (double) sample_rate;
    resample_lower_limit = static_resample_factor * 0.25;
    resample_upper_limit = static_resample_factor * 4.0;
    resample_mean = static_resample_factor;

    offset_array = malloc( sizeof(double) * smooth_size );
    if( offset_array == NULL ) {
	    fprintf( stderr, "no memory for offset_array !!!\n" );
	    exit(20);
    }
    window_array = malloc( sizeof(double) * smooth_size );
    if( window_array == NULL ) {
	    fprintf( stderr, "no memory for window_array !!!\n" );
	    exit(20);
    }
    int i;
    for( i=0; i<smooth_size; i++ ) {
	    offset_array[i] = 0.0;
	    window_array[i] = hann( (double) i / ((double) smooth_size - 1.0) );
    }

    jack_buffer_size = jack_get_buffer_size( client );
    // Setup target delay and max_diff for the normal user, who does not play with them...
    if( !target_delay ) 
	target_delay = (num_periods*period_size / 2) + jack_buffer_size/2;

    if( !max_diff )
	max_diff = num_periods*period_size - target_delay ;	

    if( max_diff > target_delay ) {
	    fprintf( stderr, "target_delay (%d) cant be smaller than max_diff(%d)\n", target_delay, max_diff );
	    exit(20);
    }
    if( (target_delay+max_diff) > (num_periods*period_size) ) {
	    fprintf( stderr, "target_delay+max_diff (%d) cant be bigger than buffersize(%d)\n", target_delay+max_diff, num_periods*period_size );
	    exit(20);
    }
    // alloc input ports, which are blasted out to alsa...
    alloc_ports( num_channels, 0 );

    outbuf = malloc( num_periods * period_size * formats[format].sample_size * num_channels );
    resampbuf = malloc( num_periods * period_size * sizeof( float ) );
    tmpbuf = malloc( 512 * formats[format].sample_size * num_channels );

    if ((outbuf == NULL) || (resampbuf == NULL) || (tmpbuf == NULL))
    {
	    fprintf( stderr, "no memory for buffers.\n" );
	    exit(20);
    }

    memset( tmpbuf, 0, 512 * formats[format].sample_size * num_channels);

    /* tell the JACK server that we are ready to roll */

    if (jack_activate (client)) {
	fprintf (stderr, "cannot activate client");
	return 1;
    }

    signal( SIGTERM, sigterm_handler );
    signal( SIGINT, sigterm_handler );

    if( verbose ) {
	    while(!quit) {
		    usleep(500000);
		    if( output_new_delay ) {
			    printf( "delay = %d\n", output_new_delay );
			    output_new_delay = 0;
		    }
		    printf( "res: %f, \tdiff = %f, \toffset = %f \n", output_resampling_factor, output_diff, output_offset );
	    }
    } else if( instrument ) {
	    printf( "# n\tresamp\tdiff\toffseti\tintegral\n");
	    int n=0;
	    while(!quit) {
		    usleep(1000);
		    printf( "%d\t%f\t%f\t%f\t%f\n", n++, output_resampling_factor, output_diff, output_offset, output_integral );
	    }
    } else {
	    while(!quit)
	    {
		    usleep(500000);
		    if( output_new_delay ) {
			    printf( "delay = %d\n", output_new_delay );
			    output_new_delay = 0;
		    }
	    }
    }

    jack_deactivate( client );
    jack_client_close (client);
    exit (0);
}


      

Conclusion

This chapter has considered using Jack from a user viewpoint and also looked at programming Jack clients.



Copyright © Jan Newmarch, jan@newmarch.name
Creative Commons License
"Programming and Using Linux Sound - in depth" by Jan Newmarch is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License .
Based on a work at https://jan.newmarch.name/LinuxSound/ .

If you like this book, please contribute using PayPal

Or Flattr me:
Flattr this book