Exploring Tekkotsu Programming on Mobile Robots:

Playing Sounds

Prev: Events
Up: Contents
Next: State machines

Tekkotsu includes a small collection of wavefiles (extension .wav), which you will find in /usr/local/Tekkotsu/project/ms/data/sound. These can be played on your PC using many popular audio programs. Under Linux you can use "play" or "aplay".

Playing sounds on the robot raises the same kind of realtime performance concerns as executing motions: we must be able to keep the buffer filled. Tekkotsu provides a separate Sound process to accomplish this.

You can play a bark sound by writing: sndman->playFile("barkhigh.wav");

Exercise: Playing a Sound in Response to a Button Press

It's easy to make the robot bark in response to button press events. We'll use a high-pitched bark for activate events and a low-pitched one for deactivate events. The sound manager is accessed via the global variable sndman, and we must include the file Sound/SoundManager.h.

#include "Behaviors/BehaviorBase.h"
#include "Events/EventRouter.h"
#include "Sound/SoundManager.h"

class DstBehavior : public BehaviorBase {
public:
  DstBehavior() : BehaviorBase("DstBehavior") {}

  virtual void doStart() {
    std::cout << getName() << " is starting up." << endl;
    erouter->addListener(this,EventBase::buttonEGID);
  }

  virtual void doStop() {
    std::cout << getName() << " is shutting down." << endl;
  }

  virtual void doEvent() {
    switch ( event->getGeneratorID() ) {

    case EventBase::buttonEGID:
      std::cout << getName() << " got event: " << event->getDescription() << std::endl;
      sndman->playFile((event->getMagnitude()==0) ? "barklow.wav" : "barkhigh.wav");
      break;

    default:
      std::cout << "Unexpected event:" << event->getDescription() << std::endl;
    };
  }

};

REGISTER_BEHAVIOR(DstBehavior);

Commands that play sounds return a value of type SoundManager::PlayID. When a sound finishes playing, the Sound process posts a deactivation event (the event generator is audioEGID) with source equal to this PlayID. On the AIBO, the PlayID can also be used to stop a sound that is currently playing.

There are functions available for doing more complicated things than just playing a sound. Examples include chaining several sounds together to play as a sequence, or interrupting a sound that is currently playing. One recommended trick for the AIBO is to preload all required sound files, e.g., by doing loadFile("howl.wav") within doStart(), so that sounds will already be present in memory when needed. Retrieving files from the memory stick can tie up the processor for as much as 500 msec, so failure to preload sounds hinders the AIBO's ability to respond rapidly to new stimuli. This trick is not needed for other robot types which do not use memory sticks.

Exercise: Stopping a Sound, and Mixing Sounds

We will modify the previous example to play a long, drawn-out howl sound when the AIBO's back button is pressed. (For the ERS-7, which has three back buttons, use MiddleBackButOffset instead of BackButOffset.) If the back button is pressed again while the AIBO is already howling, it should stop that howl and begin a new one. In order to do this, we will retain the current howl's PlayID in a protected data member called howl_id.

#include "Behaviors/BehaviorBase.h"
#include "Events/EventRouter.h"
#include "Sound/SoundManager.h"

class DstBehavior : public BehaviorBase {
protected:
  SoundManager::Play_ID howl_id;

public:
  DstBehavior() : BehaviorBase("DstBehavior"), howl_id(SoundManager::invalid_Play_ID) {}

  virtual void doStart() {
    BehaviorBase::doStart();
    std::cout << getName() << " is starting up." << endl;
    erouter->addListener(this,EventBase::buttonEGID);
    sndman->loadFile("barkhigh.wav");
    sndman->loadFile("barklow.wav");
    sndman->loadFile("howl.wav");
  }

  virtual void doStop() {
    sndman->releaseFile("barkhigh.wav");
    sndman->releaseFile("barklow.wav");
    sndman->releaseFile("howl.wav");
    std::cout << getName() << " is shutting down." << endl;
    BehaviorBase::doStop();
  }

  virtual void doEvent() {
    switch ( event->getGeneratorID() ) {

    case EventBase::buttonEGID:
      std::cout << getName() << " got event: " << event->getDescription() << std::endl;
      if (event->getSourceID()==RobotInfo::BackButOffset) {
	if (event->getTypeID()==EventBase::activateETID) {
	  sndman->StopPlay(howl_id);
	  howl_id = sndman->playFile("howl.wav");
	};
      }
      else
	sndman->playFile((event->getMagnitude()==0) ? "barklow.wav" : "barkhigh.wav");
      break;

    default:
      std::cout << "Unexpected event:" << event->getDescription() << std::endl;
    };
  }

};

REGISTER_BEHAVIOR(DstBehavior);

The sound manager will automatically mix the audio if we try to play several sounds at once. Try pressing one of the paw buttons while the AIBO is howling, and you'll hear a bark superimposed on the howl. If you comment out the call to StopPlay() and press the back button while the AIBO is howling, you'll hear a second howl superimposed on the first.

Explore more:

  1. Read the Sound documentation and modify the first example program so that in response to a button press activation, the AIBO emits a high bark followed immediately by a low bark. (Hint: look at the ChainFile function.) No sound should be played for deactivation events.

  2. Modify the second example program so that when the AIBO starts to howl, it raises its head, and the red (but not the green) LEDs in its face cycle on and off in a spooky manner. When the sound finishes, the LEDs should shut off and it should lower its head.

Prev: Events
Up: Contents
Next: State machines


Last modified: Thu Jun 17 21:13:16 EDT 2010