Upto: Table of Contents of full book "Programming and Using Linux Sound"

Session Management

A complex sound system may consist of multiple sound sources, multiple filters and multiple outputs. If they have to be set up afresh each time they are used, then there can be errors, wasted time, etc. Session management attempts to solve these problems.

Resources

Session management issues

Whenever there are multiple modules linked in some way, there can be a need to manage the modules and their linkages. These arise quite quickly in the Jack environment, which is designed for multiple linkages. It doesn't take a very complex arrangement of Jack modules for management to become tedious. For example, consider the mixer session of the previous chapter:

To set this up from the beginning requires

You don't want to do this every time you play a song!

The LADISH session manager identifies different levels of control of applications by session managers. Removing the explicit references to particular managers and frameworks, they are:

As Dave Phillips points out, "The use of these levels is an attempt to sort and regulate the various possible conditions for any Linux audio application. Those conditions include the degree of JACK compliance, any WINE or DOS requirements, network operation, the multiplicity of existing APIs, and so forth. "

The current batch of session management frameworks used for Linux audio includes

The existence of multiple managers means that most applications will only support the protocols of only one or at most a few. If you choose a particular manager then you will be restricted to the applications you can run under its control.

jack_connect

The programs jack_connect and jack_disconnect canbe used to reconfigure connections between clients. For example, the MIDI player TiMidity will connect its output ports to the first available Jack input ports, which are generally the system ports connected to the sound card. If you wish to connect TiMidity to, say, jack-rack then its output ports have to be first disconnected and then connected to the correct ones. On the other hand, jack-rack does not connect to anything by default so may need to be connected to the system ports. This is done by e.g.

jack_disconnect TiMidity:port_1 system:playback_1
jack_disconnect TiMidity:port_2 system:playback_2

jack_connect TiMidity:port_1 jack_rack:in_1
jack_connect TiMidity:port_2 jack_rack:in_2

jack_connect jack_rack:out_1 system:playback_1
jack_connect jack_rack:out_2 system:playback_2
      

LASH

This was the earliest successful session mananager for Linux audio but has since fallen out of use. It does not seem to be in the Ubuntu repositories any more.

One of the applications requiring LASH is jack_mixer. Even worse, it uses the Python LASH module from the python-lash.2.7.4-0ubuntu package. The only copy I can find requires a version of Python less than 2.7 and the installed version of Python is 2.7.4. This is an application which at present will not benefit from current session management tools - while it might run as Level 1 with LASH, it can only run at Level 0 with other session managers.

So there are Jack applications which require LASH for session management but no such support seems to exist any more.

Jack sessions

A list of Jack session aware applications as at 2010 is at apps supporting jack-session and Jack Session

qjackctl has a session manager which will allow you to save and restore sessions. You save a session by clicking on the Session button and then choosing a session name and directory. It stores the session information as an XML file in whatever directory you save it. For the session above, this looks like

	<!DOCTYPE qjackctlSession>
<session name="session2">
 <client name="jack_mixer">
  <port type="out" name="MAIN L">
   <connect port="playback_1" client="system"/>
  </port>
  <port type="out" name="MAIN R">
   <connect port="playback_2" client="system"/>
  </port>
  <port type="in" name="midi in"/>
  <port type="out" name="Monitor L"/>
  <port type="out" name="Monitor R"/>
  <port type="in" name="Mixer L">
   <connect port="capture_1" client="system"/>
  </port>
  <port type="in" name="Mixer R">
   <connect port="capture_2" client="system"/>
  </port>
  <port type="out" name="Mixer Out L"/>
  <port type="out" name="Mixer Out R"/>
  <port type="in" name="mixer2 L">
   <connect port="out_0" client="MPlayer [8955]"/>
  </port>
  <port type="in" name="mixer2 R">
   <connect port="out_1" client="MPlayer [8955]"/>
  </port>
  <port type="out" name="mixer2 Out L"/>
  <port type="out" name="mixer2 Out R"/>
 </client>
 <client name="system">
  <port type="out" name="capture_1">
   <connect port="Mixer L" client="jack_mixer"/>
  </port>
  <port type="out" name="capture_2">
   <connect port="Mixer R" client="jack_mixer"/>
  </port>
  <port type="in" name="playback_1">
   <connect port="MAIN L" client="jack_mixer"/>
  </port>
  <port type="in" name="playback_2">
   <connect port="MAIN R" client="jack_mixer"/>
  </port>
 </client>
 <client name="MPlayer [8955]">
  <port type="out" name="out_0">
   <connect port="mixer2 L" client="jack_mixer"/>
  </port>
  <port type="out" name="out_1">
   <connect port="mixer2 R" client="jack_mixer"/>
  </port>
 </client>
