scripted session management for linux audio

Installation instructions are inside the INSTALL files that come with the tarballs.


__________________________TOC

_______a quick howto

_______steps and tasks

_______methods and applications

_______files

_______presets and templates

_______dependencies and port groups

_______helper functions



__________________________a quick howto

This section is just a brief teaser showing a typical workflow. Better read on before starting off, also check the usual -h option.

Before starting chino, Jack and Alsa-sequencer must be up and running (unless not required by any application). We must not use the -v or -Xseq options of jackd, chino will not work together with those.

We must point chino to a preset, either by using the -p option or via configuration file. It's probably a good idea to copy the system-wide configuratiuon file <sysconfdir>/chino.conf to a per-user configuration file ~/.chinorc, then enter the path to the preset that we'll use most often.

Two ways exist to create a new session:

  1. by starting an empty session and adding applications via the runtime user interface, then saving;
  2. by editing a session definition file prototype (a simple file format chino uses to save sessions) with a text editor, then opening that file with chino.

Now both in more detail:

(1)

$ chino -n opus3

will start an empty new session named opus3. The present working directory will become the base directory of the session. To create the session in some other directory we can prepend the desired session name with a path.

$ chino -n ~/sessions/opus3

Optionally, we can point to a template and/or a preset on the command line. The preset given will override the corresponding setting in the configuration file.

$ chino -n opus3 -t /home/eeyore/studio/opus2.sdef -p /home/eeyore/presets/opusN.sdef

This way the session opus2, living in /home/eeyore/studio/, will be used to copy required files from. Files missing in opus2 will be copied from the preset opusN in /home/eeyore/presets/.

Now we can start with adding a method, then adding applications and methods to our liking, using keybindings from the runtime user interface. In between, entering w will save the session; a session definition file named opus3.sdef gets written to the base directory.

To recover the session later on, we use the -o (open) flag with the path to the session definition file as an argument.

$ chino -o ~/sessions/opus3.sdef

Session names may only contain uppercase and lowercase letters, numbers and underscores.

(2)

This is much like opening a session, as just described, with the only difference that we manually fill the session definition file, starting from a prototype. This is often faster than adding things one by one via the runtime UI.

$ chino -w

will write out a prototype session definition file proto.sdef to the current directory.

Now we can edit this file. We must enter a name for the session and we may fill in some applications to start with and point to a preset and template.

To now create (open) the session, we just enter

$ chino -o proto.sdef

Then we may watch and pray until all applications are started and properly interconnected. For adding further applications (or for removing, restarting or [...]), we may then start the full runtime user interface.

When saving the first time, (the w key, again), <session_name>.sdef will be written to the base directory. Since proto.sdef still works—it is just the same format—we may or may not consider it obsolete after its initial job.


That's about it when it comes to using chino, keybindings of the runtime user interface are self explaining.


     M  -  add a method
     A  -  add an application
     X  -  remove an application
     R  -  restart an application

     .  -  localise application/method library
     :  -  localise all used libraries
     f  -  force sourcing of application/method library

     l  -  list current session
     d  -  check dependency tree
     D  -  check dependency tree including optional depends
     g  -  toggle session graph display
     w  -  write changes to session definition file

     a  -  redo audio connections
     m  -  redo midi connections

     s  -  store connection snapshot
     r  -  restore connection snapshot
     u  -  un-store connection snapshot

ctrl+c  -  quit

     h  -  viev keybindings

The three snapshot-keybindings will only work if the aj-snapshot utility is installed, but they are in no way required to run chino. They just might come in handy when we ocasionally want to save some connections on top of those that are part of the session.

^
TOC |

__________________________steps and tasks

Now a few words on the internals of chino. Users won't have to bother with most of this but it might be helpful for getting an idea of what the whole thing is is about.

Imagine starting one Jack audio application manually—the steps to be done will be something along the line of: starting the application, then establishing audio and Midi connections, maybe also some file copying/loading/saving to keep things together in an appropriate location.

Similarly, chino internally divides everything into steps. On the next level, tasks are used. A task is just a series of steps that accomplishes something useful. This is a minor simplification, since tasks often require some logic around the steps, but that's not the point here.

