## Boodler: Programming Reference

Herein are described four classes.

Okay, three classes and a module.

- Agent: base class for boodler agents
- EventAgent: base class for boodler agents that listen for events
- Channel: a class for creating hierarchical trees of sounds and agents
- stereo: a module of stereo pan objects

### Agent: base class for boodler agents

- Methods and fields to be overridden:
  name
    a string which describes the agent
  run()
    perform the agent's action
  getname()
    return a string which describes the agent
- Publically readable fields:
  channel
    the channel in which this agent is running
- Methods which can be called from a run() method:
  sched_note()
    schedule a note to be played
  sched_note_duration()
    schedule a note to be played for an extended time
  sched_note_pan()
    schedule a note to be played at a stereo position
  sched_note_params()
    schedule a note, allowing all parameters
  sched_agent()
    schedule another agent to run
  resched()
    schedule self to run again
  post_agent()
    post another agent to watch for events
  send_event()
    create an event, which posted agents may receive
  new_channel()
    create a channel
  new_channel_pan()
    create a channel at a stereo position
  get_root_channel()
    return the root channel of the channel tree

`sched_note(sample [, pitch=1, volume=1, delay=0, chan=self.channel]) -> duration`

  Schedule a note to play. The sound is loaded from the file sample (which is relative to $BOODLER_SOUND_PATH). The pitch is given as a multiple of the sound's original frequency; the volume is given as a fraction of the sound's original volume. The delay is a time (in seconds) to delay before the note is played. The channel, if `None` or not supplied, defaults to the same channel the agent is running in.

  This returns the expected duration of the sound, in seconds.

`sched_note_pan(sample [, pan=0, pitch=1, volume=1, delay=0, chan=self.channel]) -> duration`

  Schedule a note to play, panning the stereo origin of the sound. The pan value defaults to 0, meaning no shift in origin; -1 means all the way left; 1 means all the way right. The value may also be an object created by the `stereo` module.

  The sound is loaded from the file sample (which is relative to $BOODLER_SOUND_PATH). The pitch is given as a multiple of the sound's original frequency; the volume is given as a fraction of the sound's original volume. The delay is a time (in seconds) to delay before the note is played. The channel, if `None` or not supplied, defaults to the same channel the agent is running in.

  This returns the expected duration of the sound, in seconds.

`sched_note_duration(sample, duration [, pitch=1, volume=1, delay=0, chan=self.channel]) -> duration`

  Schedule a note to play, extending the original sound sample to a longer period of time. The duration is given in seconds.

  The sound is loaded from the file sample (which is relative to $BOODLER_SOUND_PATH). The pitch is given as a multiple of the sound's original frequency; the volume is given as a fraction of the sound's original volume. The delay is a time (in seconds) to delay before the note is played. The channel, if `None` or not supplied, defaults to the same channel the agent is running in.

  This returns the expected duration of the sound, in seconds. Due to the way sounds are looped, this may be slightly longer than the given duration.

