YARP
Yet Another Robot Platform
AudioRecorderWrapper.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
7#include <yarp/os/LogStream.h>
9
10using namespace yarp::dev;
11using namespace yarp::os;
12
13namespace {
14YARP_LOG_COMPONENT(AUDIORECORDERWRAPPER, "yarp.device.AudioRecorderWrapper")
15constexpr double DEFAULT_THREAD_PERIOD = 0.02; // seconds
16constexpr size_t DEFAULT_MIN_NUMBER_OF_SAMPLES_OVER_NETWORK = 11250;
17constexpr size_t DEFAULT_MAX_NUMBER_OF_SAMPLES_OVER_NETWORK = 11250;
18constexpr double DEFAULT_GETSOUND_TIMEOUT = 1.0;
19
20}
21
22
24 m_period(DEFAULT_THREAD_PERIOD),
25 m_min_number_of_samples_over_network(DEFAULT_MIN_NUMBER_OF_SAMPLES_OVER_NETWORK),
26 m_max_number_of_samples_over_network(DEFAULT_MAX_NUMBER_OF_SAMPLES_OVER_NETWORK),
27 m_getSound_timeout(DEFAULT_GETSOUND_TIMEOUT)
28{
29 m_stamp.update();
30}
31
33{
34 if (m_mic != nullptr)
35 {
36 close();
37 }
38}
39
41{
42 if (config.check("period"))
43 {
44 m_period = config.find("period").asFloat64();
45 }
46
47 if (config.check("subdevice"))
48 {
50 PolyDriverList driverlist;
51 p.fromString(config.toString(), false);
52 p.put("device", config.find("subdevice").asString());
53
54 if (!m_driver.open(p) || !m_driver.isValid())
55 {
56 yCError(AUDIORECORDERWRAPPER) << "Failed to open subdevice.. check params";
57 return false;
58 }
59
60 driverlist.push(&m_driver, "1");
61 if (!attachAll(driverlist))
62 {
63 yCError(AUDIORECORDERWRAPPER) << "Failed to open subdevice.. check params";
64 return false;
65 }
66 m_isDeviceOwned = true;
67 }
68
69 if (m_mic == nullptr)
70 {
71 yCError(AUDIORECORDERWRAPPER, "Failed to open IAudioGrabberSound interface");
72 return false;
73 }
74
75 // Get parameter samples_over_network
76 if (config.check("min_samples_over_network"))
77 {
78 m_min_number_of_samples_over_network = config.find("min_samples_over_network").asInt64();
79 }
80 if (config.check("max_samples_over_network"))
81 {
82 m_max_number_of_samples_over_network = config.find("max_samples_over_network").asInt64();
83 }
84 yCInfo(AUDIORECORDERWRAPPER) << "Wrapper configured to produce packets with the following size (in samples): " <<
85 m_min_number_of_samples_over_network << " < samples < " << m_max_number_of_samples_over_network;
86
87
88 // Get parameter samples_over_network
89 if (config.check("max_samples_timeout"))
90 {
91 m_getSound_timeout = config.find("max_samples_timeout").asFloat64();
92 }
93 yCInfo(AUDIORECORDERWRAPPER) << "Wrapper configured with max_samples_timeout: " << m_getSound_timeout << "s";
94
95 // Set the streaming port
96 std::string portname = "/audioRecorderWrapper";
97 if (config.check("name"))
98 {
99 portname= config.find("name").asString();
100 }
101 if (m_streamingPort.open(portname + "/audio:o") == false)
102 {
103 yCError(AUDIORECORDERWRAPPER) << "Unable to open port" << portname + "/audio:o";
104 return false;
105 }
106 if (m_statusPort.open(portname + "/status:o") == false)
107 {
108 yCError(AUDIORECORDERWRAPPER) << "Unable to open port" << portname + "/status:o";
109 return false;
110 }
111 // Set the RPC port
112 if (m_rpcPort.open(portname + "/rpc") == false)
113 {
114 yCError(AUDIORECORDERWRAPPER) << "Unable to open port" << portname + "/rpc";
115 return false;
116 }
117 m_rpcPort.setReader(*this);
118
119 bool b = m_mic->getRecordingAudioBufferMaxSize(m_max_buffer_size);
120 if (!b)
121 {
122 yCError(AUDIORECORDERWRAPPER, "getPlaybackAudioBufferMaxSize failed\n");
123 return false;
124 }
125
126 // Wait a little and then start if requested
127 if (config.check("start")) {
129 m_mic->startRecording();
130 m_mic->isRecording(m_isRecording);
131 }
132
133 return true;
134}
135
137{
138 if (m_mic != nullptr)
139 {
140 m_dataThread->stop();
141 m_statusThread->stop();
142 m_mic->stopRecording();
143 m_mic = nullptr;
144
145 m_streamingPort.interrupt();
146 m_streamingPort.close();
147 m_rpcPort.interrupt();
148 m_rpcPort.close();
149 m_statusPort.interrupt();
150 m_statusPort.close();
151
152 return true;
153 }
154 return false;
155}
156
158{
159 yarp::os::Bottle command;
160 yarp::os::Bottle reply;
161 bool ok = command.read(connection);
162 if (!ok) {
163 return false;
164 }
165 reply.clear();
166
167 if (command.get(0).asString()=="start")
168 {
169 m_mic->startRecording();
170 m_mic->isRecording(m_isRecording);
171 reply.addVocab32(VOCAB_OK);
172 }
173 else if (command.get(0).asString() == "stop")
174 {
175 m_mic->stopRecording();
176 m_mic->isRecording(m_isRecording);
177 reply.addVocab32(VOCAB_OK);
178 }
179 else if (command.get(0).asString() == "sw_audio_gain")
180 {
181 double val = command.get(1).asFloat64();
182 if (val >= 0)
183 {
184 m_mic->setSWGain(val);
185 reply.addVocab32(VOCAB_OK);
186 }
187 else
188 {
189 yCError(AUDIORECORDERWRAPPER) << "Invalid audio gain";
190 reply.addVocab32(VOCAB_ERR);
191 }
192 }
193 else if (command.get(0).asString() == "hw_audio_gain")
194 {
195 double val = command.get(1).asFloat64();
196 if (val >= 0)
197 {
198 m_mic->setHWGain(val);
199 reply.addVocab32(VOCAB_OK);
200 }
201 else
202 {
203 yCError(AUDIORECORDERWRAPPER) << "Invalid audio gain";
204 reply.addVocab32(VOCAB_ERR);
205 }
206 }
207 else if (command.get(0).asString() == "clear")
208 {
210 reply.addVocab32(VOCAB_OK);
211 }
212 else if (command.get(0).asString() == "help")
213 {
214 reply.addVocab32("many");
215 reply.addString("start");
216 reply.addString("stop");
217 reply.addString("clear");
218 reply.addString("sw_audio_gain <gain>");
219 reply.addString("hw_audio_gain <gain>");
220 }
221 else
222 {
223 yCError(AUDIORECORDERWRAPPER) << "Invalid command";
224 reply.addVocab32(VOCAB_ERR);
225 }
226
227 yarp::os::ConnectionWriter *returnToSender = connection.getWriter();
228 if (returnToSender != nullptr)
229 {
230 reply.write(*returnToSender);
231 }
232 return true;
233}
234
236{
237 if (device2attach.size() != 1)
238 {
239 yCError(AUDIORECORDERWRAPPER, "Cannot attach more than one device");
240 return false;
241 }
242
243 yarp::dev::PolyDriver * Idevice2attach = device2attach[0]->poly;
244
245 if (Idevice2attach->isValid())
246 {
247 Idevice2attach->view(m_mic);
248 }
249
250 if (nullptr == m_mic)
251 {
252 yCError(AUDIORECORDERWRAPPER, "Subdevice passed to attach method is invalid");
253 return false;
254 }
255 attach(m_mic);
256
257 m_dataThread = new AudioRecorderDataThread(this);
258 m_statusThread = new AudioRecorderStatusThread(this);
259 m_dataThread->setPeriod(m_period);
260 m_dataThread->start();
261 m_statusThread->start();
262 return true;
263}
264
266{
267 if (m_dataThread->isRunning())
268 {
269 m_dataThread->stop();
270 }
271 if (m_statusThread->isRunning())
272 {
273 m_statusThread->stop();
274 }
275 m_mic = nullptr;
276 return true;
277}
278
280{
281 m_mic = igrab;
282}
283
285{
286 if (m_dataThread->isRunning())
287 {
288 m_dataThread->stop();
289 }
290 if (m_statusThread->isRunning())
291 {
292 m_statusThread->stop();
293 }
294 m_mic = nullptr;
295}
296
298{
299 m_ARW->m_mic->getRecordingAudioBufferCurrentSize(m_ARW->m_current_buffer_size);
300 if (m_ARW->m_debug_enabled)
301 {
302 static double printer_wdt = yarp::os::Time::now();
303 if (yarp::os::Time::now() - printer_wdt > 1.0)
304 {
305 yCDebug(AUDIORECORDERWRAPPER) << m_ARW->m_current_buffer_size.getSamples() << "/" << m_ARW->m_max_buffer_size.getSamples() << "samples";
306 printer_wdt = yarp::os::Time::now();
307 }
308 }
309
310 m_ARW->m_mic->isRecording(m_ARW->m_isRecording);
311
312 //status port
314 status.enabled = m_ARW->m_isRecording;
315 status.current_buffer_size = m_ARW->m_current_buffer_size.getSamples();
316 status.max_buffer_size = m_ARW->m_max_buffer_size.getSamples();
317 m_ARW->m_statusPort.write(status);
318}
319
321{
322 if (0)
323 {
324 //debug: print the duration of the thread
325 double current_time = yarp::os::Time::now();
326 yCDebug(AUDIORECORDERWRAPPER) << (current_time - m_ARW->m_debug_last_time);
327 m_ARW->m_debug_last_time = current_time;
328 }
329
330 if (m_ARW->m_mic == nullptr)
331 {
332 yCError(AUDIORECORDERWRAPPER) << "The IAudioGrabberSound interface is not available yet!";
333 return;
334 }
335
336#ifdef PRINT_DEBUG_MESSAGES
337 {
338 audio_buffer_size buf_max;
339 audio_buffer_size buf_cur;
340 mic->getRecordingAudioBufferMaxSize(buf_max);
341 mic->getRecordingAudioBufferCurrentSize(buf_cur);
342 yCDebug(AUDIORECORDERWRAPPER) << "BEFORE Buffer status:" << buf_cur.getBytes() << "/" << buf_max.getBytes() << "bytes";
343 }
344#endif
345
347 m_ARW->m_mic->getSound(snd, m_ARW->m_min_number_of_samples_over_network, m_ARW->m_max_number_of_samples_over_network, m_ARW->m_getSound_timeout);
348
349 if (snd.getSamples() < m_ARW->m_min_number_of_samples_over_network ||
350 snd.getSamples() < m_ARW->m_max_number_of_samples_over_network)
351 {
352 yCWarning(AUDIORECORDERWRAPPER) << "subdevice->getSound() is not producing sounds of the requested size ("
353 << m_ARW->m_min_number_of_samples_over_network << "<"
354 << snd.getSamples() << "<"
355 << m_ARW->m_max_number_of_samples_over_network << ") failed";
356 }
357
358#ifdef PRINT_DEBUG_MESSAGES
359 {
360 audio_buffer_size buf_max;
361 audio_buffer_size buf_cur;
362 mic->getRecordingAudioBufferMaxSize(buf_max);
363 mic->getRecordingAudioBufferCurrentSize(buf_cur);
364 yCDebug(AUDIORECORDERWRAPPER) << "AFTER Buffer status:" << buf_cur.getBytes() << "/" << buf_max.getBytes() << "bytes";
365 }
366#endif
367#ifdef PRINT_DEBUG_MESSAGES
368 yCDebug(AUDIORECORDERWRAPPER) << "Sound size:" << snd.getSamples() * snd.getChannels() * snd.getBytesPerSample() << " bytes";
369 yCDebug(AUDIORECORDERWRAPPER);
370#endif
371
372 //prepare the timestamp
373 m_ARW->m_stamp.update();
374 m_ARW->m_streamingPort.setEnvelope(m_ARW->m_stamp);
375
376 //check before sending data
377 if (snd.getSamples() == 0)
378 {
379 yCError(AUDIORECORDERWRAPPER) << "Subdevice produced sound of 0 samples!";
380 return;
381 }
382 if (snd.getChannels() == 0)
383 {
384 yCError(AUDIORECORDERWRAPPER) << "Subdevice produced sound of 0 channels!";
385 return;
386 }
387 if (snd.getFrequency() == 0)
388 {
389 yCError(AUDIORECORDERWRAPPER) << "Subdevice produced sound with 0 frequency!";
390 return;
391 }
392
393 //send data
394 m_ARW->m_streamingPort.write(snd);
395}
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
void run() override
Loop function.
AudioRecorderWrapper * m_ARW
AudioRecorderWrapper * m_ARW
void run() override
Loop function.
AudioRecorderWrapper: A Wrapper which streams audio over the network, after grabbing it from a device
bool detachAll() override
Detach the object (you must have first called attach).
friend class AudioRecorderDataThread
bool close() override
Close the DeviceDriver.
friend class AudioRecorderStatusThread
bool attachAll(const yarp::dev::PolyDriverList &p) override
Attach to a list of objects.
bool open(yarp::os::Searchable &config) override
Open the DeviceDriver.
void attach(yarp::dev::IAudioGrabberSound *igrab)
bool read(yarp::os::ConnectionReader &connection) override
Read this object from a network connection.
AudioRecorderStatus: A class used to describe the status of an audio recorder device.
bool enabled
true if the playback is currently enabled
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 view(T *&x)
Get an interface to the device driver.
Definition: DeviceDriver.h:88
Read a YARP-format sound block from a device.
virtual bool getSound(yarp::sig::Sound &sound, size_t min_number_of_samples, size_t max_number_of_samples, double max_samples_timeout_s)=0
Get a sound from a device.
virtual bool resetRecordingAudioBuffer()=0
virtual bool setSWGain(double gain)=0
Sets a software gain for the grabbed audio.
virtual bool getRecordingAudioBufferCurrentSize(yarp::dev::AudioBufferSize &size)=0
virtual bool getRecordingAudioBufferMaxSize(yarp::dev::AudioBufferSize &size)=0
virtual bool stopRecording()=0
Stop the recording.
virtual bool startRecording()=0
Start the recording.
virtual bool isRecording(bool &recording_enabled)=0
Check if the recording has been enabled (e.g.
virtual bool setHWGain(double gain)=0
Sets the hardware gain of the grabbing device (if supported by the hardware)
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
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.
bool setPeriod(double period)
Set the (new) period of the thread.
bool isRunning() const
Returns true when the thread is started, false otherwise.
bool start()
Call this to start the thread.
void stop()
Call this to stop the thread, this call blocks until the thread is terminated (and releaseThread() ca...
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
bool setEnvelope(PortWriter &envelope) override
Set an envelope (e.g., a timestamp) to the next message which will be sent.
Definition: Port.cpp:547
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.
void update()
Set the timestamp to the current time, and increment the sequence number (wrapping to 0 if the sequen...
Definition: Stamp.cpp:124
static void delaySystem(double seconds)
Definition: SystemClock.cpp:29
virtual yarp::conf::float64_t asFloat64() const
Get 64-bit floating point value.
Definition: Value.cpp:222
virtual std::int64_t asInt64() const
Get 64-bit integer value.
Definition: Value.cpp:210
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
size_t getBytesPerSample() const
Get the number of bytes per sample.
Definition: Sound.cpp:414
size_t getChannels() const
Get the number of channels of the sound.
Definition: Sound.cpp:424
int getFrequency() const
Get the frequency of the sound (i.e.
Definition: Sound.cpp:224
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 yCWarning(component,...)
Definition: LogComponent.h:192
#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.