</session>


      

On loading the session, it looks like

There are many red crosses - restoring a session doesn't start these particular applications. If you re-start jack_mixer by hand, then it establishes the links between its MAIN output ports and system playback ports, and several of the red crosses disappear. But it doesn't create the extra ports that we created earlier. We need to repeat the work of creating new input ports with the right names and then qjackctl does re-establish the connections, and more red crosses disappear.

If we run mplayer again, it just establishes its own default connections to the playback ports and has to be re-mapped by hand. It doesn't even seem to meet Level 0, as qjackctl doesn't remap its connections automatically.

The issue here is that the mplayer and jack_mixer do not talk the Jack session management protocol. The session manager does reset any connections made by some applications, but not all of them. An example is given later of adding Jack session management to an application and then it will be restarted and reconnected properly.

LADISH

LADISH is designed as the successor to LASH and is available in the repositories.

LADISH can start, stop and configure sessions. In particular, it can setup different Jack configurations. This means that you do not start Jack and then start LADISH, but the other way around: start the GUI tool gladish, configure Jack and then start a session. The process is described in the LADI Session Handler Wiki - follow it, in particular connecting Jack to, say, ALSA. Otherwise you will get no sound! See also The LADI Session Handler by the Penguin Producer.

Once set up, start a new Studio and then start applications from its Applications menu. To run mplayer you need to give the full command such as

	
	  mplayer -ao jack 54154.mp3
	
      

You can start jack_mixer from the Applications menu and then add two new sets of input ports, as in the Jack chapter. After reconnecting them, you end with a connection graph like

Connection graphs are stored as an XML file in $HOME/.ladish. For example, the above graph is stored as

	<?xml version="1.0"?>
