YARP
Yet Another Robot Platform
AudioRecorderWrapper.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 #include "AudioRecorderWrapper.h"
20 #include <yarp/os/LogStream.h>
21 
22 using namespace yarp::dev;
23 
24 namespace {
25 YARP_LOG_COMPONENT(AUDIORECORDERWRAPPER, "yarp.device.AudioRecorderWrapper")
26 constexpr double DEFAULT_THREAD_PERIOD = 0.02; // seconds
27 constexpr size_t DEFAULT_MIN_NUMBER_OF_SAMPLES_OVER_NETWORK = 11250;
28 constexpr size_t DEFAULT_MAX_NUMBER_OF_SAMPLES_OVER_NETWORK = 11250;
29 constexpr double DEFAULT_GETSOUND_TIMEOUT = 1.0;
30 
31 #ifdef DEBUG_TIME_SPENT
32 double last_time;
33 #endif
34 }
35 
36 
38  PeriodicThread(DEFAULT_THREAD_PERIOD),
39  m_mic(nullptr),
40  m_period(DEFAULT_THREAD_PERIOD),
41  m_min_number_of_samples_over_network(DEFAULT_MIN_NUMBER_OF_SAMPLES_OVER_NETWORK),
42  m_max_number_of_samples_over_network(DEFAULT_MAX_NUMBER_OF_SAMPLES_OVER_NETWORK),
43  m_getSound_timeout(DEFAULT_GETSOUND_TIMEOUT),
44  m_isDeviceOwned(false)
45 #ifdef DEBUG_TIME_SPENT
46  , last_time(yarp::os::Time::now()),
47 #endif
48 {
49  m_stamp.update();
50 }
51 
53 {
54  if (m_mic != nullptr)
55  {
56  close();
57  }
58 }
59 
61 {
62  if (config.check("period"))
63  {
64  m_period = config.find("period").asFloat64();
65  }
66 
67  if (config.check("subdevice"))
68  {
70  PolyDriverList driverlist;
71  p.fromString(config.toString(), false);
72  p.put("device", config.find("subdevice").asString());
73 
74  if (!m_driver.open(p) || !m_driver.isValid())
75  {
76  yCError(AUDIORECORDERWRAPPER) << "Failed to open subdevice.. check params";
77  return false;
78  }
79 
80  driverlist.push(&m_driver, "1");
81  if (!attachAll(driverlist))
82  {
83  yCError(AUDIORECORDERWRAPPER) << "Failed to open subdevice.. check params";
84  return false;
85  }
86  m_isDeviceOwned = true;
87  }
88 
89  if (m_mic == nullptr)
90  {
91  yCError(AUDIORECORDERWRAPPER, "Failed to open IAudioGrabberSound interface");
92  return false;
93  }
94 
95  // Get parameter samples_over_network
96  if (config.check("min_samples_over_network"))
97  {
98  m_min_number_of_samples_over_network = config.find("min_samples_over_network").asInt64();
99  }
100  if (config.check("max_samples_over_network"))
101  {
102  m_max_number_of_samples_over_network = config.find("max_samples_over_network").asInt64();
103  }
104  yCInfo(AUDIORECORDERWRAPPER) << "Wrapper configured to produce packets with the following size (in samples): " <<
105  m_min_number_of_samples_over_network << " < samples < " << m_max_number_of_samples_over_network;
106 
107 
108  // Get parameter samples_over_network
109  if (config.check("max_samples_timeout"))
110  {
111  m_getSound_timeout = config.find("max_samples_timeout").asFloat64();
112  }
113  yCInfo(AUDIORECORDERWRAPPER) << "Wrapper configured with max_samples_timeout: " << m_getSound_timeout << "s";
114 
115  // Set the streaming port
116  std::string portname = "/audioRecorderWrapper";
117  if (config.check("name"))
118  {
119  portname= config.find("name").asString();
120  }
121  if (m_streamingPort.open(portname + "/audio:o") == false)
122  {
123  yCError(AUDIORECORDERWRAPPER) << "Unable to open port" << portname;
124  return false;
125  }
126 
127  // Set the RPC port
128  if (m_rpcPort.open(portname + "/rpc") == false)
129  {
130  yCError(AUDIORECORDERWRAPPER) << "Unable to open port" << portname + "/rpc";
131  return false;
132  }
133  m_rpcPort.setReader(*this);
134 
135  // Wait a little and then start if requested
136  if (config.check("start")) {
138  m_mic->startRecording();
139  }
140 
141  return true;
142 }
143 
145 {
146  if (m_mic != nullptr)
147  {
148  PeriodicThread::stop();
149  m_mic->stopRecording();
150  m_mic = nullptr;
151 
152  m_streamingPort.interrupt();
153  m_streamingPort.close();
154  m_rpcPort.interrupt();
155  m_rpcPort.close();
156 
157  return true;
158  }
159  return false;
160 }
161 
163 {
164 #ifdef DEBUG_TIME_SPENT
165  double current_time = yarp::os::Time::now();
166  yCDebug(AUDIORECORDERWRAPPER) << current_time - m_last_time;
167  m_last_time = current_time;
168 #endif
169 
170  if (m_mic == nullptr)
171  {
172  yCError(AUDIORECORDERWRAPPER) << "The IAudioGrabberSound interface is not available yet!";
173  return;
174  }
175 
176 #ifdef PRINT_DEBUG_MESSAGES
177  {
178  audio_buffer_size buf_max;
179  audio_buffer_size buf_cur;
180  mic->getRecordingAudioBufferMaxSize(buf_max);
181  mic->getRecordingAudioBufferCurrentSize(buf_cur);
182  yCDebug(AUDIORECORDERWRAPPER) << "BEFORE Buffer status:" << buf_cur.getBytes() << "/" << buf_max.getBytes() << "bytes";
183  }
184 #endif
185 
186  yarp::sig::Sound snd;
187  m_mic->getSound(snd, m_min_number_of_samples_over_network, m_max_number_of_samples_over_network, m_getSound_timeout);
188 
189  if (snd.getSamples() < m_min_number_of_samples_over_network ||
190  snd.getSamples() < m_max_number_of_samples_over_network)
191  {
192  yCWarning(AUDIORECORDERWRAPPER) << "subdevice->getSound() is not producing sounds of the requested size ("
193  << m_min_number_of_samples_over_network << "<"
194  << snd.getSamples() << "<"
195  << m_max_number_of_samples_over_network << ") failed";
196  }
197 
198 #ifdef PRINT_DEBUG_MESSAGES
199  {
200  audio_buffer_size buf_max;
201  audio_buffer_size buf_cur;
202  mic->getRecordingAudioBufferMaxSize(buf_max);
203  mic->getRecordingAudioBufferCurrentSize(buf_cur);
204  yCDebug(AUDIORECORDERWRAPPER) << "AFTER Buffer status:" << buf_cur.getBytes() << "/" << buf_max.getBytes() << "bytes";
205  }
206 #endif
207 #ifdef PRINT_DEBUG_MESSAGES
208  yCDebug(AUDIORECORDERWRAPPER) << "Sound size:" << snd.getSamples()*snd.getChannels()*snd.getBytesPerSample() << " bytes";
209  yCDebug(AUDIORECORDERWRAPPER);
210 #endif
211 
212  //prepare the timestamp
213  m_stamp.update();
214  m_streamingPort.setEnvelope(m_stamp);
215 
216  //check before sending data
217  if (snd.getSamples() == 0)
218  {
219  yCError(AUDIORECORDERWRAPPER) << "Subdevice produced sound of 0 samples!";
220  return;
221  }
222  if (snd.getChannels() == 0)
223  {
224  yCError(AUDIORECORDERWRAPPER) << "Subdevice produced sound of 0 channels!";
225  return;
226  }
227  if (snd.getFrequency() == 0)
228  {
229  yCError(AUDIORECORDERWRAPPER) << "Subdevice produced sound with 0 frequency!";
230  return;
231  }
232 
233  //send data
234  m_streamingPort.write(snd);
235 }
236 
238 {
239  yarp::os::Bottle command;
240  yarp::os::Bottle reply;
241  bool ok = command.read(connection);
242  if (!ok) return false;
243  reply.clear();
244 
245  if (command.get(0).asString()=="start")
246  {
247  m_mic->startRecording();
248  reply.addVocab(VOCAB_OK);
249  }
250  else if (command.get(0).asString() == "stop")
251  {
252  m_mic->stopRecording();
253  reply.addVocab(VOCAB_OK);
254  }
255  else if (command.get(0).asString() == "clear")
256  {
257  m_mic->resetRecordingAudioBuffer();
258  reply.addVocab(VOCAB_OK);
259  }
260  else if (command.get(0).asString() == "help")
261  {
262  reply.addVocab(yarp::os::Vocab::encode("many"));
263  reply.addString("start");
264  reply.addString("stop");
265  reply.addString("clear");
266  }
267  else
268  {
269  yCError(AUDIORECORDERWRAPPER) << "Invalid command";
270  reply.addVocab(VOCAB_ERR);
271  }
272 
273  yarp::os::ConnectionWriter *returnToSender = connection.getWriter();
274  if (returnToSender != nullptr)
275  {
276  reply.write(*returnToSender);
277  }
278  return true;
279 }
280 
282 {
283  if (device2attach.size() != 1)
284  {
285  yCError(AUDIORECORDERWRAPPER, "Cannot attach more than one device");
286  return false;
287  }
288 
289  yarp::dev::PolyDriver * Idevice2attach = device2attach[0]->poly;
290 
291  if (Idevice2attach->isValid())
292  {
293  Idevice2attach->view(m_mic);
294  }
295 
296  if (nullptr == m_mic)
297  {
298  yCError(AUDIORECORDERWRAPPER, "Subdevice passed to attach method is invalid");
299  return false;
300  }
301  attach(m_mic);
302 
303  PeriodicThread::setPeriod(m_period);
304  return PeriodicThread::start();
305 }
306 
308 {
309  if (PeriodicThread::isRunning())
310  {
311  PeriodicThread::stop();
312  }
313  m_mic = nullptr;
314  return true;
315 }
316 
318 {
319  m_mic = igrab;
320 }
321 
323 {
324  if (PeriodicThread::isRunning())
325  {
326  PeriodicThread::stop();
327  }
328  m_mic = nullptr;
329 }
330 
332 {
333  return true;
334 }
335 
337 {
338 /* m_audioInPort.interrupt();
339  m_audioInPort.close();
340  m_rpcPort.interrupt();
341  m_rpcPort.close();
342  m_statusPort.interrupt();
343  m_statusPort.close();*/
344 }
#define DEFAULT_THREAD_PERIOD
Definition: AnalogWrapper.h:46
constexpr yarp::conf::vocab32_t VOCAB_OK
Definition: GenericVocabs.h:18
constexpr yarp::conf::vocab32_t VOCAB_ERR
Definition: GenericVocabs.h:20
bool detachAll() override
Detach the object (you must have first called attach).
AudioRecorderWrapper()
Constructor.
void run() override
Loop function.
bool close() override
Close the DeviceDriver.
bool threadInit() override
Initialization method.
bool attachAll(const yarp::dev::PolyDriverList &p) override
Attach to a list of objects.
void threadRelease() override
Release method.
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.
bool view(T *&x)
Get an interface to the device driver.
Definition: DeviceDriver.h:77
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 stopRecording()=0
Stop the recording.
virtual bool startRecording()=0
Start the recording.
void push(PolyDriver *p, const char *k)
A container for a device driver.
Definition: PolyDriver.h:27
bool isValid() const
Check if device is valid.
Definition: PolyDriver.cpp:199
bool open(const std::string &txt)
Construct and configure a device by its common name.
Definition: PolyDriver.cpp:143
A simple collection of objects that can be described and transmitted in a portable way.
Definition: Bottle.h:73
bool read(ConnectionReader &reader) override
Set the bottle's value based on input from a network connection.
Definition: Bottle.cpp:243
void addVocab(int x)
Places a vocabulary item in the bottle, at the end of the list.
Definition: Bottle.cpp:167
Value & get(size_type index) const
Reads a Value v from a certain part of the list.
Definition: Bottle.cpp:249
void clear()
Empties the bottle of any objects it contains.
Definition: Bottle.cpp:124
bool write(ConnectionWriter &writer) const override
Output a representation of the bottle to a network connection.
Definition: Bottle.cpp:233
void addString(const char *str)
Places a string in the bottle, at the end of the list.
Definition: Bottle.cpp:173
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 write(const PortWriter &writer, const PortWriter *callback=nullptr) const override
Write an object to the port.
Definition: Port.cpp:430
void setReader(PortReader &reader) override
Set an external reader for port data.
Definition: Port.cpp:505
void interrupt() override
Interrupt any current reads or writes attached to the port.
Definition: Port.cpp:377
bool setEnvelope(PortWriter &envelope) override
Set an envelope (e.g., a timestamp) to the next message which will be sent.
Definition: Port.cpp:541
void close() override
Stop port activity.
Definition: Port.cpp:357
bool open(const std::string &name) override
Start port operation, with a specific name, with automatically-chosen network parameters.
Definition: Port.cpp:82
A class for storing options and configuration information.
Definition: Property.h:37
void fromString(const std::string &txt, bool wipe=true)
Interprets a string as a list of properties.
Definition: Property.cpp:1046
void put(const std::string &key, const std::string &value)
Associate the given key with the given string.
Definition: Property.cpp:998
A base class for nested structures that can be searched.
Definition: Searchable.h:69
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.
void update()
Set the timestamp to the current time, and increment the sequence number (wrapping to 0 if the sequen...
Definition: Stamp.cpp:113
static void delaySystem(double seconds)
Definition: SystemClock.cpp:32
virtual yarp::conf::float64_t asFloat64() const
Get 64-bit floating point value.
Definition: Value.cpp:225
virtual std::int64_t asInt64() const
Get 64-bit integer value.
Definition: Value.cpp:213
virtual std::string asString() const
Get string value.
Definition: Value.cpp:237
Class for storing sounds.
Definition: Sound.h:28
size_t getBytesPerSample() const
Get the number of bytes per sample.
Definition: Sound.cpp:399
size_t getChannels() const
Get the number of channels of the sound.
Definition: Sound.cpp:409
int getFrequency() const
Get the frequency of the sound (i.e.
Definition: Sound.cpp:221
size_t getSamples() const
Get the number of samples contained in the sound.
Definition: Sound.cpp:404
#define yCInfo(component,...)
Definition: LogComponent.h:135
#define yCError(component,...)
Definition: LogComponent.h:157
#define yCWarning(component,...)
Definition: LogComponent.h:146
#define yCDebug(component,...)
Definition: LogComponent.h:112
#define YARP_LOG_COMPONENT(name,...)
Definition: LogComponent.h:80
yarp::rosmsg::std_msgs::Time Time
Definition: Time.h:24
An interface for the device drivers.
double now()
Return the current time in seconds, relative to an arbitrary starting point.
Definition: Time.cpp:124
NetInt32 encode(const std::string &str)
Convert a string into a vocabulary identifier.
Definition: Vocab.cpp:14
The main, catch-all namespace for YARP.
Definition: environment.h:18