├── .github ├── labeler.yml └── workflows │ ├── labeler.yml │ └── main.yml ├── .gitignore ├── LICENSE ├── README.md ├── audio_capture ├── .gitignore ├── CHANGELOG.rst ├── CMakeLists.txt ├── launch │ ├── capture.launch │ ├── capture_to_file.launch │ └── capture_wave.launch ├── mainpage.dox ├── package.xml └── src │ └── audio_capture.cpp ├── audio_common ├── CHANGELOG.rst ├── CMakeLists.txt └── package.xml ├── audio_common_msgs ├── .gitignore ├── CHANGELOG.rst ├── CMakeLists.txt ├── mainpage.dox ├── msg │ ├── AudioData.msg │ ├── AudioDataStamped.msg │ └── AudioInfo.msg └── package.xml ├── audio_play ├── .gitignore ├── CHANGELOG.rst ├── CMakeLists.txt ├── launch │ ├── play.launch │ └── record_to_file.launch ├── mainpage.dox ├── package.xml └── src │ └── audio_play.cpp └── sound_play ├── CHANGELOG.rst ├── CMakeLists.txt ├── README.md ├── action └── SoundRequest.action ├── include └── sound_play │ └── sound_play.h ├── mainpage.dox ├── msg └── SoundRequest.msg ├── package.xml ├── resources └── flitevox │ └── .gitignore ├── scripts ├── is_speaking.py ├── play.py ├── playbuiltin.py ├── playpackage.py ├── say.py ├── shutup.py ├── soundclient_example.py ├── soundplay_node.py ├── test.py ├── test │ └── test_sound_client.py └── test_actionlib_client.py ├── setup.py ├── sound_play_plugin.yaml ├── soundplay_node.launch ├── sounds ├── BACKINGUP.ogg ├── NEEDS_PLUGGING.ogg ├── NEEDS_PLUGGING_BADLY.ogg ├── NEEDS_UNPLUGGING.ogg ├── NEEDS_UNPLUGGING_BADLY.ogg └── say-beep.wav ├── src └── sound_play │ ├── __init__.py │ ├── festival_plugin.py │ ├── flite_plugin.py │ ├── libsoundplay.py │ ├── sound_play_plugin.py │ └── sound_type.py ├── test.launch └── test ├── CMakeLists.txt └── test.cpp /.github/labeler.yml: -------------------------------------------------------------------------------- 1 | github-action: 2 | - .github/**/* 3 | audio_capture: 4 | - audio_capture/**/* 5 | audio_common: 6 | - audio_common/**/* 7 | audio_common_msgs: 8 | - audio_common_msgs/**/* 9 | audio_play: 10 | - audio_play/**/* 11 | sound_play: 12 | - sound_play/**/* 13 | readme: 14 | - REAEME.md 15 | -------------------------------------------------------------------------------- /.github/workflows/labeler.yml: -------------------------------------------------------------------------------- 1 | name: "Pull Request Labeler" 2 | on: 3 | - pull_request_target 4 | 5 | permissions: 6 | contents: read 7 | 8 | jobs: 9 | triage: 10 | permissions: 11 | contents: read 12 | pull-requests: write 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/labeler@v3 16 | with: 17 | repo-token: "${{ secrets.GITHUB_TOKEN }}" 18 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: ROS1 CI 2 | on: 3 | push: 4 | pull_request: 5 | schedule: 6 | - cron: "0 0 * * *" 7 | jobs: 8 | industrial_ci: 9 | strategy: 10 | matrix: 11 | env: 12 | - ROS_DISTRO: melodic 13 | ROS_REPO: testing 14 | CMAKE_ARGS: '-DCMAKE_BUILD_TYPE=Debug' 15 | - ROS_DISTRO: melodic 16 | ROS_REPO: testing 17 | CMAKE_ARGS: '-DCMAKE_BUILD_TYPE=Release' 18 | - ROS_DISTRO: noetic 19 | ROS_REPO: testing 20 | CMAKE_ARGS: '-DCMAKE_BUILD_TYPE=Debug' 21 | - ROS_DISTRO: noetic 22 | ROS_REPO: testing 23 | CMAKE_ARGS: '-DCMAKE_BUILD_TYPE=Release' 24 | runs-on: ubuntu-latest 25 | steps: 26 | - uses: actions/checkout@v1 27 | - uses: 'ros-industrial/industrial_ci@master' 28 | env: ${{matrix.env}} 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2009, Willow Garage, Inc. 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ROS audio\_common Package 2 | 3 | [![ROS1 CI](https://github.com/ros-drivers/audio_common/actions/workflows/main.yml/badge.svg)](https://github.com/ros-drivers/audio_common/actions/workflows/main.yml) 4 | [![ROS2 CI](https://github.com/ros-drivers/audio_common/actions/workflows/ros2.yml/badge.svg)](https://github.com/ros-drivers/audio_common/actions/workflows/ros2.yml) 5 | 6 | This repository contains the ROS audio\_common package. 7 | 8 | For user documentation, please refer to the [ROS Wiki page for audio\_common](http://wiki.ros.org/audio_common) 9 | 10 | ## Deb Build Status 11 | 12 | | Package | Melodic (Bionic) | Noetic (Focal) | Noetic (Buster) | 13 | |:---------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| 14 | | audio_common (arm64) | [![Build Status](http://build.ros.org/job/Mbin_ubv8_uBv8__audio_common__ubuntu_bionic_arm64__binary/badge/icon)](http://build.ros.org/job/Mbin_ubv8_uBv8__audio_common__ubuntu_bionic_arm64__binary) | [![Build Status](http://build.ros.org/job/Nbin_ufv8_uFv8__audio_common__ubuntu_focal_arm64__binary/badge/icon)](http://build.ros.org/job/Nbin_ufv8_uFv8__audio_common__ubuntu_focal_arm64__binary) | [![Build Status](http://build.ros.org/job/Nbin_dbv8_dBv8__audio_common__debian_buster_arm64__binary/badge/icon)](http://build.ros.org/job/Nbin_dbv8_dBv8__audio_common__debian_buster_arm64__binary) | 15 | | audio_common (armhf) | [![Build Status](http://build.ros.org/job/Mbin_ubhf_uBhf__audio_common__ubuntu_bionic_armhf__binary/badge/icon)](http://build.ros.org/job/Mbin_ubhf_uBhf__audio_common__ubuntu_bionic_armhf__binary) | [![Build Status](http://build.ros.org/job/Nbin_ufhf_uFhf__audio_common__ubuntu_focal_armhf__binary/badge/icon)](http://build.ros.org/job/Nbin_ufhf_uFhf__audio_common__ubuntu_focal_armhf__binary) | --- | 16 | | audio_common (i386) | --- | --- | --- | 17 | | audio_common (amd64) | [![Build Status](http://build.ros.org/job/Mbin_uB64__audio_common__ubuntu_bionic_amd64__binary/badge/icon)](http://build.ros.org/job/Mbin_uB64__audio_common__ubuntu_bionic_amd64__binary) | [![Build Status](http://build.ros.org/job/Nbin_uF64__audio_common__ubuntu_focal_amd64__binary/badge/icon)](http://build.ros.org/job/Nbin_uF64__audio_common__ubuntu_focal_amd64__binary) | [![Build Status](http://build.ros.org/job/Nbin_db_dB64__audio_common__debian_buster_amd64__binary/badge/icon)](http://build.ros.org/job/Nbin_db_dB64__audio_common__debian_buster_amd64__binary) | 18 | 19 | ## ROS1 source build 20 | 21 | On ROS Kinetic, Melodic and Noetic, the [master](https://github.com/ros-drivers/audio_common/tree/master) branch is recommended. 22 | 23 | ## ROS2 source build 24 | 25 | On ROS2, the [ros2](https://github.com/ros-drivers/audio_common/tree/ros2) branch is recommended 26 | 27 | ## Development, Branch and Release Policy 28 | 29 | The `master` branch is currently considered the development branch, and is released into ROS Kinetic, Melodic and Noetic with version numbers in the 0.3.x range. 30 | `master` is accepting new, non-breaking features and bug fixes. 31 | 32 | Large, breaking changes such as changes to dependencies or the package API will be considered, 33 | but they will probably be staged into a development branch for release into the next major release of ROS (ROS L) 34 | 35 | ## Support 36 | 37 | Please ask support questions on [ROS Answers](http://answers.ros.org/questions/). 38 | -------------------------------------------------------------------------------- /audio_capture/.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | -------------------------------------------------------------------------------- /audio_capture/CHANGELOG.rst: -------------------------------------------------------------------------------- 1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 2 | Changelog for package audio_capture 3 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 4 | 5 | 0.3.18 (2024-08-13) 6 | ------------------- 7 | 8 | 0.3.17 (2023-06-08) 9 | ------------------- 10 | * Merge pull request `#220 `_ from v4hn/master 11 | * on real systems publish system clock time in capture node 12 | * The capture node is hard-coded to alsasrc 13 | * Contributors: Shingo Kitagawa, v4hn 14 | 15 | 0.3.16 (2022-12-23) 16 | ------------------- 17 | * Merge pull request `#204 `_ from knorth55/audio-capture-stamped 18 | * add todo comment 19 | * publish audio stamped in audio_capture.cpp 20 | * Merge pull request `#216 `_ from knorth55/launch-update 21 | * update audio_capture launch 22 | * Contributors: Shingo Kitagawa 23 | 24 | 0.3.15 (2022-08-29) 25 | ------------------- 26 | 27 | 0.3.14 (2022-08-18) 28 | ------------------- 29 | 30 | 0.3.13 (2022-04-07) 31 | ------------------- 32 | 33 | 0.3.12 (2021-09-01) 34 | ------------------- 35 | * Merge branch 'master' into master 36 | * Contributors: Shingo Kitagawa 37 | 38 | 0.3.11 (2021-04-08) 39 | ------------------- 40 | 41 | 0.3.10 (2021-01-07) 42 | ------------------- 43 | * add bitrate in capture launch 44 | * [audio_capture] Publish audio info once before publishing /audio 45 | * Contributors: Naoya Yamaguchi, Shingo Kitagawa 46 | 47 | 0.3.9 (2020-10-22) 48 | ------------------ 49 | * Merge pull request `#160 `_ from knorth55/add-device-play 50 | * use ROS_INFO instead of printf 51 | * Contributors: Shingo Kitagawa 52 | 53 | 0.3.8 (2020-09-13) 54 | ------------------ 55 | 56 | 0.3.7 (2020-08-08) 57 | ------------------ 58 | * Merge pull request `#150 `_ from sktometometo/fix_mp3_options 59 | Fix property of lamemp3enc element in audio_capture so that the bitrate parameter work properly. 60 | * fix property of lamemp3enc element so that it will use the specified bitrate 61 | * Merge pull request `#146 `_ from knorth55/mp3-support 62 | * use space instead of tab 63 | * use same caps 64 | * support channls for mp3 65 | * Merge pull request `#145 `_ from knorth55/mp3-channel-rate 66 | [audio_capture] add sample_format in audio_capture 67 | * Merge pull request `#147 `_ from knorth55/fix-filesink 68 | [audio_capture] fix filesink for wave format 69 | * add sample_format arg in capture_to_file.launch 70 | * fix filesink for wave format 71 | * add sample_format in audio_capture 72 | * Contributors: Koki Shinjo, Shingo Kitagawa 73 | 74 | 0.3.6 (2020-05-29) 75 | ------------------ 76 | * Merge pull request `#141 `_ from knorth55/add-maintainer 77 | add maintainer 78 | * add maintainer 79 | * Contributors: Shingo Kitagawa 80 | 81 | 0.3.5 (2020-04-28) 82 | ------------------ 83 | 84 | 0.3.4 (2020-04-02) 85 | ------------------ 86 | * Merge branch 'master' of github.com:ros-drivers/audio_common 87 | * Contributors: Gerard Canal 88 | 89 | 0.3.3 (2018-05-22) 90 | ------------------ 91 | 92 | 0.3.2 (2018-05-02) 93 | ------------------ 94 | * [sound_play] add option to select audio device to play / record (`#87 `_) 95 | * [sound_play] add option to select audio device to play 96 | * [sound_play] reformat README to markdown; add usage to set device via rosparam 97 | * audio_capture: add option for selecting device to use 98 | * audio_play: add option to select device for playing audio 99 | * add device argument to launch files 100 | Conflicts: 101 | audio_capture/launch/capture.launch 102 | audio_capture/launch/capture_to_file.launch 103 | audio_capture/src/audio_capture.cpp 104 | audio_play/launch/play.launch 105 | sound_play/scripts/soundplay_node.py 106 | * Merge pull request `#102 `_ from EndPointCorp/fix_capture_leak 107 | Fix audio_capture leak 108 | * Fix audio_capture sample/buffer leak 109 | * Merge pull request `#90 `_ from prarobo/master 110 | Error checking code and improvements to launch files 111 | * Bug fix 112 | * fix(audio_capture): capturing wave using gst1.0 113 | 0.10-style raw audio caps were being created, according to GStreamer warning. Should be audio/x-raw,format=(string).. now. 114 | * Merge pull request `#1 `_ from prarobo/fixes 115 | Error checking code and improvements to launch files 116 | * Removed default device 117 | * Added error checking code 118 | * Added parameters to launch files 119 | * Contributors: Austin, Matt Vollrath, Prasanna Kannappan, Rokus, Yuki Furuta, prarobo 120 | 121 | 0.3.1 (2016-08-28) 122 | ------------------ 123 | * Update to new gstreamer rosdeps 124 | * #70 can launch these in different namespaces with different microphones, and both are operating. 125 | * #70 can switch between different microphones, but the first microphone doesn't like the hw:1, it only works with device:="" - so must be doing something wrong still. 126 | * Add changelogs 127 | * [audio_capture] add error handler 128 | * [audio_capture] add option to publish captured audio data as wav format 129 | Conflicts: 130 | audio_capture/src/audio_capture.cpp 131 | * Fixed memory leak (see #18). 132 | * Removed trailing whitespace. 133 | * Fixed problem that CMake uses gstreamer-0.1 instead of gstreamer-1.0 134 | * Added gstreamer 1.0 dependecies 135 | * Ported to gstreamer 1.0 136 | package.xml dependencies still missing 137 | * Update maintainer email 138 | * Contributors: Benny, Felix Duvallet, Furushchev, Lucas Walter, trainman419 139 | 140 | 0.2.11 (2016-02-16) 141 | ------------------- 142 | * Add changelogs 143 | * Contributors: trainman419 144 | 145 | 0.2.10 (2016-01-21) 146 | ------------------- 147 | * Add changelogs 148 | * Contributors: trainman419 149 | 150 | 0.2.9 (2015-12-02) 151 | ------------------ 152 | * Add changelogs 153 | * [audio_capture] add error handler 154 | * [audio_capture] add option to publish captured audio data as wav format 155 | * Fixed memory leak (see `#18 `_). 156 | * Removed trailing whitespace. 157 | * Contributors: Felix Duvallet, Furushchev, trainman419 158 | 159 | 0.2.8 (2015-10-02) 160 | ------------------ 161 | * Update maintainer email 162 | * Contributors: trainman419 163 | 164 | 0.2.7 (2014-07-25) 165 | ------------------ 166 | * audio_capture.cpp has to wait for generated AudioData headers 167 | * Contributors: v4hn 168 | 169 | 0.2.6 (2014-02-26) 170 | ------------------ 171 | * audio_capture and play _require\_ gstreamer, it's not optional 172 | * Contributors: v4hn 173 | 174 | 0.2.5 (2014-01-23) 175 | ------------------ 176 | * "0.2.5" 177 | * Contributors: trainman419 178 | 179 | 0.2.4 (2013-09-10) 180 | ------------------ 181 | * Update CMakeLists.txt 182 | * audio_capture: install launchfiles 183 | * Contributors: David Gossow 184 | 185 | 0.2.3 (2013-07-15) 186 | ------------------ 187 | * Fix install rule for audio_capture. 188 | * Contributors: Austin Hendrix 189 | 190 | 0.2.2 (2013-04-10) 191 | ------------------ 192 | 193 | 0.2.1 (2013-04-08 13:59) 194 | ------------------------ 195 | 196 | 0.2.0 (2013-04-08 13:49) 197 | ------------------------ 198 | * Finish catkinizing audio_common. 199 | * Catkinize audio_play. 200 | * Catkinize audio_capture. 201 | * Fix typo in package.xml 202 | * Versions and more URLs. 203 | * Convert manifests to package.xml 204 | * Convert audio_capture manifest to package.xml 205 | * Ditch old makefiles. 206 | * Updates manifest 207 | * Updated manifests for rodep2 208 | * oneiric build fixes, bump version to 0.1.6 209 | * Removed redundant thread::thread 210 | * Added a rosdep.yaml file 211 | * Fixed to use audio_common_msgs 212 | * Added ability to use different festival voices 213 | * Updated documentation 214 | * Added ability to capture to file 215 | * Fixed ignore files 216 | * Added hgignore files 217 | * Audio_capture and audio_play working 218 | * Making separate audio_capture and audio_play packages 219 | * Moved audio_transport to audio_capture 220 | * Contributors: Austin Hendrix, Brian Gerkey, Nate Koenig, nkoenig 221 | -------------------------------------------------------------------------------- /audio_capture/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.3) 2 | 3 | project(audio_capture) 4 | 5 | find_package(catkin REQUIRED COMPONENTS roscpp audio_common_msgs) 6 | 7 | find_package(PkgConfig) 8 | pkg_check_modules(GST1.0 gstreamer-1.0 REQUIRED) 9 | 10 | find_package(Boost REQUIRED COMPONENTS thread) 11 | 12 | include_directories(${catkin_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS} ${GST1.0_INCLUDE_DIRS}) 13 | 14 | catkin_package() 15 | 16 | add_executable(audio_capture src/audio_capture.cpp) 17 | target_link_libraries(audio_capture ${catkin_LIBRARIES} ${GST1.0_LIBRARIES} ${Boost_LIBRARIES}) 18 | add_dependencies(audio_capture ${catkin_EXPORTED_TARGETS}) 19 | 20 | install(TARGETS audio_capture 21 | DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}) 22 | 23 | install(DIRECTORY launch 24 | DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}) 25 | -------------------------------------------------------------------------------- /audio_capture/launch/capture.launch: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /audio_capture/launch/capture_to_file.launch: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /audio_capture/launch/capture_wave.launch: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /audio_capture/mainpage.dox: -------------------------------------------------------------------------------- 1 | /** 2 | \mainpage 3 | \htmlinclude manifest.html 4 | 5 | \b audio_capture is a package that records audio from a microphone and makes it available to other ROS nodes. 6 | 7 | \section codeapi Code API 8 | 9 | 19 | 20 | 21 | */ 22 | -------------------------------------------------------------------------------- /audio_capture/package.xml: -------------------------------------------------------------------------------- 1 | 2 | audio_capture 3 | 0.3.18 4 | 5 | Transports audio from a source to a destination. Audio sources can come 6 | from a microphone or file. The destination can play the audio or save it 7 | to an mp3 file. 8 | 9 | Austin Hendrix 10 | Shingo Kitagawa 11 | Nate Koenig 12 | BSD 13 | http://ros.org/wiki/audio_capture 14 | https://github.com/ros-drivers/audio_common 15 | https://github.com/ros-drivers/audio_common/issues 16 | 17 | catkin 18 | 19 | roscpp 20 | audio_common_msgs 21 | libgstreamer1.0-dev 22 | libgstreamer-plugins-base1.0-dev 23 | 24 | roscpp 25 | audio_common_msgs 26 | gstreamer1.0 27 | gstreamer1.0-alsa 28 | gstreamer1.0-plugins-base 29 | gstreamer1.0-plugins-good 30 | gstreamer1.0-plugins-ugly 31 | 32 | -------------------------------------------------------------------------------- /audio_capture/src/audio_capture.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | #include "audio_common_msgs/AudioData.h" 9 | #include "audio_common_msgs/AudioDataStamped.h" 10 | #include "audio_common_msgs/AudioInfo.h" 11 | 12 | namespace audio_transport 13 | { 14 | class RosGstCapture 15 | { 16 | public: 17 | RosGstCapture() 18 | { 19 | _bitrate = 192; 20 | 21 | std::string dst_type; 22 | 23 | // Need to encoding or publish raw wave data 24 | ros::param::param("~format", _format, "mp3"); 25 | ros::param::param("~sample_format", _sample_format, "S16LE"); 26 | 27 | // The bitrate at which to encode the audio 28 | ros::param::param("~bitrate", _bitrate, 192); 29 | 30 | // only available for raw data 31 | ros::param::param("~channels", _channels, 1); 32 | ros::param::param("~depth", _depth, 16); 33 | ros::param::param("~sample_rate", _sample_rate, 16000); 34 | 35 | // The destination of the audio 36 | ros::param::param("~dst", dst_type, "appsink"); 37 | 38 | // The source of the audio 39 | //ros::param::param("~src", source_type, "alsasrc"); 40 | std::string device; 41 | ros::param::param("~device", device, ""); 42 | 43 | _pub = _nh.advertise("audio", 10, true); 44 | _pub_stamped = _nh.advertise("audio_stamped", 10, true); 45 | _pub_info = _nh.advertise("audio_info", 1, true); 46 | 47 | _loop = g_main_loop_new(NULL, false); 48 | _pipeline = gst_pipeline_new("ros_pipeline"); 49 | GstClock *clock = gst_system_clock_obtain(); 50 | g_object_set(clock, "clock-type", GST_CLOCK_TYPE_REALTIME, NULL); 51 | gst_pipeline_use_clock(GST_PIPELINE_CAST(_pipeline), clock); 52 | gst_object_unref(clock); 53 | 54 | _bus = gst_pipeline_get_bus(GST_PIPELINE(_pipeline)); 55 | gst_bus_add_signal_watch(_bus); 56 | g_signal_connect(_bus, "message::error", 57 | G_CALLBACK(onMessage), this); 58 | g_object_unref(_bus); 59 | 60 | // We create the sink first, just for convenience 61 | if (dst_type == "appsink") 62 | { 63 | _sink = gst_element_factory_make("appsink", "sink"); 64 | g_object_set(G_OBJECT(_sink), "emit-signals", true, NULL); 65 | g_object_set(G_OBJECT(_sink), "max-buffers", 100, NULL); 66 | g_signal_connect( G_OBJECT(_sink), "new-sample", 67 | G_CALLBACK(onNewBuffer), this); 68 | } 69 | else 70 | { 71 | ROS_INFO("file sink to %s", dst_type.c_str()); 72 | _sink = gst_element_factory_make("filesink", "sink"); 73 | g_object_set( G_OBJECT(_sink), "location", dst_type.c_str(), NULL); 74 | } 75 | 76 | _source = gst_element_factory_make("alsasrc", "source"); 77 | // if device isn't specified, it will use the default which is 78 | // the alsa default source. 79 | // A valid device will be of the foram hw:0,0 with other numbers 80 | // than 0 and 0 as are available. 81 | if (device != "") 82 | { 83 | // ghcar *gst_device = device.c_str(); 84 | g_object_set(G_OBJECT(_source), "device", device.c_str(), NULL); 85 | } 86 | 87 | GstCaps *caps; 88 | caps = gst_caps_new_simple("audio/x-raw", 89 | "format", G_TYPE_STRING, _sample_format.c_str(), 90 | "channels", G_TYPE_INT, _channels, 91 | "width", G_TYPE_INT, _depth, 92 | "depth", G_TYPE_INT, _depth, 93 | "rate", G_TYPE_INT, _sample_rate, 94 | "signed", G_TYPE_BOOLEAN, TRUE, 95 | NULL); 96 | 97 | gboolean link_ok; 98 | if (_format == "mp3"){ 99 | _filter = gst_element_factory_make("capsfilter", "filter"); 100 | g_object_set( G_OBJECT(_filter), "caps", caps, NULL); 101 | gst_caps_unref(caps); 102 | 103 | _convert = gst_element_factory_make("audioconvert", "convert"); 104 | if (!_convert) { 105 | ROS_ERROR_STREAM("Failed to create audioconvert element"); 106 | exitOnMainThread(1); 107 | } 108 | 109 | _encode = gst_element_factory_make("lamemp3enc", "encoder"); 110 | if (!_encode) { 111 | ROS_ERROR_STREAM("Failed to create encoder element"); 112 | exitOnMainThread(1); 113 | } 114 | g_object_set( G_OBJECT(_encode), "target", 1, NULL); 115 | g_object_set( G_OBJECT(_encode), "bitrate", _bitrate, NULL); 116 | 117 | gst_bin_add_many( GST_BIN(_pipeline), _source, _filter, _convert, _encode, _sink, NULL); 118 | link_ok = gst_element_link_many(_source, _filter, _convert, _encode, _sink, NULL); 119 | } else if (_format == "wave") { 120 | if (dst_type == "appsink") { 121 | g_object_set( G_OBJECT(_sink), "caps", caps, NULL); 122 | gst_caps_unref(caps); 123 | gst_bin_add_many( GST_BIN(_pipeline), _source, _sink, NULL); 124 | link_ok = gst_element_link_many( _source, _sink, NULL); 125 | } else { 126 | _filter = gst_element_factory_make("wavenc", "filter"); 127 | gst_bin_add_many( GST_BIN(_pipeline), _source, _filter, _sink, NULL); 128 | link_ok = gst_element_link_many( _source, _filter, _sink, NULL); 129 | } 130 | } else { 131 | ROS_ERROR_STREAM("format must be \"wave\" or \"mp3\""); 132 | exitOnMainThread(1); 133 | } 134 | /*} 135 | else 136 | { 137 | _sleep_time = 10000; 138 | _source = gst_element_factory_make("filesrc", "source"); 139 | g_object_set(G_OBJECT(_source), "location", source_type.c_str(), NULL); 140 | 141 | gst_bin_add_many( GST_BIN(_pipeline), _source, _sink, NULL); 142 | gst_element_link_many(_source, _sink, NULL); 143 | } 144 | */ 145 | 146 | if (!link_ok) { 147 | ROS_ERROR_STREAM("Unsupported media type."); 148 | exitOnMainThread(1); 149 | } 150 | 151 | gst_element_set_state(GST_ELEMENT(_pipeline), GST_STATE_PLAYING); 152 | 153 | _gst_thread = boost::thread( boost::bind(g_main_loop_run, _loop) ); 154 | 155 | audio_common_msgs::AudioInfo info_msg; 156 | info_msg.channels = _channels; 157 | info_msg.sample_rate = _sample_rate; 158 | info_msg.sample_format = _sample_format; 159 | info_msg.bitrate = _bitrate; 160 | info_msg.coding_format = _format; 161 | _pub_info.publish(info_msg); 162 | } 163 | 164 | ~RosGstCapture() 165 | { 166 | g_main_loop_quit(_loop); 167 | gst_element_set_state(_pipeline, GST_STATE_NULL); 168 | gst_object_unref(_pipeline); 169 | g_main_loop_unref(_loop); 170 | } 171 | 172 | void exitOnMainThread(int code) 173 | { 174 | exit(code); 175 | } 176 | 177 | void publish( const audio_common_msgs::AudioData &msg ) 178 | { 179 | _pub.publish(msg); 180 | } 181 | 182 | void publishStamped( const audio_common_msgs::AudioDataStamped &msg ) 183 | { 184 | _pub_stamped.publish(msg); 185 | } 186 | 187 | static GstFlowReturn onNewBuffer (GstAppSink *appsink, gpointer userData) 188 | { 189 | audio_common_msgs::AudioData msg; 190 | audio_common_msgs::AudioDataStamped stamped_msg; 191 | 192 | RosGstCapture *server = reinterpret_cast(userData); 193 | GstMapInfo map; 194 | 195 | GstSample *sample; 196 | g_signal_emit_by_name(appsink, "pull-sample", &sample); 197 | 198 | GstBuffer *buffer = gst_sample_get_buffer(sample); 199 | 200 | if( ros::Time::isSimTime() ) 201 | { 202 | stamped_msg.header.stamp = ros::Time::now(); 203 | } 204 | else 205 | { 206 | GstClockTime buffer_time = gst_element_get_base_time(server->_source)+GST_BUFFER_PTS(buffer); 207 | stamped_msg.header.stamp.fromNSec(buffer_time); 208 | } 209 | 210 | gst_buffer_map(buffer, &map, GST_MAP_READ); 211 | msg.data.resize( map.size ); 212 | 213 | memcpy( &msg.data[0], map.data, map.size ); 214 | stamped_msg.audio = msg; 215 | 216 | gst_buffer_unmap(buffer, &map); 217 | gst_sample_unref(sample); 218 | 219 | server->publish(msg); 220 | server->publishStamped(stamped_msg); 221 | 222 | return GST_FLOW_OK; 223 | } 224 | 225 | static gboolean onMessage (GstBus *bus, GstMessage *message, gpointer userData) 226 | { 227 | RosGstCapture *server = reinterpret_cast(userData); 228 | GError *err; 229 | gchar *debug; 230 | 231 | gst_message_parse_error(message, &err, &debug); 232 | ROS_ERROR_STREAM("gstreamer: " << err->message); 233 | g_error_free(err); 234 | g_free(debug); 235 | g_main_loop_quit(server->_loop); 236 | server->exitOnMainThread(1); 237 | return FALSE; 238 | } 239 | 240 | private: 241 | ros::NodeHandle _nh; 242 | ros::Publisher _pub; 243 | ros::Publisher _pub_stamped; 244 | ros::Publisher _pub_info; 245 | 246 | boost::thread _gst_thread; 247 | 248 | GstElement *_pipeline, *_source, *_filter, *_sink, *_convert, *_encode; 249 | GstBus *_bus; 250 | int _bitrate, _channels, _depth, _sample_rate; 251 | GMainLoop *_loop; 252 | std::string _format, _sample_format; 253 | }; 254 | } 255 | 256 | int main (int argc, char **argv) 257 | { 258 | ros::init(argc, argv, "audio_capture"); 259 | gst_init(&argc, &argv); 260 | 261 | audio_transport::RosGstCapture server; 262 | ros::spin(); 263 | } 264 | -------------------------------------------------------------------------------- /audio_common/CHANGELOG.rst: -------------------------------------------------------------------------------- 1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 2 | Changelog for package audio_common 3 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 4 | 5 | 0.3.18 (2024-08-13) 6 | ------------------- 7 | 8 | 0.3.17 (2023-06-08) 9 | ------------------- 10 | 11 | 0.3.16 (2022-12-23) 12 | ------------------- 13 | 14 | 0.3.15 (2022-08-29) 15 | ------------------- 16 | 17 | 0.3.14 (2022-08-18) 18 | ------------------- 19 | 20 | 0.3.13 (2022-04-07) 21 | ------------------- 22 | 23 | 0.3.12 (2021-09-01) 24 | ------------------- 25 | * Merge branch 'master' into master 26 | * Contributors: Shingo Kitagawa 27 | 28 | 0.3.11 (2021-04-08) 29 | ------------------- 30 | 31 | 0.3.10 (2021-01-07) 32 | ------------------- 33 | 34 | 0.3.9 (2020-10-22) 35 | ------------------ 36 | 37 | 0.3.8 (2020-09-13) 38 | ------------------ 39 | 40 | 0.3.7 (2020-08-08) 41 | ------------------ 42 | 43 | 0.3.6 (2020-05-29) 44 | ------------------ 45 | * Merge pull request `#141 `_ from knorth55/add-maintainer 46 | add maintainer 47 | * add maintainer 48 | * Contributors: Shingo Kitagawa 49 | 50 | 0.3.5 (2020-04-28) 51 | ------------------ 52 | 53 | 0.3.4 (2020-04-02) 54 | ------------------ 55 | * Merge branch 'master' of github.com:ros-drivers/audio_common 56 | * Contributors: Gerard Canal 57 | 58 | 0.3.3 (2018-05-22) 59 | ------------------ 60 | 61 | 0.3.2 (2018-05-02) 62 | ------------------ 63 | 64 | 0.3.1 (2016-08-28) 65 | ------------------ 66 | * Add changelogs 67 | * Update maintainer email 68 | * Contributors: trainman419 69 | 70 | 0.2.11 (2016-02-16) 71 | ------------------- 72 | * Add changelogs 73 | * Contributors: trainman419 74 | 75 | 0.2.10 (2016-01-21) 76 | ------------------- 77 | * Add changelogs 78 | * Contributors: trainman419 79 | 80 | 0.2.9 (2015-12-02) 81 | ------------------ 82 | * Add changelogs 83 | * Contributors: trainman419 84 | 85 | 0.2.8 (2015-10-02) 86 | ------------------ 87 | * Update maintainer email 88 | * Contributors: trainman419 89 | 90 | 0.2.7 (2014-07-25) 91 | ------------------ 92 | 93 | 0.2.6 (2014-02-26) 94 | ------------------ 95 | 96 | 0.2.5 (2014-01-23) 97 | ------------------ 98 | * "0.2.5" 99 | * Contributors: trainman419 100 | 101 | 0.2.4 (2013-09-10) 102 | ------------------ 103 | 104 | 0.2.3 (2013-07-15) 105 | ------------------ 106 | 107 | 0.2.2 (2013-04-10) 108 | ------------------ 109 | 110 | 0.2.1 (2013-04-08 13:59) 111 | ------------------------ 112 | * Fix metapackage for REP 127. 113 | * Contributors: Austin Hendrix 114 | 115 | 0.2.0 (2013-04-08 13:49) 116 | ------------------------ 117 | * Versions and more URLs. 118 | * Convert stack.xml to metapackage package.xml 119 | * Start catkinizing. 120 | * Contributors: Austin Hendrix 121 | -------------------------------------------------------------------------------- /audio_common/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.3) 2 | project(audio_common) 3 | find_package(catkin REQUIRED) 4 | catkin_metapackage() 5 | -------------------------------------------------------------------------------- /audio_common/package.xml: -------------------------------------------------------------------------------- 1 | 2 | audio_common 3 | 0.3.18 4 | 5 | Common code for working with audio in ROS 6 | 7 | Austin Hendrix 8 | Shingo Kitagawa 9 | Blaise Gassend 10 | BSD 11 | http://ros.org/wiki/audio_common 12 | https://github.com/ros-drivers/audio_common 13 | https://github.com/ros-drivers/audio_common/issues 14 | 15 | catkin 16 | 17 | audio_capture 18 | audio_common_msgs 19 | audio_play 20 | sound_play 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /audio_common_msgs/.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | -------------------------------------------------------------------------------- /audio_common_msgs/CHANGELOG.rst: -------------------------------------------------------------------------------- 1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 2 | Changelog for package audio_common_msgs 3 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 4 | 5 | 0.3.18 (2024-08-13) 6 | ------------------- 7 | 8 | 0.3.17 (2023-06-08) 9 | ------------------- 10 | 11 | 0.3.16 (2022-12-23) 12 | ------------------- 13 | * Merge pull request `#202 `_ from knorth55/audio-stamped-msg 14 | * add AudioDataStamped.msg 15 | * Contributors: Shingo Kitagawa 16 | 17 | 0.3.15 (2022-08-29) 18 | ------------------- 19 | 20 | 0.3.14 (2022-08-18) 21 | ------------------- 22 | 23 | 0.3.13 (2022-04-07) 24 | ------------------- 25 | 26 | 0.3.12 (2021-09-01) 27 | ------------------- 28 | * Merge branch 'master' into master 29 | * Contributors: Shingo Kitagawa 30 | 31 | 0.3.11 (2021-04-08) 32 | ------------------- 33 | 34 | 0.3.10 (2021-01-07) 35 | ------------------- 36 | * Change comment style in AudioInfo.msg 37 | * [audio_common_msgs] AudioInfo.msg to add audio meta data 38 | * Contributors: Naoya Yamaguchi 39 | 40 | 0.3.9 (2020-10-22) 41 | ------------------ 42 | 43 | 0.3.8 (2020-09-13) 44 | ------------------ 45 | 46 | 0.3.7 (2020-08-08) 47 | ------------------ 48 | 49 | 0.3.6 (2020-05-29) 50 | ------------------ 51 | * Merge pull request `#141 `_ from knorth55/add-maintainer 52 | add maintainer 53 | * add maintainer 54 | * Contributors: Shingo Kitagawa 55 | 56 | 0.3.5 (2020-04-28) 57 | ------------------ 58 | 59 | 0.3.4 (2020-04-02) 60 | ------------------ 61 | * Merge branch 'master' of github.com:ros-drivers/audio_common 62 | * Contributors: Gerard Canal 63 | 64 | 0.3.3 (2018-05-22) 65 | ------------------ 66 | 67 | 0.3.2 (2018-05-02) 68 | ------------------ 69 | 70 | 0.3.1 (2016-08-28) 71 | ------------------ 72 | * Add changelogs 73 | * Update maintainer email 74 | * Contributors: trainman419 75 | 76 | 0.2.11 (2016-02-16) 77 | ------------------- 78 | * Add changelogs 79 | * Contributors: trainman419 80 | 81 | 0.2.10 (2016-01-21) 82 | ------------------- 83 | * Add changelogs 84 | * Contributors: trainman419 85 | 86 | 0.2.9 (2015-12-02) 87 | ------------------ 88 | * Add changelogs 89 | * Contributors: trainman419 90 | 91 | 0.2.8 (2015-10-02) 92 | ------------------ 93 | * Update maintainer email 94 | * Contributors: trainman419 95 | 96 | 0.2.7 (2014-07-25) 97 | ------------------ 98 | 99 | 0.2.6 (2014-02-26) 100 | ------------------ 101 | 102 | 0.2.5 (2014-01-23) 103 | ------------------ 104 | * "0.2.5" 105 | * Contributors: trainman419 106 | 107 | 0.2.4 (2013-09-10) 108 | ------------------ 109 | 110 | 0.2.3 (2013-07-15) 111 | ------------------ 112 | 113 | 0.2.2 (2013-04-10) 114 | ------------------ 115 | 116 | 0.2.1 (2013-04-08 13:59) 117 | ------------------------ 118 | 119 | 0.2.0 (2013-04-08 13:49) 120 | ------------------------ 121 | * Catkinize audio_common_msgs. 122 | * Versions and more URLs. 123 | * Convert manifests to package.xml 124 | * Ditch old makefiles. 125 | * Fixed audio_msgs names to audio_common_msgs 126 | * Renamed audio_msgs to audio_common_msgs 127 | * Contributors: Austin Hendrix, Nate Koenig 128 | -------------------------------------------------------------------------------- /audio_common_msgs/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.3) 2 | 3 | project(audio_common_msgs) 4 | 5 | find_package(catkin REQUIRED 6 | COMPONENTS 7 | message_generation 8 | std_msgs 9 | ) 10 | 11 | add_message_files(DIRECTORY msg FILES 12 | AudioData.msg 13 | AudioDataStamped.msg 14 | AudioInfo.msg 15 | ) 16 | 17 | generate_messages( 18 | DEPENDENCIES 19 | std_msgs 20 | ) 21 | 22 | catkin_package(CATKIN_DEPENDS message_runtime) 23 | -------------------------------------------------------------------------------- /audio_common_msgs/mainpage.dox: -------------------------------------------------------------------------------- 1 | /** 2 | \mainpage 3 | \htmlinclude manifest.html 4 | 5 | \b audio_common_msgs contain messages for transimitting audio via ROS. 6 | 7 | 8 | \section codeapi Code API 9 | 10 | 11 | */ 12 | -------------------------------------------------------------------------------- /audio_common_msgs/msg/AudioData.msg: -------------------------------------------------------------------------------- 1 | uint8[] data 2 | -------------------------------------------------------------------------------- /audio_common_msgs/msg/AudioDataStamped.msg: -------------------------------------------------------------------------------- 1 | std_msgs/Header header 2 | audio_common_msgs/AudioData audio 3 | -------------------------------------------------------------------------------- /audio_common_msgs/msg/AudioInfo.msg: -------------------------------------------------------------------------------- 1 | # This message contains the audio meta data 2 | 3 | # Number of channels 4 | uint8 channels 5 | # Sampling rate [Hz] 6 | uint32 sample_rate 7 | # Audio format (e.g. S16LE) 8 | string sample_format 9 | # Amount of audio data per second [bits/s] 10 | uint32 bitrate 11 | # Audio coding format (e.g. WAVE, MP3) 12 | string coding_format 13 | -------------------------------------------------------------------------------- /audio_common_msgs/package.xml: -------------------------------------------------------------------------------- 1 | 2 | audio_common_msgs 3 | 0.3.18 4 | 5 | Messages for transmitting audio via ROS 6 | 7 | Austin Hendrix 8 | Shingo Kitagawa 9 | Nate Koenig 10 | BSD 11 | http://ros.org/wiki/audio_common_msgs 12 | https://github.com/ros-drivers/audio_common 13 | https://github.com/ros-drivers/audio_common/issues 14 | 15 | catkin 16 | 17 | message_generation 18 | std_msgs 19 | 20 | message_runtime 21 | std_msgs 22 | 23 | -------------------------------------------------------------------------------- /audio_play/.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | -------------------------------------------------------------------------------- /audio_play/CHANGELOG.rst: -------------------------------------------------------------------------------- 1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 2 | Changelog for package audio_play 3 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 4 | 5 | 0.3.18 (2024-08-13) 6 | ------------------- 7 | * Merge pull request `#238 `_ from Kanazawanaoaki/add-record-to-file-launch 8 | * Add record to file launch 9 | * Contributors: Kanazawanaoaki, Shingo Kitagawa 10 | 11 | 0.3.17 (2023-06-08) 12 | ------------------- 13 | 14 | 0.3.16 (2022-12-23) 15 | ------------------- 16 | * Merge pull request `#216 `_ from knorth55/launch-update 17 | * update play.launch 18 | * Merge pull request `#206 `_ from knorth55/fix-205 19 | * unref buffer in audio_play to avoid memory leak 20 | * Contributors: Shingo Kitagawa 21 | 22 | 0.3.15 (2022-08-29) 23 | ------------------- 24 | 25 | 0.3.14 (2022-08-18) 26 | ------------------- 27 | 28 | 0.3.13 (2022-04-07) 29 | ------------------- 30 | * Merge pull request `#179 `_ from tkmtnt7000/PR-remap-audio-topic 31 | * audio_play: add audio_topic option 32 | * Contributors: Naoto Tsukamoto, Shingo Kitagawa 33 | 34 | 0.3.12 (2021-09-01) 35 | ------------------- 36 | * Merge branch 'master' into master 37 | * Contributors: Shingo Kitagawa 38 | 39 | 0.3.11 (2021-04-08) 40 | ------------------- 41 | 42 | 0.3.10 (2021-01-07) 43 | ------------------- 44 | 45 | 0.3.9 (2020-10-22) 46 | ------------------ 47 | * Merge pull request `#160 `_ from knorth55/add-device-play 48 | * refactor audio_play to use same code 49 | * add audioresample in audio_play 50 | * apply caps for both formats 51 | * add device for wave format 52 | * add sync false for alsasink 53 | * use alsasink 54 | * add depth rosparam 55 | * add device arg in play.launch 56 | * fix audio_play to save file 57 | * Contributors: Shingo Kitagawa 58 | 59 | 0.3.8 (2020-09-13) 60 | ------------------ 61 | * Merge pull request `#151 `_ from knorth55/do-timestamp-false 62 | [audio_play] set do_timestamp false 63 | * set do_timestamp false 64 | * Contributors: Shingo Kitagawa 65 | 66 | 0.3.7 (2020-08-08) 67 | ------------------ 68 | * Merge pull request `#146 `_ from knorth55/mp3-support 69 | * support format, rate, channels in mp3 70 | * Merge pull request `#127 `_ from knorth55/audio-play-wave 71 | [audio_play] support wave format 72 | * add sample_format param in audio_play 73 | * add channels and sample_rate in audio_play/play.launch 74 | * add channels and sample_rate in audio_play.cpp 75 | * add format arg in play.launch 76 | * support wave in audio_play 77 | * Merge pull request `#144 `_ from ros-drivers/knorth55-patch-1 78 | * add gstreamer1.0-alsa for run_depend in audio_play 79 | * Contributors: Shingo Kitagawa 80 | 81 | 0.3.6 (2020-05-29) 82 | ------------------ 83 | * Merge pull request `#141 `_ from knorth55/add-maintainer 84 | add maintainer 85 | * add maintainer 86 | * Contributors: Shingo Kitagawa 87 | 88 | 0.3.5 (2020-04-28) 89 | ------------------ 90 | 91 | 0.3.4 (2020-04-02) 92 | ------------------ 93 | * audio_play fix for reproducing livestream sound (`#122 `_) 94 | * Added capability to read from a udpsrc. To generalize it, possibly similarly to gscam. 95 | * Added parameter to control do-timestamp, as this fixes the problem with audio_play not being able to play livestream sound. 96 | * Aligning with the base master, removing the changes from the branch that included the udpsrc. 97 | Co-authored-by: Alberto Quattrini Li 98 | * Merge branch 'master' of github.com:ros-drivers/audio_common 99 | * Contributors: Alberto Quattrini Li, Gerard Canal 100 | 101 | 0.3.3 (2018-05-22) 102 | ------------------ 103 | 104 | 0.3.2 (2018-05-02) 105 | ------------------ 106 | * [sound_play] add option to select audio device to play / record (`#87 `_) 107 | * [sound_play] add option to select audio device to play 108 | * [sound_play] reformat README to markdown; add usage to set device via rosparam 109 | * audio_capture: add option for selecting device to use 110 | * audio_play: add option to select device for playing audio 111 | * add device argument to launch files 112 | Conflicts: 113 | audio_capture/launch/capture.launch 114 | audio_capture/launch/capture_to_file.launch 115 | audio_capture/src/audio_capture.cpp 116 | audio_play/launch/play.launch 117 | sound_play/scripts/soundplay_node.py 118 | * Merge pull request `#101 `_ from EndPointCorp/audio_play_dont_pause_pipeline 119 | audio_play: Fix mp3 clip overlap by never pausing the pipeline 120 | * audio_play: Don't pause the pipeline 121 | This prevents glitches when playing short mp3 clips. 122 | * Merge pull request `#90 `_ from prarobo/master 123 | Error checking code and improvements to launch files 124 | * Merge pull request `#1 `_ from prarobo/fixes 125 | Error checking code and improvements to launch files 126 | * Added parameters to launch files 127 | * Contributors: Austin, Matt Vollrath, Prasanna Kannappan, Yuki Furuta, prarobo 128 | 129 | 0.3.1 (2016-08-28) 130 | ------------------ 131 | * Update to new gstreamer rosdeps 132 | * #70 can launch these in different namespaces with different microphones, and both are operating. 133 | * Add changelogs 134 | * Changed message level to warning 135 | * Fixed problem that CMake uses gstreamer-0.1 instead of gstreamer-1.0 136 | * Fixed underflow. 137 | Before the sink buffer underflows the pipeline is paused. When data is received again the pipeline is set to playing again. 138 | * Added gstreamer 1.0 dependecies 139 | * Ported to gstreamer 1.0 140 | package.xml dependencies still missing 141 | * Change audio sink to autoaudiosink 142 | * Update maintainer email 143 | * Contributors: Benny, Hans Gaiser, Lucas Walter, trainman419 144 | 145 | 0.2.11 (2016-02-16) 146 | ------------------- 147 | * Add changelogs 148 | * Contributors: trainman419 149 | 150 | 0.2.10 (2016-01-21) 151 | ------------------- 152 | * Add changelogs 153 | * Contributors: trainman419 154 | 155 | 0.2.9 (2015-12-02) 156 | ------------------ 157 | * Add changelogs 158 | * Contributors: trainman419 159 | 160 | 0.2.8 (2015-10-02) 161 | ------------------ 162 | * Changed message level to warning 163 | * Fixed underflow. 164 | Before the sink buffer underflows the pipeline is paused. When data is received again the pipeline is set to playing again. 165 | * Change audio sink to autoaudiosink 166 | * Update maintainer email 167 | * Contributors: Benny, Hans Gaiser, trainman419 168 | 169 | 0.2.7 (2014-07-25) 170 | ------------------ 171 | 172 | 0.2.6 (2014-02-26) 173 | ------------------ 174 | * audio_capture and play _require\_ gstreamer, it's not optional 175 | * Contributors: v4hn 176 | 177 | 0.2.5 (2014-01-23) 178 | ------------------ 179 | * "0.2.5" 180 | * Contributors: trainman419 181 | 182 | 0.2.4 (2013-09-10) 183 | ------------------ 184 | 185 | 0.2.3 (2013-07-15) 186 | ------------------ 187 | * Fix dependencies and install rules. 188 | * Contributors: Austin Hendrix 189 | 190 | 0.2.2 (2013-04-10) 191 | ------------------ 192 | 193 | 0.2.1 (2013-04-08 13:59) 194 | ------------------------ 195 | 196 | 0.2.0 (2013-04-08 13:49) 197 | ------------------------ 198 | * Finish catkinizing audio_common. 199 | * Catkinize audio_play. 200 | * Fix typo in package.xml 201 | * Versions and more URLs. 202 | * Convert manifests to package.xml 203 | * Ditch old makefiles. 204 | * Updates manifest 205 | * Updated manifests for rodep2 206 | * oneiric build fixes, bump version to 0.1.6 207 | * Removed another duplicate thread::thread 208 | * Added a rosdep.yaml file 209 | * Fixed to use audio_common_msgs 210 | * Added ability to use different festival voices 211 | * Updated documentation 212 | * Update to audio_play 213 | * Fixed ignore files 214 | * Added hgignore files 215 | * Audio_capture and audio_play working 216 | * Making separate audio_capture and audio_play packages 217 | * Contributors: Austin Hendrix, Brian Gerkey, Nate Koenig, nkoenig 218 | -------------------------------------------------------------------------------- /audio_play/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.3) 2 | 3 | project(audio_play) 4 | 5 | find_package(catkin REQUIRED COMPONENTS roscpp audio_common_msgs) 6 | 7 | find_package(PkgConfig) 8 | pkg_check_modules(GST1.0 gstreamer-1.0 REQUIRED) 9 | 10 | find_package(Boost REQUIRED COMPONENTS thread) 11 | 12 | include_directories(${catkin_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS} ${GST1.0_INCLUDE_DIRS}) 13 | 14 | catkin_package() 15 | 16 | add_executable(audio_play src/audio_play.cpp) 17 | target_link_libraries(audio_play ${catkin_LIBRARIES} ${GST1.0_LIBRARIES} ${Boost_LIBRARIES}) 18 | add_dependencies(audio_play ${catkin_EXPORTED_TARGETS}) 19 | 20 | install(TARGETS audio_play 21 | DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}) 22 | 23 | install(DIRECTORY launch 24 | DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}) 25 | -------------------------------------------------------------------------------- /audio_play/launch/play.launch: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /audio_play/launch/record_to_file.launch: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /audio_play/mainpage.dox: -------------------------------------------------------------------------------- 1 | /** 2 | \mainpage 3 | \htmlinclude manifest.html 4 | 5 | \b audio_play is a package that listens to a node that produces audio_msgs, and plays them through a connected speaker. 6 | 7 | 8 | \section codeapi Code API 9 | 10 | 20 | 21 | 22 | */ 23 | -------------------------------------------------------------------------------- /audio_play/package.xml: -------------------------------------------------------------------------------- 1 | 2 | audio_play 3 | 0.3.18 4 | 5 | Outputs audio to a speaker from a source node. 6 | 7 | Austin Hendrix 8 | Shingo Kitagawa 9 | Nate Koenig 10 | BSD 11 | http://ros.org/wiki/audio_play 12 | https://github.com/ros-drivers/audio_common 13 | https://github.com/ros-drivers/audio_common/issues 14 | 15 | catkin 16 | 17 | roscpp 18 | audio_common_msgs 19 | libgstreamer1.0-dev 20 | libgstreamer-plugins-base1.0-dev 21 | 22 | roscpp 23 | audio_common_msgs 24 | gstreamer1.0 25 | gstreamer1.0-alsa 26 | gstreamer1.0-plugins-base 27 | gstreamer1.0-plugins-ugly 28 | gstreamer1.0-plugins-good 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /audio_play/src/audio_play.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "audio_common_msgs/AudioData.h" 7 | 8 | namespace audio_transport 9 | { 10 | class RosGstPlay 11 | { 12 | public: 13 | RosGstPlay() 14 | { 15 | GstPad *audiopad; 16 | GstCaps *caps; 17 | 18 | std::string dst_type; 19 | std::string device; 20 | bool do_timestamp; 21 | std::string format; 22 | int channels; 23 | int depth; 24 | int sample_rate; 25 | std::string sample_format; 26 | 27 | // The destination of the audio 28 | ros::param::param("~dst", dst_type, "alsasink"); 29 | ros::param::param("~device", device, std::string()); 30 | ros::param::param("~do_timestamp", do_timestamp, true); 31 | ros::param::param("~format", format, "mp3"); 32 | ros::param::param("~channels", channels, 1); 33 | ros::param::param("~depth", depth, 16); 34 | ros::param::param("~sample_rate", sample_rate, 16000); 35 | ros::param::param("~sample_format", sample_format, "S16LE"); 36 | 37 | _sub = _nh.subscribe("audio", 10, &RosGstPlay::onAudio, this); 38 | 39 | _loop = g_main_loop_new(NULL, false); 40 | 41 | _pipeline = gst_pipeline_new("app_pipeline"); 42 | _source = gst_element_factory_make("appsrc", "app_source"); 43 | g_object_set(G_OBJECT(_source), "do-timestamp", (do_timestamp) ? TRUE : FALSE, NULL); 44 | 45 | //_playbin = gst_element_factory_make("playbin2", "uri_play"); 46 | //g_object_set( G_OBJECT(_playbin), "uri", "file:///home/test/test.mp3", NULL); 47 | caps = gst_caps_new_simple( 48 | "audio/x-raw", 49 | "format", G_TYPE_STRING, sample_format.c_str(), 50 | "rate", G_TYPE_INT, sample_rate, 51 | "channels", G_TYPE_INT, channels, 52 | "width", G_TYPE_INT, depth, 53 | "depth", G_TYPE_INT, depth, 54 | "signed", G_TYPE_BOOLEAN, TRUE, 55 | "layout", G_TYPE_STRING, "interleaved", 56 | NULL); 57 | 58 | if (dst_type == "alsasink") 59 | { 60 | _audio = gst_bin_new("audiobin"); 61 | _convert = gst_element_factory_make("audioconvert", "convert"); 62 | audiopad = gst_element_get_static_pad(_convert, "sink"); 63 | _resample = gst_element_factory_make("audioresample", "resample"); 64 | 65 | _sink = gst_element_factory_make("alsasink", "sink"); 66 | g_object_set(G_OBJECT(_sink), "sync", FALSE, NULL); 67 | if (!device.empty()) { 68 | g_object_set(G_OBJECT(_sink), "device", device.c_str(), NULL); 69 | } 70 | gst_bin_add_many( GST_BIN(_audio), _convert, _resample, _sink, NULL); 71 | gst_element_link_many(_convert, _resample, _sink, NULL); 72 | gst_element_add_pad(_audio, gst_ghost_pad_new("sink", audiopad)); 73 | } 74 | else 75 | { 76 | ROS_INFO("file sink to %s", dst_type.c_str()); 77 | _sink = gst_element_factory_make("filesink", "sink"); 78 | g_object_set(G_OBJECT(_sink), "location", dst_type.c_str(), NULL); 79 | } 80 | 81 | if (format == "mp3") 82 | { 83 | if (dst_type == "alsasink") 84 | { 85 | _decoder = gst_element_factory_make("decodebin", "decoder"); 86 | g_signal_connect(_decoder, "pad-added", G_CALLBACK(cb_newpad),this); 87 | 88 | _filter = gst_element_factory_make("capsfilter", "filter"); 89 | g_object_set( G_OBJECT(_filter), "caps", caps, NULL); 90 | 91 | gst_bin_add_many(GST_BIN(_pipeline), _source, _decoder, _filter, _audio, NULL); 92 | gst_element_link_many(_source, _decoder, _filter, _audio, NULL); 93 | gst_object_unref(audiopad); 94 | gst_caps_unref(caps); 95 | } 96 | else 97 | { 98 | gst_bin_add_many(GST_BIN(_pipeline), _source, _sink, NULL); 99 | gst_element_link(_source, _sink); 100 | } 101 | } 102 | else if (format == "wave") 103 | { 104 | g_object_set( G_OBJECT(_source), "caps", caps, NULL); 105 | g_object_set (G_OBJECT (_source), "format", GST_FORMAT_TIME, NULL); 106 | if (dst_type == "alsasink") 107 | { 108 | gst_bin_add_many( GST_BIN(_pipeline), _source, _audio, NULL); 109 | gst_element_link_many( _source, _audio, NULL); 110 | gst_object_unref(audiopad); 111 | } 112 | else 113 | { 114 | _filter = gst_element_factory_make("wavenc", "filter"); 115 | gst_bin_add_many(GST_BIN(_pipeline), _source, _filter, _sink, NULL); 116 | gst_element_link_many( _source, _filter, _sink, NULL); 117 | } 118 | gst_caps_unref(caps); 119 | } 120 | else 121 | { 122 | ROS_ERROR("Unsupported format: %s", format.c_str()); 123 | } 124 | 125 | gst_element_set_state(GST_ELEMENT(_pipeline), GST_STATE_PLAYING); 126 | //gst_element_set_state(GST_ELEMENT(_playbin), GST_STATE_PLAYING); 127 | 128 | _gst_thread = boost::thread( boost::bind(g_main_loop_run, _loop) ); 129 | } 130 | 131 | private: 132 | 133 | void onAudio(const audio_common_msgs::AudioDataConstPtr &msg) 134 | { 135 | GstBuffer *buffer = gst_buffer_new_and_alloc(msg->data.size()); 136 | gst_buffer_fill(buffer, 0, &msg->data[0], msg->data.size()); 137 | GstFlowReturn ret; 138 | 139 | g_signal_emit_by_name(_source, "push-buffer", buffer, &ret); 140 | gst_buffer_unref(buffer); 141 | } 142 | 143 | static void cb_newpad (GstElement *decodebin, GstPad *pad, 144 | gpointer data) 145 | { 146 | RosGstPlay *client = reinterpret_cast(data); 147 | 148 | GstCaps *caps; 149 | GstStructure *str; 150 | GstPad *audiopad; 151 | 152 | /* only link once */ 153 | audiopad = gst_element_get_static_pad (client->_audio, "sink"); 154 | if (GST_PAD_IS_LINKED (audiopad)) 155 | { 156 | g_object_unref (audiopad); 157 | return; 158 | } 159 | 160 | /* check media type */ 161 | caps = gst_pad_query_caps (pad, NULL); 162 | str = gst_caps_get_structure (caps, 0); 163 | if (!g_strrstr (gst_structure_get_name (str), "audio")) { 164 | gst_caps_unref (caps); 165 | gst_object_unref (audiopad); 166 | return; 167 | } 168 | 169 | gst_caps_unref (caps); 170 | 171 | /* link'n'play */ 172 | gst_pad_link (pad, audiopad); 173 | 174 | g_object_unref (audiopad); 175 | } 176 | 177 | ros::NodeHandle _nh; 178 | ros::Subscriber _sub; 179 | boost::thread _gst_thread; 180 | 181 | GstElement *_pipeline, *_source, *_sink, *_decoder, *_convert, *_audio, *_resample, *_filter; 182 | GstElement *_playbin; 183 | GMainLoop *_loop; 184 | }; 185 | } 186 | 187 | 188 | int main (int argc, char **argv) 189 | { 190 | ros::init(argc, argv, "audio_play"); 191 | gst_init(&argc, &argv); 192 | 193 | audio_transport::RosGstPlay client; 194 | 195 | ros::spin(); 196 | } 197 | -------------------------------------------------------------------------------- /sound_play/CHANGELOG.rst: -------------------------------------------------------------------------------- 1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 2 | Changelog for package sound_play 3 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 4 | 5 | 0.3.18 (2024-08-13) 6 | ------------------- 7 | * Merge pull request `#249 `_ from peci1/patch-1 8 | festival_plugin: add support for different encodings 9 | * festival_plugin: add support for different encodings 10 | * Contributors: Martin Pecka, Shingo Kitagawa 11 | 12 | 0.3.17 (2023-06-08) 13 | ------------------- 14 | * Merge pull request `#231 `_ from knorth55/no-wait-mode 15 | * dont wait when rospy.Duration(0) is set for timeout 16 | * Merge pull request `#229 `_ from furushchev/flite-plugin-lazy-load 17 | FlitePlugin: Lazy loading default voice path 18 | * FlitePlugin: Lazy loading default voice path 19 | * Contributors: Shingo Kitagawa, Yuki Furuta 20 | 21 | 0.3.16 (2022-12-23) 22 | ------------------- 23 | * Merge pull request `#203 `_ from nakane11/timeout 24 | * refactor libsoundplay.py 25 | * Add timeout to wait_for_server and wait_for_result 26 | * Contributors: Aoi Nakane, Shingo Kitagawa 27 | 28 | 0.3.15 (2022-08-29) 29 | ------------------- 30 | * Merge pull request `#200 `_ from knorth55/yaml-missing 31 | * show error and skip loading when plugin yaml is missing 32 | * Merge pull request `#199 `_ from knorth55/install-plugin-yaml 33 | * fix missing install in CMakeLists.txt 34 | * Contributors: Shingo Kitagawa 35 | 36 | 0.3.14 (2022-08-18) 37 | ------------------- 38 | * Merge pull request `#193 `_ from knorth55/refactor-soundplay-node 39 | * refactor soundplay_node.py 40 | * Merge pull request `#192 `_ from knorth55/fix-file-open-issue 41 | * fix typo causing file open issue 42 | * Merge pull request `#191 `_ from knorth55/flite-default-voice-dir 43 | * add default voice dir for flite_plugin 44 | * Merge pull request `#190 `_ from ros-drivers/knorth55-patch-1 45 | * Update soundplay_node.py 46 | * Merge pull request `#187 `_ from knorth55/fix-typo 47 | * fix typo in soundplay_node.py 48 | * Merge pull request `#185 `_ from knorth55/sound-play-flite-plugin 49 | add flite plugin for sound_play 50 | * Merge pull request `#183 `_ from knorth55/sound-play-plugin 51 | add soundplay plugin feature 52 | * add flite in sound_play dependency 53 | * refactor FestivalPlugin 54 | * add flite plugin 55 | * change default_voice to None 56 | * add plugin arg in soundplay_node.launch 57 | * refactor codes 58 | * add output screen in soundplay_node.launch 59 | * add soundplay plugin attribute 60 | * Merge pull request `#184 `_ from knorth55/default-voice 61 | * add default_voice in soundplay_node.launch 62 | * Merge pull request `#182 `_ from iory/is-speaking 63 | * Improve is_speaking by checking goal status 64 | * Merge pull request `#181 `_ from knorth55/refactor-is-speaking 65 | * refactor is_speaking.py 66 | * Contributors: JSK fetch user, Shingo Kitagawa, iory 67 | 68 | 0.3.13 (2022-04-07) 69 | ------------------- 70 | * Merge pull request `#176 `_ from iory/is-speeching 71 | * Add is_speaking.py to catkin_install_python 72 | * Fixed name speeching to speaking 73 | * Add is_speeching node for checking robot is speaking 74 | * Contributors: Shingo Kitagawa, iory 75 | 76 | 0.3.12 (2021-09-01) 77 | ------------------- 78 | * Merge pull request `#175 `_ from iory/rate 79 | Modified loop rate for action execution 80 | * Modified loop rate for action execution 81 | * Merge pull request `#131 `_ from yann-bourrigault/master 82 | Handle playing sound in loop 83 | * import GObject in try section 84 | * Merge pull request `#174 `_ from iory/cache 85 | Add arg2 information for cache 86 | * Add arg2 information for cache 87 | * Merge pull request `#173 `_ from knorth55/replace-sound-client 88 | * Merge pull request `#172 `_ from knorth55/start-action-after-init 89 | [sound_play] start ActionServer after initialize in soundplay_node.py 90 | * Merge pull request `#171 `_ from knorth55/set-aborted 91 | [sound_play] add proper set_aborted in soundplay_node.py 92 | * add replace in sendMsg 93 | * start actionserver after initialize in soundplay_node.py 94 | * add proper set_aborted in soundplay_node.py 95 | * Merge branch 'master' into master 96 | * Handle playing sound repeatedly 97 | * Contributors: Shingo Kitagawa, Yann BOURRIGAULT, iory 98 | 99 | 0.3.11 (2021-04-08) 100 | ------------------- 101 | * Merge pull request `#167 `_ from k-okada/fix_155 102 | * Use rospy.myargv() instead of sys.argv to support remapping 103 | * Contributors: Kei Okada, Shingo Kitagawa 104 | 105 | 0.3.10 (2021-01-07) 106 | ------------------- 107 | 108 | 0.3.9 (2020-10-22) 109 | ------------------ 110 | 111 | 0.3.8 (2020-09-13) 112 | ------------------ 113 | * Merge pull request `#155 `_ from garaemon/use-myargv 114 | Use rospy.myargv() instead of sys.argv to support remapping 115 | * Use rospy.myargv() instead of sys.argv to support remapping 116 | * Merge pull request `#154 `_ from mikaelarguedas/fix_say_python3 117 | * update to support no iso-8859-15 language (`#1 `_) 118 | * support non iso-8859-15 language 119 | * encode only for python2 120 | * convert items to an iterator 121 | * make cleanup compatible with Python 3 122 | * catch AttributeError to handle python3 strings 123 | * Contributors: Mikael Arguedas, Ryohei Ueda, Shingo Kitagawa 124 | 125 | 0.3.7 (2020-08-08) 126 | ------------------ 127 | * Merge pull request `#149 `_ from garaemon/specify-topic-to-play-sound 128 | Support use different topic and actionlib to play sound 129 | * Support use different topic and actionlib to play sound 130 | * Add two keywords to the constructor of SoundClient class in order to 131 | specify actionlib namespace and topic name to play sound. 132 | * See `#119 `_. 133 | * Merge pull request `#144 `_ from ros-drivers/knorth55-patch-1 134 | * add gstreamer1.0-alsa exec_depend in sound_play 135 | * Contributors: Ryohei Ueda, Shingo Kitagawa 136 | 137 | 0.3.6 (2020-05-29) 138 | ------------------ 139 | * Merge pull request `#140 `_ from knorth55/support-python3 140 | fix syntax for python3 141 | * Merge pull request `#141 `_ from knorth55/add-maintainer 142 | add maintainer 143 | * add maintainer 144 | * fix syntax for python3 145 | * Contributors: Shingo Kitagawa 146 | 147 | 0.3.5 (2020-04-28) 148 | ------------------ 149 | * Merge pull request `#133 `_ from knorth55/noetic-build 150 | * remove unnecessary shebang 151 | * use setuptools instead of distutils.core 152 | * use package format=3 for python3 153 | * refactor CMakeLists.txt 154 | * use catkin_install_python for python shebang 155 | * Merge pull request `#135 `_ from knorth55/add-travis 156 | * disable sound_play test 157 | * Contributors: Shingo Kitagawa 158 | 159 | 0.3.4 (2020-04-02) 160 | ------------------ 161 | * Merge pull request `#126 `_ from itohdak/fix-Gstreamer-memory-leak 162 | [sound_play/scripts/soundplay_node.py] fix Gstreamer memory leak 163 | * Merge pull request `#123 `_ from 708yamaguchi/fix-encode 164 | Do not encode text when using langages which ISO-8859-15 does not support 165 | * [sound_play/scripts/soundplay_node.py] fix Gstreamer memory leak 166 | * do not encode text when using langages which ISO-8859-15 does not support 167 | * Merge pull request `#118 `_ from v4hn/patch-1 168 | use default audio output by default 169 | * use default audio output by default 170 | Not specifying a sound device defaults to *the first* sound device starting from Ubuntu 16.04., not to the one configured as default. 171 | The change is backward compatible and tested on ROS indigo and kinetic on a PR2 robot. 172 | * Merge pull request `#110 `_ from gerardcanal/master 173 | Encoded text to be said in ISO-8859-15 174 | * Merge branch 'master' of github.com:ros-drivers/audio_common 175 | * Sound play: Encoded file to be said in ISO-8859-15 so that accents in languages such as Spanish, Catalan or French are correctly pronounced (based on http://festcat.talp.cat/en/usage.php which says festival expects ISO-8859-15 encoding) 176 | * Contributors: Austin, Gerard Canal, Michael Görner, Naoya Yamaguchi, Shingo Kitagawa, itohdak 177 | 178 | 0.3.3 (2018-05-22) 179 | ------------------ 180 | * Fix gstreamer errors. Fixes `#108 `_ 181 | * Contributors: trainman419 182 | 183 | 0.3.2 (2018-05-02) 184 | ------------------ 185 | * [sound_play] add option to select audio device to play / record (`#87 `_) 186 | * [sound_play] add option to select audio device to play 187 | * [sound_play] reformat README to markdown; add usage to set device via rosparam 188 | * audio_capture: add option for selecting device to use 189 | * audio_play: add option to select device for playing audio 190 | * add device argument to launch files 191 | Conflicts: 192 | audio_capture/launch/capture.launch 193 | audio_capture/launch/capture_to_file.launch 194 | audio_capture/src/audio_capture.cpp 195 | audio_play/launch/play.launch 196 | sound_play/scripts/soundplay_node.py 197 | * Merge pull request `#95 `_ from yujinrobot/volume_check 198 | [sound_play] volume check for cached sounds 199 | * [sound_play] checks if sound's Gst instance's volume has changed and resets it 200 | * Contributors: Austin, Naveed Usmani, Yuki Furuta 201 | 202 | 0.3.1 (2016-08-28) 203 | ------------------ 204 | * Update to new gstreamer rosdeps 205 | * Update sound_play to gstreamer 1.0 206 | * remove chance of uninitialised variable being called in a subscriber callback. 207 | * Add changelogs 208 | * Issue: The error checks for missing publisher/action client in sendMsg were inverted. 209 | The non-blocking brach tested the action client while the blocking branch 210 | tested the publisher. 211 | Fix: Inverted the blocking boolean for both branchs. 212 | * sound_play: Fix build with -DCATKIN_ENABLE_TESTING=OFF. 213 | https://bugs.gentoo.org/show_bug.cgi?id=567466 214 | * [soundplay_node] fix resources not being released on dict cleanup 215 | This was resulting in the number of sink inputs reaching the maximum threshold, 216 | (32 on ubuntu 14.04 with pulseaudio 4.0) after which no more sounds could be 217 | played by the node. It would only happen if the rate of sounds being played was 218 | slower than the dictionary cleanup. 219 | * depend on actionlib. 220 | * Introduce unit test to ensure soundclient is started correctly. 221 | * Example of using the explicit blocking parameter to override the class setting. 222 | * SoundClient can also explicitly specify whether or not to block while playing the sound. 223 | Each play/repeat/say/... method can take an option blocking=True|False argument (using **kwargs), which over-rides the class-wide setting. 224 | * Merge pull request #62 from felixduvallet/set_queue_size 225 | Set queue_size in soundplay_node Publisher 226 | * do both in same script. 227 | * Added script showing the various blocking/non-blocking ways of using SoundClient. 228 | * removed trailing whitespace only 229 | * loginfo -> logdebug. 230 | * Slightly more condensed version of thresholding. 231 | * Enable blocking calls inside libsoundplay's SoundClient. 232 | This makes use of the actionlib interface provided by soundplay_node, by ensuring SoundClient receives a response before returning. 233 | Turn this on by: SoundClient(blocking=true). 234 | * Use new-style python classes (inherits from object). 235 | * removed trailing whitespace. 236 | * Set the volume in each of the sound_play actionlib tests. 237 | This makes the script actually play the sounds it requests. 238 | * Specify queue size explicitly. 239 | Removed warning message printed each time soundplay_node was started. 240 | * remove trailing whitespace only. 241 | * Change wiki urls 242 | * Fix test target name collision. Fixes #49 243 | * sound_play: cpp header conforms to the style guide 244 | * sound_play: update scripts to allow volume to be set 245 | * sound_play: updated tests to include volume changes 246 | * sound_play: add ability to specify volume at which to play sounds 247 | Also changed error to warning as per todo 248 | * sound_play: fix indentation and comment inconsistencies 249 | * sound_play: remove some raw prints cluttering output 250 | * sound_play: added queue_size to SoundClient init 251 | Should prevent warning being displayed whenever the client is created. 252 | Fixes issue #43 253 | * add simple-actionlib functionality to sound_play 254 | * sound_play: Added functions to play files relative to a package path 255 | * Update maintainer email 256 | * Contributors: Alexis Ballier, Austin, Daniel Stonier, David V. Lu, Felix Duvallet, Matthias Nieuwenhuisen, Michal Staniaszek, Neowizard, aginika, trainman419 257 | 258 | 0.2.11 (2016-02-16) 259 | ------------------- 260 | * Add changelogs 261 | * Fix bug in say.py. Fixes `#72 `_ 262 | * Contributors: trainman419 263 | 264 | 0.2.10 (2016-01-21) 265 | ------------------- 266 | * Add changelogs 267 | * Issue: The error checks for missing publisher/action client in sendMsg were inverted. 268 | The non-blocking brach tested the action client while the blocking branch 269 | tested the publisher. 270 | Fix: Inverted the blocking boolean for both branchs. 271 | * sound_play: Fix build with -DCATKIN_ENABLE_TESTING=OFF. 272 | https://bugs.gentoo.org/show_bug.cgi?id=567466 273 | * Contributors: Alexis Ballier, Neowizard, trainman419 274 | 275 | 0.2.9 (2015-12-02) 276 | ------------------ 277 | * Add changelogs 278 | * [soundplay_node] fix resources not being released on dict cleanup 279 | This was resulting in the number of sink inputs reaching the maximum threshold, 280 | (32 on ubuntu 14.04 with pulseaudio 4.0) after which no more sounds could be 281 | played by the node. It would only happen if the rate of sounds being played was 282 | slower than the dictionary cleanup. 283 | * depend on actionlib. 284 | * Introduce unit test to ensure soundclient is started correctly. 285 | * Example of using the explicit blocking parameter to override the class setting. 286 | * SoundClient can also explicitly specify whether or not to block while playing the sound. 287 | Each play/repeat/say/... method can take an option blocking=True|False argument (using **kwargs), which over-rides the class-wide setting. 288 | Conflicts: 289 | sound_play/src/sound_play/libsoundplay.py 290 | * do both in same script. 291 | * Added script showing the various blocking/non-blocking ways of using SoundClient. 292 | * removed trailing whitespace only 293 | Conflicts: 294 | sound_play/scripts/say.py 295 | * loginfo -> logdebug. 296 | * Enable blocking calls inside libsoundplay's SoundClient. 297 | This makes use of the actionlib interface provided by soundplay_node, by ensuring SoundClient receives a response before returning. 298 | Turn this on by: SoundClient(blocking=true). 299 | Conflicts: 300 | sound_play/src/sound_play/libsoundplay.py 301 | * Use new-style python classes (inherits from object). 302 | Conflicts: 303 | sound_play/src/sound_play/libsoundplay.py 304 | * removed trailing whitespace. 305 | Conflicts: 306 | sound_play/src/sound_play/libsoundplay.py 307 | * Revert "Set the volume in each of the sound_play actionlib tests." 308 | This reverts commit 55ab08c882809fc6d21affb849a7dac9f1901867. 309 | Indigo-devel does not have the volume API 310 | * Set the volume in each of the sound_play actionlib tests. 311 | This makes the script actually play the sounds it requests. 312 | * Specify queue size explicitly. 313 | Removed warning message printed each time soundplay_node was started. 314 | * remove trailing whitespace only. 315 | * Fix wiki links 316 | * Contributors: David V. Lu, Felix Duvallet, Michal Staniaszek, trainman419 317 | 318 | 0.2.8 (2015-10-02) 319 | ------------------ 320 | * Fix test target name collision. Fixes `#49 `_ 321 | * sound_play: remove some raw prints cluttering output 322 | * sound_play: added queue_size to SoundClient init 323 | Should prevent warning being displayed whenever the client is created. 324 | Fixes issue `#43 `_ 325 | * add simple-actionlib functionality to sound_play 326 | * sound_play: Added functions to play files relative to a package path 327 | * Update maintainer email 328 | * Contributors: Matthias Nieuwenhuisen, Michal Staniaszek, aginika, trainman419 329 | 330 | 0.2.7 (2014-07-25) 331 | ------------------ 332 | 333 | 0.2.6 (2014-02-26) 334 | ------------------ 335 | * Fix path resolution in python soundplay lib. 336 | * now importing roslib. closes `#33 `_ 337 | * Contributors: Piyush Khandelwal, trainman419 338 | 339 | 0.2.5 (2014-01-23) 340 | ------------------ 341 | * "0.2.5" 342 | * Install sounds. Fixes `#29 `_. 343 | * install sound_play.h and export include folder 344 | * Contributors: ahendrix, trainman419, v4hn 345 | 346 | 0.2.4 (2013-09-10) 347 | ------------------ 348 | * Fix cmake ordering. 349 | * Contributors: Austin Hendrix 350 | 351 | 0.2.3 (2013-07-15) 352 | ------------------ 353 | * Fix python. 354 | * Contributors: Austin Hendrix 355 | 356 | 0.2.2 (2013-04-10) 357 | ------------------ 358 | * Actually add proper dependency on message generation. 359 | * Reorder CMakeLists.txt. 360 | * Contributors: Austin Hendrix 361 | 362 | 0.2.1 (2013-04-08 13:59) 363 | ------------------------ 364 | 365 | 0.2.0 (2013-04-08 13:49) 366 | ------------------------ 367 | * Finish catkinizing audio_common. 368 | * Start catkinizing sound_play. 369 | * Fix typo in package.xml 370 | * Versions and more URLs. 371 | * Convert manifests to package.xml 372 | * Ditch old makefiles. 373 | * Use festival default voice from libsoundplay. 374 | * Set myself as the maintainer. 375 | * Fix filehandle leak and add debug statements. 376 | * Updates manifest 377 | * Updated manifests for rodep2 378 | * Fixed sound_play 379 | * Added test wave 380 | * Cleaned up the test script 381 | * Added default voice to say command 382 | * Updated the gstreamer rosdeps 383 | * Removed comment 384 | * Added diagnostic_msgs to sound_play 385 | * Added a rosdep.yaml file 386 | * Added ability to use different festival voices 387 | * Added exit(1) when import of pygame fails. This makes the error message easier to notice. 388 | * Added Ubuntu platform tags to manifest 389 | * Added a link to the troubleshooting wiki page in the diagnostic message as requested by `#4070 `_. 390 | * Took out the deprecated API. 391 | * Sound play now publishes header timestamp in message. `#3822 `_ 392 | * Cleaned up temp file generation when doing text to speach. Now uses the tempfile module. 393 | * Adding missing export of headers for sound_play C++ API 394 | * Changing node name for sound play diagnostics, `#3599 `_ 395 | * Added test.launch to run sound server and a test client. 396 | * Remove use of deprecated rosbuild macros 397 | * Replaced review tag with standardized message 398 | * Updated review status 399 | * Added a launch file to start soundplay_node.py 400 | * Made the sound_play client libraries be more explicit about what to do when the node is not running. 401 | * Updated manifest description 402 | * Updated copyright year 403 | * fixed XML typo 404 | * updated package description 405 | * Added a copyright message. 406 | * Removed debugging message from sound_play node. 407 | * Added tests for new sound_play python API and fixed a few bugs. 408 | * Fixed missing self arguments in sound_play libsoundplay.py 409 | * Upgraded the python sound_play API 410 | * Converted non-camelCase methods to camelCase in sound_play C++ API 411 | * Changed Lock to RLock to fix `#2801 `_ 412 | * Made the deprecation of SoundHandle into a warning. 413 | * Added debug messages 414 | * Updated soundplay_node to publish diagnostics and increased the number of active channels. 415 | * Added diagnostic_msgs dependency to sound_play 416 | * sound_play: Renamed SoundHandle to SoundClient. Added Sound-centric C++ API. Changed byte to int8 in msg file. Updated documentation. 417 | * migration part 1 418 | * Contributors: Austin Hendrix, Nate Koenig, blaise, blaisegassend, eitan, gerkey, kwc, nkoenig, watts, wheeler 419 | -------------------------------------------------------------------------------- /sound_play/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.3) 2 | 3 | project(sound_play) 4 | 5 | find_package(catkin REQUIRED COMPONENTS message_generation roscpp actionlib_msgs) 6 | 7 | add_action_files(DIRECTORY action FILES SoundRequest.action) 8 | add_message_files(DIRECTORY msg FILES SoundRequest.msg) 9 | 10 | include_directories(include ${catkin_INCLUDE_DIRS}) 11 | 12 | catkin_python_setup() 13 | 14 | generate_messages(DEPENDENCIES actionlib_msgs) 15 | 16 | catkin_package(CATKIN_DEPENDS message_runtime actionlib_msgs 17 | INCLUDE_DIRS include) 18 | 19 | catkin_install_python(PROGRAMS 20 | scripts/is_speaking.py 21 | scripts/playbuiltin.py 22 | scripts/play.py 23 | scripts/say.py 24 | scripts/shutup.py 25 | scripts/soundplay_node.py 26 | scripts/test.py 27 | scripts/test_actionlib_client.py 28 | DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}) 29 | 30 | install(FILES 31 | soundplay_node.launch 32 | sound_play_plugin.yaml 33 | test.launch 34 | DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}) 35 | 36 | install(DIRECTORY include/${PROJECT_NAME}/ 37 | DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION}) 38 | 39 | install(DIRECTORY sounds 40 | DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}) 41 | 42 | # if(CATKIN_ENABLE_TESTING) 43 | # catkin_add_nosetests(scripts/test) 44 | # add_subdirectory(test) 45 | # endif() 46 | -------------------------------------------------------------------------------- /sound_play/README.md: -------------------------------------------------------------------------------- 1 | sound_play 2 | ========= 3 | 4 | ## Dependencies 5 | 6 | - python-pygame 7 | - festival 8 | - festvox-don 9 | - alsa-base 10 | - alsa-tools 11 | 12 | ## Checking that the speaker/sound card is recognized by the kernel 13 | 14 | `cat /proc/asound/cards` 15 | 16 | Your card should be in the list. Make note of the number in front of the 17 | card, it will be used to tell alsa where to play sound from. 18 | 19 | If your sound device does not show up, your kernel may not support it, or 20 | the module may not be loaded. For usb speakers, you may want to try: 21 | 22 | `modprobe snd-usb-audio` 23 | 24 | (not sure if this list is exhaustive) 25 | 26 | ## Telling alsa which sound card/speaker to use 27 | 28 | Run (replace 75 with the number of the sound device to use): 29 | 30 | `asoundconf set-default-card 75` 31 | 32 | This will create .asoundrc.asoundconf in your home directory. 33 | To make alsa use these settings, add the following line to `~/.asoundrc` 34 | 35 | `include ".asoundrc.asoundconf"` 36 | 37 | To set this default to all users, copy this to the system-wide alsa 38 | configuration file: 39 | 40 | `mv ~/.asoundrc.asoundconf /etc/asound.conf` 41 | 42 | ## Getting started 43 | 44 | Start the sound play node, and have a look at the scripts in the scripts 45 | directory that exercise the node's functionality. 46 | 47 | ## Specify Device via ROS Param 48 | 49 | Besides setting default device as system wide settings, you can also specify audio device via `rosparam`: 50 | 51 | ``` xml 52 | 53 | 54 | 55 | 56 | 57 | ``` 58 | 59 | or simply run: `rosrun sound_play soundplay_node.py _device:="hw:1,0"` 60 | 61 | In the launch file above, `~device` parameter is set to `hw:1,0`, which tells `soundplay_node` to use audio device No. `0` connected to audio card No.`1`. 62 | To find card/device number which you want to use, execute: 63 | 64 | ``` bash 65 | sudo aplay -l 66 | ``` 67 | 68 | -------------------------------------------------------------------------------- /sound_play/action/SoundRequest.action: -------------------------------------------------------------------------------- 1 | SoundRequest sound_request 2 | --- 3 | bool playing 4 | time stamp 5 | --- 6 | bool playing 7 | time stamp -------------------------------------------------------------------------------- /sound_play/include/sound_play/sound_play.h: -------------------------------------------------------------------------------- 1 | /* 2 | *********************************************************** 3 | * Software License Agreement (BSD License) 4 | * 5 | * Copyright (c) 2009, Willow Garage, Inc. 6 | * All rights reserved. 7 | * 8 | * Redistribution and use in source and binary forms, with or without 9 | * modification, are permitted provided that the following conditions 10 | * are met: 11 | * 12 | * * Redistributions of source code must retain the above copyright 13 | * notice, this list of conditions and the following disclaimer. 14 | * * Redistributions in binary form must reproduce the above 15 | * copyright notice, this list of conditions and the following 16 | * disclaimer in the documentation and/or other materials provided 17 | * with the distribution. 18 | * * Neither the name of the Willow Garage nor the names of its 19 | * contributors may be used to endorse or promote products derived 20 | * from this software without specific prior written permission. 21 | * 22 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 23 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 24 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 25 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 26 | * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 27 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 28 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 29 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 30 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 32 | * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 33 | * POSSIBILITY OF SUCH DAMAGE. 34 | *********************************************************** 35 | */ 36 | 37 | #ifndef __SOUND_PLAY__SOUND_PLAY__H__ 38 | #define __SOUND_PLAY__SOUND_PLAY__H__ 39 | 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | 46 | namespace sound_play 47 | { 48 | 49 | /** \brief Class that publishes messages to the sound_play node. 50 | * 51 | * This class is a helper class for communicating with the sound_play node 52 | * via the \ref sound_play::SoundRequest message. It has two ways of being used: 53 | * 54 | * - It can create Sound classes that represent a particular sound which 55 | * can be played, repeated or stopped. 56 | * 57 | * - It provides methods for each way in which the sound_play::SoundRequest 58 | * message can be invoked. 59 | */ 60 | 61 | class SoundClient 62 | { 63 | public: 64 | class Sound 65 | { 66 | friend class SoundClient; 67 | private: 68 | int snd_; 69 | float vol_; 70 | std::string arg_; 71 | std::string arg2_; 72 | SoundClient *client_; 73 | 74 | Sound(SoundClient *sc, int snd, const std::string &arg, const std::string arg2 = std::string(), const float vol = 1.0f) 75 | { 76 | client_ = sc; 77 | snd_ = snd; 78 | arg_ = arg; 79 | arg2_ = arg2; 80 | vol_ = vol; 81 | } 82 | 83 | public: 84 | /** \brief Play the Sound. 85 | * 86 | * This method causes the Sound to be played once. 87 | */ 88 | void play() 89 | { 90 | client_->sendMsg(snd_, SoundRequest::PLAY_ONCE, arg_, arg2_, vol_); 91 | } 92 | 93 | /** \brief Play the Sound repeatedly. 94 | * 95 | * This method causes the Sound to be played repeatedly until stop() is 96 | * called. 97 | */ 98 | void repeat() 99 | { 100 | client_->sendMsg(snd_, SoundRequest::PLAY_START, arg_, arg2_, vol_); 101 | } 102 | 103 | /** \brief Stop Sound playback. 104 | * 105 | * This method causes the Sound to stop playing. 106 | */ 107 | void stop() 108 | { 109 | client_->sendMsg(snd_, SoundRequest::PLAY_STOP, arg_, arg2_, vol_); 110 | } 111 | }; 112 | 113 | /** \brief Create a SoundClient that publishes on the given topic 114 | * 115 | * Creates a SoundClient that publishes to the given topic relative to the 116 | * given NodeHandle. 117 | * 118 | * \param nh Node handle to use when creating the topic. 119 | * 120 | * \param topic Topic to publish to. 121 | */ 122 | SoundClient(ros::NodeHandle &nh, const std::string &topic) 123 | { 124 | init(nh, topic); 125 | } 126 | 127 | /** \brief Create a SoundClient with the default topic 128 | * 129 | * Creates a SoundClient that publishes to "robotsound". 130 | */ 131 | SoundClient() 132 | { 133 | init(ros::NodeHandle(), "robotsound"); 134 | } 135 | 136 | /** \brief Create a voice Sound. 137 | * 138 | * Creates a Sound corresponding to saying the indicated text. 139 | * 140 | * \param s Text to say 141 | * \param volume Volume at which to play the sound. 0 is mute, 1.0 is 100%. 142 | */ 143 | Sound voiceSound(const std::string &s, float volume = 1.0f) 144 | { 145 | return Sound(this, SoundRequest::SAY, s, "", volume); 146 | } 147 | 148 | /** \brief Create a wave Sound. 149 | * 150 | * Creates a Sound corresponding to indicated file. 151 | * 152 | * \param s File to play. Should be an absolute path that exists on the 153 | * machine running the sound_play node. 154 | * \param volume Volume at which to play the sound. 0 is mute, 1.0 is 100%. 155 | */ 156 | Sound waveSound(const std::string &s, float volume = 1.0f) 157 | { 158 | return Sound(this, SoundRequest::PLAY_FILE, s, "", volume); 159 | } 160 | 161 | /** \brief Create a wave Sound from a package. 162 | * 163 | * Creates a Sound corresponding to indicated file. 164 | * 165 | * \param p Package containing the sound file. 166 | * \param s Filename of the WAV or OGG file. Must be an path relative to the package valid 167 | * on the computer on which the sound_play node is running 168 | * \param volume Volume at which to play the sound. 0 is mute, 1.0 is 100%. 169 | */ 170 | Sound waveSoundFromPkg(const std::string &p, const std::string &s, float volume = 1.0f) 171 | { 172 | return Sound(this, SoundRequest::PLAY_FILE, s, p, volume); 173 | } 174 | 175 | /** \brief Create a builtin Sound. 176 | * 177 | * Creates a Sound corresponding to indicated builtin wave. 178 | * 179 | * \param id Identifier of the sound to play. 180 | * \param volume Volume at which to play the sound. 0 is mute, 1.0 is 100%. 181 | */ 182 | Sound builtinSound(int id, float volume = 1.0f) 183 | { 184 | return Sound(this, id, "", "", volume); 185 | } 186 | 187 | /** \brief Say a string 188 | * 189 | * Send a string to be said by the sound_node. The vocalization can be 190 | * stopped using stopSaying or stopAll. 191 | * 192 | * \param s String to say 193 | * \param volume Volume at which to play the sound. 0 is mute, 1.0 is 100%. 194 | */ 195 | void say(const std::string &s, const std::string &voice="voice_kal_diphone", float volume = 1.0f) 196 | { 197 | sendMsg(SoundRequest::SAY, SoundRequest::PLAY_ONCE, s, voice, volume); 198 | } 199 | 200 | /** \brief Say a string repeatedly 201 | * 202 | * The string is said repeatedly until stopSaying or stopAll is used. 203 | * 204 | * \param s String to say repeatedly 205 | * \param volume Volume at which to play the sound. 0 is mute, 1.0 is 100%. 206 | */ 207 | void repeat(const std::string &s, float volume = 1.0f) 208 | { 209 | sendMsg(SoundRequest::SAY, SoundRequest::PLAY_START, s, "", volume); 210 | } 211 | 212 | /** \brief Stop saying a string 213 | * 214 | * Stops saying a string that was previously started by say or repeat. The 215 | * argument indicates which string to stop saying. 216 | * 217 | * \param s Same string as in the say or repeat command 218 | */ 219 | void stopSaying(const std::string &s) 220 | { 221 | sendMsg(SoundRequest::SAY, SoundRequest::PLAY_STOP, s, ""); 222 | } 223 | 224 | /** \brief Plays a WAV or OGG file 225 | * 226 | * Plays a WAV or OGG file once. The playback can be stopped by stopWave or 227 | * stopAll. 228 | * 229 | * \param s Filename of the WAV or OGG file. Must be an absolute path valid 230 | * on the computer on which the sound_play node is running 231 | * \param volume Volume at which to play the sound. 0 is mute, 1.0 is 100%. 232 | */ 233 | void playWave(const std::string &s, float volume = 1.0f) 234 | { 235 | sendMsg(SoundRequest::PLAY_FILE, SoundRequest::PLAY_ONCE, s, "", volume); 236 | } 237 | 238 | /** \brief Plays a WAV or OGG file repeatedly 239 | * 240 | * Plays a WAV or OGG file repeatedly until stopWave or stopAll is used. 241 | * 242 | * \param s Filename of the WAV or OGG file. Must be an absolute path valid 243 | * on the computer on which the sound_play node is running. 244 | * \param volume Volume at which to play the sound. 0 is mute, 1.0 is 100%. 245 | */ 246 | void startWave(const std::string &s, float volume = 1.0f) 247 | { 248 | sendMsg(SoundRequest::PLAY_FILE, SoundRequest::PLAY_START, s, "", volume); 249 | } 250 | 251 | /** \brief Stop playing a WAV or OGG file 252 | * 253 | * Stops playing a file that was previously started by playWave or 254 | * startWave. 255 | * 256 | * \param s Same string as in the playWave or startWave command 257 | */ 258 | void stopWave(const std::string &s) 259 | { 260 | sendMsg(SoundRequest::PLAY_FILE, SoundRequest::PLAY_STOP, s); 261 | } 262 | 263 | /** \brief Plays a WAV or OGG file from a package 264 | * 265 | * Plays a WAV or OGG file once. The playback can be stopped by stopWaveFromPkg or 266 | * stopAll. 267 | * 268 | * \param p Package name containing the sound file. 269 | * \param s Filename of the WAV or OGG file. Must be an path relative to the package valid 270 | * on the computer on which the sound_play node is running 271 | * \param volume Volume at which to play the sound. 0 is mute, 1.0 is 100%. 272 | */ 273 | void playWaveFromPkg(const std::string &p, const std::string &s, float volume = 1.0f) 274 | { 275 | sendMsg(SoundRequest::PLAY_FILE, SoundRequest::PLAY_ONCE, s, p, volume); 276 | } 277 | 278 | /** \brief Plays a WAV or OGG file repeatedly 279 | * 280 | * Plays a WAV or OGG file repeatedly until stopWaveFromPkg or stopAll is used. 281 | * 282 | * \param p Package name containing the sound file. 283 | * \param s Filename of the WAV or OGG file. Must be an path relative to the package valid 284 | * on the computer on which the sound_play node is running 285 | * \param volume Volume at which to play the sound. 0 is mute, 1.0 is 100%. 286 | */ 287 | void startWaveFromPkg(const std::string &p, const std::string &s, float volume = 1.0f) 288 | { 289 | sendMsg(SoundRequest::PLAY_FILE, SoundRequest::PLAY_START, s, p, volume); 290 | } 291 | 292 | /** \brief Stop playing a WAV or OGG file 293 | * 294 | * Stops playing a file that was previously started by playWaveFromPkg or 295 | * startWaveFromPkg. 296 | * 297 | * \param p Package name containing the sound file. 298 | * \param s Filename of the WAV or OGG file. Must be an path relative to the package valid 299 | * on the computer on which the sound_play node is running 300 | */ 301 | void stopWaveFromPkg(const std::string &p, const std::string &s) 302 | { 303 | sendMsg(SoundRequest::PLAY_FILE, SoundRequest::PLAY_STOP, s, p); 304 | } 305 | 306 | /** \brief Play a buildin sound 307 | * 308 | * Starts playing one of the built-in sounds. built-ing sounds are documented 309 | * in \ref SoundRequest.msg. Playback can be stopped by stopAll. 310 | * 311 | * \param sound Identifier of the sound to play. 312 | * \param volume Volume at which to play the sound. 0 is mute, 1.0 is 100%. 313 | */ 314 | void play(int sound, float volume = 1.0f) 315 | { 316 | sendMsg(sound, SoundRequest::PLAY_ONCE, "", "", volume); 317 | } 318 | 319 | /** \brief Play a buildin sound repeatedly 320 | * 321 | * Starts playing one of the built-in sounds repeatedly until stop or stopAll 322 | * is used. Built-in sounds are documented in \ref SoundRequest.msg. 323 | * 324 | * \param sound Identifier of the sound to play. 325 | * \param volume Volume at which to play the sound. 0 is mute, 1.0 is 100%. 326 | */ 327 | void start(int sound, float volume = 1.0f) 328 | { 329 | sendMsg(sound, SoundRequest::PLAY_START, "", "", volume); 330 | } 331 | 332 | /** \brief Stop playing a built-in sound 333 | * 334 | * Stops playing a built-in sound started with play or start. 335 | * 336 | * \param sound Same sound that was used to start playback. 337 | */ 338 | void stop(int sound) 339 | { 340 | sendMsg(sound, SoundRequest::PLAY_STOP); 341 | } 342 | 343 | /** \brief Stop all currently playing sounds 344 | * 345 | * This method stops all speech, wave file, and built-in sound playback. 346 | */ 347 | void stopAll() 348 | { 349 | stop(SoundRequest::ALL); 350 | } 351 | 352 | /** \brief Turns warning messages on or off. 353 | * 354 | * If a message is sent when no node is subscribed to the topic, a 355 | * warning message is printed. This method can be used to enable or 356 | * disable warnings. 357 | * 358 | * \param state True to turn off messages, false to turn them on. 359 | */ 360 | void setQuiet(bool state) 361 | { 362 | quiet_ = state; 363 | } 364 | 365 | private: 366 | void init(ros::NodeHandle nh, const std::string &topic) 367 | { 368 | nh_ = nh; 369 | pub_ = nh.advertise(topic, 5); 370 | quiet_ = false; 371 | } 372 | 373 | void sendMsg(int snd, int cmd, const std::string &s = "", const std::string &arg2 = "", const float &vol = 1.0f) 374 | { 375 | boost::mutex::scoped_lock lock(mutex_); 376 | 377 | if (!nh_.ok()) 378 | return; 379 | 380 | SoundRequest msg; 381 | msg.sound = snd; 382 | msg.command = cmd; 383 | msg.arg = s; 384 | msg.arg2 = arg2; 385 | 386 | // ensure volume is in the correct range 387 | if (vol < 0) 388 | msg.volume = 0; 389 | else if (vol > 1.0) 390 | msg.volume = 1.0f; 391 | else 392 | msg.volume = vol; 393 | 394 | pub_.publish(msg); 395 | 396 | if (pub_.getNumSubscribers() == 0 && !quiet_) 397 | ROS_WARN("Sound command issued, but no node is subscribed to the topic. Perhaps you forgot to run soundplay_node.py"); 398 | } 399 | 400 | bool quiet_; 401 | ros::NodeHandle nh_; 402 | ros::Publisher pub_; 403 | boost::mutex mutex_; 404 | }; 405 | 406 | typedef SoundClient::Sound Sound; 407 | 408 | }; 409 | 410 | #endif 411 | -------------------------------------------------------------------------------- /sound_play/mainpage.dox: -------------------------------------------------------------------------------- 1 | /** 2 | 3 | \mainpage 4 | \htmlinclude manifest.html 5 | 6 | The \b sound_play package provides a way to say strings, 7 | play WAV or OGG files and to play builtin sounds. Documentation for the 8 | package can be found here at http://www.ros.org/wiki/sound_play 9 | 10 | Multiple sounds can be played concurrently (up to 4 currently because of 11 | limitations in pygame). 12 | 13 | Python and C++ client classes are provide for ease of use: 14 | 15 | - sound_play::SoundClient and sound_play::SoundClient::Sound for C++ 16 | - libsoundplay::SoundClient and libsoundplay::SoundClient::Sound for Python 17 | 18 | Example uses are in: 19 | 20 | - test.cpp 21 | - test.py 22 | 23 | */ 24 | -------------------------------------------------------------------------------- /sound_play/msg/SoundRequest.msg: -------------------------------------------------------------------------------- 1 | # IMPORTANT: You should never have to generate this message yourself. 2 | # Use the sound_play::SoundClient C++ helper or the 3 | # sound_play.libsoundplay.SoundClient Python helper. 4 | 5 | # Sounds 6 | int8 BACKINGUP = 1 7 | int8 NEEDS_UNPLUGGING = 2 8 | int8 NEEDS_PLUGGING = 3 9 | int8 NEEDS_UNPLUGGING_BADLY = 4 10 | int8 NEEDS_PLUGGING_BADLY = 5 11 | 12 | # Sound identifiers that have special meaning 13 | int8 ALL = -1 # Only legal with PLAY_STOP 14 | int8 PLAY_FILE = -2 15 | int8 SAY = -3 16 | 17 | int8 sound # Selects which sound to play (see above) 18 | 19 | # Commands 20 | int8 PLAY_STOP = 0 # Stop this sound from playing 21 | int8 PLAY_ONCE = 1 # Play the sound once 22 | int8 PLAY_START = 2 # Play the sound in a loop until a stop request occurs 23 | 24 | int8 command # Indicates what to do with the sound 25 | 26 | # Volume at which to play the sound, with 0 as mute and 1.0 as 100%. 27 | float32 volume 28 | 29 | string arg # file name or text to say 30 | string arg2 # other arguments 31 | -------------------------------------------------------------------------------- /sound_play/package.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | sound_play 7 | 0.3.18 8 | 9 | sound_play provides a ROS node that translates commands on a ROS topic (robotsound) into sounds. The node supports built-in sounds, playing OGG/WAV files, and doing speech synthesis via festival. C++ and Python bindings allow this node to be used without understanding the details of the message format, allowing faster development and resilience to message format changes. 10 | 11 | Austin Hendrix 12 | Shingo Kitagawa 13 | Blaise Gassend 14 | BSD 15 | http://ros.org/wiki/sound_play 16 | https://github.com/ros-drivers/audio_common 17 | https://github.com/ros-drivers/audio_common/issues 18 | 19 | catkin 20 | python-setuptools 21 | python3-setuptools 22 | 23 | roscpp 24 | roslib 25 | actionlib_msgs 26 | actionlib 27 | audio_common_msgs 28 | diagnostic_msgs 29 | message_generation 30 | 31 | roscpp 32 | roslib 33 | actionlib_msgs 34 | audio_common_msgs 35 | diagnostic_msgs 36 | 37 | python-gi 38 | python3-gi 39 | gstreamer1.0 40 | gstreamer1.0-alsa 41 | gstreamer1.0-plugins-base 42 | gstreamer1.0-plugins-ugly 43 | gstreamer1.0-plugins-good 44 | 45 | rospy 46 | festival 47 | flite 48 | message_runtime 49 | resource_retriever 50 | 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /sound_play/resources/flitevox/.gitignore: -------------------------------------------------------------------------------- 1 | *.flitevox 2 | -------------------------------------------------------------------------------- /sound_play/scripts/is_speaking.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import rospy 4 | 5 | from actionlib_msgs.msg import GoalStatus 6 | from actionlib_msgs.msg import GoalStatusArray 7 | import std_msgs.msg 8 | 9 | 10 | class IsSpeaking(object): 11 | 12 | def __init__(self): 13 | super(IsSpeaking, self).__init__() 14 | 15 | self.sub = rospy.Subscriber( 16 | '~robotsound', 17 | GoalStatusArray, 18 | callback=self.callback, 19 | queue_size=1) 20 | 21 | self.is_speaking = False 22 | self.pub_speech_flag = rospy.Publisher( 23 | '~output/is_speaking', 24 | std_msgs.msg.Bool, queue_size=1) 25 | 26 | self.timer = rospy.Timer(rospy.Duration(0.01), self.speech_timer_cb) 27 | 28 | def check_speak_status(self, status_msg): 29 | """Returns True when speaking. 30 | 31 | """ 32 | # If it is not included in the terminal state, 33 | # it is determined as a speaking state. 34 | if status_msg.status in [GoalStatus.ACTIVE, 35 | GoalStatus.PREEMPTING, 36 | GoalStatus.RECALLING]: 37 | return True 38 | return False 39 | 40 | def callback(self, msg): 41 | for status in msg.status_list: 42 | if self.check_speak_status(status): 43 | self.is_speaking = True 44 | return 45 | self.is_speaking = False 46 | 47 | def speech_timer_cb(self, timer): 48 | self.pub_speech_flag.publish( 49 | std_msgs.msg.Bool(self.is_speaking)) 50 | 51 | 52 | if __name__ == '__main__': 53 | rospy.init_node('is_speaking') 54 | app = IsSpeaking() 55 | rospy.spin() 56 | -------------------------------------------------------------------------------- /sound_play/scripts/play.py: -------------------------------------------------------------------------------- 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 | 40 | if __name__ == '__main__': 41 | import rospy 42 | rospy.init_node('play', anonymous=True) 43 | argv = rospy.myargv() 44 | if len(argv) < 2 or len(argv) > 3 or argv[1] == '--help': 45 | print('Usage: %s sound_to_play.(ogg|wav) [volume]' % argv[0]) 46 | print() 47 | print('Plays an .OGG or .WAV file. The path to the file should be absolute, and be valid on the computer on which sound_play is running.\n The (optional) second parameter sets the volume for the sound as a value between 0 and 1.0, where 0 is mute.') 48 | exit(1) 49 | 50 | # Import after printing usage for speed. 51 | from sound_play.msg import SoundRequest 52 | from sound_play.libsoundplay import SoundClient 53 | 54 | soundhandle = SoundClient() 55 | 56 | rospy.sleep(1) 57 | rospy.loginfo('Playing "%s".' % argv[1]) 58 | 59 | volume = float(argv[2]) if len(argv) == 3 else 1.0 60 | 61 | soundhandle.playWave(argv[1], volume) 62 | rospy.sleep(1) 63 | -------------------------------------------------------------------------------- /sound_play/scripts/playbuiltin.py: -------------------------------------------------------------------------------- 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 | import sys 40 | 41 | if __name__ == '__main__': 42 | import rospy 43 | argv = rospy.myargv() 44 | if len(argv) < 2 or len(argv) > 3 or argv[1] == '--help': 45 | print('Usage: %s [volume]' % argv[0]) 46 | print() 47 | print('Plays one of the built-in sounds based on its integer ID. Look at the sound_play/SoundRequest message definition for IDs.\n The (optional) volume parameter sets the volume for the sound as a value between 0 and 1.0, where 0 is mute.') 48 | exit(1) 49 | 50 | # Import here so that usage is fast. 51 | from sound_play.msg import SoundRequest 52 | from sound_play.libsoundplay import SoundClient 53 | 54 | rospy.init_node('play', anonymous=True) 55 | 56 | soundhandle = SoundClient() 57 | rospy.sleep(1) 58 | 59 | num = int(argv[1]) 60 | volume = float(argv[2]) if len(argv) == 3 else 1.0 61 | 62 | rospy.loginfo('Playing sound %i.' % num) 63 | 64 | soundhandle.play(num, volume) 65 | 66 | rospy.sleep(1) 67 | -------------------------------------------------------------------------------- /sound_play/scripts/playpackage.py: -------------------------------------------------------------------------------- 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: Matthias Nieuwenhuisen, Blaise Gassend 38 | 39 | 40 | import sys 41 | 42 | if __name__ == '__main__': 43 | import rospy 44 | argv = rospy.myargv() 45 | if len(argv) < 3 or len(argv) > 4 or argv[1] == '--help': 46 | print('Usage: %s package sound_to_play.(ogg|wav) [volume]' % argv[0]) 47 | print() 48 | print('Plays an .OGG or .WAV file. The path to the file should be relative to the package, and be valid on the computer on which sound_play is running. \n The (optional) volume parameter sets the volume for the sound as a value between 0 and 1.0, where 0 is mute.') 49 | exit(1) 50 | 51 | # Import after printing usage for speed. 52 | from sound_play.msg import SoundRequest 53 | from sound_play.libsoundplay import SoundClient 54 | 55 | rospy.init_node('play', anonymous=True) 56 | soundhandle = SoundClient() 57 | 58 | volume = float(argv[3]) if len(argv) == 4 else 1.0 59 | 60 | rospy.sleep(1) 61 | rospy.loginfo('Playing "%s" from pkg "%s".' % (argv[2], argv[1])) 62 | soundhandle.playWaveFromPkg(argv[1], argv[2], volume) 63 | rospy.sleep(1) 64 | -------------------------------------------------------------------------------- /sound_play/scripts/say.py: -------------------------------------------------------------------------------- 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 | 40 | import sys 41 | 42 | if __name__ == '__main__': 43 | import rospy 44 | argv = rospy.myargv() 45 | if len(argv) > 1 and argv[1] == '--help': 46 | print('Usage: %s \'String to say.\'' % argv[0]) 47 | print(' %s < file_to_say.txt' % argv[0]) 48 | print() 49 | print('Says a string. For a string on the command line, you must use quotes as') 50 | print('appropriate. For a string on standard input, the command will wait for') 51 | print('EOF before saying anything.') 52 | exit(-1) 53 | 54 | # Import after printing usage for speed. 55 | from sound_play.msg import SoundRequest 56 | from sound_play.libsoundplay import SoundClient 57 | 58 | if len(argv) == 1: 59 | print('Awaiting something to say on standard input.') 60 | 61 | # Ordered this way to minimize wait time. 62 | rospy.init_node('say', anonymous=True) 63 | soundhandle = SoundClient() 64 | rospy.sleep(1) 65 | 66 | voice = 'voice_kal_diphone' 67 | volume = 1.0 68 | 69 | if len(argv) == 1: 70 | s = sys.stdin.read() 71 | else: 72 | s = argv[1] 73 | 74 | if len(argv) > 2: 75 | voice = argv[2] 76 | if len(argv) > 3: 77 | volume = float(argv[3]) 78 | 79 | rospy.loginfo('Saying: %s' % s) 80 | rospy.loginfo('Voice: %s' % voice) 81 | rospy.loginfo('Volume: %s' % volume) 82 | 83 | soundhandle.say(s, voice, volume) 84 | rospy.sleep(1) 85 | -------------------------------------------------------------------------------- /sound_play/scripts/shutup.py: -------------------------------------------------------------------------------- 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 | import rospy 40 | from sound_play.msg import SoundRequest 41 | from sound_play.libsoundplay import SoundClient 42 | 43 | if __name__ == '__main__': 44 | rospy.init_node('shutup', anonymous=True) 45 | 46 | soundhandle = SoundClient() 47 | rospy.sleep(0.5) # let ROS get started... 48 | 49 | rospy.loginfo("Sending stopAll commande every 100 ms.") 50 | rospy.loginfo("Note: This will not prevent a node that is continuing to issue commands") 51 | rospy.loginfo("from producing sound.") 52 | rospy.loginfo("Press Ctrl+C to exit.") 53 | 54 | while not rospy.is_shutdown(): 55 | soundhandle.stopAll() 56 | try: 57 | rospy.sleep(.1) 58 | except: 59 | pass 60 | -------------------------------------------------------------------------------- /sound_play/scripts/soundclient_example.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | """ 4 | Simple example showing how to use the SoundClient provided by libsoundplay, 5 | in blocking, non-blocking, and explicit usage. 6 | """ 7 | 8 | import rospy 9 | from sound_play.libsoundplay import SoundClient 10 | from sound_play.msg import SoundRequest 11 | 12 | 13 | def play_explicit(): 14 | rospy.loginfo('Example: SoundClient play methods can take in an explicit' 15 | ' blocking parameter') 16 | soundhandle = SoundClient() # blocking = False by default 17 | rospy.sleep(0.5) # Ensure publisher connection is successful. 18 | 19 | sound_beep = soundhandle.waveSound("say-beep.wav", volume=0.5) 20 | # Play the same sound twice, once blocking and once not. The first call is 21 | # blocking (explicitly specified). 22 | sound_beep.play(blocking=True) 23 | # This call is not blocking (uses the SoundClient's setting). 24 | sound_beep.play() 25 | rospy.sleep(0.5) # Let sound complete. 26 | 27 | # Play a blocking sound. 28 | soundhandle.play(SoundRequest.NEEDS_UNPLUGGING, blocking=True) 29 | 30 | # Create a new SoundClient where the default behavior *is* to block. 31 | soundhandle = SoundClient(blocking=True) 32 | soundhandle.say('Say-ing stuff while block-ing') 33 | soundhandle.say('Say-ing stuff without block-ing', blocking=False) 34 | rospy.sleep(1) 35 | 36 | 37 | def play_blocking(): 38 | """ 39 | Play various sounds, blocking until each is completed before going to the 40 | next. 41 | """ 42 | rospy.loginfo('Example: Playing sounds in *blocking* mode.') 43 | soundhandle = SoundClient(blocking=True) 44 | 45 | rospy.loginfo('Playing say-beep at full volume.') 46 | soundhandle.playWave('say-beep.wav') 47 | 48 | rospy.loginfo('Playing say-beep at volume 0.3.') 49 | soundhandle.playWave('say-beep.wav', volume=0.3) 50 | 51 | rospy.loginfo('Playing sound for NEEDS_PLUGGING.') 52 | soundhandle.play(SoundRequest.NEEDS_PLUGGING) 53 | 54 | rospy.loginfo('Speaking some long string.') 55 | soundhandle.say('It was the best of times, it was the worst of times.') 56 | 57 | 58 | def play_nonblocking(): 59 | """ 60 | Play the same sounds with manual pauses between them. 61 | """ 62 | rospy.loginfo('Example: Playing sounds in *non-blocking* mode.') 63 | # NOTE: you must sleep at the beginning to let the SoundClient publisher 64 | # establish a connection to the soundplay_node. 65 | soundhandle = SoundClient(blocking=False) 66 | rospy.sleep(1) 67 | 68 | # In the non-blocking version you need to sleep between calls. 69 | rospy.loginfo('Playing say-beep at full volume.') 70 | soundhandle.playWave('say-beep.wav') 71 | rospy.sleep(1) 72 | 73 | rospy.loginfo('Playing say-beep at volume 0.3.') 74 | soundhandle.playWave('say-beep.wav', volume=0.3) 75 | rospy.sleep(1) 76 | 77 | rospy.loginfo('Playing sound for NEEDS_PLUGGING.') 78 | soundhandle.play(SoundRequest.NEEDS_PLUGGING) 79 | rospy.sleep(1) 80 | 81 | rospy.loginfo('Speaking some long string.') 82 | soundhandle.say('It was the best of times, it was the worst of times.') 83 | # Note we will return before the string has finished playing. 84 | 85 | 86 | if __name__ == '__main__': 87 | rospy.init_node('soundclient_example', anonymous=False) 88 | play_explicit() 89 | play_blocking() 90 | play_nonblocking() 91 | rospy.loginfo('Finished') 92 | -------------------------------------------------------------------------------- /sound_play/scripts/soundplay_node.py: -------------------------------------------------------------------------------- 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 | import os 38 | import sys 39 | import threading 40 | import traceback 41 | import yaml 42 | 43 | import actionlib 44 | import roslib 45 | import rospkg 46 | import rospy 47 | 48 | from diagnostic_msgs.msg import DiagnosticArray 49 | from diagnostic_msgs.msg import DiagnosticStatus 50 | from diagnostic_msgs.msg import KeyValue 51 | from sound_play.msg import SoundRequest 52 | from sound_play.msg import SoundRequestAction 53 | from sound_play.msg import SoundRequestFeedback 54 | from sound_play.msg import SoundRequestResult 55 | from sound_play.sound_type import SoundType 56 | 57 | try: 58 | import gi 59 | gi.require_version('Gst', '1.0') 60 | from gi.repository import GObject as GObject 61 | from gi.repository import Gst as Gst 62 | except Exception: 63 | str = """ 64 | ************************************************************** 65 | Error opening pygst. Is gstreamer installed? 66 | ************************************************************** 67 | """ 68 | rospy.logfatal(str) 69 | # print str 70 | exit(1) 71 | 72 | 73 | class SoundPlayNode(object): 74 | _feedback = SoundRequestFeedback() 75 | _result = SoundRequestResult() 76 | 77 | def stopdict(self, dict): 78 | for sound in dict.values(): 79 | sound.stop() 80 | 81 | def stopall(self): 82 | self.stopdict(self.builtinsounds) 83 | self.stopdict(self.filesounds) 84 | self.stopdict(self.voicesounds) 85 | 86 | def select_sound(self, data): 87 | if data.sound == SoundRequest.PLAY_FILE: 88 | if not data.arg2: 89 | if data.arg not in self.filesounds.keys(): 90 | rospy.logdebug( 91 | 'command for uncached wave: "%s"' % data.arg) 92 | try: 93 | self.filesounds[data.arg] = SoundType( 94 | data.arg, self.device, data.volume) 95 | except Exception: 96 | rospy.logerr( 97 | 'Error setting up to play "%s".' 98 | 'Does this file exist on the machine' 99 | 'on which sound_play is running?' % data.arg) 100 | return 101 | else: 102 | rospy.logdebug('command for cached wave: "%s"' % data.arg) 103 | filesound = self.filesounds[data.arg] 104 | if filesound.sound.get_property('volume') != data.volume: 105 | rospy.logdebug( 106 | 'volume for cached wave has changed,' 107 | 'resetting volume') 108 | filesound.sound.set_property('volume', data.volume) 109 | sound = self.filesounds[data.arg] 110 | else: 111 | absfilename = os.path.join( 112 | roslib.packages.get_pkg_dir(data.arg2), data.arg) 113 | if absfilename not in self.filesounds.keys(): 114 | rospy.logdebug( 115 | 'command for uncached wave: "%s"' % absfilename) 116 | try: 117 | self.filesounds[absfilename] = SoundType( 118 | absfilename, self.device, data.volume) 119 | except Exception: 120 | rospy.logerr( 121 | 'Error setting up to play "%s" from package "%s".' 122 | 'Does this file exist on the machine ' 123 | 'on which sound_play is running?' 124 | % (data.arg, data.arg2)) 125 | return 126 | else: 127 | rospy.logdebug( 128 | 'command for cached wave: "%s"' % absfilename) 129 | filesound = self.filesounds[absfilename] 130 | if filesound.sound.get_property('volume') != data.volume: 131 | rospy.logdebug( 132 | 'volume for cached wave has changed,' 133 | 'resetting volume') 134 | filesound.sound.set_property('volume', data.volume) 135 | sound = self.filesounds[absfilename] 136 | elif data.sound == SoundRequest.SAY: 137 | voice_key = data.arg + '---' + data.arg2 138 | if voice_key not in self.voicesounds.keys(): 139 | rospy.logdebug('command for uncached text: "%s"' % voice_key) 140 | if self.plugin is None: 141 | rospy.logerr( 142 | 'Plugin is not found {}.'.format(self.plugin_name)) 143 | else: 144 | if data.arg2 == '': 145 | voice = self.default_voice 146 | else: 147 | voice = data.arg2 148 | wavfilename = self.plugin.sound_play_say_plugin( 149 | data.arg, voice) 150 | if wavfilename is None: 151 | rospy.logerr('Failed to generate wavfile.') 152 | else: 153 | self.voicesounds[voice_key] = SoundType( 154 | wavfilename, self.device, data.volume) 155 | else: 156 | rospy.logdebug('command for cached text: "%s"' % voice_key) 157 | voicesound = self.voicesounds[voice_key] 158 | if voicesound.sound.get_property('volume') != data.volume: 159 | rospy.logdebug( 160 | 'volume for cached text has changed, resetting volume') 161 | voicesound.sound.set_property('volume', data.volume) 162 | sound = self.voicesounds[voice_key] 163 | else: 164 | rospy.logdebug('command for builtin wave: %i' % data.sound) 165 | if ((data.sound in self.builtinsounds and 166 | data.volume != self.builtinsounds[data.sound].volume) 167 | or data.sound not in self.builtinsounds): 168 | params = self.builtinsoundparams[data.sound] 169 | volume = data.volume 170 | # use the second param as a scaling for the input volume 171 | if params[1] != 1: 172 | volume = (volume + params[1])/2 173 | self.builtinsounds[data.sound] = SoundType( 174 | params[0], self.device, volume) 175 | sound = self.builtinsounds[data.sound] 176 | if sound.staleness != 0 and data.command != SoundRequest.PLAY_STOP: 177 | # This sound isn't counted in active_sounds 178 | rospy.logdebug("activating %i %s" % (data.sound, data.arg)) 179 | self.active_sounds = self.active_sounds + 1 180 | sound.staleness = 0 181 | return sound 182 | 183 | def callback(self, data): 184 | if not self.initialized: 185 | return 186 | self.mutex.acquire() 187 | try: 188 | if (data.sound == SoundRequest.ALL 189 | and data.command == SoundRequest.PLAY_STOP): 190 | self.stopall() 191 | else: 192 | sound = self.select_sound(data) 193 | sound.command(data.command) 194 | except Exception as e: 195 | rospy.logerr('Exception in callback: %s' % str(e)) 196 | rospy.loginfo(traceback.format_exc()) 197 | finally: 198 | self.mutex.release() 199 | rospy.logdebug("done callback") 200 | 201 | # Purge sounds that haven't been played in a while. 202 | def cleanupdict(self, dict): 203 | purgelist = [] 204 | for key, sound in iter(dict.items()): 205 | try: 206 | staleness = sound.get_staleness() 207 | except Exception as e: 208 | rospy.logerr( 209 | 'Exception in cleanupdict for sound (%s): %s' 210 | % (str(key), str(e))) 211 | # Something is wrong. Let's purge and try again. 212 | staleness = 100 213 | # print "%s %i"%(key, staleness) 214 | if staleness >= 10: 215 | purgelist.append(key) 216 | # Sound is playing 217 | if staleness == 0: 218 | self.active_sounds = self.active_sounds + 1 219 | for key in purgelist: 220 | rospy.logdebug('Purging %s from cache' % key) 221 | # clean up resources 222 | dict[key].dispose() 223 | del dict[key] 224 | 225 | def cleanup(self): 226 | self.mutex.acquire() 227 | try: 228 | self.active_sounds = 0 229 | self.cleanupdict(self.filesounds) 230 | self.cleanupdict(self.voicesounds) 231 | self.cleanupdict(self.builtinsounds) 232 | except Exception: 233 | rospy.loginfo( 234 | 'Exception in cleanup: %s' % sys.exc_info()[0]) 235 | finally: 236 | self.mutex.release() 237 | 238 | def diagnostics(self, state): 239 | try: 240 | da = DiagnosticArray() 241 | ds = DiagnosticStatus() 242 | ds.name = rospy.get_caller_id().lstrip('/') + ": Node State" 243 | if state == 0: 244 | ds.level = DiagnosticStatus.OK 245 | ds.message = "%i sounds playing" % self.active_sounds 246 | ds.values.append( 247 | KeyValue("Active sounds", str(self.active_sounds))) 248 | ds.values.append( 249 | KeyValue( 250 | "Allocated sound channels", 251 | str(self.num_channels))) 252 | ds.values.append( 253 | KeyValue( 254 | "Buffered builtin sounds", 255 | str(len(self.builtinsounds)))) 256 | ds.values.append( 257 | KeyValue( 258 | "Buffered wave sounds", 259 | str(len(self.filesounds)))) 260 | ds.values.append( 261 | KeyValue( 262 | "Buffered voice sounds", 263 | str(len(self.voicesounds)))) 264 | elif state == 1: 265 | ds.level = DiagnosticStatus.WARN 266 | ds.message = "Sound device not open yet." 267 | else: 268 | ds.level = DiagnosticStatus.ERROR 269 | ds.message = "Can't open sound device." +\ 270 | "See http://wiki.ros.org/sound_play/Troubleshooting" 271 | da.status.append(ds) 272 | da.header.stamp = rospy.get_rostime() 273 | self.diagnostic_pub.publish(da) 274 | except Exception as e: 275 | rospy.loginfo('Exception in diagnostics: %s' % str(e)) 276 | 277 | def execute_cb(self, data): 278 | data = data.sound_request 279 | if not self.initialized: 280 | rospy.logerr('soundplay_node is not initialized yet.') 281 | self._as.set_aborted() 282 | return 283 | self.mutex.acquire() 284 | # Force only one sound at a time 285 | self.stopall() 286 | try: 287 | if (data.sound == SoundRequest.ALL 288 | and data.command == SoundRequest.PLAY_STOP): 289 | self.stopall() 290 | else: 291 | sound = self.select_sound(data) 292 | sound.command(data.command) 293 | 294 | r = rospy.Rate(self.loop_rate) 295 | start_time = rospy.get_rostime() 296 | success = True 297 | while sound.get_playing(): 298 | sound.update() 299 | if self._as.is_preempt_requested(): 300 | rospy.loginfo('sound_play action: Preempted') 301 | sound.stop() 302 | self._as.set_preempted() 303 | success = False 304 | break 305 | 306 | self._feedback.playing = sound.get_playing() 307 | self._feedback.stamp = rospy.get_rostime() - start_time 308 | self._as.publish_feedback(self._feedback) 309 | r.sleep() 310 | 311 | if success: 312 | self._result.playing = self._feedback.playing 313 | self._result.stamp = self._feedback.stamp 314 | rospy.loginfo('sound_play action: Succeeded') 315 | self._as.set_succeeded(self._result) 316 | 317 | except Exception as e: 318 | self._as.set_aborted() 319 | rospy.logerr( 320 | 'Exception in actionlib callback: %s' % str(e)) 321 | rospy.loginfo(traceback.format_exc()) 322 | finally: 323 | self.mutex.release() 324 | rospy.logdebug("done actionlib callback") 325 | 326 | def __init__(self): 327 | Gst.init(None) 328 | 329 | # Start gobject thread to receive gstreamer messages 330 | GObject.threads_init() 331 | self.g_loop = threading.Thread(target=GObject.MainLoop().run) 332 | self.g_loop.daemon = True 333 | self.g_loop.start() 334 | 335 | rospy.init_node('sound_play') 336 | self.loop_rate = rospy.get_param('~loop_rate', 100) 337 | self.device = rospy.get_param("~device", "default") 338 | self.default_voice = rospy.get_param('~default_voice', None) 339 | self.plugin_name = rospy.get_param( 340 | '~plugin', 'sound_play/festival_plugin') 341 | self.diagnostic_pub = rospy.Publisher( 342 | "/diagnostics", DiagnosticArray, queue_size=1) 343 | rootdir = os.path.join( 344 | roslib.packages.get_pkg_dir('sound_play'), 'sounds') 345 | 346 | # load plugin 347 | rospack = rospkg.RosPack() 348 | depend_pkgs = rospack.get_depends_on('sound_play', implicit=False) 349 | depend_pkgs = ['sound_play'] + depend_pkgs 350 | rospy.loginfo("Loading from plugin definitions") 351 | plugin_yamls = [] 352 | for depend_pkg in depend_pkgs: 353 | manifest = rospack.get_manifest(depend_pkg) 354 | plugin_yaml = manifest.get_export('sound_play', 'plugin') 355 | if len(plugin_yaml) != 0: 356 | plugin_yamls += plugin_yaml 357 | for plugin_y in plugin_yaml: 358 | rospy.logdebug('Loading plugin in {}'.format(plugin_y)) 359 | plugin_dict = {} 360 | for plugin_yaml in plugin_yamls: 361 | if not os.path.exists(plugin_yaml): 362 | rospy.logerr( 363 | 'Failed to load plugin yaml: {}'.format(plugin_yaml)) 364 | rospy.logerr( 365 | 'Missing plugin yaml: {}'.format(plugin_yaml)) 366 | continue 367 | with open(plugin_yaml) as f: 368 | plugin_descs = yaml.safe_load(f) 369 | for plugin_desc in plugin_descs: 370 | plugin_dict[plugin_desc['name']] = plugin_desc['module'] 371 | 372 | self.plugin = None 373 | if self.plugin_name in plugin_dict.keys(): 374 | plugin_module = plugin_dict[self.plugin_name] 375 | mod = __import__(plugin_module.split('.')[0]) 376 | for sub_mod in plugin_module.split('.')[1:]: 377 | mod = getattr(mod, sub_mod) 378 | self.plugin = mod() 379 | 380 | self.builtinsoundparams = { 381 | SoundRequest.BACKINGUP: ( 382 | os.path.join(rootdir, 'BACKINGUP.ogg'), 0.1), 383 | SoundRequest.NEEDS_UNPLUGGING: ( 384 | os.path.join(rootdir, 'NEEDS_UNPLUGGING.ogg'), 1), 385 | SoundRequest.NEEDS_PLUGGING: ( 386 | os.path.join(rootdir, 'NEEDS_PLUGGING.ogg'), 1), 387 | SoundRequest.NEEDS_UNPLUGGING_BADLY: ( 388 | os.path.join(rootdir, 'NEEDS_UNPLUGGING_BADLY.ogg'), 1), 389 | SoundRequest.NEEDS_PLUGGING_BADLY: ( 390 | os.path.join(rootdir, 'NEEDS_PLUGGING_BADLY.ogg'), 1), 391 | } 392 | 393 | self.no_error = True 394 | self.initialized = False 395 | self.active_sounds = 0 396 | 397 | self.mutex = threading.Lock() 398 | self.sub = rospy.Subscriber("robotsound", SoundRequest, self.callback) 399 | self._as = actionlib.SimpleActionServer( 400 | 'sound_play', SoundRequestAction, 401 | execute_cb=self.execute_cb, auto_start=False) 402 | 403 | self.mutex.acquire() 404 | # For ros startup race condition 405 | self.sleep(0.5) 406 | self.diagnostics(1) 407 | 408 | while not rospy.is_shutdown(): 409 | while not rospy.is_shutdown(): 410 | self.init_vars() 411 | self.no_error = True 412 | self.initialized = True 413 | self.mutex.release() 414 | if not self._as.action_server.started: 415 | self._as.start() 416 | try: 417 | self.idle_loop() 418 | # Returns after inactive period to test device availability 419 | # print "Exiting idle" 420 | except Exception: 421 | rospy.loginfo( 422 | 'Exception in idle_loop: %s' % sys.exc_info()[0]) 423 | finally: 424 | self.mutex.acquire() 425 | 426 | self.diagnostics(2) 427 | self.mutex.release() 428 | 429 | def init_vars(self): 430 | self.num_channels = 10 431 | self.builtinsounds = {} 432 | self.filesounds = {} 433 | self.voicesounds = {} 434 | self.hotlist = [] 435 | if not self.initialized: 436 | rospy.loginfo('sound_play node is ready to play sound') 437 | 438 | def sleep(self, duration): 439 | try: 440 | rospy.sleep(duration) 441 | except rospy.exceptions.ROSInterruptException: 442 | pass 443 | 444 | def get_sound_length(self): 445 | sound_length = len(self.builtinsounds) +\ 446 | len(self.voicesounds) + len(self.filesounds) 447 | return sound_length 448 | 449 | def idle_loop(self): 450 | self.last_activity_time = rospy.get_time() 451 | while (not rospy.is_shutdown() 452 | and (rospy.get_time() - self.last_activity_time < 10 453 | or self.get_sound_length() > 0)): 454 | # print("idle_loop") 455 | self.diagnostics(0) 456 | self.sleep(1) 457 | self.cleanup() 458 | # print("idle_exiting") 459 | 460 | 461 | if __name__ == '__main__': 462 | SoundPlayNode() 463 | -------------------------------------------------------------------------------- /sound_play/scripts/test.py: -------------------------------------------------------------------------------- 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 | import rospy, os, sys 40 | from sound_play.msg import SoundRequest 41 | 42 | from sound_play.libsoundplay import SoundClient 43 | 44 | def sleep(t): 45 | try: 46 | rospy.sleep(t) 47 | except: 48 | pass 49 | 50 | if __name__ == '__main__': 51 | rospy.init_node('soundplay_test', anonymous = True) 52 | soundhandle = SoundClient() 53 | 54 | rospy.sleep(1) 55 | 56 | soundhandle.stopAll() 57 | 58 | rospy.loginfo("This script will run continuously until you hit CTRL+C, testing various sound_node sound types.") 59 | 60 | #print 'Try to play wave files that do not exist.' 61 | #soundhandle.playWave('17') 62 | #soundhandle.playWave('dummy') 63 | 64 | # print 'say' 65 | # soundhandle.say('Hello world!') 66 | # sleep(3) 67 | 68 | rospy.loginfo('wave') 69 | soundhandle.playWave('say-beep.wav') 70 | sleep(2) 71 | 72 | rospy.loginfo('quiet wave') 73 | soundhandle.playWave('say-beep.wav', 0.3) 74 | sleep(2) 75 | 76 | rospy.loginfo('plugging') 77 | soundhandle.play(SoundRequest.NEEDS_PLUGGING) 78 | sleep(2) 79 | 80 | rospy.loginfo('quiet plugging') 81 | soundhandle.play(SoundRequest.NEEDS_PLUGGING, 0.3) 82 | sleep(2) 83 | 84 | rospy.loginfo('unplugging') 85 | soundhandle.play(SoundRequest.NEEDS_UNPLUGGING) 86 | sleep(2) 87 | 88 | rospy.loginfo('plugging badly') 89 | soundhandle.play(SoundRequest.NEEDS_PLUGGING_BADLY) 90 | sleep(2) 91 | 92 | rospy.loginfo('unplugging badly') 93 | soundhandle.play(SoundRequest.NEEDS_UNPLUGGING_BADLY) 94 | sleep(2) 95 | 96 | s1 = soundhandle.builtinSound(SoundRequest.NEEDS_UNPLUGGING_BADLY) 97 | s2 = soundhandle.waveSound("say-beep.wav") 98 | s3 = soundhandle.voiceSound("Testing the new A P I") 99 | s4 = soundhandle.builtinSound(SoundRequest.NEEDS_UNPLUGGING_BADLY, 0.3) 100 | s5 = soundhandle.waveSound("say-beep.wav", 0.3) 101 | s6 = soundhandle.voiceSound("Testing the new A P I", 0.3) 102 | 103 | rospy.loginfo("New API start voice") 104 | s3.repeat() 105 | sleep(3) 106 | 107 | rospy.loginfo("New API start voice quiet") 108 | s6.play() 109 | sleep(3) 110 | 111 | rospy.loginfo("New API wave") 112 | s2.repeat() 113 | sleep(2) 114 | 115 | rospy.loginfo("New API wave quiet") 116 | s5.play() 117 | sleep(2) 118 | 119 | rospy.loginfo("New API builtin") 120 | s1.play() 121 | sleep(2) 122 | 123 | rospy.loginfo("New API builtin quiet") 124 | s4.play() 125 | sleep(2) 126 | 127 | rospy.loginfo("New API stop") 128 | s3.stop() 129 | -------------------------------------------------------------------------------- /sound_play/scripts/test/test_sound_client.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import unittest 4 | 5 | import rospy 6 | import rostest 7 | from sound_play.libsoundplay import SoundClient 8 | 9 | class TestCase(unittest.TestCase): 10 | def test_soundclient_constructor(self): 11 | s = SoundClient() 12 | self.assertIsNotNone(s) 13 | 14 | if __name__ == '__main__': 15 | rostest.rosrun('sound_play', 'test_sound_client', TestCase) 16 | 17 | __author__ = 'Felix Duvallet' 18 | -------------------------------------------------------------------------------- /sound_play/scripts/test_actionlib_client.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | 3 | import roslib; roslib.load_manifest('sound_play') 4 | import rospy 5 | import actionlib 6 | from sound_play.msg import SoundRequest, SoundRequestAction, SoundRequestGoal 7 | 8 | import os 9 | 10 | def sound_play_client(volume=1.0): 11 | client = actionlib.SimpleActionClient('sound_play', SoundRequestAction) 12 | 13 | client.wait_for_server() 14 | 15 | rospy.loginfo("Need Unplugging") 16 | goal = SoundRequestGoal() 17 | goal.sound_request.sound = SoundRequest.NEEDS_UNPLUGGING 18 | goal.sound_request.command = SoundRequest.PLAY_ONCE 19 | goal.sound_request.volume = volume 20 | 21 | client.send_goal(goal) 22 | client.wait_for_result() 23 | # print client.get_result() 24 | rospy.loginfo("End Need Unplugging") 25 | 26 | rospy.loginfo("Need Plugging") 27 | goal = SoundRequestGoal() 28 | goal.sound_request.sound = SoundRequest.NEEDS_PLUGGING 29 | goal.sound_request.command = SoundRequest.PLAY_ONCE 30 | goal.sound_request.volume = volume 31 | client.send_goal(goal) 32 | client.wait_for_result() 33 | # print client.get_result() 34 | rospy.loginfo("End Need Plugging") 35 | 36 | rospy.loginfo("Say") 37 | goal = SoundRequestGoal() 38 | goal.sound_request.sound = SoundRequest.SAY 39 | goal.sound_request.command = SoundRequest.PLAY_ONCE 40 | goal.sound_request.arg = "Testing the actionlib interface A P I" 41 | goal.sound_request.volume = volume 42 | client.send_goal(goal) 43 | client.wait_for_result() 44 | # print client.get_result() 45 | rospy.loginfo("End Say") 46 | 47 | rospy.loginfo("Wav") 48 | goal = SoundRequestGoal() 49 | goal.sound_request.sound = SoundRequest.PLAY_FILE 50 | goal.sound_request.command = SoundRequest.PLAY_ONCE 51 | goal.sound_request.arg = os.path.join(roslib.packages.get_pkg_dir('sound_play'),'sounds') + "/say-beep.wav" 52 | goal.sound_request.volume = volume 53 | client.send_goal(goal) 54 | client.wait_for_result() 55 | # print client.get_result() 56 | rospy.loginfo("End wav") 57 | 58 | if __name__ == '__main__': 59 | rospy.init_node('soundplay_client_test') 60 | sound_play_client() 61 | -------------------------------------------------------------------------------- /sound_play/setup.py: -------------------------------------------------------------------------------- 1 | from catkin_pkg.python_setup import generate_distutils_setup 2 | from setuptools import setup 3 | 4 | d = generate_distutils_setup( 5 | packages=['sound_play'], 6 | package_dir={'': 'src'} 7 | ) 8 | 9 | setup(**d) 10 | -------------------------------------------------------------------------------- /sound_play/sound_play_plugin.yaml: -------------------------------------------------------------------------------- 1 | - name: sound_play/festival_plugin 2 | module: sound_play.festival_plugin.FestivalPlugin 3 | - name: sound_play/flite_plugin 4 | module: sound_play.flite_plugin.FlitePlugin 5 | -------------------------------------------------------------------------------- /sound_play/soundplay_node.launch: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /sound_play/sounds/BACKINGUP.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ros-drivers/audio_common/6a84996653eada328f6ea7b14e1804e9876aa3af/sound_play/sounds/BACKINGUP.ogg -------------------------------------------------------------------------------- /sound_play/sounds/NEEDS_PLUGGING.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ros-drivers/audio_common/6a84996653eada328f6ea7b14e1804e9876aa3af/sound_play/sounds/NEEDS_PLUGGING.ogg -------------------------------------------------------------------------------- /sound_play/sounds/NEEDS_PLUGGING_BADLY.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ros-drivers/audio_common/6a84996653eada328f6ea7b14e1804e9876aa3af/sound_play/sounds/NEEDS_PLUGGING_BADLY.ogg -------------------------------------------------------------------------------- /sound_play/sounds/NEEDS_UNPLUGGING.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ros-drivers/audio_common/6a84996653eada328f6ea7b14e1804e9876aa3af/sound_play/sounds/NEEDS_UNPLUGGING.ogg -------------------------------------------------------------------------------- /sound_play/sounds/NEEDS_UNPLUGGING_BADLY.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ros-drivers/audio_common/6a84996653eada328f6ea7b14e1804e9876aa3af/sound_play/sounds/NEEDS_UNPLUGGING_BADLY.ogg -------------------------------------------------------------------------------- /sound_play/sounds/say-beep.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ros-drivers/audio_common/6a84996653eada328f6ea7b14e1804e9876aa3af/sound_play/sounds/say-beep.wav -------------------------------------------------------------------------------- /sound_play/src/sound_play/__init__.py: -------------------------------------------------------------------------------- 1 | # Software License Agreement (BSD License) 2 | # 3 | # Copyright (c) 2013, Willow Garage, Inc. 4 | # All rights reserved. 5 | # 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions 8 | # are met: 9 | # 10 | # * Redistributions of source code must retain the above copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # * Redistributions in binary form must reproduce the above 13 | # copyright notice, this list of conditions and the following 14 | # disclaimer in the documentation and/or other materials provided 15 | # with the distribution. 16 | # * Neither the name of Willow Garage, Inc. nor the names of its 17 | # contributors may be used to endorse or promote products derived 18 | # from this software without specific prior written permission. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 23 | # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 24 | # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 25 | # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 26 | # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 27 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 28 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 | # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 30 | # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | # POSSIBILITY OF SUCH DAMAGE. 32 | 33 | from sound_play.festival_plugin import FestivalPlugin 34 | from sound_play.flite_plugin import FlitePlugin 35 | import sound_play.libsoundplay as libsoundplay 36 | from sound_play.sound_play_plugin import SoundPlayPlugin 37 | -------------------------------------------------------------------------------- /sound_play/src/sound_play/festival_plugin.py: -------------------------------------------------------------------------------- 1 | import os 2 | import tempfile 3 | 4 | import rospy 5 | 6 | from sound_play.sound_play_plugin import SoundPlayPlugin 7 | 8 | 9 | class FestivalPlugin(SoundPlayPlugin): 10 | 11 | _default_voice = 'voice_kal_diphone' 12 | 13 | def __init__(self): 14 | super(FestivalPlugin, self).__init__() 15 | 16 | def sound_play_say_plugin(self, text, voice): 17 | if voice is None or voice == '': 18 | voice = self._default_voice 19 | encoding = 'ISO-8859-15' 20 | if ':' in voice: 21 | voice, encoding = voice.split(':', maxsplit=1) 22 | txtfile = tempfile.NamedTemporaryFile( 23 | prefix='sound_play', suffix='.txt') 24 | (wavfile, wavfilename) = tempfile.mkstemp( 25 | prefix='sound_play', suffix='.wav') 26 | txtfilename = txtfile.name 27 | os.close(wavfile) 28 | try: 29 | try: 30 | if hasattr(text, 'decode'): 31 | txtfile.write( 32 | text.decode('UTF-8').encode(encoding)) 33 | else: 34 | txtfile.write( 35 | text.encode(encoding)) 36 | except UnicodeEncodeError: 37 | if hasattr(text, 'decode'): 38 | txtfile.write(text) 39 | else: 40 | txtfile.write(text.encode('UTF-8')) 41 | txtfile.flush() 42 | cmd = "text2wave -eval '({0})' {1} -o {2}".format( 43 | voice, txtfilename, wavfilename) 44 | os.system(cmd) 45 | try: 46 | if os.stat(wavfilename).st_size == 0: 47 | # So we hit the same catch block 48 | raise OSError 49 | except OSError: 50 | rospy.logerr( 51 | 'Sound synthesis failed.' 52 | 'Is festival installed?' 53 | 'Is a festival voice installed?' 54 | 'Try running "rosdep satisfy sound_play|sh".' 55 | 'Refer to http://wiki.ros.org/' 56 | 'sound_play/Troubleshooting' 57 | ) 58 | return None 59 | finally: 60 | txtfile.close() 61 | return wavfilename 62 | -------------------------------------------------------------------------------- /sound_play/src/sound_play/flite_plugin.py: -------------------------------------------------------------------------------- 1 | import os 2 | import tempfile 3 | 4 | import resource_retriever 5 | import rospkg 6 | import rospy 7 | 8 | from sound_play.sound_play_plugin import SoundPlayPlugin 9 | 10 | 11 | class FlitePlugin(SoundPlayPlugin): 12 | 13 | _default_voice = 'kal' 14 | 15 | def __init__(self): 16 | super(FlitePlugin, self).__init__() 17 | self._default_voice_path = None 18 | 19 | def get_default_voice_path(self): 20 | if self._default_voice_path is None: 21 | self._default_voice_path = os.path.join( 22 | rospkg.RosPack().get_path('sound_play'), 23 | 'resources/flitevox') 24 | return self._default_voice_path 25 | 26 | def sound_play_say_plugin(self, text, voice): 27 | if voice is None or voice == '': 28 | voice = self._default_voice 29 | if voice.endswith('.flitevox'): 30 | if voice.startswith('package://'): 31 | voice = resource_retriever.get(voice) 32 | elif voice.startswith('/'): 33 | voice = voice 34 | else: 35 | voice = os.path.join( 36 | self.get_default_voice_path(), voice) 37 | (wavfile, wavfilename) = tempfile.mkstemp( 38 | prefix='sound_play', suffix='.wav') 39 | os.close(wavfile) 40 | cmd = "flite -voice {0} -t \"{1}\" -o {2}".format( 41 | voice, text, wavfilename) 42 | os.system(cmd) 43 | try: 44 | if os.stat(wavfilename).st_size == 0: 45 | # So we hit the same catch block 46 | raise OSError 47 | except OSError: 48 | rospy.logerr( 49 | 'Sound synthesis failed.' 50 | 'Is flite installed?' 51 | 'Is a flite voice installed?' 52 | 'Try running "rosdep satisfy sound_play|sh".' 53 | 'Refer to http://wiki.ros.org/' 54 | 'sound_play/Troubleshooting' 55 | ) 56 | return None 57 | return wavfilename 58 | -------------------------------------------------------------------------------- /sound_play/src/sound_play/libsoundplay.py: -------------------------------------------------------------------------------- 1 | #*********************************************************** 2 | #* Software License Agreement (BSD License) 3 | #* 4 | #* Copyright (c) 2009, Willow Garage, Inc. 5 | #* All rights reserved. 6 | #* 7 | #* Redistribution and use in source and binary forms, with or without 8 | #* modification, are permitted provided that the following conditions 9 | #* are met: 10 | #* 11 | #* * Redistributions of source code must retain the above copyright 12 | #* notice, this list of conditions and the following disclaimer. 13 | #* * Redistributions in binary form must reproduce the above 14 | #* copyright notice, this list of conditions and the following 15 | #* disclaimer in the documentation and/or other materials provided 16 | #* with the distribution. 17 | #* * Neither the name of the Willow Garage nor the names of its 18 | #* contributors may be used to endorse or promote products derived 19 | #* from this software without specific prior written permission. 20 | #* 21 | #* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 | #* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 | #* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 24 | #* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 25 | #* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 | #* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 27 | #* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28 | #* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 29 | #* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 | #* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 31 | #* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 32 | #* POSSIBILITY OF SUCH DAMAGE. 33 | #*********************************************************** 34 | 35 | # Author: Blaise Gassend 36 | 37 | import rospy 38 | import roslib 39 | import actionlib 40 | import os, sys 41 | from actionlib_msgs.msg import GoalStatusArray 42 | from sound_play.msg import SoundRequest 43 | from sound_play.msg import SoundRequestGoal 44 | from sound_play.msg import SoundRequestAction 45 | 46 | ## \brief Class that publishes messages to the sound_play node. 47 | ## 48 | ## This class is a helper class for communicating with the sound_play node 49 | ## via the \ref sound_play.SoundRequest message. It has two ways of being used: 50 | ## 51 | ## - It can create Sound classes that represent a particular sound which 52 | ## can be played, repeated or stopped. 53 | ## 54 | ## - It provides methods for each way in which the sound_play.SoundRequest 55 | ## message can be invoked. 56 | 57 | class Sound(object): 58 | def __init__(self, client, snd, arg, volume=1.0): 59 | self.client = client 60 | self.snd = snd 61 | self.arg = arg 62 | self.vol = volume 63 | 64 | ## \brief Play the Sound. 65 | ## 66 | ## This method causes the Sound to be played once. 67 | 68 | def play(self, **kwargs): 69 | self.client.sendMsg(self.snd, SoundRequest.PLAY_ONCE, self.arg, 70 | vol=self.vol, **kwargs) 71 | 72 | ## \brief Play the Sound repeatedly. 73 | ## 74 | ## This method causes the Sound to be played repeatedly until stop() is 75 | ## called. 76 | 77 | def repeat(self, **kwargs): 78 | self.client.sendMsg(self.snd, SoundRequest.PLAY_START, self.arg, 79 | vol=self.vol, **kwargs) 80 | 81 | ## \brief Stop Sound playback. 82 | ## 83 | ## This method causes the Sound to stop playing. 84 | 85 | def stop(self): 86 | self.client.sendMsg(self.snd, SoundRequest.PLAY_STOP, self.arg) 87 | 88 | ## This class is a helper class for communicating with the sound_play node 89 | ## via the \ref sound_play.SoundRequest message. There is a one-to-one mapping 90 | ## between methods and invocations of the \ref sound_play.SoundRequest message. 91 | 92 | class SoundClient(object): 93 | 94 | def __init__(self, blocking=False, sound_action='sound_play', sound_topic='robotsound'): 95 | """ 96 | 97 | The SoundClient can send SoundRequests in two modes: non-blocking mode 98 | (by publishing a message to the soundplay_node directly) which will 99 | return as soon as the sound request has been sent, or blocking mode (by 100 | using the actionlib interface) which will wait until the sound has 101 | finished playing completely. 102 | 103 | The blocking parameter here is the standard behavior, but can be 104 | over-ridden. Each say/play/start/repeat method can take in an optional 105 | `blocking=True|False` argument that will over-ride the class-wide 106 | behavior. See soundclient_example.py for an example of this behavior. 107 | 108 | :param blocking: Used as the default behavior unless over-ridden, 109 | (default = false) 110 | 111 | :param sound_action: Namespace of actionlib to play sound. The actionlib interface is used 112 | only if blocking parameter is True. (default='sound_play') 113 | 114 | :param sound_topic: Topic name to play sound. The topic interface is used only if blocking 115 | parameter is False. (default='robotsound') 116 | """ 117 | 118 | self._blocking = blocking 119 | self._playing = False 120 | 121 | # NOTE: only one of these will be used at once, but we need to create 122 | # both the publisher and actionlib client here. 123 | self.actionclient = actionlib.SimpleActionClient( 124 | sound_action, SoundRequestAction) 125 | self.pub = rospy.Publisher(sound_topic, SoundRequest, queue_size=5) 126 | self.sub = rospy.Subscriber( 127 | '{}/status'.format(sound_action), GoalStatusArray, self._action_status_cb) 128 | 129 | ## \brief Create a voice Sound. 130 | ## 131 | ## Creates a Sound corresponding to saying the indicated text. 132 | ## 133 | ## \param s Text to say 134 | 135 | def voiceSound(self, s, volume=1.0): 136 | return Sound(self, SoundRequest.SAY, s, volume=volume) 137 | 138 | ## \brief Create a wave Sound. 139 | ## 140 | ## Creates a Sound corresponding to indicated file. 141 | ## 142 | ## \param s File to play. Should be an absolute path that exists on the 143 | ## machine running the sound_play node. 144 | def waveSound(self, sound, volume=1.0): 145 | if sound[0] != "/": 146 | rootdir = os.path.join(roslib.packages.get_pkg_dir('sound_play'),'sounds') 147 | sound = rootdir + "/" + sound 148 | return Sound(self, SoundRequest.PLAY_FILE, sound, volume=volume) 149 | 150 | ## \brief Create a builtin Sound. 151 | ## 152 | ## Creates a Sound corresponding to indicated builtin wave. 153 | ## 154 | ## \param id Identifier of the sound to play. 155 | 156 | def builtinSound(self, id, volume=1.0): 157 | return Sound(self, id, "", volume) 158 | 159 | ## \brief Say a string 160 | ## 161 | ## Send a string to be said by the sound_node. The vocalization can be 162 | ## stopped using stopSaying or stopAll. 163 | ## 164 | ## \param text String to say 165 | 166 | def say(self,text, voice='', volume=1.0, **kwargs): 167 | self.sendMsg(SoundRequest.SAY, SoundRequest.PLAY_ONCE, text, voice, 168 | volume, **kwargs) 169 | 170 | ## \brief Say a string repeatedly 171 | ## 172 | ## The string is said repeatedly until stopSaying or stopAll is used. 173 | ## 174 | ## \param text String to say repeatedly 175 | 176 | def repeat(self,text, volume=1.0, **kwargs): 177 | self.sendMsg(SoundRequest.SAY, SoundRequest.PLAY_START, text, 178 | vol=volume, **kwargs) 179 | 180 | ## \brief Stop saying a string 181 | ## 182 | ## Stops saying a string that was previously started by say or repeat. The 183 | ## argument indicates which string to stop saying. 184 | ## 185 | ## \param text Same string as in the say or repeat command 186 | 187 | def stopSaying(self,text): 188 | self.sendMsg(SoundRequest.SAY, SoundRequest.PLAY_STOP, text) 189 | 190 | ## \brief Plays a WAV or OGG file 191 | ## 192 | ## Plays a WAV or OGG file once. The playback can be stopped by stopWave or 193 | ## stopAll. 194 | ## 195 | ## \param sound Filename of the WAV or OGG file. Must be an absolute path valid 196 | ## on the computer on which the sound_play node is running 197 | 198 | def playWave(self, sound, volume=1.0, **kwargs): 199 | if sound[0] != "/": 200 | rootdir = os.path.join(roslib.packages.get_pkg_dir('sound_play'),'sounds') 201 | sound = rootdir + "/" + sound 202 | self.sendMsg(SoundRequest.PLAY_FILE, SoundRequest.PLAY_ONCE, sound, 203 | vol=volume, **kwargs) 204 | 205 | ## \brief Plays a WAV or OGG file repeatedly 206 | ## 207 | ## Plays a WAV or OGG file repeatedly until stopWave or stopAll is used. 208 | ## 209 | ## \param sound Filename of the WAV or OGG file. Must be an absolute path valid 210 | ## on the computer on which the sound_play node is running. 211 | 212 | def startWave(self, sound, volume=1.0, **kwargs): 213 | if sound[0] != "/": 214 | rootdir = os.path.join(roslib.packages.get_pkg_dir('sound_play'),'sounds') 215 | sound = rootdir + "/" + sound 216 | self.sendMsg(SoundRequest.PLAY_FILE, SoundRequest.PLAY_START, sound, 217 | vol=volume, **kwargs) 218 | 219 | ## \brief Stop playing a WAV or OGG file 220 | ## 221 | ## Stops playing a file that was previously started by playWave or 222 | ## startWave. 223 | ## 224 | ## \param sound Same string as in the playWave or startWave command 225 | 226 | def stopWave(self,sound): 227 | if sound[0] != "/": 228 | rootdir = os.path.join(roslib.package.get_pkg_dir('sound_play'),'sounds') 229 | sound = rootdir + "/" + sound 230 | self.sendMsg(SoundRequest.PLAY_FILE, SoundRequest.PLAY_STOP, sound) 231 | 232 | ## \brief Plays a WAV or OGG file 233 | ## 234 | ## Plays a WAV or OGG file once. The playback can be stopped by stopWaveFromPkg or 235 | ## stopAll. 236 | ## 237 | ## \param package Package name containing the sound file. 238 | ## \param sound Filename of the WAV or OGG file. Must be an path relative to the package valid 239 | ## on the computer on which the sound_play node is running 240 | 241 | def playWaveFromPkg(self, package, sound, volume=1.0, **kwargs): 242 | self.sendMsg(SoundRequest.PLAY_FILE, SoundRequest.PLAY_ONCE, sound, package, 243 | volume, **kwargs) 244 | 245 | ## \brief Plays a WAV or OGG file repeatedly 246 | ## 247 | ## Plays a WAV or OGG file repeatedly until stopWaveFromPkg or stopAll is used. 248 | ## 249 | ## \param package Package name containing the sound file. 250 | ## \param sound Filename of the WAV or OGG file. Must be an path relative to the package valid 251 | ## on the computer on which the sound_play node is running 252 | 253 | def startWaveFromPkg(self, package, sound, volume=1.0, **kwargs): 254 | self.sendMsg(SoundRequest.PLAY_FILE, SoundRequest.PLAY_START, sound, 255 | package, volume, **kwargs) 256 | 257 | ## \brief Stop playing a WAV or OGG file 258 | ## 259 | ## Stops playing a file that was previously started by playWaveFromPkg or 260 | ## startWaveFromPkg. 261 | ## 262 | ## \param package Package name containing the sound file. 263 | ## \param sound Filename of the WAV or OGG file. Must be an path relative to the package valid 264 | ## on the computer on which the sound_play node is running 265 | 266 | def stopWaveFromPkg(self,sound, package): 267 | self.sendMsg(SoundRequest.PLAY_FILE, SoundRequest.PLAY_STOP, sound, package) 268 | 269 | ## \brief Play a buildin sound 270 | ## 271 | ## Starts playing one of the built-in sounds. built-ing sounds are documented 272 | ## in \ref SoundRequest.msg. Playback can be stopped by stopall. 273 | ## 274 | ## \param sound Identifier of the sound to play. 275 | 276 | def play(self,sound, volume=1.0, **kwargs): 277 | self.sendMsg(sound, SoundRequest.PLAY_ONCE, "", vol=volume, **kwargs) 278 | 279 | ## \brief Play a buildin sound repeatedly 280 | ## 281 | ## Starts playing one of the built-in sounds repeatedly until stop or 282 | ## stopall is used. Built-in sounds are documented in \ref SoundRequest.msg. 283 | ## 284 | ## \param sound Identifier of the sound to play. 285 | 286 | def start(self,sound, volume=1.0, **kwargs): 287 | self.sendMsg(sound, SoundRequest.PLAY_START, "", vol=volume, **kwargs) 288 | 289 | ## \brief Stop playing a built-in sound 290 | ## 291 | ## Stops playing a built-in sound started with play or start. 292 | ## 293 | ## \param sound Same sound that was used to start playback 294 | 295 | def stop(self,sound): 296 | self.sendMsg(sound, SoundRequest.PLAY_STOP, "") 297 | 298 | ## \brief Stop all currently playing sounds 299 | ## 300 | ## This method stops all speech, wave file, and built-in sound playback. 301 | 302 | def stopAll(self): 303 | self.stop(SoundRequest.ALL) 304 | 305 | def sendMsg( 306 | self, snd, cmd, s, arg2="", vol=1.0, replace=True, 307 | server_timeout=None, result_timeout=None, 308 | **kwargs 309 | ): 310 | """ 311 | Internal method that publishes the sound request, either directly as a 312 | SoundRequest to the soundplay_node or through the actionlib interface 313 | (which blocks until the sound has finished playing). 314 | 315 | The blocking behavior is nominally the class-wide setting unless it has 316 | been explicitly specified in the play call. 317 | """ 318 | 319 | # Use the passed-in argument if it exists, otherwise fall back to the 320 | # class-wide setting. 321 | blocking = kwargs.get('blocking', self._blocking) 322 | 323 | msg = SoundRequest() 324 | msg.sound = snd 325 | # Threshold volume between 0 and 1. 326 | msg.volume = max(0, min(1, vol)) 327 | msg.command = cmd 328 | msg.arg = s 329 | msg.arg2 = arg2 330 | 331 | rospy.logdebug('Sending sound request with volume = {}' 332 | ' and blocking = {}'.format(msg.volume, blocking)) 333 | 334 | # Defensive check for the existence of the correct communicator. 335 | if not blocking and not self.pub: 336 | rospy.logerr('Publisher for SoundRequest must exist') 337 | return 338 | if blocking and not self.actionclient: 339 | rospy.logerr('Action client for SoundRequest does not exist.') 340 | return 341 | 342 | if not blocking: # Publish message directly and return immediately 343 | self.pub.publish(msg) 344 | if self.pub.get_num_connections() < 1: 345 | rospy.logwarn("Sound command issued, but no node is subscribed" 346 | " to the topic. Perhaps you forgot to run" 347 | " soundplay_node.py?") 348 | else: # Block until result comes back. 349 | assert self.actionclient, 'Actionclient must exist' 350 | rospy.logdebug('Sending action client sound request [blocking]') 351 | 352 | if server_timeout is None or server_timeout > rospy.Duration(0): 353 | if server_timeout is None: 354 | server_timeout = rospy.Duration() 355 | if not self.actionclient.wait_for_server(timeout=server_timeout): 356 | return 357 | 358 | goal = SoundRequestGoal() 359 | goal.sound_request = msg 360 | while not replace and self._playing: 361 | rospy.sleep(0.1) 362 | self.actionclient.send_goal(goal) 363 | if result_timeout is None or result_timeout > rospy.Duration(0): 364 | if result_timeout is None: 365 | result_timeout = rospy.Duration() 366 | if self.actionclient.wait_for_result(timeout=result_timeout): 367 | rospy.logdebug('sound request response received') 368 | return 369 | 370 | def _action_status_cb(self, msg): 371 | if 1 in [s.status for s in msg.status_list]: 372 | self._playing = True 373 | else: 374 | self._playing = False 375 | -------------------------------------------------------------------------------- /sound_play/src/sound_play/sound_play_plugin.py: -------------------------------------------------------------------------------- 1 | class SoundPlayPlugin(object): 2 | 3 | """Base class for sound_play plugin 4 | 5 | This is a base class for sound_play plugin. 6 | sound_play plugin has one method; sound_play_say_plugin. 7 | sound_play_start_plugin run when command is SAY. 8 | 9 | sound_play plugin is defined in yaml format as below; 10 | - name: sound_play/festival_plugin # plugin name 11 | module: sound_play.festival_plugin.FestivalPlugin 12 | # plugin module name 13 | 14 | Also, sound_play plugin yaml file is exported in package.xml as below; 15 | 16 | 17 | 18 | """ 19 | 20 | def __init__(self): 21 | pass 22 | 23 | def sound_play_say_plugin(self, text, voice): 24 | """Start plugin for sound_play 25 | 26 | Args: 27 | text (string): speech text 28 | voice (string): speech voice 29 | """ 30 | return None 31 | -------------------------------------------------------------------------------- /sound_play/src/sound_play/sound_type.py: -------------------------------------------------------------------------------- 1 | import os 2 | import threading 3 | 4 | import rospy 5 | 6 | from sound_play.msg import SoundRequest 7 | 8 | try: 9 | import gi 10 | gi.require_version('Gst', '1.0') 11 | from gi.repository import Gst as Gst 12 | except Exception: 13 | str = """ 14 | ************************************************************** 15 | Error opening pygst. Is gstreamer installed? 16 | ************************************************************** 17 | """ 18 | rospy.logfatal(str) 19 | # print str 20 | exit(1) 21 | 22 | 23 | class SoundType(object): 24 | STOPPED = 0 25 | LOOPING = 1 26 | COUNTING = 2 27 | 28 | def __init__(self, file, device, volume=1.0): 29 | self.lock = threading.RLock() 30 | self.state = self.STOPPED 31 | self.sound = Gst.ElementFactory.make("playbin", None) 32 | if self.sound is None: 33 | raise Exception("Could not create sound player") 34 | 35 | if device: 36 | self.sink = Gst.ElementFactory.make("alsasink", "sink") 37 | self.sink.set_property("device", device) 38 | self.sound.set_property("audio-sink", self.sink) 39 | 40 | if (":" in file): 41 | uri = file 42 | elif os.path.isfile(file): 43 | uri = "file://" + os.path.abspath(file) 44 | else: 45 | rospy.logerr('Error: URI is invalid: %s' % file) 46 | 47 | self.uri = uri 48 | self.volume = volume 49 | self.sound.set_property('uri', uri) 50 | self.sound.set_property("volume", volume) 51 | self.staleness = 1 52 | self.file = file 53 | 54 | self.bus = self.sound.get_bus() 55 | self.bus.add_signal_watch() 56 | self.bus_conn_id = self.bus.connect("message", self.on_stream_end) 57 | 58 | def on_stream_end(self, bus, message): 59 | if message.type == Gst.MessageType.EOS: 60 | if (self.state == self.LOOPING): 61 | self.sound.seek_simple(Gst.Format.TIME, Gst.SeekFlags.FLUSH, 0) 62 | else: 63 | self.stop() 64 | 65 | def __del__(self): 66 | # stop our GST object so that it gets garbage-collected 67 | self.dispose() 68 | 69 | def update(self): 70 | if self.bus is not None: 71 | self.bus.poll(Gst.MessageType.ERROR, 10) 72 | 73 | def loop(self): 74 | self.lock.acquire() 75 | try: 76 | self.staleness = 0 77 | if self.state == self.COUNTING: 78 | self.stop() 79 | 80 | if self.state == self.STOPPED: 81 | self.sound.seek_simple(Gst.Format.TIME, Gst.SeekFlags.FLUSH, 0) 82 | self.sound.set_state(Gst.State.PLAYING) 83 | self.state = self.LOOPING 84 | finally: 85 | self.lock.release() 86 | 87 | def dispose(self): 88 | self.lock.acquire() 89 | try: 90 | if self.bus is not None: 91 | self.sound.set_state(Gst.State.NULL) 92 | self.bus.disconnect(self.bus_conn_id) 93 | self.bus.remove_signal_watch() 94 | self.bus = None 95 | self.sound = None 96 | self.sink = None 97 | self.state = self.STOPPED 98 | except Exception as e: 99 | rospy.logerr('Exception in dispose: %s' % str(e)) 100 | finally: 101 | self.lock.release() 102 | 103 | def stop(self): 104 | if self.state != self.STOPPED: 105 | self.lock.acquire() 106 | try: 107 | self.sound.set_state(Gst.State.NULL) 108 | self.state = self.STOPPED 109 | finally: 110 | self.lock.release() 111 | 112 | def single(self): 113 | self.lock.acquire() 114 | try: 115 | rospy.logdebug("Playing %s" % self.uri) 116 | self.staleness = 0 117 | if self.state == self.LOOPING: 118 | self.stop() 119 | 120 | self.sound.seek_simple(Gst.Format.TIME, Gst.SeekFlags.FLUSH, 0) 121 | self.sound.set_state(Gst.State.PLAYING) 122 | self.state = self.COUNTING 123 | finally: 124 | self.lock.release() 125 | 126 | def command(self, cmd): 127 | if cmd == SoundRequest.PLAY_STOP: 128 | self.stop() 129 | elif cmd == SoundRequest.PLAY_ONCE: 130 | self.single() 131 | elif cmd == SoundRequest.PLAY_START: 132 | self.loop() 133 | 134 | def get_staleness(self): 135 | self.lock.acquire() 136 | position = 0 137 | duration = 0 138 | try: 139 | position = self.sound.query_position(Gst.Format.TIME)[1] 140 | duration = self.sound.query_duration(Gst.Format.TIME)[1] 141 | except Exception: 142 | position = 0 143 | duration = 0 144 | finally: 145 | self.lock.release() 146 | 147 | if position != duration: 148 | self.staleness = 0 149 | else: 150 | self.staleness = self.staleness + 1 151 | return self.staleness 152 | 153 | def get_playing(self): 154 | return self.state == self.COUNTING 155 | -------------------------------------------------------------------------------- /sound_play/test.launch: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /sound_play/test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(test_sound_play test.cpp) 2 | target_link_libraries(test_sound_play ${catkin_LIBRARIES}) 3 | add_dependencies(test_sound_play sound_play_gencpp) 4 | set_target_properties(test_sound_play PROPERTIES OUTPUT_NAME test) 5 | -------------------------------------------------------------------------------- /sound_play/test/test.cpp: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * Software License Agreement (BSD License) 3 | * 4 | * Copyright (c) 2009, Willow Garage, Inc. 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions 9 | * are met: 10 | * 11 | * * Redistributions of source code must retain the above copyright 12 | * notice, this list of conditions and the following disclaimer. 13 | * * Redistributions in binary form must reproduce the above 14 | * copyright notice, this list of conditions and the following 15 | * disclaimer in the documentation and/or other materials provided 16 | * with the distribution. 17 | * * Neither the name of the Willow Garage nor the names of its 18 | * contributors may be used to endorse or promote products derived 19 | * from this software without specific prior written permission. 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 24 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 25 | * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 27 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 29 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 31 | * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 32 | * POSSIBILITY OF SUCH DAMAGE. 33 | *********************************************************************/ 34 | 35 | #include 36 | #include 37 | 38 | void sleepok(int t, ros::NodeHandle &nh) 39 | { 40 | if (nh.ok()) 41 | sleep(t); 42 | } 43 | 44 | int main(int argc, char **argv) 45 | { 46 | ros::init(argc, argv, "sound_play_test"); 47 | 48 | ros::NodeHandle nh; 49 | sound_play::SoundClient sc; 50 | sound_play::SoundClient quiet_sc; 51 | 52 | sleepok(1, nh); 53 | 54 | while(nh.ok()) 55 | { 56 | sc.say("Hello world!"); 57 | sleepok(2, nh); 58 | quiet_sc.say("Hello world!"); 59 | sleepok(2, nh); 60 | 61 | const char *str1 = "I am annoying."; 62 | sc.repeat(str1); 63 | sleepok(4, nh); 64 | sc.stopSaying(str1); 65 | quiet_sc.repeat(str1); 66 | sleepok(4, nh); 67 | quiet_sc.stopSaying(str1); 68 | 69 | sc.playWave("/usr/share/xemacs21/xemacs-packages/etc/sounds/boing.wav"); 70 | sleepok(2, nh); 71 | quiet_sc.playWave("/usr/share/xemacs21/xemacs-packages/etc/sounds/boing.wav"); 72 | sleepok(2, nh); 73 | 74 | const char *str2 = "/usr/share/xemacs21/xemacs-packages/etc/sounds/piano-beep.wav"; 75 | sc.startWave(str2); 76 | sleepok(4, nh); 77 | sc.stopWave(str2); 78 | quiet_sc.startWave(str2); 79 | sleepok(4, nh); 80 | quiet_sc.stopWave(str2); 81 | 82 | sc.play(sound_play::SoundRequest::NEEDS_UNPLUGGING); 83 | sleepok(2, nh); 84 | quiet_sc.play(sound_play::SoundRequest::NEEDS_UNPLUGGING); 85 | sleepok(2, nh); 86 | 87 | sc.play(sound_play::SoundRequest::NEEDS_UNPLUGGING); 88 | sleepok(2, nh); 89 | quiet_sc.play(sound_play::SoundRequest::NEEDS_UNPLUGGING); 90 | sleepok(2, nh); 91 | 92 | sc.start(sound_play::SoundRequest::BACKINGUP); 93 | sleepok(4, nh); 94 | sc.stop(sound_play::SoundRequest::BACKINGUP); 95 | quiet_sc.start(sound_play::SoundRequest::BACKINGUP); 96 | sleepok(4, nh); 97 | quiet_sc.stop(sound_play::SoundRequest::BACKINGUP); 98 | 99 | sleepok(2, nh); 100 | sound_play::Sound s1 = sc.waveSound("/usr/share/xemacs21/xemacs-packages/etc/sounds/boing.wav"); 101 | s1.repeat(); 102 | sleepok(1, nh); 103 | s1.stop(); 104 | 105 | sleepok(2, nh); 106 | sound_play::Sound s2 = quiet_sc.waveSound("/usr/share/xemacs21/xemacs-packages/etc/sounds/boing.wav"); 107 | s2.repeat(); 108 | sleepok(1, nh); 109 | s2.stop(); 110 | 111 | sleepok(2, nh); 112 | sound_play::Sound s3 = sc.voiceSound("This is a really long sentence that will get cut off."); 113 | s3.play(); 114 | sleepok(1, nh); 115 | s3.stop(); 116 | 117 | sleepok(2, nh); 118 | sound_play::Sound s4 = quiet_sc.voiceSound("This is a really long sentence that will get cut off."); 119 | s4.play(); 120 | sleepok(1, nh); 121 | s4.stop(); 122 | 123 | sleepok(2, nh); 124 | sound_play::Sound s5 = sc.builtinSound(sound_play::SoundRequest::NEEDS_UNPLUGGING_BADLY); 125 | s5.play(); 126 | sleepok(1, nh); 127 | s5.stop(); 128 | 129 | sleepok(2, nh); 130 | sound_play::Sound s6 = quiet_sc.builtinSound(sound_play::SoundRequest::NEEDS_UNPLUGGING_BADLY); 131 | s6.play(); 132 | sleepok(1, nh); 133 | s6.stop(); 134 | 135 | sleepok(2, nh); 136 | sound_play::Sound s7 = sc.waveSoundFromPkg("sound_play", "sounds/BACKINGUP.ogg"); 137 | s7.play(); 138 | sleepok(1, nh); 139 | s7.stop(); 140 | 141 | sleepok(2, nh); 142 | sound_play::Sound s8 = quiet_sc.waveSoundFromPkg("sound_play", "sounds/BACKINGUP.ogg"); 143 | s8.play(); 144 | sleepok(1, nh); 145 | s8.stop(); 146 | } 147 | } 148 | --------------------------------------------------------------------------------