<!--
ladish Studio configuration.
-->
<!-- Sun Sep 29 10:49:54 2013 -->
<studio>
  <jack>
    <conf>
      <parameter path="/engine/driver">alsa</parameter>
      <parameter path="/engine/client-timeout">500</parameter>
      <parameter path="/engine/port-max">64</parameter>
    </conf>
    <clients>
      <client name="system" uuid="5ef937c6-46f7-45cd-8441-8ff6e2aee4eb">
        <ports>
          <port name="capture_1" uuid="9432f206-44c3-45cb-8024-3ba7160962bc" />
          <port name="capture_2" uuid="3c9acf5c-c91d-4692-add2-e3defb7c508a" />
          <port name="playback_1" uuid="95c68011-dab9-401c-8904-b3d149e20570" />
          <port name="playback_2" uuid="5b8e9215-3ff4-4973-8c0b-1eb5ab7ccc9b" />
        </ports>
      </client>
      <client name="jack_mixer-3" uuid="4538833e-d7e7-47d0-8a43-67ee25d17898">
        <ports>
          <port name="midi in" uuid="17d04191-f59d-4d16-970c-55030162aae7" />
          <port name="MAIN L" uuid="9d986401-c303-4f35-89b7-a32e10120ce4" />
          <port name="MAIN R" uuid="fae94d01-00ef-449d-8e05-f95df84c5357" />
          <port name="Monitor L" uuid="1758d824-75cd-46b3-8e53-82c6be1ca200" />
          <port name="Monitor R" uuid="d14815e9-d3bc-457b-8e4f-29ad29ea36f7" />
          <port name="Mixer L" uuid="07d388ed-d00a-4ee0-92aa-3ae79200e11e" />
          <port name="Mixer R" uuid="d1eb3400-75ce-422d-b9b8-b7e670f95428" />
          <port name="Mixer Out L" uuid="fad2a77e-6146-4919-856f-b6f7befdb84d" />
          <port name="Mixer Out R" uuid="920c5d12-9f62-46aa-b191-52bfbb94065d" />
          <port name="mixer2 L" uuid="c2b96996-9cd1-41dd-a750-192bb5717438" />
          <port name="mixer2 R" uuid="3de52738-d7e8-4733-bf08-3ea2b6372a4c" />
          <port name="mixer2 Out L" uuid="4e08eba4-a0c1-4e76-9dff-c14f76d5328e" />
          <port name="mixer2 Out R" uuid="9d2f79a5-e2d0-484b-b094-98ef7a4f61a7" />
        </ports>
      </client>
      <client name="mplayer" uuid="66e0d45f-2e21-4fbf-ac34-5d3658ee018a">
        <ports>
          <port name="out_0" uuid="83152a6e-e6f6-4357-93ce-020ba58b7d00" />
          <port name="out_1" uuid="55a05594-174d-48a5-805b-96d2c9e77cf1" />
        </ports>
      </client>
    </clients>
  </jack>
  <clients>
    <client name="Hardware Capture" uuid="47c1cd18-7b21-4389-bec4-6e0658e1d6b1" naming="app">
      <ports>
        <port name="capture_1" uuid="9432f206-44c3-45cb-8024-3ba7160962bc" type="audio" direction="output" />
        <port name="capture_2" uuid="3c9acf5c-c91d-4692-add2-e3defb7c508a" type="audio" direction="output" />
      </ports>
      <dict>
        <key name="http://ladish.org/ns/canvas/x">1364.000000</key>
        <key name="http://ladish.org/ns/canvas/y">1083.000000</key>
      </dict>
    </client>
    <client name="Hardware Playback" uuid="b2a0bb06-28d8-4bfe-956e-eb24378f9629" naming="app">
      <ports>
        <port name="playback_1" uuid="95c68011-dab9-401c-8904-b3d149e20570" type="audio" direction="input" />
        <port name="playback_2" uuid="5b8e9215-3ff4-4973-8c0b-1eb5ab7ccc9b" type="audio" direction="input" />
      </ports>
      <dict>
        <key name="http://ladish.org/ns/canvas/x">1745.000000</key>
        <key name="http://ladish.org/ns/canvas/y">1112.000000</key>
      </dict>
    </client>
    <client name="jack_mixer-3" uuid="4b198f0f-5a77-4486-9f54-f7ec044d9bf2" naming="app" app="98729282-8b18-4bcf-b929-41bc53f2b4ed">
      <ports>
        <port name="midi in" uuid="17d04191-f59d-4d16-970c-55030162aae7" type="midi" direction="input" />
        <port name="MAIN L" uuid="9d986401-c303-4f35-89b7-a32e10120ce4" type="audio" direction="output" />
        <port name="MAIN R" uuid="fae94d01-00ef-449d-8e05-f95df84c5357" type="audio" direction="output" />
        <port name="Monitor L" uuid="1758d824-75cd-46b3-8e53-82c6be1ca200" type="audio" direction="output" />
        <port name="Monitor R" uuid="d14815e9-d3bc-457b-8e4f-29ad29ea36f7" type="audio" direction="output" />
        <port name="Mixer L" uuid="07d388ed-d00a-4ee0-92aa-3ae79200e11e" type="audio" direction="input" />
        <port name="Mixer R" uuid="d1eb3400-75ce-422d-b9b8-b7e670f95428" type="audio" direction="input" />
        <port name="Mixer Out L" uuid="fad2a77e-6146-4919-856f-b6f7befdb84d" type="audio" direction="output" />
        <port name="Mixer Out R" uuid="920c5d12-9f62-46aa-b191-52bfbb94065d" type="audio" direction="output" />
        <port name="mixer2 L" uuid="c2b96996-9cd1-41dd-a750-192bb5717438" type="audio" direction="input" />
        <port name="mixer2 R" uuid="3de52738-d7e8-4733-bf08-3ea2b6372a4c" type="audio" direction="input" />
        <port name="mixer2 Out L" uuid="4e08eba4-a0c1-4e76-9dff-c14f76d5328e" type="audio" direction="output" />
        <port name="mixer2 Out R" uuid="9d2f79a5-e2d0-484b-b094-98ef7a4f61a7" type="audio" direction="output" />
      </ports>
      <dict>
        <key name="http://ladish.org/ns/canvas/x">1560.000000</key>
        <key name="http://ladish.org/ns/canvas/y">1104.000000</key>
      </dict>
    </client>
    <client name="mplayer" uuid="2f15cfec-7f6d-41b4-80e8-e1ae80c3be9e" naming="app" app="7a9be17b-eb40-4be3-a9dc-82f36bbceeeb">
      <ports>
        <port name="out_0" uuid="83152a6e-e6f6-4357-93ce-020ba58b7d00" type="audio" direction="output" />
        <port name="out_1" uuid="55a05594-174d-48a5-805b-96d2c9e77cf1" type="audio" direction="output" />
      </ports>
      <dict>
        <key name="http://ladish.org/ns/canvas/x">1350.000000</key>
        <key name="http://ladish.org/ns/canvas/y">1229.000000</key>
      </dict>
    </client>
  </clients>
  <connections>
    <connection port1="9432f206-44c3-45cb-8024-3ba7160962bc" port2="07d388ed-d00a-4ee0-92aa-3ae79200e11e" />
    <connection port1="3c9acf5c-c91d-4692-add2-e3defb7c508a" port2="d1eb3400-75ce-422d-b9b8-b7e670f95428" />
    <connection port1="fad2a77e-6146-4919-856f-b6f7befdb84d" port2="95c68011-dab9-401c-8904-b3d149e20570" />
    <connection port1="920c5d12-9f62-46aa-b191-52bfbb94065d" port2="5b8e9215-3ff4-4973-8c0b-1eb5ab7ccc9b" />
    <connection port1="83152a6e-e6f6-4357-93ce-020ba58b7d00" port2="c2b96996-9cd1-41dd-a750-192bb5717438" />
    <connection port1="55a05594-174d-48a5-805b-96d2c9e77cf1" port2="3de52738-d7e8-4733-bf08-3ea2b6372a4c" />
  </connections>
  <applications>
    <application name="jack_mixer-3" uuid="98729282-8b18-4bcf-b929-41bc53f2b4ed" terminal="false" level="0" autorun="true">jack_mixer</application>
    <application name="mplayer" uuid="7a9be17b-eb40-4be3-a9dc-82f36bbceeeb" terminal="true" level="0" autorun="true">mplayer -ao jack %2Fhome%2Fhttpd%2Fhtml%2FLinuxSound%2FKaraoke%2FSubtitles%2Fsongs%2F54154.mp3</application>
  </applications>
