test_dummy_soundplay_node.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 
3 #***********************************************************
4 #* Software License Agreement (BSD License)
5 #*
6 #* Copyright (c) 2009, Willow Garage, Inc.
7 #* All rights reserved.
8 #*
9 #* Redistribution and use in source and binary forms, with or without
10 #* modification, are permitted provided that the following conditions
11 #* are met:
12 #*
13 #* * Redistributions of source code must retain the above copyright
14 #* notice, this list of conditions and the following disclaimer.
15 #* * Redistributions in binary form must reproduce the above
16 #* copyright notice, this list of conditions and the following
17 #* disclaimer in the documentation and/or other materials provided
18 #* with the distribution.
19 #* * Neither the name of the Willow Garage nor the names of its
20 #* contributors may be used to endorse or promote products derived
21 #* from this software without specific prior written permission.
22 #*
23 #* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24 #* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25 #* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
26 #* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
27 #* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
28 #* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
29 #* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
30 #* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
31 #* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 #* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
33 #* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34 #* POSSIBILITY OF SUCH DAMAGE.
35 #***********************************************************
36 
37 # Author: Blaise Gassend
38 
39 # 08-Feb-2019: Kei Okada remove gst stuff for Travis tests
40 import roslib
41 import rospy
42 import threading
43 import os
44 import logging
45 import sys
46 import traceback
47 import tempfile
48 from diagnostic_msgs.msg import DiagnosticStatus, KeyValue, DiagnosticArray
49 from sound_play.msg import SoundRequest, SoundRequestAction, SoundRequestResult, SoundRequestFeedback
50 import actionlib
51 
52 # try:
53 # import pygst
54 # pygst.require('0.10')
55 # import gst
56 # import gobject
57 # except:
58 # str="""
59 # **************************************************************
60 # Error opening pygst. Is gstreamer installed? (sudo apt-get install python-gst0.10
61 # **************************************************************
62 # """
63 # rospy.logfatal(str)
64 # print str
65 # exit(1)
66 
67 def sleep(t):
68  try:
69  rospy.sleep(t)
70  except:
71  pass
72 
73 
74 class soundtype:
75  STOPPED = 0
76  LOOPING = 1
77  COUNTING = 2
78 
79  def __init__(self, file, device, volume = 1.0):
80  self.lock = threading.RLock()
81  self.state = self.STOPPED
82  # self.sound = gst.element_factory_make("playbin2","player")
83  if device:
84  pass
85  # self.sink = gst.element_factory_make("alsasink", "sink")
86  # self.sink.set_property("device", device)
87  # self.sound.set_property("audio-sink", self.sink)
88  if (":" in file):
89  uri = file
90  elif os.path.isfile(file):
91  uri = "file://" + os.path.abspath(file)
92  else:
93  rospy.logerr('Error: URI is invalid: %s'%file)
94 
95  self.uri = uri
96  self.volume = volume
97  #self.sound.set_property('uri', uri)
98  #self.sound.set_property("volume",volume)
99  self.staleness = 1
100  self.file = file
101 
102  # self.bus = self.sound.get_bus()
103  # self.bus.add_signal_watch()
104  # self.bus.connect("message", self.on_stream_end)
105 
106  # def on_stream_end(self, bus, message):
107  # if message.type == gst.MESSAGE_EOS:
108  # self.state = self.STOPPED
109 
110  def __del__(self):
111  # stop our GST object so that it gets garbage-collected
112  self.stop()
113 
114  def update(self):
115  #self.bus.poll(gst.MESSAGE_ERROR, 10)
116  self.state = self.STOPPED
117 
118  def loop(self):
119  self.lock.acquire()
120  try:
121  self.staleness = 0
122 
123  if self.state == self.COUNTING:
124  self.stop()
125 
126  if self.state == self.STOPPED:
127  pass
128  # self.sound.seek_simple(gst.FORMAT_TIME, gst.SEEK_FLAG_FLUSH, 0)
129  # self.sound.set_state(gst.STATE_PLAYING)
130  self.state = self.LOOPING
131  finally:
132  self.lock.release()
133 
134  def stop(self):
135  if self.state != self.STOPPED:
136  self.lock.acquire()
137  try:
138  # self.sound.set_state(gst.STATE_NULL)
139  self.state = self.STOPPED
140  finally:
141  self.lock.release()
142 
143  def single(self):
144  self.lock.acquire()
145  try:
146  rospy.logdebug("Playing %s"%self.uri)
147  self.staleness = 0
148  if self.state == self.LOOPING:
149  self.stop()
150 
151  # self.sound.seek_simple(gst.FORMAT_TIME, gst.SEEK_FLAG_FLUSH, 0)
152  # self.sound.set_state(gst.STATE_PLAYING)
153  self.state = self.COUNTING
154  finally:
155  self.lock.release()
156 
157  def command(self, cmd):
158  if cmd == SoundRequest.PLAY_STOP:
159  self.stop()
160  elif cmd == SoundRequest.PLAY_ONCE:
161  self.single()
162  elif cmd == SoundRequest.PLAY_START:
163  self.loop()
164 
165  def get_staleness(self):
166  self.lock.acquire()
167  position = 0
168  duration = 0
169  try:
170  pass
171  # position = self.sound.query_position(gst.FORMAT_TIME)[0]
172  # duration = self.sound.query_duration(gst.FORMAT_TIME)[0]
173  except Exception as e:
174  position = 0
175  duration = 0
176  finally:
177  self.lock.release()
178 
179  if position != duration:
180  self.staleness = 0
181  else:
182  self.staleness = self.staleness + 1
183  return self.staleness
184 
185  def get_playing(self):
186  return self.state == self.COUNTING
187 
188 class soundplay:
189  _feedback = SoundRequestFeedback()
190  _result = SoundRequestResult()
191 
192  def stopdict(self,dict):
193  for sound in dict.values():
194  sound.stop()
195 
196  def stopall(self):
197  self.stopdict(self.builtinsounds)
198  self.stopdict(self.filesounds)
199  self.stopdict(self.voicesounds)
200 
201  def select_sound(self, data):
202  if data.sound == SoundRequest.PLAY_FILE:
203  if not data.arg2:
204  if not data.arg in self.filesounds.keys():
205  rospy.logdebug('command for uncached wave: "%s"'%data.arg)
206  try:
207  self.filesounds[data.arg] = soundtype(data.arg, self.device)
208  except:
209  rospy.logerr('Error setting up to play "%s". Does this file exist on the machine on which sound_play is running?'%data.arg)
210  return
211  else:
212  rospy.logdebug('command for cached wave: "%s"'%data.arg)
213  sound = self.filesounds[data.arg]
214  else:
215  absfilename = os.path.join(roslib.packages.get_pkg_dir(data.arg2), data.arg)
216  if not absfilename in self.filesounds.keys():
217  rospy.logdebug('command for uncached wave: "%s"'%absfilename)
218  try:
219  self.filesounds[absfilename] = soundtype(absfilename, self.device)
220  except:
221  rospy.logerr('Error setting up to play "%s" from package "%s". Does this file exist on the machine on which sound_play is running?'%(data.arg, data.arg2))
222  return
223  else:
224  rospy.logdebug('command for cached wave: "%s"'%absfilename)
225  sound = self.filesounds[absfilename]
226  elif data.sound == SoundRequest.SAY:
227  if not data.arg in self.voicesounds.keys():
228  rospy.logdebug('command for uncached text: "%s"' % data.arg)
229  txtfile = tempfile.NamedTemporaryFile(prefix='sound_play', suffix='.txt')
230  (wavfile,wavfilename) = tempfile.mkstemp(prefix='sound_play', suffix='.wav')
231  txtfilename=txtfile.name
232  os.close(wavfile)
233  voice = data.arg2
234  try:
235  # try to encode if data.args is not string for pytho3n
236  if sys.version_info.major >= 3 and type(data.arg) is str:
237  data.arg = data.arg.encode()
238  txtfile.write(data.arg)
239  txtfile.flush()
240  rospy.loginfo("text2wave -eval '("+voice+")' "+txtfilename+" -o "+wavfilename)
241  '''
242  os.system("text2wave -eval '("+voice+")' "+txtfilename+" -o "+wavfilename)
243  try:
244  if os.stat(wavfilename).st_size == 0:
245  raise OSError # So we hit the same catch block
246  except OSError:
247  rospy.logerr("text2wave -eval '("+voice+")' "+txtfilename+" -o "+wavfilename)
248  rospy.logerr('Sound synthesis failed. Is festival installed? Is a festival voice installed? Try running "rosdep satisfy sound_play|sh". Refer to http://wiki.ros.org/sound_play/Troubleshooting')
249  return
250  '''
251  self.voicesounds[data.arg] = soundtype(wavfilename, self.device)
252  finally:
253  txtfile.close()
254  else:
255  rospy.logdebug('command for cached text: "%s"'%data.arg)
256  sound = self.voicesounds[data.arg]
257  else:
258  rospy.logdebug('command for builtin wave: %i'%data.sound)
259  if not data.sound in self.builtinsounds:
260  params = self.builtinsoundparams[data.sound]
261  self.builtinsounds[data.sound] = soundtype(params[0], self.device, params[1])
262  sound = self.builtinsounds[data.sound]
263  if sound.staleness != 0 and data.command != SoundRequest.PLAY_STOP:
264  # This sound isn't counted in active_sounds
265  rospy.logdebug("activating %i %s"%(data.sound,data.arg))
266  self.active_sounds = self.active_sounds + 1
267  sound.staleness = 0
268  # if self.active_sounds > self.num_channels:
269  # mixer.set_num_channels(self.active_sounds)
270  # self.num_channels = self.active_sounds
271  return sound
272 
273  def callback(self,data):
274  if not self.initialized:
275  return
276  self.mutex.acquire()
277  # Force only one sound at a time
278  self.stopall()
279  try:
280  if data.sound == SoundRequest.ALL and data.command == SoundRequest.PLAY_STOP:
281  self.stopall()
282  else:
283  sound = self.select_sound(data)
284  sound.command(data.command)
285  except Exception as e:
286  rospy.logerr('Exception in callback: %s'%str(e))
287  rospy.loginfo(traceback.format_exc())
288  finally:
289  self.mutex.release()
290  rospy.logdebug("done callback")
291 
292  # Purge sounds that haven't been played in a while.
293  def cleanupdict(self, dict):
294  purgelist = []
295  for (key,sound) in dict.iteritems():
296  try:
297  staleness = sound.get_staleness()
298  except Exception as e:
299  rospy.logerr('Exception in cleanupdict for sound (%s): %s'%(str(key),str(e)))
300  staleness = 100 # Something is wrong. Let's purge and try again.
301  #print "%s %i"%(key, staleness)
302  if staleness >= 10:
303  purgelist.append(key)
304  if staleness == 0: # Sound is playing
305  self.active_sounds = self.active_sounds + 1
306  for key in purgelist:
307  rospy.logdebug('Purging %s from cache'%key)
308  dict[key].stop() # clean up resources
309  del dict[key]
310 
311  def cleanup(self):
312  self.mutex.acquire()
313  try:
314  self.active_sounds = 0
315  self.cleanupdict(self.filesounds)
316  self.cleanupdict(self.voicesounds)
317  self.cleanupdict(self.builtinsounds)
318  except:
319  rospy.loginfo('Exception in cleanup: %s'%sys.exc_info()[0])
320  finally:
321  self.mutex.release()
322 
323  def diagnostics(self, state):
324  try:
325  da = DiagnosticArray()
326  ds = DiagnosticStatus()
327  ds.name = rospy.get_caller_id().lstrip('/') + ": Node State"
328  if state == 0:
329  ds.level = DiagnosticStatus.OK
330  ds.message = "%i sounds playing"%self.active_sounds
331  ds.values.append(KeyValue("Active sounds", str(self.active_sounds)))
332  ds.values.append(KeyValue("Allocated sound channels", str(self.num_channels)))
333  ds.values.append(KeyValue("Buffered builtin sounds", str(len(self.builtinsounds))))
334  ds.values.append(KeyValue("Buffered wave sounds", str(len(self.filesounds))))
335  ds.values.append(KeyValue("Buffered voice sounds", str(len(self.voicesounds))))
336  elif state == 1:
337  ds.level = DiagnosticStatus.WARN
338  ds.message = "Sound device not open yet."
339  else:
340  ds.level = DiagnosticStatus.ERROR
341  ds.message = "Can't open sound device. See http://wiki.ros.org/sound_play/Troubleshooting"
342  da.status.append(ds)
343  da.header.stamp = rospy.get_rostime()
344  self.diagnostic_pub.publish(da)
345  except Exception as e:
346  rospy.loginfo('Exception in diagnostics: %s'%str(e))
347 
348  def execute_cb(self, data):
349  data = data.sound_request
350  if not self.initialized:
351  return
352  self.mutex.acquire()
353  # Force only one sound at a time
354  self.stopall()
355  try:
356  if data.sound == SoundRequest.ALL and data.command == SoundRequest.PLAY_STOP:
357  self.stopall()
358  else:
359  sound = self.select_sound(data)
360  sound.command(data.command)
361 
362  r = rospy.Rate(1)
363  start_time = rospy.get_rostime()
364  success = True
365  while sound.get_playing():
366  sound.update()
367  if self._as.is_preempt_requested():
368  rospy.loginfo('sound_play action: Preempted')
369  sound.stop()
370  self._as.set_preempted()
371  success = False
372  break
373 
374  self._feedback.playing = sound.get_playing()
375  self._feedback.stamp = rospy.get_rostime() - start_time
376  self._as.publish_feedback(self._feedback)
377  r.sleep()
378 
379  if success:
380  self._result.playing = self._feedback.playing
381  self._result.stamp = self._feedback.stamp
382  rospy.loginfo('sound_play action: Succeeded')
383  self._as.set_succeeded(self._result)
384 
385  except Exception as e:
386  rospy.logerr('Exception in actionlib callback: %s'%str(e))
387  rospy.loginfo(traceback.format_exc())
388  finally:
389  self.mutex.release()
390  rospy.logdebug("done actionlib callback")
391 
392  def __init__(self):
393  rospy.init_node('sound_play')
394  self.device = rospy.get_param("~device", str())
395  self.diagnostic_pub = rospy.Publisher("/diagnostics", DiagnosticArray, queue_size=1)
396  rootdir = os.path.join(roslib.packages.get_pkg_dir('sound_play'),'sounds')
397 
399  SoundRequest.BACKINGUP : (os.path.join(rootdir, 'BACKINGUP.ogg'), 0.1),
400  SoundRequest.NEEDS_UNPLUGGING : (os.path.join(rootdir, 'NEEDS_UNPLUGGING.ogg'), 1),
401  SoundRequest.NEEDS_PLUGGING : (os.path.join(rootdir, 'NEEDS_PLUGGING.ogg'), 1),
402  SoundRequest.NEEDS_UNPLUGGING_BADLY : (os.path.join(rootdir, 'NEEDS_UNPLUGGING_BADLY.ogg'), 1),
403  SoundRequest.NEEDS_PLUGGING_BADLY : (os.path.join(rootdir, 'NEEDS_PLUGGING_BADLY.ogg'), 1),
404  }
405 
406  self.no_error = True
407  self.initialized = False
408  self.active_sounds = 0
409 
410  self.mutex = threading.Lock()
411  sub = rospy.Subscriber("robotsound", SoundRequest, self.callback)
412  self._as = actionlib.SimpleActionServer('sound_play', SoundRequestAction, execute_cb=self.execute_cb, auto_start = False)
413  self._as.start()
414 
415  self.mutex.acquire()
416  self.sleep(0.5) # For ros startup race condition
417  self.diagnostics(1)
418 
419  while not rospy.is_shutdown():
420  while not rospy.is_shutdown():
421  self.init_vars()
422  self.no_error = True
423  self.initialized = True
424  self.mutex.release()
425  try:
426  self.idle_loop()
427  # Returns after inactive period to test device availability
428  #print "Exiting idle"
429  except:
430  rospy.loginfo('Exception in idle_loop: %s'%sys.exc_info()[0])
431  finally:
432  self.mutex.acquire()
433 
434  self.diagnostics(2)
435  self.mutex.release()
436 
437  def init_vars(self):
438  self.num_channels = 10
439  self.builtinsounds = {}
440  self.filesounds = {}
441  self.voicesounds = {}
442  self.hotlist = []
443  if not self.initialized:
444  rospy.loginfo('sound_play node is ready to play sound')
445 
446  def sleep(self, duration):
447  try:
448  rospy.sleep(duration)
449  except rospy.exceptions.ROSInterruptException:
450  pass
451 
452  def idle_loop(self):
453  self.last_activity_time = rospy.get_time()
454  while (rospy.get_time() - self.last_activity_time < 10 or
455  len(self.builtinsounds) + len(self.voicesounds) + len(self.filesounds) > 0) \
456  and not rospy.is_shutdown():
457  #print "idle_loop"
458  self.diagnostics(0)
459  self.sleep(1)
460  self.cleanup()
461  #print "idle_exiting"
462 
463 if __name__ == '__main__':
464  soundplay()
test_dummy_soundplay_node.soundtype.loop
def loop(self)
Definition: test_dummy_soundplay_node.py:118
test_dummy_soundplay_node.soundplay.__init__
def __init__(self)
Definition: test_dummy_soundplay_node.py:392
test_dummy_soundplay_node.soundtype.single
def single(self)
Definition: test_dummy_soundplay_node.py:143
test_dummy_soundplay_node.soundplay.filesounds
filesounds
Definition: test_dummy_soundplay_node.py:440
test_dummy_soundplay_node.soundplay.mutex
mutex
Definition: test_dummy_soundplay_node.py:410
test_dummy_soundplay_node.soundtype.stop
def stop(self)
Definition: test_dummy_soundplay_node.py:134
test_dummy_soundplay_node.soundplay.builtinsounds
builtinsounds
Definition: test_dummy_soundplay_node.py:439
test_dummy_soundplay_node.soundtype.get_staleness
def get_staleness(self)
Definition: test_dummy_soundplay_node.py:165
test_dummy_soundplay_node.soundplay.diagnostics
def diagnostics(self, state)
Definition: test_dummy_soundplay_node.py:323
test_dummy_soundplay_node.soundtype.__del__
def __del__(self)
Definition: test_dummy_soundplay_node.py:110
test_dummy_soundplay_node.soundplay.sleep
def sleep(self, duration)
Definition: test_dummy_soundplay_node.py:446
test_dummy_soundplay_node.soundplay._result
_result
Definition: test_dummy_soundplay_node.py:190
test_dummy_soundplay_node.soundtype
Definition: test_dummy_soundplay_node.py:74
test_dummy_soundplay_node.soundplay._feedback
_feedback
Definition: test_dummy_soundplay_node.py:189
test_dummy_soundplay_node.soundtype.staleness
staleness
Definition: test_dummy_soundplay_node.py:99
test_dummy_soundplay_node.soundplay.stopall
def stopall(self)
Definition: test_dummy_soundplay_node.py:196
test_dummy_soundplay_node.soundtype.LOOPING
int LOOPING
Definition: test_dummy_soundplay_node.py:76
test_dummy_soundplay_node.soundtype.__init__
def __init__(self, file, device, volume=1.0)
Definition: test_dummy_soundplay_node.py:79
test_dummy_soundplay_node.soundtype.update
def update(self)
Definition: test_dummy_soundplay_node.py:114
test_dummy_soundplay_node.soundplay.active_sounds
active_sounds
Definition: test_dummy_soundplay_node.py:266
test_dummy_soundplay_node.soundtype.volume
volume
Definition: test_dummy_soundplay_node.py:96
test_dummy_soundplay_node.soundplay.builtinsoundparams
builtinsoundparams
Definition: test_dummy_soundplay_node.py:398
test_dummy_soundplay_node.soundtype.STOPPED
int STOPPED
Definition: test_dummy_soundplay_node.py:75
test_dummy_soundplay_node.soundtype.file
file
Definition: test_dummy_soundplay_node.py:100
test_dummy_soundplay_node.soundplay.select_sound
def select_sound(self, data)
Definition: test_dummy_soundplay_node.py:201
test_dummy_soundplay_node.soundtype.COUNTING
int COUNTING
Definition: test_dummy_soundplay_node.py:77
test_dummy_soundplay_node.soundplay.diagnostic_pub
diagnostic_pub
Definition: test_dummy_soundplay_node.py:395
test_dummy_soundplay_node.sleep
def sleep(t)
Definition: test_dummy_soundplay_node.py:67
test_dummy_soundplay_node.soundplay.execute_cb
def execute_cb(self, data)
Definition: test_dummy_soundplay_node.py:348
test_dummy_soundplay_node.soundplay.stopdict
def stopdict(self, dict)
Definition: test_dummy_soundplay_node.py:192
test_dummy_soundplay_node.soundtype.lock
lock
Definition: test_dummy_soundplay_node.py:80
test_dummy_soundplay_node.soundtype.command
def command(self, cmd)
Definition: test_dummy_soundplay_node.py:157
test_dummy_soundplay_node.soundtype.uri
uri
Definition: test_dummy_soundplay_node.py:95
test_dummy_soundplay_node.soundplay.callback
def callback(self, data)
Definition: test_dummy_soundplay_node.py:273
test_dummy_soundplay_node.soundplay.device
device
Definition: test_dummy_soundplay_node.py:394
test_dummy_soundplay_node.soundplay.initialized
initialized
Definition: test_dummy_soundplay_node.py:407
test_dummy_soundplay_node.soundplay._as
_as
Definition: test_dummy_soundplay_node.py:412
test_dummy_soundplay_node.soundplay.hotlist
hotlist
Definition: test_dummy_soundplay_node.py:442
test_dummy_soundplay_node.soundplay.idle_loop
def idle_loop(self)
Definition: test_dummy_soundplay_node.py:452
actionlib::SimpleActionServer
test_dummy_soundplay_node.soundtype.state
state
Definition: test_dummy_soundplay_node.py:81
test_dummy_soundplay_node.soundplay
Definition: test_dummy_soundplay_node.py:188
test_dummy_soundplay_node.soundplay.num_channels
num_channels
Definition: test_dummy_soundplay_node.py:438
test_dummy_soundplay_node.soundtype.get_playing
def get_playing(self)
Definition: test_dummy_soundplay_node.py:185
test_dummy_soundplay_node.soundplay.cleanupdict
def cleanupdict(self, dict)
Definition: test_dummy_soundplay_node.py:293
test_dummy_soundplay_node.soundplay.voicesounds
voicesounds
Definition: test_dummy_soundplay_node.py:441
test_dummy_soundplay_node.soundplay.no_error
no_error
Definition: test_dummy_soundplay_node.py:406
test_dummy_soundplay_node.soundplay.cleanup
def cleanup(self)
Definition: test_dummy_soundplay_node.py:311
test_dummy_soundplay_node.soundplay.init_vars
def init_vars(self)
Definition: test_dummy_soundplay_node.py:437
test_dummy_soundplay_node.soundplay.last_activity_time
last_activity_time
Definition: test_dummy_soundplay_node.py:453


pr2eus
Author(s): Kei Okada
autogenerated on Sun May 11 2025 02:39:49