YARP
Yet Another Robot Platform
AudioPlayerWrapper.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2006-2020 Istituto Italiano di Tecnologia (IIT)
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17  */
18 
19 #define _USE_MATH_DEFINES
20 
21 #include "AudioPlayerWrapper.h"
22 
23 #include <yarp/os/LogComponent.h>
24 #include <yarp/os/LogStream.h>
25 
27 
28 #include <cmath>
29 #include <sstream>
30 
31 using namespace yarp::sig;
32 using namespace yarp::dev;
33 using namespace yarp::os;
34 using namespace std;
35 
36 namespace {
37 YARP_LOG_COMPONENT(AUDIOPLAYERWRAPPER, "yarp.device.AudioPlayerWrapper")
38 constexpr double DEFAULT_THREAD_PERIOD = 0.02; // seconds
39 constexpr double DEFAULT_BUFFER_DELAY = 5.0; // seconds
40 }
41 
44  m_irender(nullptr),
45  m_period(DEFAULT_THREAD_PERIOD),
46  m_buffer_delay(DEFAULT_BUFFER_DELAY),
47  m_isDeviceOwned(false),
48  m_debug_enabled(false)
49 {
50 }
51 
53 {
54  m_irender = nullptr;
55 }
56 
62 {
63  if (device2attach.size() != 1)
64  {
65  yCError(AUDIOPLAYERWRAPPER, "Cannot attach more than one device");
66  return false;
67  }
68 
69  yarp::dev::PolyDriver * Idevice2attach = device2attach[0]->poly;
70 
71  if (Idevice2attach->isValid())
72  {
73  Idevice2attach->view(m_irender);
74  }
75 
76  if (nullptr == m_irender)
77  {
78  yCError(AUDIOPLAYERWRAPPER, "Subdevice passed to attach method is invalid");
79  return false;
80  }
81  attach(m_irender);
82 
83  PeriodicThread::setPeriod(m_period);
84  return PeriodicThread::start();
85 }
86 
88 {
89  if (PeriodicThread::isRunning())
90  {
91  PeriodicThread::stop();
92  }
93  m_irender = nullptr;
94  return true;
95 }
96 
98 {
99  m_irender = irend;
100 }
101 
103 {
104  if (PeriodicThread::isRunning())
105  {
106  PeriodicThread::stop();
107  }
108  m_irender = nullptr;
109 }
110 
111 bool AudioPlayerWrapper::read(yarp::os::ConnectionReader& connection)
112 {
113  yarp::os::Bottle command;
114  yarp::os::Bottle reply;
115  bool ok = command.read(connection);
116  if (!ok) {
117  return false;
118  }
119  reply.clear();
120 
121  if (command.get(0).asString() == "start")
122  {
123  m_isPlaying = true;
124  m_irender->startPlayback();
125  reply.addVocab(VOCAB_OK);
126  }
127  else if (command.get(0).asString() == "stop")
128  {
129  m_isPlaying = false;
130  m_irender->stopPlayback();
131  reply.addVocab(VOCAB_OK);
132  }
133  else if (command.get(0).asString() == "clear")
134  {
135  m_irender->resetPlaybackAudioBuffer();
136  reply.addVocab(VOCAB_OK);
137  }
138  else if (command.get(0).asString() == "help")
139  {
140  reply.addVocab(yarp::os::Vocab::encode("many"));
141  reply.addString("start");
142  reply.addString("stop");
143  reply.addString("clear");
144  }
145  else
146  {
147  yCError(AUDIOPLAYERWRAPPER) << "Invalid command";
148  reply.addVocab(VOCAB_ERR);
149  }
150 
151  yarp::os::ConnectionWriter *returnToSender = connection.getWriter();
152  if (returnToSender != nullptr)
153  {
154  reply.write(*returnToSender);
155  }
156  return true;
157 }
158 
160 {
161  return true;
162 }
163 
165 {
166  Property params;
167  params.fromString(config.toString());
168 
169  if (config.check("debug"))
170  {
171  m_debug_enabled = true;
172  }
173 
174  if (config.check("period"))
175  {
176  m_period = config.find("period").asFloat64();
177  }
178 
179  string name = "/audioPlayerWrapper";
180  if (config.check("name"))
181  {
182  name = config.find("name").asString();
183  }
184  m_audioInPortName = name + "/audio:i";
185  m_rpcPortName = name + "/rpc:i";
186  m_statusPortName = name + "/status:o";
187 
188  if(!initialize_YARP(config) )
189  {
190  yCError(AUDIOPLAYERWRAPPER) << "Error initializing YARP ports";
191  return false;
192  }
193 
194  if (config.check("playback_network_buffer_size"))
195  {
196  m_buffer_delay = config.find("playback_network_buffer_size").asFloat64();
197  }
198  yCInfo(AUDIOPLAYERWRAPPER) << "Using a 'playback_network_buffer_size' of" << m_buffer_delay << "s";
199  yCInfo(AUDIOPLAYERWRAPPER) << "Increase this value to robustify the real-time audio stream (it will increase latency too)";
200 
201  if(config.check("subdevice"))
202  {
203  Property p;
204  PolyDriverList driverlist;
205  p.fromString(config.toString(), false);
206  p.put("device", config.find("subdevice").asString());
207 
208  if(!m_driver.open(p) || !m_driver.isValid())
209  {
210  yCError(AUDIOPLAYERWRAPPER) << "Failed to open subdevice.. check params";
211  return false;
212  }
213 
214  driverlist.push(&m_driver, "1");
215  if(!attachAll(driverlist))
216  {
217  yCError(AUDIOPLAYERWRAPPER) << "Failed to open subdevice.. check params";
218  return false;
219  }
220  m_isDeviceOwned = true;
221  }
222 
223  if (config.check("start"))
224  {
225  m_isPlaying = true;
226  m_irender->startPlayback();
227  }
228 
229  if (m_irender == nullptr)
230  {
231  yCError(AUDIOPLAYERWRAPPER, "m_irender is null\n");
232  return false;
233  }
234 
235  bool b=m_irender->getPlaybackAudioBufferMaxSize(m_max_buffer_size);
236  if (!b)
237  {
238  yCError(AUDIOPLAYERWRAPPER, "getPlaybackAudioBufferMaxSize failed\n");
239  return false;
240  }
241 
242  return true;
243 }
244 
245 bool AudioPlayerWrapper::initialize_YARP(yarp::os::Searchable &params)
246 {
247  if (!m_audioInPort.open(m_audioInPortName))
248  {
249  yCError(AUDIOPLAYERWRAPPER, "Failed to open port %s", m_audioInPortName.c_str());
250  return false;
251  }
252  if (!m_statusPort.open(m_statusPortName))
253  {
254  yCError(AUDIOPLAYERWRAPPER, "Failed to open port %s", m_statusPortName.c_str());
255  return false;
256  }
257  if (!m_rpcPort.open(m_rpcPortName))
258  {
259  yCError(AUDIOPLAYERWRAPPER, "Failed to open port %s", m_rpcPortName.c_str());
260  return false;
261  }
262  m_rpcPort.setReader(*this);
263  return true;
264 }
265 
267 {
268  m_audioInPort.interrupt();
269  m_audioInPort.close();
270  m_rpcPort.interrupt();
271  m_rpcPort.close();
272  m_statusPort.interrupt();
273  m_statusPort.close();
274 }
275 
277 {
278  double current_time = yarp::os::Time::now();
279 
280  Sound* s = m_audioInPort.read(false);
281  if (s != nullptr)
282  {
283  scheduled_sound_type ss;
284 #if 1
285  //This is simple, but we don't know how big the sound is...
286  ss.scheduled_time = current_time + m_buffer_delay;
287 #elif 0
288  //This is ok, but it doesn't work if the sounds have different durations...
289  ss.scheduled_time = current_time + 5.0 * s.getDuration();
290 #else
291  ss.scheduled_time = current_time + m_buffer_delay > 5.0 * s.getDuration() ? (m_buffer_delay) : (5.0 * s.getDuration());
292 #endif
293  ss.sound_data = *s;
294  m_sound_buffer.push(ss);
295  }
296 
297  if (!m_sound_buffer.empty() && current_time > m_sound_buffer.front().scheduled_time)
298  {
299  m_irender->renderSound(m_sound_buffer.front().sound_data);
300  m_sound_buffer.pop();
301  }
302 
303  m_irender->getPlaybackAudioBufferCurrentSize(m_current_buffer_size);
304  if (m_debug_enabled)
305  {
306  static double printer_wdt = yarp::os::Time::now();
307  if (yarp::os::Time::now() - printer_wdt > 1.0)
308  {
309  yCDebug(AUDIOPLAYERWRAPPER) << m_current_buffer_size.getSamples() << "/" << m_max_buffer_size.getSamples() << "samples";
310  printer_wdt = yarp::os::Time::now();
311  }
312  }
313 }
314 
316 {
317  yCTrace(AUDIOPLAYERWRAPPER, "AudioPlayerWrapper::Close");
318  if (PeriodicThread::isRunning())
319  {
320  PeriodicThread::stop();
321  }
322 
323  detachAll();
324  return true;
325 }
yarp::os::Port::close
void close() override
Stop port activity.
Definition: Port.cpp:357
LogStream.h
AudioPlayerWrapper::attach
void attach(yarp::dev::IAudioRender *irend)
Definition: AudioPlayerWrapper.cpp:97
yarp::os::Bottle
A simple collection of objects that can be described and transmitted in a portable way.
Definition: Bottle.h:73
yarp::os::Property::put
void put(const std::string &key, const std::string &value)
Associate the given key with the given string.
Definition: Property.cpp:998
yarp::dev::IAudioRender::renderSound
virtual bool renderSound(const yarp::sig::Sound &sound)=0
Render a sound using a device (i.e.
yarp::os::Bottle::clear
void clear()
Empties the bottle of any objects it contains.
Definition: Bottle.cpp:124
yarp::os::Searchable
A base class for nested structures that can be searched.
Definition: Searchable.h:69
AudioPlayerWrapper::detach
void detach()
Definition: AudioPlayerWrapper.cpp:102
yarp::sig
Signal processing.
Definition: Image.h:25
AudioPlayerWrapper::run
void run() override
Loop function.
Definition: AudioPlayerWrapper.cpp:276
yarp::os::BufferedPort::read
T * read(bool shouldWait=true) override
Read an available object from the port.
Definition: BufferedPort-inl.h:154
AudioPlayerWrapper::attachAll
bool attachAll(const yarp::dev::PolyDriverList &p) override
Specify which sensor this thread has to read from.
Definition: AudioPlayerWrapper.cpp:61
yarp::os::Searchable::toString
virtual std::string toString() const =0
Return a standard text representation of the content of the object.
yarp::os::Property::fromString
void fromString(const std::string &txt, bool wipe=true)
Interprets a string as a list of properties.
Definition: Property.cpp:1046
yarp::dev::PolyDriverList::size
int size() const
Definition: PolyDriverList.cpp:39
AudioPlayerWrapper::~AudioPlayerWrapper
~AudioPlayerWrapper() override
Definition: AudioPlayerWrapper.cpp:52
yarp::dev::PolyDriver::isValid
bool isValid() const
Check if device is valid.
Definition: PolyDriver.cpp:199
YARP_LOG_COMPONENT
#define YARP_LOG_COMPONENT(name,...)
Definition: LogComponent.h:80
AudioPlayerWrapper::AudioPlayerWrapper
AudioPlayerWrapper()
Definition: AudioPlayerWrapper.cpp:42
yarp::os::BufferedPort::interrupt
void interrupt() override
Interrupt any current reads or writes attached to the port.
Definition: BufferedPort-inl.h:82
yarp::dev::DeviceDriver::view
bool view(T *&x)
Get an interface to the device driver.
Definition: DeviceDriver.h:77
yarp::os::Port::open
bool open(const std::string &name) override
Start port operation, with a specific name, with automatically-chosen network parameters.
Definition: Port.cpp:82
AudioPlayerWrapper::threadInit
bool threadInit() override
Initialization method.
Definition: AudioPlayerWrapper.cpp:159
yarp::dev::PolyDriverList
Definition: PolyDriverList.h:22
ControlBoardInterfaces.h
define control board standard interfaces
yarp::dev::PolyDriver::open
bool open(const std::string &txt)
Construct and configure a device by its common name.
Definition: PolyDriver.cpp:143
AudioPlayerWrapper.h
yarp::os::Time::now
double now()
Return the current time in seconds, relative to an arbitrary starting point.
Definition: Time.cpp:124
yarp::dev
An interface for the device drivers.
Definition: audioBufferSizeData.cpp:17
yarp::dev::IAudioRender
Definition: IAudioRender.h:21
yarp::dev::PolyDriverList::push
void push(PolyDriver *p, const char *k)
Definition: PolyDriverList.cpp:44
yarp::dev::IAudioRender::getPlaybackAudioBufferMaxSize
virtual bool getPlaybackAudioBufferMaxSize(yarp::dev::AudioBufferSize &size)=0
yarp::os::Bottle::get
Value & get(size_type index) const
Reads a Value v from a certain part of the list.
Definition: Bottle.cpp:249
yarp::os::Bottle::write
bool write(ConnectionWriter &writer) const override
Output a representation of the bottle to a network connection.
Definition: Bottle.cpp:233
yarp::os::ConnectionWriter
An interface for writing to a network connection.
Definition: ConnectionWriter.h:40
yarp::dev::IAudioRender::getPlaybackAudioBufferCurrentSize
virtual bool getPlaybackAudioBufferCurrentSize(yarp::dev::AudioBufferSize &size)=0
yarp::os::Value::asString
virtual std::string asString() const
Get string value.
Definition: Value.cpp:237
AudioPlayerWrapper::close
bool close() override
Close the DeviceDriver.
Definition: AudioPlayerWrapper.cpp:315
yarp::dev::PolyDriver
A container for a device driver.
Definition: PolyDriver.h:27
yarp::os::BufferedPort::open
bool open(const std::string &name) override
Start port operation, with a specific name, with automatically-chosen network parameters.
Definition: BufferedPort-inl.h:41
yarp::os::Searchable::check
virtual bool check(const std::string &key) const =0
Check if there exists a property of the given name.
yarp::os::Searchable::find
virtual Value & find(const std::string &key) const =0
Gets a value corresponding to a given keyword.
yarp::os::Vocab::encode
NetInt32 encode(const std::string &str)
Convert a string into a vocabulary identifier.
Definition: Vocab.cpp:14
AudioPlayerWrapper::open
bool open(yarp::os::Searchable &params) override
Open the DeviceDriver.
Definition: AudioPlayerWrapper.cpp:164
yarp::os::ConnectionReader::getWriter
virtual ConnectionWriter * getWriter()=0
Gets a way to reply to the message, if possible.
yarp::dev::IAudioRender::resetPlaybackAudioBuffer
virtual bool resetPlaybackAudioBuffer()=0
yarp::os::Bottle::addString
void addString(const char *str)
Places a string in the bottle, at the end of the list.
Definition: Bottle.cpp:173
yarp::os::Port::setReader
void setReader(PortReader &reader) override
Set an external reader for port data.
Definition: Port.cpp:505
yarp::os::PeriodicThread
An abstraction for a periodic thread.
Definition: PeriodicThread.h:25
yarp::dev::AudioBufferSize::getSamples
size_t getSamples()
Definition: AudioBufferSize.h:31
yarp::os::Bottle::addVocab
void addVocab(int x)
Places a vocabulary item in the bottle, at the end of the list.
Definition: Bottle.cpp:167
LogComponent.h
VOCAB_ERR
constexpr yarp::conf::vocab32_t VOCAB_ERR
Definition: GenericVocabs.h:20
yarp::os::ConnectionReader
An interface for reading from a network connection.
Definition: ConnectionReader.h:40
yCError
#define yCError(component,...)
Definition: LogComponent.h:157
yarp::dev::IAudioRender::startPlayback
virtual bool startPlayback()=0
Start the playback.
yarp::sig::Sound::getDuration
double getDuration() const
Get the duration of sound in seconds.
Definition: Sound.cpp:414
yCInfo
#define yCInfo(component,...)
Definition: LogComponent.h:135
yarp::os
An interface to the operating system, including Port based communication.
Definition: AbstractCarrier.h:17
yCDebug
#define yCDebug(component,...)
Definition: LogComponent.h:112
yarp::sig::Sound
Class for storing sounds.
Definition: Sound.h:28
DEFAULT_THREAD_PERIOD
#define DEFAULT_THREAD_PERIOD
Definition: AnalogWrapper.h:45
AudioPlayerWrapper::threadRelease
void threadRelease() override
Release method.
Definition: AudioPlayerWrapper.cpp:266
yarp::dev::IAudioRender::stopPlayback
virtual bool stopPlayback()=0
Stop the playback.
yarp::os::BufferedPort::close
void close() override
Stop port activity.
Definition: BufferedPort-inl.h:73
yarp::os::Bottle::read
bool read(ConnectionReader &reader) override
Set the bottle's value based on input from a network connection.
Definition: Bottle.cpp:243
yarp::os::Port::interrupt
void interrupt() override
Interrupt any current reads or writes attached to the port.
Definition: Port.cpp:377
AudioPlayerWrapper::detachAll
bool detachAll() override
Detach the object (you must have first called attach).
Definition: AudioPlayerWrapper.cpp:87
yCTrace
#define yCTrace(component,...)
Definition: LogComponent.h:88
yarp::os::Value::asFloat64
virtual yarp::conf::float64_t asFloat64() const
Get 64-bit floating point value.
Definition: Value.cpp:225
VOCAB_OK
constexpr yarp::conf::vocab32_t VOCAB_OK
Definition: GenericVocabs.h:18
yarp::os::Property
A class for storing options and configuration information.
Definition: Property.h:37