Boodler: The boodler.py Reference Manual

# Boodler: The `boodler.py` Reference Manual

The `boodler.py` script plays soundscapes from your package collection. (To install soundscapes and data in your collection, see boodle-mgr.py.)

    boodler.py [ options ] package/Agent [ data ... ]

You must usually supply the name of a soundscape (Agent class) to play, in the form package.name/Agent.

Normally, this loads the most current version of the package which is installed. To specify a particular version, you can say package.name:X.Y/Agent, where X and Y are numbers. This locates a version with major version exactly X, and minor version Y or later. (For other ways to specify versions, see this wiki page.) The assumption is that if you want version 3.1 of a package, then 3.2 or 3.10 will be acceptable, but 4.0 is too big a change. To choose an exact version of a package, use two colons: `package.name::3.1.37a/Agent`

Any arguments after the soundscape are passed along to that soundscape as data. See Soundscape Arguments for the format of these arguments. (If a soundscape argument begins with a minus sign, precede it with `--`, so that it is not grabbed as a script argument instead.)

This special form:

    boodler.py --testsound

...plays a test melody. This is always available; it does not have to be loaded from your collection.

Type `boodler.py --help` or `boodler.py -h` for a complete list of options.

## Options that control sound generation

`--output driver`
  Select one of the available driver modules. (See below.) The default is `oss` on Unix, `osxaq` on MacOSX 10.5 or later, or `macosx` on MacOSX through 10.4.
`--list-drivers`
  List the driver modules which are available. Any of the listed drivers may be used with the `--output` option.
`--device device`
  Set the device which Boodler will write to. The details depend on which driver module you have selected. (For a file-writing driver, this specifies the file to write.)
`--list-devices`
  List the devices which are available for this driver module. (Not all drivers provide such a list. For a file-writing driver, any valid filename is a "device", so it makes no sense to list them.)
`--rate soundrate`
  Set the sampling rate (in frames per second) which Boodler will try to use. If the sound device does not support this rate, Boodler will try to fall back upon some sampling rate which is supported. The default depends on the output driver, but is often 44100.