`sched_note_params(sample [, param=value, param=value...]) -> duration`

  Schedule a note to play. This method understands all the arguments used by the other sched_note methods, but they must be supplied as named keywords. The arguments may be in any order. For example: "self.sched_note_params(snd, volume=0.5, pitch=2)"

  The valid arguments, and their default values:

  - `pitch = 1` (original pitch)
  - `volume = 1` (full volume)
  - `delay = 0` (play immediately)
  - `pan = None` (no stereo shift)
  - `duration = 0` (exactly once through the sound)
  - `chan = None` (play in agent's own channel)

`post_agent(agent [, chan=self.channel])`

  Post an agent to watch for events; the agent will be scheduled to run whenever an appropriate event occurs. The channel, if `None` or not supplied, defaults to the same channel that self is running in.

  The posted agent must be an instance of the `EventAgent` class. It must have a `watch_events` field, which lists the events which it is interested in. This field must be a string, a list of strings, or a function such that `agent.watch_events()` return a string or list of strings.

`send_event(event)`

  Send an event. Boodler interprets the event just as if it had been received from the outside world. Any agents that are posted watching for that type of event will run. (Due to the way the scheduler works, there may be a short delay before they run. The delay is not predictable. For reliable scheduling, use sched_agent(), not send_event().)

  The event should be a tuple of strings, or a string (which will be split into a tuple at whitespace).

`sched_agent(agent [, delay=0, chan=self.channel])`

  Schedule an agent to run. This may be the current agent (self) or a newly-created agent. The delay is a time (in seconds) to delay before the agent runs. The channel, if `None` or not supplied, defaults to the same channel that self is running in.

`resched([delay, chan=self.channel])`

  Reschedule the current agent (self). The delay is a time (in seconds) to delay before the agent runs again. The channel, if `None` or not supplied, defaults to the same channel that self is running in.

  If delay is not supplied, it defaults to the delay used when this agent was first scheduled. Note that if this value was zero, you will probably cause an infinite loop.

`new_channel([startvolume=1, parent=self.channel]) -> channel`

  Create a new channel. The startvolume is the volume the channel is initially set to; this will affect all sounds played in the channel and any subchannels. The new channel will be a subchannel of parent -- if `None` or not supplied, it will be a subchannel of the channel that the agent (self) is running in.

`new_channel_pan([pan=stereo.default(), startvolume=1, parent=self.channel]) -> channel`

  Create a new channel, panning the stereo origin of its sounds. (See the `stereo` module.) The startvolume is the volume the channel is initially set to; this will affect all sounds played in the channel and any subchannels. The new channel will be a subchannel of parent -- if `None` or not supplied, it will be a subchannel of the channel that the agent (self) is running in.

`get_root_channel() -> channel`

  Return the root channel of the channel tree.

`run()`

  Perform the agent's action. Each subclass of Agent must override this method.

`getname() -> string`

  Return the name of the agent. This defaults to returning `self.name`, if that is defined.

### EventAgent: base class for boodler agents that listen for events

See Agent class definition for inherited methods and fields.

- Methods and fields to be overridden:
  run()
    defaults to `post_agent(self)`
  receive()
    perform the agent's action
  watch_events
    events to watch for (this may be a string, a list of strings, or a method that returns a string or list of strings)
- Methods which can be called:
  unpost()
    remove self from event-watching post

`run()`

  By default, this calls `post_agent(self)`. In most cases, you will not want to override this.

`receive(event)`

  Perform the agent's action when an appropriate event arrives. Each subclass of `EventAgent` must override this method. The event is a tuple of one or more strings.

`unpost()`

  Remove self from event-watching post.

### Channel: a class for creating hierarchical trees of sounds and agents

Channel objects should be created via `Agent.new_channel()` and destroyed with the `channel.stop()` method.

- Public methods and fields:
  parent
    the parent of the channel (None if root)
  get_root_channel()
    return the root channel of the tree
  set_volume()
    change the volume of the channel
  stop()
    stop the channel immediately

`stop()`

  Stop the channel immediately. All sounds playing in the channel (or any subchannels) are cut off; all sounds and agents scheduled to run are discarded.

  If any notes are playing with non-zero volume, their termination may cause undesirable clicks and pops. It is best to set the volume of a channel to zero before stopping it. (The `FadeOutAgent` class can be used for this.)

  Due to the way sound generation is buffered, when an agent calls `channel.stop()`, the channel may be stopped slightly later than it ought to be.

`get_root_channel() -> channel`

  Return the root channel of the tree.

`set_volume(newvolume [, interval=0.005])`

  Change the volume of the channel to a new level (0 means silence, 1 means full volume). This affects all notes in the channel and any subchannels.

  The volume change begins immediately, and occurs smoothly over the interval given (in seconds). If no value is given, the interval defaults to 0.005 (five milliseconds), which is short enough that it will sound instantaneous. (You should not use an interval shorter than 0.005; it can cause undesirable clicks and pops.)

  Due to the way the volume code is written (a cheap and dirty hack), two volume changes scheduled too close together on the same channel (within about one second) can interfere with each other. The earlier one may be ignored entirely in favor of the later. Therefore, you should not rely on rapid sequences of `set_volume()` calls for your sound effects. Set volumes on individual notes instead, or else create several channels.

### stereo: a module of stereo pan objects

This is a separate module, not a class of methods. Each function is a top-level function in the module.

These functions are for creating stereo panning description objects. A stereo object is a stereo shift, a contraction, or a combination of these. (See Soundscape Basics.) Stereo objects can be passed to `new_channel_pan()` or `sched_note_pan()`.

Note that in the current implementation, stereo objects are represented by simple tuples, or by `None` (for the default stereo position). You should not rely on this remaining true. I may get cleverer in a future release, and re-implement them as a Python class. Use the utility functions.

- Module functions:
  default()
    return the default stereo position
  shift()
    return a simple stereo shift
  scale()
    return a stretched or compressed stereo effect
  fixed()
    return a stereo effect which is compressed to a point
  compose()
    apply one stereo effect on top of another
  cast()
    convert an object to a stereo effect

`default() -> stereo`

  Return a stereo object which describes the default stereo position -- no shift, no contraction.

`shift(pos) -> stereo`

  Return a simple stereo shift. If pos is zero, there is no shift in origin; this returns the default stereo position. -1 means all the way left; 1 means all the way right.

`scale(size) -> stereo`

  Return a stereo effect which is not shifted left or right, but is compressed or stretched from the center. If size is less than 1, the channels are compressed; zero causes every sound to be perfectly centered. If size is greater than 1, the channels are spread apart. Negative values cause the left and right channels to swap.

`fixed(pos) -> stereo`

  Return a stereo effect which is compressed to a point. All sounds contained in this effect, no matter how shifted, will come from a single point. If pos is zero, this will be the center; if -1, the left side; if 1, the right.

  `fixed(pos)` is equivalent to `compose(shift(pos), scale(0))`. You figure it out.

`compose(stereo1, stereo2) -> stereo`

  Return a stereo effect which is the result of applying of stereo1 on top of stereo2. This is the equivalent of a channel set to stereo1, containing a channel stereo2.

`cast(obj) -> stereo`

  Convert obj into a stereo object. If obj is `None`, this returns the default stereo position. If obj is a number, this returns a simple stereo shift -- no scaling. If obj is a stereo object, this returns it (or an equivalent object).

Designing Soundscapes

Return to Boodler docs index