</studio>

      

The full command to restart mplayer is stored in this file, as are all the connections made.

On stopping and restarting a session, mplayer is started with the same MP3 file, but has the default connections. It ignores the connections of the LADISH session. Similarly, jack_mixer is restarted, but the additional ports have to be recreated by hand - this is not a LADISH aware application so it runs at Level 0. However, once created, the LADISH reconnections are made okay.

A list of LADISH-aware applications is at ladish: LADI Session Handler

From the user's viewpoint, the difference between these session managers are

From the developer's viewpoint (see later), the difference between these session managers are

Non-session manager

List of NSM applications at NSM - Non Session Management

Non Session Management API

Jack session API

See the session API at trac.

Applications that can be managed by Jack Sessions (JS) may be JS aware at Level 1 or JS unaware. For the unaware ones, the best that can be done is for the session manager to maybe start and stop them. For the JS aware applications, they must be set up to

Response to a JS message will generally

JS aware clients identify themselves to the session manager by a UUID (unique universal identifier). It doesn't seem to matter what this is or how it is generated: the client application just makes it up as long as it is an integer represented as a string. This is passed to the session manager when registering, but should also be passed back to the client when it is restarted by the session manager. This is done by a command line argument to the application, and the format of the command line is also up to the client.

A simple case might be two options -u for UUID and -f for saved state file. This would be parsed using getopt by