Tasks may be vertical, calling a number of steps for one application, e.g. for adding an application to a session.

Tasks may be horizontal, calling one step for all applications, e.g. for establishing Midi connections for all applications.

Tasks may be both horizontal and vertical, e.g. for opening a session.

Some steps are just used internally, e.g. for writing the session graph, others are exposed to the user (public steps). We'll come back to public steps in the next section, for now there's just a list describing them:

assign
for assigning an application to an array and sourcing application libraries;
check
for checking whether an application's file or directory is present, if applicable;
list
for displaying a summary to the user;
copy
for copying and renaming the application file if the above check was negative;
start
for starting the application, includes assigning of port variables;
acn
for establishing audio connections using the assigned port variables;
mcn
for establishing Midi connections using the assigned port variables;
unassign
for unassigning and killing an application when removed using the runtime user interface;
kill
for killing an application on quitting the session.

^
TOC |

__________________________methods and applications

Methods are categories for applications that are to be handled in similar ways. Every application belongs to one method.

An application in this context can be (almost) anything: whether one or more (audio)-programs, audio/Midi hardware or netjack ports—many things can be crammed into an application.

While most applications within the default preset are named after the real program's name, like yoshimi or ardour2, this is not a requirement. An application in chino's terms is a combination of the actual program (or soundcard or...) and a particular way to use it. One could for instance have two applications called pdsynth and pdseq, both using the program Pure Data; in one case as a synth, in the other as a sequencer.

Every method and application is defined by its library, a file in Bash syntax, containing some variables and functions.

For every public step, a method library must provide a step function (not to be understood in the mathematical sense). That function will be called by chino whenever the step is to be done for one of the method's applications. It is then up to the method to determine what exactly to do for a step. To give an example: the hw (hardware) method from the default preset does nothing for the copy-step, since audio and Midi hardware will never need files copied.

An application's method thus largely determines what functions and variables are required in the application library—these can be very few for some methods, making it cheap to add support for more applications.

Two method types are hard-coded into chino: unique methods and channel methods.

For unique methods, application names are assigned to a consecutively filled array of variable length. Entries must be unique, thus the name.

For channel methods, application names are assigned to indices within a fixed-sized array, where indices may be left empty. Applications need not occur uniquely within a channel method. An obvious use case (though not the only possible) is using the index to connect an application to a certain audio or Midi channel, thus the name.

The size of method arrays is limited to 1000 (indices 0 – 999).

Methods may add a keybinding to the runtime user interface, for doing custom stuff of their choice.

Method and application names may only contain uppercase and lowercase letters, numbers and underscores. Application names must be unique within their method, method names' first two characters may only be letters and must be unique within their method type.

Every method has a method ID, which consists of its name prefixed with uq_ for unique methods and ch_ for channel methods, e.g. ch_instrument for a channel method named instrument.

Similarly, application IDs exist, consisting of the application name and a prefix <type><az>_, with <type> being either ch or uq, depending on the application's method's type, and <az> being the first two letters of the application's method's name. To give an example from the default preset: the application yoshimi, being a member of the channel method synth, has the ID chsy_yoshimi.

The purpose of those naming rules is to make sure all methods and applications (within one preset) have unique IDs. The IDs will be used for naming files, directories, variables and functions; thus they must not mix up.

^
TOC |

__________________________files

chino

The program itself consists of a main script and some files that are sourced on demand. For documentation purposes, a dummy preset is included.

Installation will place the following files in your system:

<prefix>/bin/chino
the main script
<prefix>/lib/chino/
a directory containing the remaining source files
chinobase
chinoprep
chinoopen
chinorun
chinohelper
chinograph
chinortui
chinomknew
<prefix>/share/chino/presets/dummy/
A directory containing the dummy preset, consisting of two methods (uq_dummy, ch_dummy) and two applications (uqdu_dummy, chdu_dummy).
<sysconfdir>/chino.conf
The system wide configuration file. To override, copy it to ~/.chinorc which will then take precedence.

sessions

One session has all its files below one base directory. Multiple sessions may share the same base directory.

Once populated, a session directory tree looks like this:

