YARP
Yet Another Robot Platform
AudioPlayerWrapper.cpp
Go to the documentation of this file.
1 /*
2  * SPDX-FileCopyrightText: 2006-2021 Istituto Italiano di Tecnologia (IIT)
3  * SPDX-License-Identifier: LGPL-2.1-or-later
4  */
5 
6 #define _USE_MATH_DEFINES
7 
8 #include "AudioPlayerWrapper.h"
9 
10 #include <yarp/os/LogComponent.h>
11 #include <yarp/os/LogStream.h>
12 
15 
16 #include <cmath>
17 #include <sstream>
18 
19 using namespace yarp::sig;
20 using namespace yarp::dev;
21 using namespace yarp::os;
22 
23 namespace {
24 YARP_LOG_COMPONENT(AUDIOPLAYERWRAPPER, "yarp.device.AudioPlayerWrapper")
25 constexpr double DEFAULT_THREAD_PERIOD = 0.02; // seconds
26 constexpr double DEFAULT_BUFFER_DELAY = 5.0; // seconds
27 }
28 
31  m_period(DEFAULT_THREAD_PERIOD),
32  m_buffer_delay(DEFAULT_BUFFER_DELAY)
33 {
34 }
35 
37 {
38  m_irender = nullptr;
39 }
40 
46 {
47  if (device2attach.size() != 1)
48  {
49  yCError(AUDIOPLAYERWRAPPER, "Cannot attach more than one device");
50  return false;
51  }
52 
53  yarp::dev::PolyDriver * Idevice2attach = device2attach[0]->poly;
54 
55  if (Idevice2attach->isValid())
56  {
57  Idevice2attach->view(m_irender);
58  }
59 
60  if (nullptr == m_irender)
61  {
62  yCError(AUDIOPLAYERWRAPPER, "Subdevice passed to attach method is invalid");
63  return false;
64  }
65  attach(m_irender);
66 
67  PeriodicThread::setPeriod(m_period);
68  return PeriodicThread::start();
69 }
70 
72 {
73  if (PeriodicThread::isRunning())
74  {
75  PeriodicThread::stop();
76  }
77  m_irender = nullptr;
78  return true;
79 }
80 
82 {
83  m_irender = irend;
84 }
85 
87 {
88  if (PeriodicThread::isRunning())
89  {
90  PeriodicThread::stop();
91  }
92  m_irender = nullptr;
93 }
94 
95 bool AudioPlayerWrapper::read(yarp::os::ConnectionReader& connection)
96 {
97  yarp::os::Bottle command;
98  yarp::os::Bottle reply;
99  bool ok = command.read(connection);
100  if (!ok) {
101  return false;
102  }
103  reply.clear();
104 
105  if (command.get(0).asString() == "start")
106  {
107  m_irender->startPlayback();
108  m_irender->isPlaying(m_isPlaying);
109  reply.addVocab32(VOCAB_OK);
110  }
111  else if (command.get(0).asString() == "stop")
112  {
113  m_irender->stopPlayback();
114  m_irender->isPlaying(m_isPlaying);
115  reply.addVocab32(VOCAB_OK);
116  }
117  else if (command.get(0).asString() == "sw_audio_gain")
118  {
119  double val = command.get(1).asFloat64();
120  if (val>=0)
121  {
122  m_irender->setSWGain(val);
123  reply.addVocab32(VOCAB_OK);
124  }
125  else
126  {
127  yCError(AUDIOPLAYERWRAPPER) << "Invalid audio gain";
128  reply.addVocab32(VOCAB_ERR);
129  }
130  }
131  else if (command.get(0).asString() == "hw_audio_gain")
132  {
133  double val = command.get(1).asFloat64();
134  if (val >= 0)
135  {
136  m_irender->setHWGain(val);
137  reply.addVocab32(VOCAB_OK);
138  }
139  else
140  {
141  yCError(AUDIOPLAYERWRAPPER) << "Invalid audio gain";
142  reply.addVocab32(VOCAB_ERR);
143  }
144  }
145  else if (command.get(0).asString() == "clear")
146  {
147  m_irender->resetPlaybackAudioBuffer();
148  reply.addVocab32(VOCAB_OK);
149  }
150  else if (command.get(0).asString() == "help")
151  {
152  reply.addVocab32("many");
153  reply.addString("start");
154  reply.addString("stop");
155  reply.addString("clear");
156  reply.addString("sw_audio_gain <gain>");
157  reply.addString("hw_audio_gain <gain>");
158  }
159  else
160  {
161  yCError(AUDIOPLAYERWRAPPER) << "Invalid command";
162  reply.addVocab32(VOCAB_ERR);
163  }
164 
165  yarp::os::ConnectionWriter *returnToSender = connection.getWriter();
166  if (returnToSender != nullptr)
167  {
168  reply.write(*returnToSender);
169  }
170  return true;
171 }
172 
174 {
175  return true;
176 }
177 
179 {
180  Property params;
181  params.fromString(config.toString());
182 
183  if (config.check("debug"))
184  {
185  m_debug_enabled = true;
186  }
187 
188  if (config.check("period"))
189  {
190  m_period = config.find("period").asFloat64();
191  }
192 
193  std::string name = "/audioPlayerWrapper";
194  if (config.check("name"))
195  {
196  name = config.find("name").asString();
197  }
198  m_audioInPortName = name + "/audio:i";
199  m_rpcPortName = name + "/rpc:i";
200  m_statusPortName = name + "/status:o";
201 
202  if(!initialize_YARP(config) )
203  {
204  yCError(AUDIOPLAYERWRAPPER) << "Error initializing YARP ports";
205  return false;
206  }
207 
208  if (config.check("playback_network_buffer_size"))
209  {
210  m_buffer_delay = config.find("playback_network_buffer_size").asFloat64();
211  }
212  yCInfo(AUDIOPLAYERWRAPPER) << "Using a 'playback_network_buffer_size' of" << m_buffer_delay << "s";
213  yCInfo(AUDIOPLAYERWRAPPER) << "Increase this value to robustify the real-time audio stream (it will increase latency too)";
214 
215  if(config.check("subdevice"))
216  {
217  Property p;
218  PolyDriverList driverlist;
219  p.fromString(config.toString(), false);
220  p.put("device", config.find("subdevice").asString());
221 
222  if(!m_driver.open(p) || !m_driver.isValid())
223  {
224  yCError(AUDIOPLAYERWRAPPER) << "Failed to open subdevice.. check params";
225  return false;
226  }
227 
228  driverlist.push(&m_driver, "1");
229  if(!attachAll(driverlist))
230  {
231  yCError(AUDIOPLAYERWRAPPER) << "Failed to open subdevice.. check params";
232  return false;
233  }
234  m_isDeviceOwned = true;
235  }
236 
237  if (m_irender == nullptr)
238  {
239  yCError(AUDIOPLAYERWRAPPER, "m_irender is null\n");
240  return false;
241  }
242 
243  bool b=m_irender->getPlaybackAudioBufferMaxSize(m_max_buffer_size);
244  if (!b)
245  {
246  yCError(AUDIOPLAYERWRAPPER, "getPlaybackAudioBufferMaxSize failed\n");
247  return false;
248  }
249 
250  if (config.check("start"))
251  {
252  m_irender->startPlayback();
253  m_irender->isPlaying(m_isPlaying);
254  }
255 
256  return true;
257 }
258 
259 bool AudioPlayerWrapper::initialize_YARP(yarp::os::Searchable &params)
260 {
261  if (!m_audioInPort.open(m_audioInPortName))
262  {
263  yCError(AUDIOPLAYERWRAPPER, "Failed to open port %s", m_audioInPortName.c_str());
264  return false;
265  }
266  if (!m_statusPort.open(m_statusPortName))
267  {
268  yCError(AUDIOPLAYERWRAPPER, "Failed to open port %s", m_statusPortName.c_str());
269  return false;
270  }
271  if (!m_rpcPort.open(m_rpcPortName))
272  {
273  yCError(AUDIOPLAYERWRAPPER, "Failed to open port %s", m_rpcPortName.c_str());
274  return false;
275  }
276  m_rpcPort.setReader(*this);
277  return true;
278 }
279 
281 {
282  m_audioInPort.interrupt();
283  m_audioInPort.close();
284  m_rpcPort.interrupt();
285  m_rpcPort.close();
286  m_statusPort.interrupt();
287  m_statusPort.close();
288 }
289 
291 {
292  double current_time = yarp::os::Time::now();
293 
294  Sound* s = m_audioInPort.read(false);
295  if (s != nullptr)
296  {
297  if (m_debug_enabled)
298  {
299  yCDebug(AUDIOPLAYERWRAPPER) << "Received sound of:" << s->getSamples() << " samples";
300  }
301 
302  scheduled_sound_type ss;
303 #if 1
304  //This is simple, but we don't know how big the sound is...
305  ss.scheduled_time = current_time + m_buffer_delay;
306 #elif 0
307  //This is ok, but it doesn't work if the sounds have different durations...
308  ss.scheduled_time = current_time + 5.0 * s.getDuration();
309 #else
310  ss.scheduled_time = current_time + m_buffer_delay > 5.0 * s.getDuration() ? (m_buffer_delay) : (5.0 * s.getDuration());
311 #endif
312  ss.sound_data = *s;
313  m_sound_buffer.push(ss);
314  }
315 
316  if (!m_sound_buffer.empty() && current_time > m_sound_buffer.front().scheduled_time)
317  {
318  m_irender->renderSound(m_sound_buffer.front().sound_data);
319  m_sound_buffer.pop();
320  }
321 
322  m_irender->getPlaybackAudioBufferCurrentSize(m_current_buffer_size);
323  if (m_debug_enabled)
324  {
325  static double printer_wdt = yarp::os::Time::now();
326  if (yarp::os::Time::now() - printer_wdt > 1.0)
327  {
328  yCDebug(AUDIOPLAYERWRAPPER) << m_current_buffer_size.getSamples() << "/" << m_max_buffer_size.getSamples() << "samples";
329  printer_wdt = yarp::os::Time::now();
330  }
331  }
332 
333  m_irender->isPlaying(m_isPlaying);
334 
335  //status port
337  status.enabled = m_isPlaying;
338  status.current_buffer_size = m_current_buffer_size.getSamples();
339  status.max_buffer_size = m_max_buffer_size.getSamples();
340  m_statusPort.write(status);
341 }
342 
344 {
345  yCTrace(AUDIOPLAYERWRAPPER, "AudioPlayerWrapper::Close");
346  if (PeriodicThread::isRunning())
347  {
348  PeriodicThread::stop();
349  }
350 
351  detachAll();
352  return true;
353 }
define control board standard interfaces
constexpr yarp::conf::vocab32_t VOCAB_OK
Definition: GenericVocabs.h:15
constexpr yarp::conf::vocab32_t VOCAB_ERR
Definition: GenericVocabs.h:17
constexpr double DEFAULT_THREAD_PERIOD
bool close() override
Close the DeviceDriver.
void attach(yarp::dev::IAudioRender *irend)
bool threadInit() override
Initialization method.
void run() override
Loop function.
bool attachAll(const yarp::dev::PolyDriverList &p) override
Specify which sensor this thread has to read from.
void threadRelease() override
Release method.
bool detachAll() override
Detach the object (you must have first called attach).
bool open(yarp::os::Searchable &params) override
Open the DeviceDriver.
AudioPlayerStatus: A class used to describe the status of an audio player device.
size_t current_buffer_size
the size of the audio buffer [samples]
size_t max_buffer_size
the max_size of the audio buffer [samples]
bool enabled
true if the playback is currently enabled
bool view(T *&x)
Get an interface to the device driver.
Definition: DeviceDriver.h:88
virtual bool isPlaying(bool &playback_enabled)=0
Check if the playback has been enabled (e.g.
virtual bool renderSound(const yarp::sig::Sound &sound)=0
Render a sound using a device (i.e.
virtual bool resetPlaybackAudioBuffer()=0
virtual bool getPlaybackAudioBufferCurrentSize(yarp::dev::AudioBufferSize &size)=0
virtual bool startPlayback()=0
Start the playback.
virtual bool setSWGain(double gain)=0
Sets a software gain for the played audio.
virtual bool setHWGain(double gain)=0
Sets the hardware gain of the playback device (if supported by the hardware)
virtual bool stopPlayback()=0
Stop the playback.
virtual bool getPlaybackAudioBufferMaxSize(yarp::dev::AudioBufferSize &size)=0
void push(PolyDriver *p, const char *k)
A container for a device driver.
Definition: PolyDriver.h:23
bool isValid() const
Check if device is valid.
Definition: PolyDriver.cpp:196
bool open(const std::string &txt)
Construct and configure a device by its common name.
Definition: PolyDriver.cpp:140
A simple collection of objects that can be described and transmitted in a portable way.
Definition: Bottle.h:73
void addVocab32(yarp::conf::vocab32_t x)
Places a vocabulary item in the bottle, at the end of the list.
Definition: Bottle.cpp:164
bool read(ConnectionReader &reader) override
Set the bottle's value based on input from a network connection.
Definition: Bottle.cpp:240
Value & get(size_type index) const
Reads a Value v from a certain part of the list.
Definition: Bottle.cpp:246
void clear()
Empties the bottle of any objects it contains.
Definition: Bottle.cpp:121
bool write(ConnectionWriter &writer) const override
Output a representation of the bottle to a network connection.
Definition: Bottle.cpp:230
void addString(const char *str)
Places a string in the bottle, at the end of the list.
Definition: Bottle.cpp:170
void close() override
Stop port activity.
bool open(const std::string &name) override
Start port operation, with a specific name, with automatically-chosen network parameters.
void interrupt() override
Interrupt any current reads or writes attached to the port.
T * read(bool shouldWait=true) override
Read an available object from the port.
An interface for reading from a network connection.
virtual ConnectionWriter * getWriter()=0
Gets a way to reply to the message, if possible.
An interface for writing to a network connection.
An abstraction for a periodic thread.
bool write(const PortWriter &writer, const PortWriter *callback=nullptr) const override
Write an object to the port.
Definition: Port.cpp:436
void setReader(PortReader &reader) override
Set an external reader for port data.
Definition: Port.cpp:511
void interrupt() override
Interrupt any current reads or writes attached to the port.
Definition: Port.cpp:383
void close() override
Stop port activity.
Definition: Port.cpp:363
bool open(const std::string &name) override
Start port operation, with a specific name, with automatically-chosen network parameters.
Definition: Port.cpp:79
A class for storing options and configuration information.
Definition: Property.h:33
void fromString(const std::string &txt, bool wipe=true)
Interprets a string as a list of properties.
Definition: Property.cpp:1063
void put(const std::string &key, const std::string &value)
Associate the given key with the given string.
Definition: Property.cpp:1015
A base class for nested structures that can be searched.
Definition: Searchable.h:63
virtual Value & find(const std::string &key) const =0
Gets a value corresponding to a given keyword.
virtual bool check(const std::string &key) const =0
Check if there exists a property of the given name.
virtual std::string toString() const =0
Return a standard text representation of the content of the object.
virtual yarp::conf::float64_t asFloat64() const
Get 64-bit floating point value.
Definition: Value.cpp:222
virtual std::string asString() const
Get string value.
Definition: Value.cpp:234
Class for storing sounds See Audio in YARP for additional documentation on YARP audio.
Definition: Sound.h:25
double getDuration() const
Get the duration of sound in seconds.
Definition: Sound.cpp:429
size_t getSamples() const
Get the number of samples contained in the sound.
Definition: Sound.cpp:419
#define yCInfo(component,...)
Definition: LogComponent.h:171
#define yCError(component,...)
Definition: LogComponent.h:213
#define yCTrace(component,...)
Definition: LogComponent.h:84
#define yCDebug(component,...)
Definition: LogComponent.h:128
#define YARP_LOG_COMPONENT(name,...)
Definition: LogComponent.h:76
For streams capable of holding different kinds of content, check what they actually have.
double now()
Return the current time in seconds, relative to an arbitrary starting point.
Definition: Time.cpp:121
An interface to the operating system, including Port based communication.