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
9
11#include <yarp/os/LogStream.h>
12
15
16#include <cmath>
17#include <sstream>
18
19using namespace yarp::sig;
20using namespace yarp::dev;
21using namespace yarp::os;
22
23namespace {
24YARP_LOG_COMPONENT(AUDIOPLAYERWRAPPER, "yarp.device.AudioPlayerWrapper")
25constexpr double DEFAULT_THREAD_PERIOD = 0.02; // seconds
26constexpr 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
95bool 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
259bool 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:64
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 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 Value & find(const std::string &key) const =0
Gets a value corresponding to a given keyword.
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.