int main(int argc, char **argv) {
  int c;
  char *file = NULL;
  char *uuid = "13";
  while ((c = getopt (argc, argv, "f:u:")) != -1)
    switch (c) {
      case 'u':
        uuid = optarg;
        break;
      case 'f':
        file = optarg;
        break;
      ...
    }
  }    
  ...
}
      

The application could then restore its state using the information it has previously stored in the state file, and then register again with a session manager by

jack_client *client;
client = jack_client_open("myapp", JackSessionID, NULL, uuid);
jack_set_session_callback(client, session_callback, NULL); 
      

The callback function session_callback is invoked whenever the session manager needs to communicate with the application. It takes a jack_session_event and whatever was passed as the last argument to jack_set_session_callback.

The job of the callback is then to save state information, pass information back to the session manager and perhaps exit (from trac - the session API):

int session_callback(jack_session_event_t *ev) {
  char filename[256];
  char command[256];

  snprintf(filename, sizeof(filename), "%smyfile.state", ev->session_dir);
  snprintf(command,  sizeof(command),  
           "my_app -u %s -f ${SESSION_DIR}myfile.state", ev->client_uuid);
  your_save_function(filename);
  ev->command_line = strdup(command);
  jack_session_reply(jack_client, ev);
  if(ev->type == JackSessionSaveAndQuit)
      quit();
  jack_session_event_free(ev);
  return 0;
}
     

trac suggests that if this is run in a multi-threaded environment such as GTK, it should be run when other threads are idle e.g. by g_idel_add.

WE can illustrate this with the delay program from the Jack chapter. Adding in the extra code gives a revised delay.c. I have enclosed the extra code with #ifdef JACK_SESSION for ease in seeing the changes.

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

#define JACK_SESSION

#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>

#ifdef JACK_SESSION
#include <jack/session.h>
#endif

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 );
}

#ifdef JACK_SESSION
/*
 * Callback function for JS
 */
void session_callback(jack_session_event_t *ev, void *args) {
    char command[256];

    snprintf(command,  sizeof(command),  
             "/home/httpd/html/LinuxSound/Sampled/SessionManagement/delay -u %s", 
	     ev->client_uuid);
    ev->flags = JackSessionNeedTerminal;
    ev->command_line = strdup(command);
    jack_session_reply(client, ev);

    if(ev->type == JackSessionSaveAndQuit)
         jack_shutdown(NULL);

    jack_session_event_free(ev);
}
#endif

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

#ifdef JACK_SESSION
    /*
     * Extra code for JS
     */
    int c;
    char *uuid = "13";
    while ((c = getopt (argc, argv, "u:")) != -1)
	switch (c) {
	case 'u':
	    uuid = optarg;
	    break;
	}
    printf("UUID is %s\n", uuid);
#endif

    client_name = strrchr ( argv[0], '/' );
    if ( client_name == 0 ) {
	client_name = argv[0];
    }
    else {
	client_name++;
    }

    /* open a client connection to the JACK server */
    /* Changed args for JS */

#ifdef JACK_SESSION
    client = jack_client_open ( client_name, JackSessionID, &status, uuid);
#else
    client = jack_client_open ( client_name, JackNullOption, &status);
#endif
    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 );
	}

#ifdef JACK_SESSION
    /* Set callback function for JS
     */
    jack_set_session_callback(client, session_callback, NULL);
#endif

    /* 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 );
}

      

LADISH API

If an application is Jack session aware, then the LADISH GUI tool gladish can manage the applicaiton as a Level 1 application. In other words, gladish can manage Jack session and LADISH clients equally. In that sense, there is no need to additionally add LADISH awareness to an application unless you prefer the LADISH way of managing sessions.

How to build LADISH-aware apps at Level 1 app examples and also see LADI Session Handler

Non-session management API

See Non Session Management API

Conclusion

This chapter has looked at some of the session management systems. The situation is not particularly satisfactory and there is substantial room for improvement.


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