## Boodler: External Events

Boodler can trigger agents in response to external events. These events are messages received via a standard network connection. You can set up other programs to send these messages, and thereby interface your Boodler sound system with anything else you desire.

(A note on security: Boodler has none. If your computer is connected to the Internet, any machine on the Internet can send events to Boodler. As a precaution, therefore, Boodler does not listen for events unless you use the `--listen` option. If you want to use sound events, but want to exclude the outside world from sending them, you should use some kind of firewall hardware or software. Boodler uses port 31863; a firewall that blocks that port will block Boodler events.)

(Another option is to give the `--port` option and specify an absolute pathname instead of a port number. This causes Boodler to listen on a Unix domain socket, instead of an Internet network socket. Unix domain sockets only work within a single machine, not between different machines, and you can use standard Unix file protections to limit access to the socket.)

### Events

An event message is a tuple of one or more strings. The first (or only) string in the tuple is considered to be the type of the event.

To listen for events, you create a special type of agent. This agent sits on a channel and waits, but it is not scheduled for a particular time. Instead, it listens for a particular type of event. When an event of that type arrives, the agent runs immediately. (Well, actually there's a small delay.)

A listening agent, unlike a normal agent, does not shut down after it runs once. It continues listening, and runs once every time an appropriate event is received.

More than one agent can be posted to listen for a particular type of event. When that event arrives, all the agents run simultaneously. Also, a particular agent can be posted to listen for several types of events.

### Writing a Listening Agent

A listening agent might look like this:

    class Example(EventAgent):
        name = 'listening example'
        watch_events = 'go'
        def receive(self, event):
            self.sched_note('environ/droplet-plink.aiff')

The agent is a member of `EventAgent`, which is a subclass of `Agent` which has been specialized for event-listening. Note that there is no `run()` method; instead, there is a `receive()` method, which takes an extra argument for the event. The class also has an extra attribute: `watch_events`. This must contain the type of event to watch for.

You can now type:

    python boodler.py --listen bootest.Example

Boodler will start up, but you will hear nothing. (Don't forget the `--listen` argument, or else you'll see an "event listening disabled" error.)

When an `EventAgent` starts up, it posts itself to listen for its `watch_events`. In other words, the `EventAgent` class has a default `run()` implementation which looks like this:

        def run(self):
            self.post_agent(self)

This is usually what you want -- when the agent is scheduled (either from the command line, or by another agent) it should begin waiting for events. (You can, of course, override the `run()` if you want the agent to do something more complicated.)

At any rate, our agent is still waiting patiently. To send an event, use the `boomsg.py` program:

    python boomsg.py go

This sends a simple message `('go')` to a listening Boodler process -- by default, on the same machine. To send a message to a different machine, you would say:

    python boomsg.py --hostname machine.addr.net go

You can also use the `--port` argument to specify a network port number, or (if the `--port` value is an absolute pathname) a Unix domain socket. Note that `--hostname` is ignored for Unix domain sockets.

### More Complex Listeners

The example above uses the simplest kind of event -- a single string, which gives the event type. You can also receive events that have more information.

    class Example(EventAgent):
        name = 'listening example'
        watch_events = 'play'
        def receive(self, event):
            snd = event[1]
            self.sched_note(snd)

This listener listens for a `'play'` event. The agent then extracts the name of a sound from the second element of the tuple, and plays the sound. So you could type:

    python boomsg.py play environ/droplet-plink.aiff

...or name any other sound in the library. You can also check `event` to discriminate between types of events, if your agent watches for more than one type.

    class Example(EventAgent):
        name = 'listening example'
        watch_events = ['droplet', 'heartbeat']
        def receive(self, event):
            if (event[0] == 'droplet'):
                self.sched_note('environ/droplet-plink.aiff')
            if (event[0] == 'heartbeat'):
                self.sched_note('environ/heartbeat.aiff')

In this example, `watch_events` is a list of strings, rather than a single string. (It could also be a function which returns a string, or a list of strings.) The agent accepts both `'droplet'` and `'heartbeat'` events, and plays each as a different sound.

### The Event Message Protocol

You don't actually have to use `boomsg.py` to send events. The protocol is simply a direct TCP/IP connection to port 31863. (`telnet` will work fine.) Each (nonempty) line sent is an event. The line is broken up into strings at space characters; the resulting tuple becomes the event.

You can keep the connection open, and send many messages in a row, if you want. Unix, Mac, or DOS linebreaks will all work.

Designing Soundscapes

Return to Boodler docs index