©  Jan Newmarch 2017

Jan Newmarch, Linux Sound Programming, 10.1007/978-1-4842-2496-0_8

8. Session Management

Jan Newmarch

(1)Oakleigh, Victoria, Australia

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

Resources

Here are some 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 needs arise quite quickly in the Jack environment, which is designed for multiple linkages. It doesn’t take a complex arrangement of Jack modules for management to become tedious. For example, consider the mixer session of the previous chapter in Figure 8-1.

A435426_1_En_8_Fig1_HTML.jpg
Figure 8-1. Jack connecting multiple applications

Setting this up from the beginning requires the following:

  1. Start jackd and qjackctl.

  2. Start jack_mixer.

  3. Open two new sets of input ports on the mixer.

  4. Connect the MAIN mixer output ports to the playback ports.

  5. Connect the microphone ports to one set of mixer input ports.

  6. Start mplayer, which automatically connects to the playback ports.

  7. Disconnect the mplayer output ports from the playback ports and reconnect them to the other set of mixer input ports.

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 ( http://ladish.org/wiki/levels ). Removing the explicit references to particular managers and frameworks, the levels are as follows:

  • Level 0: An application is not linked to a session-handling library. The user has to save application projects manually or rely on autosave support from the application.

  • Level 1: An application is not linked to a session-handling library. The application saves when particular messages or signals are received.

  • Level 2: An application is linked to a session management library. It has limited interaction with the session handler because of limitations in the session manager.

  • Level 3: An application is linked to a sophisticated session manager. It has full interaction with the session handler.

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

  • LASH

  • Jack session management

  • LADISH

  • Non-session manager

  • Chino

The existence of multiple managers means that most applications will 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 can be 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 want 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 with the following:

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 manager for Linux audio but has since fallen out of use. It does not seem to be in the Ubuntu repositories anymore.

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 that currently will not benefit from current session management tools. While it might run as Level 1 with LASH, it can run only at Level 0 with other session managers.

So, there are Jack applications that require LASH for session management, but no such support seems to exist anymore.

Jack Sessions

You can find a list of Jack session–aware applications as of 2016 at http://wiki.linuxaudio.org/apps/categories/jack_session .

qjackctlhas a session manager that will allow you to save and restore sessions. You save a session by clicking the Session button and then choosing a session name and directory. qjackctl stores the session information as an XML file in whatever directory you save it in. For the previous session, this looks like the following:

<!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 Figure 8-2.

A435426_1_En_8_Fig2_HTML.jpg
Figure 8-2. qjackctl showing Jack session

As you can see, there are many red Xs. Restoring a session doesn’t start these particular applications. If you restart jack_mixer by hand, then it establishes the links between its MAIN output ports and system playback ports, and several of the red Xs disappear. But it doesn’t create the extra ports that were created earlier. You need to repeat the work of creating new input ports with the right names; then qjackctl does reestablish the connections, and more red Xs disappear.

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

The issue here is that 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 set up different Jack configurations. This means you do not start Jack and then start LADISH; it’s 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 ( http://ladish.org/wiki/tutorial ). Follow it, in particular connecting Jack to, say, ALSA. Otherwise, you will get no sound! See also the LADI Session Handler ( www.penguinproducer.com/Blog/2011/12/the-ladi-session-handler/ ) by the Penguin Producer.

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

          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 Chapter 7. After reconnecting them, you end with a connection graph as shown in Figure 8-3.

A435426_1_En_8_Fig3_HTML.jpg
Figure 8-3. LADISH session

Connection graphs are stored as XML files in $HOME/.ladish. For example, the graph in Figure 8-3 is stored as follows:

<?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 re-created by hand. This is not a LADISH-aware application, so it runs at Level 0. However, once created, the LADISH reconnections are made.

You can find a list of LADISH-aware applications at http://wiki.linuxaudio.org/apps/all/ladish .

From the user’s viewpoint, the differences between these session managers are as follows:

  • Jack applications can be started in any manner and will be picked up by the Jack session manager. However, any specific command-line parameters will be lost.

  • Applications need to be started by the LADISH session manager in order to be managed by it. However, it can record command-line parameters and restart the application using them.

From a developer’s viewpoint, the difference between these session managers is as follows:

  • Jack session–aware applications can be started in any manner and will encode the command line required to restart them in the program.

Jack Session API

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

  • Register with a Jack session manager

  • Respond to messages from the Jack session manager

  • Be startable with session information

The response to a Jack session message will generally do the following:

  • Save the application’s state into a file, where the directory is given by the session manager.

  • Reply to the session manager with a command that can be used to restart the application, with enough information that it can restore its state (typically the name of the file in which it stored its state information).

Jack session–aware clients identify themselves to the session manager by a unique universal identifier (UUID ). 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 as follows:

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 with the following:

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.

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 ( http://trac.jackaudio.org/wiki/WalkThrough/Dev/JackSession ) that if this is run in a multithreaded environment such as GTK, it should be run when other threads are idle, for example, with g_idel_add.

I can illustrate this with the delay program from Chapter 7. Adding 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 ... " );
    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 ", 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 ", status );
            if ( status & JackServerFailed )
                {
                    fprintf ( stderr, "Unable to connect to JACK server " );
                }
            exit ( 1 );
        }
    if ( status & JackServerStarted )
        {
            fprintf ( stderr, "JACK server started " );
        }
    if ( status & JackNameNotUnique )
        {
            client_name = jack_get_client_name ( client );
            fprintf ( stderr, "unique name `%s' assigned ", 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 " );
                    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 " );
            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 " );


    free ( ports );

    ports = jack_get_ports ( client, NULL, NULL, JackPortIsPhysical|JackPortIsInput );
    if ( ports == NULL )
        {
            fprintf ( stderr, "no physical playback ports " );
            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 " );


    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 application 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.

For how to build LADISH-aware apps at Level 1, see http://ladish.org/wiki/code_examples . For LADI Session Handler, see http://ladish.org/ .

Conclusion

This chapter looked at some of the session management systems. The set of session managers covered is not exhaustive. Visit http://lwn.net/Articles/533594/ for a list of several more, such as the Non Session Manager and Chino. However, the situation is not particularly satisfactory, and there is substantial room for improvement.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset