YARP
Yet Another Robot Platform
AudioPlayerDeviceBase.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 
6 #define _USE_MATH_DEFINES
7 
9 #include <yarp/os/LogStream.h>
10 #include <mutex>
11 #include <limits>
12 #include <cmath>
13 #include <functional>
14 
15 using namespace yarp::os;
16 using namespace yarp::dev;
17 
18 constexpr double c_sleep_time=0.005;
19 
20 YARP_LOG_COMPONENT(AUDIOPLAYER_BASE, "yarp.devices.AudioPlayerDeviceBase")
21 
22 //the following macros should never be modified and are used only for development purposes
23 #define DEBUG_TIME_SPENT 0
24 
25 //Default device parameters
26 #define DEFAULT_SAMPLE_RATE (44100)
27 #define DEFAULT_NUM_CHANNELS (2)
28 #define DEFAULT_SAMPLE_SIZE (2)
29 
30 bool AudioPlayerDeviceBase::getPlaybackAudioBufferCurrentSize(yarp::dev::AudioBufferSize& size)
31 {
32  if (m_outputBuffer == nullptr)
33  {
34  yCError(AUDIOPLAYER_BASE) << "getPlaybackAudioBufferCurrentSize() called, but no audio buffer is allocated yet";
35  return false;
36  }
37  //no lock guard is needed here
38  size = this->m_outputBuffer->size();
39  return true;
40 }
41 
42 bool AudioPlayerDeviceBase::getPlaybackAudioBufferMaxSize(yarp::dev::AudioBufferSize& size)
43 {
44  if (m_outputBuffer == nullptr)
45  {
46  yCError(AUDIOPLAYER_BASE) << "getPlaybackAudioBufferMaxSize() called, but no audio buffer is allocated yet";
47  return false;
48  }
49  //no lock guard is needed here
50  size = this->m_outputBuffer->getMaxSize();
51  return true;
52 }
53 
54 AudioPlayerDeviceBase::~AudioPlayerDeviceBase()
55 {
56  delete m_outputBuffer;
57 }
58 
59 bool AudioPlayerDeviceBase::setSWGain(double gain)
60 {
61  std::lock_guard<std::recursive_mutex> lock(m_mutex);
62  if (gain>0)
63  {
64  m_sw_gain = gain;
65  return true;
66  }
67  return false;
68 }
69 
70 bool AudioPlayerDeviceBase::startPlayback()
71 {
72  std::lock_guard<std::recursive_mutex> lock(m_mutex);
73  m_playback_enabled = true;
74  if (m_enable_buffer_autoclear && this->m_outputBuffer)
75  {this->m_outputBuffer->clear();}
76  yCInfo(AUDIOPLAYER_BASE) << "Playback started";
77  return true;
78 }
79 
80 bool AudioPlayerDeviceBase::stopPlayback()
81 {
82  std::lock_guard<std::recursive_mutex> lock(m_mutex);
83  m_playback_enabled = false;
84  if (m_enable_buffer_autoclear && this->m_outputBuffer)
85  {this->m_outputBuffer->clear();}
86  yCInfo(AUDIOPLAYER_BASE) << "Playback stopped";
87  return true;
88 }
89 
90 bool AudioPlayerDeviceBase::isPlaying(bool& playback_enabled)
91 {
92  playback_enabled = m_playback_enabled;
93  return true;
94 }
95 
96 bool AudioPlayerDeviceBase::resetPlaybackAudioBuffer()
97 {
98  if (m_outputBuffer == nullptr)
99  {
100  yCError(AUDIOPLAYER_BASE) << "resetPlaybackAudioBuffer() called, but no audio buffer is allocated yet";
101  return false;
102  }
103  std::lock_guard<std::recursive_mutex> lock(m_mutex);
104  this->m_outputBuffer->clear();
105  return true;
106 }
107 
108 bool AudioPlayerDeviceBase::appendSound(const yarp::sig::Sound& sound)
109 {
110  //Do I need a lockguard?
111  size_t num_channels = sound.getChannels();
112  size_t num_samples = sound.getSamples();
113 
114  for (size_t i = 0; i < num_samples; i++) {
115  for (size_t j = 0; j < num_channels; j++) {
116  m_outputBuffer->write(sound.get(i, j));
117  }
118  }
119 
120  return true;
121 }
122 
123 bool AudioPlayerDeviceBase::immediateSound(const yarp::sig::Sound& sound)
124 {
125  //Do I need a lockguard?
126  m_outputBuffer->clear();
127 
128  size_t num_channels = sound.getChannels();
129  size_t num_samples = sound.getSamples();
130 
131  for (size_t i = 0; i < num_samples; i++) {
132  for (size_t j = 0; j < num_channels; j++) {
133  m_outputBuffer->write(sound.get(i, j));
134  }
135  }
136 
137  return true;
138 }
139 
140 bool AudioPlayerDeviceBase::renderSound(const yarp::sig::Sound& sound)
141 {
142  //prevents simultaneous start/stop/reset etc.
143  std::lock_guard<std::recursive_mutex> lock(m_mutex);
144 
145  size_t freq = sound.getFrequency();
146  size_t chans = sound.getChannels();
147  if (freq == 0)
148  {
149  yCError(AUDIOPLAYER_BASE) << "received a bad audio sample of frequency 0";
150  return false;
151  }
152  if (chans == 0)
153  {
154  yCError(AUDIOPLAYER_BASE) << "received a bad audio sample with 0 channels";
155  return false;
156  }
157 
158  //process the sound, if required
159  yarp::sig::Sound procsound = sound;
160  if (m_sw_gain != 1.0)
161  {
162  procsound.amplify(m_sw_gain);
163  }
164 
165  if (freq != this->m_audioplayer_cfg.frequency ||
166  chans != this->m_audioplayer_cfg.numChannels)
167  {
168  //wait for current playback to finish
169  waitUntilPlaybackStreamIsComplete();
170 
171  //reset the driver
172  yCInfo(AUDIOPLAYER_BASE, "***** audio driver configuration changed, resetting");
173  yCInfo(AUDIOPLAYER_BASE) << "changing from: " << this->m_audioplayer_cfg.numChannels << "channels, " << this->m_audioplayer_cfg.frequency << " Hz, ->" <<
174  chans << "channels, " << freq << " Hz";
175 
176  bool was_playing = this->m_playback_enabled;
177  //close is called in order to destroy the buffer
178  this->interruptDeviceAndClose();
179 
180  //The device is re-opened with new configuration parameters
181  m_audioplayer_cfg.numChannels = (int)(chans);
182  m_audioplayer_cfg.frequency = (int)(freq);
183  bool ok = configureDeviceAndStart();
184  if (ok == false)
185  {
186  yCError(AUDIOPLAYER_BASE, "error occurred during audio driver reconfiguration, aborting");
187  return false;
188  }
189  //restore the playback_enabled status before device reconfiguration
190  if (was_playing)
191  {
192  this->startPlayback();
193  }
194  }
195 
196  if (m_renderMode == RENDER_IMMEDIATE) {
197  return immediateSound(procsound);
198  } else if (m_renderMode == RENDER_APPEND) {
199  return appendSound(procsound);
200  }
201 
202  return false;
203 }
204 
205 bool AudioPlayerDeviceBase::configurePlayerAudioDevice(yarp::os::Searchable& config, std::string device_name)
206 {
207  m_audioplayer_cfg.frequency = config.check("rate", Value(0), "audio sample rate (0=automatic)").asInt32();
208  m_audioplayer_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();
209  m_audioplayer_cfg.numChannels = config.check("channels", Value(0), "number of audio channels (0=automatic, max is 2)").asInt32();
210  m_hw_gain = config.check("hw_gain", Value(1.0), "HW gain").asFloat32();
211  m_sw_gain = config.check("sw_gain", Value(1.0), "SW gain").asFloat32();
212 
213  if (m_audioplayer_cfg.numChannels == 0) {
214  m_audioplayer_cfg.numChannels = DEFAULT_NUM_CHANNELS;
215  }
216  if (m_audioplayer_cfg.frequency == 0) {
217  m_audioplayer_cfg.frequency = DEFAULT_SAMPLE_RATE;
218  }
219  if (m_audioplayer_cfg.numSamples == 0) {
220  m_audioplayer_cfg.numSamples = m_audioplayer_cfg.frequency; // by default let's use chunks of 1 second
221  }
222 
223  if (config.check("render_mode_append"))
224  {
225  m_renderMode = RENDER_APPEND;
226  }
227  if (config.check("render_mode_immediate"))
228  {
229  m_renderMode = RENDER_IMMEDIATE;
230  }
231 
232  yCInfo(AUDIOPLAYER_BASE) << "Device configured with the following options:";
233  yCInfo(AUDIOPLAYER_BASE) << "Frequency:" << m_audioplayer_cfg.frequency;
234  yCInfo(AUDIOPLAYER_BASE) << "Samples (buffer size):" << m_audioplayer_cfg.numSamples;
235  yCInfo(AUDIOPLAYER_BASE) << "Channels:" << m_audioplayer_cfg.numChannels;
236  yCInfo(AUDIOPLAYER_BASE) << "BytesForSample:" << m_audioplayer_cfg.bytesPerSample;
237  yCInfo(AUDIOPLAYER_BASE) << "HW gain:" << m_hw_gain;
238  yCInfo(AUDIOPLAYER_BASE) << "SW gain:" << m_sw_gain;
239  yCInfo(AUDIOPLAYER_BASE) << "Render mode:" << (m_renderMode==RENDER_APPEND?"append":"immediate");
240 
241  //create the buffer
242  AudioBufferSize buffer_size(m_audioplayer_cfg.numSamples, m_audioplayer_cfg.numChannels, m_audioplayer_cfg.bytesPerSample);
243  if (m_outputBuffer == nullptr)
244  {
245  m_outputBuffer = new yarp::dev::CircularAudioBuffer_16t(device_name, buffer_size);
246  }
247 
248  //additional options
249  m_enable_buffer_autoclear = config.check("buffer_autoclear", Value(false), "Automatically clear the buffer every time the devices is started/stopped").asBool();
250  m_audiobase_debug = config.check("debug", Value(false), "Enable debug mode").asBool();
251 
252  return true;
253 }
254 
255 void AudioPlayerDeviceBase::waitUntilPlaybackStreamIsComplete()
256 {
257  if (m_outputBuffer == nullptr)
258  {
259  yCError(AUDIOPLAYER_BASE) << "waitUntilPlaybackStreamIsComplete() called, but no audio buffer is allocated yet";
260  return;
261  }
262 
263  while (m_outputBuffer->size().getSamples() != 0)
264  {
265  yarp::os::Time::delay(0.20);
266  }
267 }
#define DEFAULT_NUM_CHANNELS
const yarp::os::LogComponent & AUDIOPLAYER_BASE()
#define DEFAULT_SAMPLE_RATE
constexpr double c_sleep_time
A base class for nested structures that can be searched.
Definition: Searchable.h:66
virtual bool check(const std::string &key) const =0
Check if there exists a property of the given name.
A single value (typically within a Bottle).
Definition: Value.h:45
Class for storing sounds See Audio in YARP for additional documentation on YARP audio.
Definition: Sound.h:26
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
int getFrequency() const
Get the frequency of the sound (i.e.
Definition: Sound.cpp:224
audio_sample get(size_t sample, size_t channel=0) const
Definition: Sound.cpp:175
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 YARP_LOG_COMPONENT(name,...)
Definition: LogComponent.h:77
An interface for the device drivers.
yarp::dev::CircularAudioBuffer< unsigned short int > CircularAudioBuffer_16t
void delay(double seconds)
Wait for a certain number of seconds.
Definition: Time.cpp:111
An interface to the operating system, including Port based communication.