YARP
Yet Another Robot Platform
AudioRecorderDeviceBase.cpp
Go to the documentation of this file.
1 /*
2  * SPDX-FileCopyrightText: 2006-2021 Istituto Italiano di Tecnologia (IIT)
3  * SPDX-License-Identifier: BSD-3-Clause
4  */
5 
7 #include <yarp/os/LogStream.h>
8 #include <mutex>
9 #include <limits>
10 #include <functional>
11 
12 using namespace yarp::os;
13 using namespace yarp::dev;
14 
15 constexpr double c_sleep_time=0.005;
16 
17 YARP_LOG_COMPONENT(AUDIORECORDER_BASE, "yarp.devices.AudioRecorderDeviceBase")
18 
19 //the following macros should never be modified and are used only for development purposes
20 #define AUTOMATIC_REC_START 0
21 #define DEBUG_TIME_SPENT 0
22 
23 //Default device parameters
24 #define DEFAULT_SAMPLE_RATE (44100)
25 #define DEFAULT_NUM_CHANNELS (2)
26 #define DEFAULT_SAMPLE_SIZE (2)
27 
28 bool AudioRecorderDeviceBase::getSound(yarp::sig::Sound& sound, size_t min_number_of_samples, size_t max_number_of_samples, double max_samples_timeout_s)
29 {
30  //check for something_to_record
31  {
32  #if AUTOMATIC_REC_START
33  if (m_isRecording == false)
34  {
35  this->startRecording();
36  }
37  #else
38  double debug_time = yarp::os::Time::now();
39  while (m_recording_enabled == false)
40  {
41  if (yarp::os::Time::now() - debug_time > 5.0)
42  {
43  yCInfo(AUDIORECORDER_BASE) << "getSound() is currently waiting. Use startRecording() to start the audio stream";
44  debug_time = yarp::os::Time::now();
45  }
47  }
48  #endif
49  }
50 
51  //prevents simultaneous start/stop/reset etc.
52  //std::lock_guard<std::mutex> lock(m_mutex); //This must be used carefully
53 
54  //check on input parameters
55  if (max_number_of_samples < min_number_of_samples)
56  {
57  yCError(AUDIORECORDER_BASE) << "max_number_of_samples must be greater than min_number_of_samples!";
58  return false;
59  }
60  if (max_number_of_samples > this->m_audiorecorder_cfg.numSamples)
61  {
62  yCWarning(AUDIORECORDER_BASE) << "max_number_of_samples ("<< max_number_of_samples <<") is bigger than the internal audio buffer! It will be truncated to:" << this->m_audiorecorder_cfg.numSamples;
63  max_number_of_samples = this->m_audiorecorder_cfg.numSamples;
64  }
65 
66  //wait until the desired number of samples are obtained
67  size_t buff_size = 0;
68  double start_time = yarp::os::Time::now();
69  double debug_time = yarp::os::Time::now();
70  do
71  {
72  buff_size = m_inputBuffer->size().getSamples();
73  if (buff_size >= max_number_of_samples) { break; }
74  if (buff_size >= min_number_of_samples && yarp::os::Time::now() - start_time > max_samples_timeout_s) { break; }
75  if (m_recording_enabled == false) { break; }
76 
77  if (yarp::os::Time::now() - debug_time > 1.0)
78  {
79  debug_time = yarp::os::Time::now();
80  yCDebug(AUDIORECORDER_BASE) << "getSound() Buffer size is " << buff_size << "/" << max_number_of_samples << " after 1s";
81  }
83  } while (true);
84 
85  //prepare the sound data struct
86  size_t samples_to_be_copied = buff_size;
87  if (samples_to_be_copied > max_number_of_samples) {
88  samples_to_be_copied = max_number_of_samples;
89  }
90  if (sound.getChannels() != this->m_audiorecorder_cfg.numChannels && sound.getSamples() != samples_to_be_copied)
91  {
92  sound.resize(samples_to_be_copied, this->m_audiorecorder_cfg.numChannels);
93  }
94  sound.setFrequency(this->m_audiorecorder_cfg.frequency);
95 
96  //fill the sound data struct, reading samples from the circular buffer
97  #if DEBUG_TIME_SPENT
98  double ct1 = yarp::os::Time::now();
99  #endif
100  for (size_t i = 0; i < samples_to_be_copied; i++)
101  {
102  for (size_t j = 0; j < this->m_audiorecorder_cfg.numChannels; j++)
103  {
104  int16_t s = (int16_t)(m_inputBuffer->read());
105  if (s > (std::numeric_limits<int16_t>::max() - m_cliptol) ||
106  s < (std::numeric_limits<int16_t>::min() + m_cliptol))
107  {
108  yCWarningThrottle(AUDIORECORDER_BASE, 0.1) << "Sound clipped!";
109  }
110  sound.set(s, i, j);
111  }
112  }
113 
114  //amplify if required
115  if (m_sw_gain!=1.0) {sound.amplify(m_sw_gain);}
116 
117  auto debug_p = sound.getInterleavedAudioRawData();
118  #if DEBUG_TIME_SPENT
119  double ct2 = yarp::os::Time::now();
120  yCDebug(AUDIORECORDER_BASE) << ct2 - ct1;
121  #endif
122  return true;
123 }
124 
125 bool AudioRecorderDeviceBase::getRecordingAudioBufferMaxSize(yarp::dev::AudioBufferSize& size)
126 {
127  if (m_inputBuffer == nullptr)
128  {
129  yCError(AUDIORECORDER_BASE) << "getRecordingAudioBufferMaxSize() called, but no audio buffer is allocated yet";
130  return false;
131  }
132  //no lock guard is needed here
133  size = this->m_inputBuffer->getMaxSize();
134  return true;
135 }
136 
137 
138 bool AudioRecorderDeviceBase::getRecordingAudioBufferCurrentSize(yarp::dev::AudioBufferSize& size)
139 {
140  if (m_inputBuffer == nullptr)
141  {
142  yCError(AUDIORECORDER_BASE) << "getRecordingAudioBufferCurrentSize() called, but no audio buffer is allocated yet";
143  return false;
144  }
145  //no lock guard is needed here
146  size = this->m_inputBuffer->size();
147  return true;
148 }
149 
150 bool AudioRecorderDeviceBase::setSWGain(double gain)
151 {
152  std::lock_guard<std::mutex> lock(m_mutex);
153  if (gain > 0)
154  {
155  m_sw_gain = gain;
156  return true;
157  }
158  return false;
159 }
160 
161 bool AudioRecorderDeviceBase::resetRecordingAudioBuffer()
162 {
163  if (m_inputBuffer == nullptr)
164  {
165  yCError(AUDIORECORDER_BASE) << "resetRecordingAudioBuffer() called, but no audio buffer is allocated yet";
166  return false;
167  }
168  std::lock_guard<std::mutex> lock(m_mutex);
169  m_inputBuffer->clear();
170  yCDebug(AUDIORECORDER_BASE) << "resetRecordingAudioBuffer";
171  return true;
172 }
173 
174 bool AudioRecorderDeviceBase::startRecording()
175 {
176  std::lock_guard<std::mutex> lock(m_mutex);
177  m_recording_enabled = true;
178  if (m_enable_buffer_autoclear && this->m_inputBuffer)
179  {
180  this->m_inputBuffer->clear();
181  }
182  yCInfo(AUDIORECORDER_BASE) << "Recording started";
183  return true;
184 }
185 
186 bool AudioRecorderDeviceBase::stopRecording()
187 {
188  std::lock_guard<std::mutex> lock(m_mutex);
189  m_recording_enabled = false;
190  if (m_enable_buffer_autoclear && this->m_inputBuffer)
191  {
192  //In this case we do not want to clear the because we want to transmit the last sound frame
193  //which has been partially captured until the stopRecording has been called.
194  //this->m_inputBuffer->clear();
195  }
196  yCInfo(AUDIORECORDER_BASE) << "Recording stopped";
197  return true;
198 }
199 
200 bool AudioRecorderDeviceBase::isRecording(bool& recording_enabled)
201 {
202  recording_enabled = m_recording_enabled;
203  return true;
204 }
205 
206 AudioRecorderDeviceBase::~AudioRecorderDeviceBase()
207 {
208  delete m_inputBuffer;
209 }
210 
211 bool AudioRecorderDeviceBase::configureRecorderAudioDevice(yarp::os::Searchable& config, std::string device_name)
212 {
213  m_audiorecorder_cfg.frequency = config.check("rate", Value(0), "audio sample rate (0=automatic)").asInt32();
214  m_audiorecorder_cfg.numSamples = config.check("samples", Value(0), "number of samples per network packet (0=automatic). For chunks of 1 second of recording set samples=rate. Channels number is handled internally.").asInt32();
215  m_audiorecorder_cfg.numChannels = config.check("channels", Value(0), "number of audio channels (0=automatic, max is 2)").asInt32();
216  m_hw_gain = config.check("hw_gain", Value(1.0), "HW gain").asFloat32();
217  m_sw_gain = config.check("sw_gain", Value(1.0), "SW gain").asFloat32();
218 
219  if (m_audiorecorder_cfg.frequency == 0) {
220  m_audiorecorder_cfg.frequency = DEFAULT_SAMPLE_RATE;
221  }
222  if (m_audiorecorder_cfg.numChannels == 0) {
223  m_audiorecorder_cfg.numChannels = DEFAULT_NUM_CHANNELS;
224  }
225  if (m_audiorecorder_cfg.numSamples == 0) {
226  m_audiorecorder_cfg.numSamples = m_audiorecorder_cfg.frequency; // by default let's use chunks of 1 second
227  }
228 
229  yCInfo(AUDIORECORDER_BASE) << "Device configured with the following options:";
230  yCInfo(AUDIORECORDER_BASE) << "Frequency:"<< m_audiorecorder_cfg.frequency;
231  yCInfo(AUDIORECORDER_BASE) << "Samples (buffer size):"<< m_audiorecorder_cfg.numSamples;
232  yCInfo(AUDIORECORDER_BASE) << "Channels:"<< m_audiorecorder_cfg.numChannels;
233  yCInfo(AUDIORECORDER_BASE) << "BytesForSample:"<< m_audiorecorder_cfg.bytesPerSample;
234  yCInfo(AUDIORECORDER_BASE) << "HW gain:" << m_hw_gain;
235  yCInfo(AUDIORECORDER_BASE) << "SW gain:" << m_sw_gain;
236 
237  AudioBufferSize rec_buffer_size(m_audiorecorder_cfg.numSamples, m_audiorecorder_cfg.numChannels, m_audiorecorder_cfg.bytesPerSample);
238  if (m_inputBuffer == nullptr)
239  {
240  m_inputBuffer = new CircularAudioBuffer_16t(device_name, rec_buffer_size);
241  }
242 
243  //additional options
244  m_enable_buffer_autoclear = config.check("buffer_autoclear", Value(true), "Automatically clear the buffer every time the devices is started/stopped").asBool();
245  m_audiobase_debug = config.check("debug", Value(false), "Enable debug mode").asBool();
246 
247  return true;
248 }
const yarp::os::LogComponent & AUDIORECORDER_BASE()
#define DEFAULT_NUM_CHANNELS
#define DEFAULT_SAMPLE_RATE
constexpr double c_sleep_time
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.
static void delaySystem(double seconds)
Definition: SystemClock.cpp:29
A single value (typically within a Bottle).
Definition: Value.h:43
Class for storing sounds See Audio in YARP for additional documentation on YARP audio.
Definition: Sound.h:25
void setFrequency(int freq)
Set the frequency of the sound (i.e.
Definition: Sound.cpp:229
std::vector< std::reference_wrapper< audio_sample > > getInterleavedAudioRawData() const
Returns a serialized version of the sound, in interleaved format, e.g.
Definition: Sound.cpp:358
size_t getChannels() const
Get the number of channels of the sound.
Definition: Sound.cpp:424
void amplify(double gain)
amplify a sound
Definition: Sound.cpp:466
void resize(size_t samples, size_t channels=1)
Set the sound size.
Definition: Sound.cpp:168
void set(audio_sample value, size_t sample, size_t channel=0)
Definition: Sound.cpp:209
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
#define yCWarningThrottle(component, period,...)
Definition: LogComponent.h:195
For streams capable of holding different kinds of content, check what they actually have.
yarp::dev::CircularAudioBuffer< unsigned short int > CircularAudioBuffer_16t
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.