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
12using namespace yarp::os;
13using namespace yarp::dev;
14
15constexpr double c_sleep_time=0.005;
16
17YARP_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
28bool 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 }
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
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
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
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
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);
170 yCDebug(AUDIORECORDER_BASE) << "resetRecordingAudioBuffer";
171 return true;
172}
173
175{
176 std::lock_guard<std::mutex> lock(m_mutex);
177 m_recording_enabled = true;
179 {
180 this->m_inputBuffer->clear();
181 }
182 yCInfo(AUDIORECORDER_BASE) << "Recording started";
183 return true;
184}
185
187{
188 std::lock_guard<std::mutex> lock(m_mutex);
189 m_recording_enabled = false;
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
200bool AudioRecorderDeviceBase::isRecording(bool& recording_enabled)
201{
202 recording_enabled = m_recording_enabled;
203 return true;
204}
205
207{
208 delete m_inputBuffer;
209}
210
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
221 }
224 }
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:";
231 yCInfo(AUDIORECORDER_BASE) << "Samples (buffer size):"<< m_audiorecorder_cfg.numSamples;
234 yCInfo(AUDIORECORDER_BASE) << "HW gain:" << m_hw_gain;
235 yCInfo(AUDIORECORDER_BASE) << "SW gain:" << m_sw_gain;
236
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}
#define DEFAULT_NUM_CHANNELS
const yarp::os::LogComponent & AUDIORECORDER_BASE()
#define DEFAULT_SAMPLE_RATE
constexpr double c_sleep_time
yarp::dev::CircularAudioBuffer_16t * m_inputBuffer
virtual bool getRecordingAudioBufferCurrentSize(yarp::dev::AudioBufferSize &size) override
virtual bool setSWGain(double gain) override
Sets a software gain for the grabbed audio.
virtual bool stopRecording() override
Stop the recording.
virtual bool startRecording() override
Start the recording.
virtual bool getRecordingAudioBufferMaxSize(yarp::dev::AudioBufferSize &size) override
bool configureRecorderAudioDevice(yarp::os::Searchable &config, std::string device_name)
virtual bool isRecording(bool &recording_enabled) override
Check if the recording has been enabled (e.g.
AudioDeviceDriverSettings m_audiorecorder_cfg
yarp::dev::AudioBufferSize getMaxSize()
A base class for nested structures that can be searched.
Definition: Searchable.h:56
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.