(!) Please ask about problems and questions regarding this tutorial on answers.ros.org. Don't forget to include in your question the link to this page, the versions of your OS & ROS, and also add appropriate tags.

How to Create a Sound Interface

Description: This tutorial covers how to create an interface for using sound_play. The interface uses a node acting as a 'funnel' as a convenient way to route sound commands through sound_play.

Keywords: sound, sound_play

Tutorial Level: INTERMEDIATE



This tutorial assumes that your linux sound drivers and speaker system are already working and that sound_play is working from the terminal, all of which can be tested in the Configuring and Using Speakers tutorial. The node created in this tutorial uses the sound_play convenience wrapper package to play sound. See the sound_play documentation for details.

Examples and References

Configuring and Using Speakers, sound_play Package Summary, sound_play/SoundRequest.msg


The structure built in this tutorial will depend on sound_play and will require sound assets for testing.

One Method for Structuring the Interface

This structure routes all sound instructions, given in the form of yak commands, through a single node, here called yak_node. yak_node is then the sole node responsible for dealing with SoundClient() and sound_play, and acts as a 'funnel', allowing multiple requesters to play sounds.

SoundClient() requires full path names, and can also become overloaded and produce a segmentation fault. This partitioning of the structure allows both of these issues to be handled in a single node.

A suitable speaker command should communicate a file name, text-to-speech string, or built-in sound, and a command for type of input. Here, speaker commands have a string for a file name or for text-to-speech, and a command for playing the .wav file or for using text-to-speech. More command types could be added for playing .ogg or built-in files if needed.

Code and Explanation

Note: It is convenient to save all sound assets in the same directory, as SoundClient() requires full path names to play sound assets. SoundClient() also prefers all sound assets to be .wav or .ogg files.

All sound instructions in the form of yak commands are published to the yak topic, to which yak_node subscribes.

   1 #!/usr/bin/env python
   2 import roslib; roslib.load_manifest('sound_yak')
   3 import rospy, os, sys
   4 from sound_play.msg import SoundRequest
   5 from sound_play.libsoundplay import SoundClient
   6 from sound_yak.msg import yak_cmd
   8 # directory with sound assets - change as needed
   9 soundAssets = '/home/shiloh/devel/audio_assets/'
  10 # duration of yak throttle
  11 throttle = 3 # seconds
  13 def sound_translator(data):
  14     print data
  15     global allow_yak
  16     if rospy.Time.now() <= allow_yak: # Throttles yak to avoid
  17         print("Sound throttled")      # SoundClient segfault
  18         return
  19     # when to reallow yak
  20     allow_yak = rospy.Time.now() + rospy.Duration.from_sec(throttle)
  21     if data.cmd == "wav":
  22         soundhandle.playWave(soundAssets + data.param)
  23     if data.cmd == "say":
  24         soundhandle.say(data.param)
  26 def yak_init():
  27     rospy.init_node('yak_node', anonymous = True)
  28     global allow_yak
  29     allow_yak = rospy.Time.now()
  30     rospy.Subscriber('yak', yak_cmd, sound_translator)
  31     rospy.spin()
  33 if __name__ == '__main__':
  34     soundhandle = SoundClient()
  35     rospy.sleep(1)
  37     yak_init()

SoundClient() may produce a segmentation fault if too many sound commands are given in a short period of time, so sound commands are throttled in lines 14-20. Sounds can only be played when the current time is later than time allow_yak, and every time a sound is played, allow_yak is updated to the current time plus a throttle time, here 3 seconds, defined in line 11.

Lines 21-24 process yak commands. SoundClient()’s playWave requires full path names for sound assets. A convenient solution allows the parameter to be the file name only and concatenates the path name to the folder containing sound assets, as in line 22.

Publishing yak commands

Like any other ROS topic, yak commands are imported, set, and published. yak_cmd is imported as usual near the beginning of the file. yak.cmd and yak.param are both set before pub.publish(yak) in the callback method, then global yak = yak_cmd() and global pub = rospy.Publisher('yak', yak_cmd) are set in the initialization method.

The callback should contain:

   1 yak.cmd = 'wav'
   2 yak.param = 'hello.wav'
   3 pub.publish(yak)

The initializer should contain:

   1 global yak
   2 yak = yak_cmd()
   3 global pub
   4 pub = rospy.Publisher('yak', yak_cmd)

Running the interface

After a roscore is running, rosrun sound_play soundplay_node.py, and rosrun sound_yak yak_node.py (or whichever package you put it in) in a new tab. Also run your nodes that send yak commands. Here is a checklist for running yak_node:

  1. Ensure that a roscore is running.

     $ roscore
  2. rosrun sound_play soundplay_node.py in a new tab.

     $ rosrun sound_play soundplay_node.py
  3. rosrun sound_yak yak_node.py (or whichever package you put it in) in a new tab.

     $ rosrun sound_yak yak_node.py
  4. Run your nodes that send yak commands.

     $ rosrun my_pack my_node.py

Wiki: sound_play/Tutorials/How to Create a Sound Interface (last edited 2013-02-20 22:38:40 by ShilohCurtis)