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 
6 #include "AudioRecorderWrapper.h"
7 #include <yarp/os/LogStream.h>
9 
10 using namespace yarp::dev;
11 using namespace yarp::os;
12 
13 namespace {
14 YARP_LOG_COMPONENT(AUDIORECORDERWRAPPER, "yarp.device.AudioRecorderWrapper")
15 constexpr double DEFAULT_THREAD_PERIOD = 0.02; // seconds
16 constexpr size_t DEFAULT_MIN_NUMBER_OF_SAMPLES_OVER_NETWORK = 11250;
17 constexpr size_t DEFAULT_MAX_NUMBER_OF_SAMPLES_OVER_NETWORK = 11250;
18 constexpr 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  {
209  m_mic->resetRecordingAudioBuffer();
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 
346  yarp::sig::Sound snd;
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.
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).
bool close() override
Close the DeviceDriver.
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:74
Read a YARP-format sound block from a device.
void push(PolyDriver *p, const char *k)
A container for a device driver.
Definition: PolyDriver.h:24
bool isValid() const
Check if device is valid.
Definition: PolyDriver.cpp:196
A simple collection of objects that can be described and transmitted in a portable way.
Definition: Bottle.h:74
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.
A class for storing options and configuration information.
Definition: Property.h:34
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:66
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.
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:26
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:132
#define yCError(component,...)
Definition: LogComponent.h:154
#define yCWarning(component,...)
Definition: LogComponent.h:143
#define yCDebug(component,...)
Definition: LogComponent.h:109
#define YARP_LOG_COMPONENT(name,...)
Definition: LogComponent.h:77
An interface for the device drivers.
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.