<base_directory>/<application_ID>/
Application subdirectories, one for each application, containing the application files (synth patches, Ardour session directories—whatever an application needs to start). Those files or directories have fixed names, files may have a suffix:
<session_name>.<suffix>
for unique method applications;
<session_name>-CH-<channel_number>.<suffix>
for channel method applications, with <channel_number> being their array index + 1, always three digits long with leading zeros.
Suffix-support is only implemented for files, not for directories. Applications not needing files, e.g. jkmeter or gjackclock, do not get such a directory.
<base_directory>/libs/
An optional directory that may contain application libraries, method libraries and a file called listlib:
<session_name>-<application_ID>lib
for application libraries;
<session_name>-<method_ID>lib
for method libraries;
<session_name>-listlib
a file where all supported methods and applications are registered.
<base_directory>/.snapshot/
A hidden directory where aj-snapshot's snapshots reside.
<base_directory>/.graph/
A hidden directory where the dot files and svg's for the graph reside.
<base_directory>/<session_name>.sdef
The session definition file.

^
TOC |

__________________________presets and templates

As shown in the previous section, method/application libraries are part of the session. Thus chino on its own will be helpless, it always needs to be pointed to a special session, called preset, from where it can source libraries and copy files.

The only thing special, distinguishing a preset from any other sesssion, is that a preset needs to be complete. That is, it must contain all files that can possibly be required by the methods and applications registered in its listlib file, including a complete set of libraries below the libs directory.

A template can optionally be given, which is just any session derived from the preset used.

When opening a session, there are three places where chino will look for files: the current session, the template and the preset.

First, the libraries are sourced, beginning with listlib. Any library found in the session's local libs/ directory takes precedence over the template session's one, last comes the preset's one.

Next, all required but locally missing application files get copied from the template or preset, in that order of precedence again.

As chino does not unsolicitedly copy libraries from the preset to the local session, they will usually only be present in the preset. To override this, to allow for customised application and method behaviour on a per session base, single libraries can easily be "localised" via the runtime user interface.

To not break behaviour for future sessions that use the current session as a template, libraries found local to the template will automatically get localised.

For making sessions self-contained, a keybinging allows to localise all used libraries. Self-contained sessions use themselves as a preset.

^
TOC |

__________________________dependencies and port groups

Some applications may not require any connections (e.g. gjackclock or a text editor). For all others that will be part of the connection graph, depends or provides must be given.

  • Applications that require connections to be established, therefore relying on the presence of certain ports to connect to, will get one or more depend.
  • Applications that allow connections to be established, therefore offering certain ports for others to connect to, will get one or more provide.

Instead of tying dependencies to specific applications, they are tied to port groups. An application may thus depend on port groups and provide for port groups.

A port group is merely a name given to a freely defined set of port variable names. Port group names must be unique within each of the the two connection types (audio and Midi), they may only consist of uppercase and lowercase letters, numbers and underscores.

The providing application assigns its real Jack or Alsa-seq port names to the defined port variables, the depending applications use those and their own ones to establish connections.

Dependencies are handled separately for the two connection types, thus an application may have any of the four: audio depends, Midi depends, audio provides and Midi provides.

While multiple applications may depend on the same port group, a port group can not be provided more than once, as this will lead to ambiguity. A providing channel method application running on multiple channels is not considered ambiguous.

chino is not as strict as to refuse starting applications that violate a sane dependency tree, it'll just do three things in response:

  • not attempt to establish connections where providing port groups are missing or ambiguous;
  • warn and suggest fixes upon request;
  • stain the graph with some supposedly disturbing colors.

Prefixing a depend with a colon suppresses the latter two responses, i.e. making it an optional depend.

Just as applications, methods may also depend and provide. For the method's depends and provides, the method will handle assignment and connecting for its applications; for application's depends and provides, the application does this itself.

On entering g in the runtime user interface, the dependency graph is displayed using graphviz and xsvg. While xsvg is nice and simple, it seems not to be packaged for some popular distributions. For that reason it can be easily replaced with some other image-viewer, by editing a line in the configuration file.

the graph explained