`--master volume`
  Set the master mixing volume for all sounds. The default is 0.5. You should not increase this. If you do, any soundscapes that play more than one sound at a time (most of them) are likely to suffer from clipping noise -- sporadic static. (To increase Boodler's volume, you should use a mixer application to adjust your sound driver, or just turn up your speakers.) If you hear clipping noise in complex soundscapes, try reducing this option below 0.5.
`--hardware`
  Display the capabilities of your sound hardware at startup time. This shows you explicitly what driver, sample format, and frame rate Boodler is using. It also dumps whatever other information about the sound interface might (conceivably) be important.
`--define opt`
`--define opt=val`
  Pass an option in to the sound driver. The available options depend on which driver you are using. Note that if an =val is supplied, there may not be any spaces before or after the equals sign. See below for the meaning of the many options.

## Options that affect printed output

`--verbose`
  Turn on verbose handling of exceptions. Normally, if an exception occurs in the execution of an agent, Boodler just prints the name of the exception and continues running. If you set `--verbose`, Boodler will print out the entire Python stack trace. Log messages are also more detailed in `--verbose` mode; you will see the entire log channel name, instead of a summary.
`--log level`
  Set logging to the given level, which may be `debug`, `info`, `warning`, `error`, `critical`. The default is `warning`, meaning that messages of that severity or worse are printed. (In other words, by default, `debug` and `info` messages are suppressed.) Set this to `debug` to see lots of picayune detail about sound generation.
`--logconfig configfile`
  Format log messages according to the given file. This file can control how messages look, which messages appear, and where they are sent. (See the Python documentation for log config file format.) A sample logging config file is distributed in the Boodler source as ./etc/logging.config.
`--stats interval`
  Turn on periodic statistics reporting. At intervals (given in seconds), Boodler will print out a message saying how many agents, channels, sounds, and note are queued up. This may be helpful for debugging soundscapes; if you see a value which increases without limit, you probably have a bug.

## Options for locating data

`--data directory`
  Boodler stores all its data in this directory. By default, this is a hidden folder in your home directory. On a Mac, it will be in `~/Library/Application Support/Boodler`. On Linux or Windows, it will be a `~/.boodler` folder.
`--collection directory`
  Boodler stores downloaded packages in this directory. By default, this is the `Collection` subdirectory of the `--data` folder (given above). There is generally no reason to change this.
`--external directory`
  This gives Boodler an additional directory, outside your normal package collection, to search for packages. (You may supply several `--external` directories if you wish.) This is generally useful only if you are developing a soundscape, and want to test it without packaging it up first.

## Options for external control

`--listen`
  Cause Boodler to listen for events on a network socket (incoming port 31863, by default). Soundscapes can pay attention to these events and react to them.
`--port port`
  If `--listen` is used, this causes Boodler to listen on the given port number (instead of the default port 31863). The port may also be an absolute pathname (beginning with "/"), in which case Boodler uses a Unix domain socket instead of a network socket.
`--stdinevents`
  Cause Boodler to listen for events on standard input. Soundscapes can pay attention to these events and react to them. This option is for programs which want to run Boodler as a subprocess.
`--prop=opt`
`--prop=opt=val`
  Set a property value. Soundscapes can pay attention to these properties and react to them. (Think of them as soundscape-specific preferences.) Note that if an =val is supplied, there may not be any spaces before or after the equals sign.

## Environment variables

All of these environment variables are optional. You can set all of this information with command-line arguments. (And command-line arguments override environment variables, if provided.)

`$BOODLER_DATA`
  Boodler stores all its data in this directory. See the `--data` option.
`$BOODLER_COLLECTION`
  Boodler stores downloaded packages in this directory. See the `--collection` option.
`$BOODLER_PROPERTIES`
  Set a property value. See the `--prop` option. To set several properties, set this variable to a comma-separated list of `opt` or `opt=val`.
`$BOODLER_SOUND_PATH`
  This is a holdover from Boodler 1.0. An old-style soundscape can load a sound from your `$BOODLER_SOUND_PATH`, rather than loading it from a package. This is supported only for the sake of old code; no modern soundscapes should rely on it.

## Boodler sound drivers

Boodler currently offers these drivers:

#### `file` -- write file containing raw sample output

The file will be named "boosound.raw" by default. To change this, pass a filename as the `--device`.

The file will contain raw PCM sound sample data, two channels, 16 bits per sample, signed (centered at 0). By default the samples will be in the native endianness of your computer, but you can change this with `--define end=big` or `--define end=little`.

This driver limits the size of the file. By default, it will only write five seconds of sound data; you can change the limit with the `--define time=interval` option. If the sound agent stops before the time limit, you'll get a shorter file.

(Note that this driver writes to the file as quickly as possible. It probably won't take five seconds to generate a five-second sound file.)

`--define time=interval`
  Generate approximately interval seconds of output. This defaults to five seconds.
`--define end=big`
  Force the sound output to be written big-endian.
`--define end=little`
  Force the sound output to be written little-endian.

#### `stdout` -- write raw sample output to stdout

This is similar to the `file` driver, but it streams its data to stdout, and there is no time limit. The `--device` argument is ignored.

The `stdout` driver, like the `file` driver, generates sound data as quickly as possible. This will probably be much faster than real time! If you pipe the output to a file, you will have a very large file very soon. This driver is intended to be piped to an application that can regulate its input, such as Ices.

`--define end=big`
  Force the sound output to be written big-endian.
`--define end=little`
  Force the sound output to be written little-endian.

#### `oss` -- Open Sound System

This is the default output driver on Linux and other Unixes. Use `--device` to control which audio hardware device is used; the default is `/dev/dsp`.

#### `alsa` -- Advanced Linux Sound Architecture

Another Unix sound framework. Use `--device` to control which ALSA device is used; the default is `default`.

`--define buffersize=frames`
  Define the length of Boodler's generation buffer, in frames. The default is 4096.
`--define hwbuffer=frames`
  Define the length of the ALSA sound buffer, in frames. The default is 16384. If you find that the sound skips when the CPU is busy, increase this value. The hwbuffer should not be shorter than the buffersize.
`--define periodsize=frames`
  Define the length of the ALSA transfer period. This should be a fraction of the buffersize. The default is left up to the ALSA driver; it depends on the buffersize.

#### `esd` -- Enlightened Sound Daemon

Yet another Unix sound framework. The `--device` option defines the hostname to send sound to; it defaults to `localhost`. If no ESD daemon is running, this will attempt to write directly to `/dev/dsp`.

#### `pulse` -- PulseAudio

Yet another Unix sound framework. The `--device` option defines the device ("sink") to send sound to. (`--list-devices` is not currently supported, but you can use Pulse's `pacmd` command-line tool to get a list of devices; try its `list-sinks` command.)

`--define buffersize=size`
  Set each write buffer to be size bytes long. The default is 32768.
`--define end=big`
  Force the sound output to be written big-endian.
`--define end=little`
  Force the sound output to be written little-endian.

#### `osxaq` -- MacOSX AudioQueue

This is the default output driver on MacOSX 10.5 and later. It is also compatible with the iPhone OS.

`--define buffercount=count`
  Create a queue of count buffers. If you find that the sound skips when the CPU is busy, increase this value. The default is 3.
`--define buffersize=size`
  Set each AudioQueue buffer to be size bytes long. The default is 32768.
`--define end=big`
  Force the sound output to be written big-endian.
`--define end=little`
  Force the sound output to be written little-endian.

#### `macosx` -- MacOSX CoreAudio

This is the default output driver on MacOSX 10.4 and earlier. (It works on 10.5, but is deprecated.)

`--define buffercount=count`
  Create a queue of count buffers. If you find that the sound skips when the CPU is busy (for example, when you're dragging or resizing windows), increase this value. The default is 6.
`--define buffersize=size`
  Set each CoreAudio buffer to be size bytes long. The default is 32768. (Values above 49152 may work poorly or be rejected by the OS.)

#### `vorbis` -- write Ogg Vorbis file

This writes a file, like the `file` driver; but it generates an Ogg Vorbis file. The default filename is "boosound.ogg". To change this, pass a filename as the `--device`.

`--define time=interval`
  Generate approximately interval seconds of output. This defaults to five seconds.
`--define quality=val`
  Set the Vorbis VBR compression quality, from 0.0 (low bitrate and quality) to 1.0 (high).

#### `shout` -- Shoutcast or Icecast source

This generates an Ogg Vorbis stream, and sends it to a Shoutcast or Icecast server (using libshout).

`--define shout-server=name`
  Connect to shoutcast or icecast server name. Can be a domain or an IP address. Defaults to "127.0.0.1".
`--define shout-port=port`
  Use port port on the streaming server. Defaults to 8000.
`--define shout-user=name`
  Login with username user to the server. Defaults to "source".
`--define shout-password=password`
  Login with password password to the server. Defaults to "hackme".
`--define shout-mount=mount`
  With an icecast server, mount the sound on mount. Defaults to "/boodler.ogg".
`--define shout-protocol=type`
  Use type as the protocol. Use "http" for icecast2, "xaudiocast" for icecast1, and "icy" for a shoutcast server. Defaults to "http".
`--define quality=val`
  Set the Vorbis VBR compression quality, from 0.0 (low bitrate and quality) to 1.0 (high).

#### `lame` -- write MP3 file with LAME encoder

This writes a file, like the `file` driver; but it generates an MP3 file. The default filename is "boosound.mp3". To change this, pass a filename as the `--device`.

`--define time=interval`
  Generate approximately interval seconds of output. This defaults to five seconds.
`--define quality=val`
  Set the LAME VBR compression quality, from 9 (low bitrate and quality) to 0 (high). Yes, it's backwards. The default is 2.
`--define fast`
  Use a faster VBR encoding algorithm. (Reduces quality somewhat.)
`--define abr=rate`
  Use ABR (fixed average bit rate) of rate KBPS. The rate can be from 8 to 320.
`--define haste=val`
  Modify the speed of the encoding algorithm, from 0 (slow and careful) to 9 (hasty). This affects the quality (but not the bit rate) of the output. The default is 2.

## Soundscape arguments

Boodler soundscape arguments are specified in a slightly addled S-expression syntax. (S-expressions are the building blocks of Lisp. (Expect parentheses.))

### A word about parsing

The first thing to remember is that Boodler wants to parse all of the argument data itself. (We're not talking about the arguments to `boodler.py` itself -- those begin with dashes, and are handled in the usual Unix way. We're talking about the soundscape and the options passed to it.)

Unix command-line tools usually get their arguments pre-split into words, but Boodler isn't interested in that, so it will jam all its arguments together and then do its own splitting. That is, the following command lines are exactly equivalent:

    boodler.py pkg/Agent arg
    boodler.py " pkg/Agent arg "
    boodler.py pkg/Agent  "  "  arg

In all but the simplest cases, it's easiest to slap quotes around all of the argument data. That prevents the Unix shell from interpreting the parentheses to mean shell-language. However, in this section, we will ignore that and write all of our S-expressions without shell-quotes.

### The syntax of S-expressions

Which is to say, the syntax of Boodler's S-expressions, which aren't really S-expressions, but I don't have a better term handy.

An expression is a string, or a parenthesized list of expressions. The following are all strings:

    abc
    123
    "string with four words"

The following are all lists:

    ()
    (abc 123 "string with four words")
    (() 123 (list with four strings))

A list can also contain named entries. Examples:

    (x=y)
    (pitch=1.0 pan=0.5)
    (abc bcd cde one=1 two=() three=(list of (lists)))

That last example contains three positional (ordinary) entries plus three named entries. This extension will look very odd to Lisp habitués, but it's just what we need to specify the argument of a Python function.

The other tedious-but-necessary details: a string can contain any characters, but you have to quote it if you want to include whitespace, single or double quotes, parentheses, backslash, or equals sign. You can quote a string with either single or double quotes, as long as you backslash any of that sort of quote inside it. Backslash backslashes, too.

### S-expressions as soundscape arguments

Boodler interprets its soundscape and following arguments as a single S-expression, with the parentheses implied. The command

    boodler.py org.boodler.play/OneSound
        org.boodler.old.clock/clock_cuckoo pitch=1.5 pan=1

...assembles itself as the S-expression

    (org.boodler.play/OneSound
        org.boodler.old.clock/clock_cuckoo pitch=1.5 pan=1)

The first entry, of course, is interpreted as a soundscape agent to look up. The following entries (and named entries) are interpreted as whatever type the soundscape is expecting for that argument.

This is important, because S-expressions don't themselves have type information (except for "list" and "not a list"). `1` is parsed simply as a string. But the `OneSound` agent declares that its `pan` argument needs to be a float. So the `1` is passed to it as the Python float value 1.0. If that argument needed to be an int, it would be passed in as the integer 1; if it needed to be a string, it would be the Python string "1".

This is all straightforward for int, float, and string arguments. Bools aren't much harder; you can enter `true` or `false`, `yes` or `no`, or single-letter abbreviations of these -- or `1` or `0` -- and they will be interpreted as booleans in the obvious way.

List or tuple arguments are assembled out of S-expression lists. If an argument expects a list of integers, `(1 23 456)` will become a Python list of length three. Types are checked carefully; `(1 xyzzy)` in the same place would raise an exception, because the string `xyzzy` cannot be interpreted as a Python integer.

A sound argument must be a string in the usual form package/resource. In the examples above, `org.boodler.old.clock/clock_cuckoo` becomes a sound argument to `OneSound`. (If that package is available!) You can also use the forms package:versionspec/resource or package::exactversion/resource.

Finally, we have soundscape arguments. If an argument expects a soundscape, you can fill in a package.name/Agent string, as usual. You can also fill in a list, of the form (package.name/Agent argument argument...). This creates a soundscape with its own arguments, and passes the entire thing along as the original arguments. The inner arguments are checked in their turn, and so the whole thing is tidily recursive.

Just a couple more special cases. If a soundscape is expected, you can enter a string beginning with a slash. This is looked up with Python's standard module system, rather than the Boodler package collection. You could use `/boodle.builtin.TestSoundAgent` to locate the class `TestSoundAgent` in the `boodle.builtin` module. (This part of the core Boodler engine; it's the agent invoked when you use the `--testsound` option.)

Finally, if a soundscape is expected, you can also enter the empty list `()`. This is interpreted as a soundscape which plays nothing and ends immediately. It is, in fact, the same as `/boodle.builtin.NullAgent`.

Return to Boodler docs index