Every application gets a node drawn, with depends listed on the bottom and provides on the top, audio on the left and Midi on the right. Every method having any depends or provides will also get such a node, as well as a cluster around it containing all its application's nodes. The cluster's or node's background colour is determined by the method type.

Audio dependency connections are drawn as red solid lines, Midi dependency connections as yellow dashed lines.

session graph node explained

Nodes with unsatisfied depends get dark blue solid borders, nodes ambiguously providing get light blue dashed borders.

To show an example from the default preset, here is a graph with one ambiguous provide and four unsatisfied depends:

  • both uqms_eq24 and uqms_nonseq provide for SEQ;
  • uqms_ardour2 depends on STEREO which is not provided, and the method ch_synth as well as the application uqms_qmidiarp both depend on SEQ which is ambiguously provided.
example graph, broken dependencies

Now with dependencies fixed, by removing uqms_nonseq and adding uqhw_stereo.

example graph, dependencies fixed

Note that the connections drawn are quite dissimilar to those in applications like qjackctl or patchage:

  • the graph just displays the session as it should be, not necessarily as it is;
  • one connecting line in the session graph may represent any number of actual Jack/Alsa connections, regardless of direction.

It is very much up to the user's taste in what direction to define dependencies—it might be uncontroversial that a mixer application will depend on the Jack ports corresponding to the sound card, but when it comes to the order in which a sequencer, an arpeggiator and a synthesizer should depend on each other, opinions could vary.

^
TOC |

__________________________helper functions

For all public steps except unassign, a helper function exists. Helper functions may be called by the method librarys' step functions, to simplify accomplishing the step.

h_assign()
Sources required application libraries.
h_check()
Checks for the existence of application files.
h_list()
Prints a line whenever the session summary is displayed to the user.
h_copy()
Copies the application file or directory. If the application requires it, an additional move-function will be called to adapt the the application file to the new name, e.g. to change the session name in Ardour's XML files.
h_start()
After starting the application by calling a function from the application library, h_start() calls assign functions according to the dependencies. On each application start—for both, the application's method and the application—the following will be done:
  • if any audio depend or provide exists, an assign function for audio is called;
  • if any midi depend or provide exists, an assign function for Midi is called.

It is the method/application library that has to provide these functions, in accordance with their depends and provides.

Every assign function gets two multi-line arguments passed, containing the application's real input and output ports as named within Jack/Alsa-seq. Using grep or sed or whatever works best, those real port names can then be assigned to the appropriate variable names. (The real port names are retrieved via a diff of two jack_lsp/aconnect snapshots taken before and after application startup, so they'll be independent of launch order.)

h_acn()

Calls one audio connect function for each of the method's and application's audio depends. If provided by a method or a channel method application, the connect function is called once for each occurrence.

If the port-variable names are suffixed with either nothing or _L or _R for mono, or with both _L and _R for stereo, msaudioconnect() may be used inside the connect function for mono-stereo-agnostic audio connecting.

h_mcn()

Calls one Midi connect function for each of the method's and application's Midi depends. If provided by a method or a channel method application, the connect function is called once for each occurrence.

Inside the connect functions, ajmidiconnect() will connect Midi ports irrespective of type. For that to work, port-variable names must be suffixed with _A for Alsa-Midi and _J for Jack-Midi. Using aconnect or jack_connect directly will likely break this feature.

h_kill()
Kills an application.

During a step, methods may also register final functions and arguments for those. Whenever a task has finished a step for all applications it intends to operate on within one method, it calls registered final functions before moving on to the next method or step.

It may appear awkward to make a step public in the first place, and then let the method call a standardised helper function that could have been called centrally from within chino. This is done to give methods flexibility, in that they may decide how exactly to accomplish a step.

In most cases it is desirable to just use the helper functions in their usual way, it's mainly the ch_dssi and ch_senv methods in the default preset that do lot's of custom stuff; to a lesser extent the uq_hw (hardware) method, leaving out obsolete steps like file-copying and working around the fact that h_start() is inapplicable.

^
TOC |


The remaining documentation consists of comments in the method libraries and application libraries. chino installs a dummy preset that doesn't do much besides containing the comments, for real-world applications there's the example preset.