YARP
Yet Another Robot Platform
PortAudioDeviceDriver.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 
7 
8 #include <cstdio>
9 #include <cstdlib>
10 #include <cstring>
11 #include <portaudio.h>
12 #include <yarp/dev/DeviceDriver.h>
13 #include <yarp/dev/api.h>
14 
15 #include <yarp/os/Time.h>
16 #include <yarp/os/LogComponent.h>
17 #include <yarp/os/LogStream.h>
18 
19 using namespace yarp::os;
20 using namespace yarp::dev;
21 
22 #define SLEEP_TIME 0.005f
23 
24 #if 0
25 #define PA_SAMPLE_TYPE paFloat32
26 typedef float SAMPLE;
27 #define SAMPLE_SILENCE (0.0f)
28 #elif 1
29 #define PA_SAMPLE_TYPE paInt16
30 typedef short SAMPLE;
31 #define SAMPLE_SILENCE (0)
32 #elif 1
33 #define PA_SAMPLE_TYPE paInt8
34 typedef char SAMPLE;
35 #define SAMPLE_SILENCE (0)
36 #else
37 #define PA_SAMPLE_TYPE paUInt8
38 typedef unsigned char SAMPLE;
39 #define SAMPLE_SILENCE (128)
40 #define SAMPLE_UNSIGNED
41 #endif
42 
43 namespace {
44 YARP_LOG_COMPONENT(PORTAUDIO, "yarp.devices.portaudio")
45 }
46 
47 /* This routine will be called by the PortAudio engine when audio is needed.
48 ** It may be called at interrupt level on some machines so don't do anything
49 ** that could mess up the system like calling malloc() or free().
50 */
51 static int bufferIOCallback( const void *inputBuffer, void *outputBuffer,
52  unsigned long framesPerBuffer,
53  const PaStreamCallbackTimeInfo* timeInfo,
54  PaStreamCallbackFlags statusFlags,
55  void *userData )
56 {
57  auto* dataBuffers = static_cast<circularDataBuffers*>(userData);
58  CircularAudioBuffer_16t *playdata = dataBuffers->playData;
59  CircularAudioBuffer_16t *recdata = dataBuffers->recData;
60  int num_rec_channels = dataBuffers->numRecChannels;
61  int num_play_channels = dataBuffers->numPlayChannels;
62  int finished = paComplete;
63 
64  if (dataBuffers->canRec)
65  {
66  const auto* rptr = (const SAMPLE*)inputBuffer;
67  unsigned int framesToCalc;
68  unsigned int i;
69  size_t framesLeft = (recdata->getMaxSize().getSamples()* recdata->getMaxSize().getChannels()) -
70  (recdata->size().getSamples() * recdata->size().getChannels());
71 
72  YARP_UNUSED(outputBuffer);
73  YARP_UNUSED(timeInfo);
74  YARP_UNUSED(statusFlags);
75  YARP_UNUSED(userData);
76 
77  if( framesLeft/ num_rec_channels < framesPerBuffer )
78  {
79  framesToCalc = framesLeft/ num_rec_channels;
80 #ifdef STOP_REC_ON_EMPTY_BUFFER
81  //if we return paComplete, then the callback is not called anymore.
82  //method Pa_IsStreamActive() will return 1.
83  //user needs to call Pa_StopStream() before starting a new recording session
84  finished = paComplete;
85 #else
86  finished = paContinue;
87 #endif
88  }
89  else
90  {
91  framesToCalc = framesPerBuffer;
92  //if we return paContinue, then the callback will be invoked again later
93  //method Pa_IsStreamActive() will return 0
94  finished = paContinue;
95  }
96 
97  if( inputBuffer == nullptr )
98  {
99  for( i=0; i<framesToCalc; i++ )
100  {
101  recdata->write(0); // left
102  if (num_rec_channels == 2) {
103  recdata->write(0); // right
104  }
105  }
106  }
107  else
108  {
109  yCTrace(PORTAUDIO) << "Writing" << framesToCalc*2*2 << "bytes in the circular buffer";
110  for( i=0; i<framesToCalc; i++ )
111  {
112  recdata->write(*rptr++); // left
113  if (num_rec_channels == 2) {
114  recdata->write(*rptr++); // right
115  }
116  }
117  }
118  //note: you can record or play but not simultaneously (for now)
119  return finished;
120  }
121 
122  if (dataBuffers->canPlay)
123  {
124  auto* wptr = (SAMPLE*)outputBuffer;
125  unsigned int i;
126 
127  size_t framesLeft = playdata->size().getSamples()* playdata->size().getChannels();
128 
129  YARP_UNUSED(inputBuffer); // just to prevent unused variable warnings
130  YARP_UNUSED(timeInfo);
131  YARP_UNUSED(statusFlags);
132  YARP_UNUSED(userData);
133 
134  if( framesLeft/ num_play_channels < framesPerBuffer )
135  {
136  // final buffer
137  for( i=0; i<framesLeft/ num_play_channels; i++ )
138  {
139  *wptr++ = playdata->read(); // left
140  if (num_play_channels == 2) {
141  *wptr++ = playdata->read(); // right
142  }
143  for (int chs = 2; chs < num_play_channels; chs++) {
144  playdata->read(); //remove all additional channels > 2
145  }
146  }
147  for( ; i<framesPerBuffer; i++ )
148  {
149  *wptr++ = 0; // left
150  if (num_play_channels == 2) {
151  *wptr++ = 0; // right
152  }
153  }
154 #ifdef STOP_PLAY_ON_EMPTY_BUFFER
155  //if we return paComplete, then the callback is not called anymore.
156  //method Pa_IsStreamActive() will return 1.
157  //user needs to call Pa_StopStream() before starting a new recording session
158  finished = paComplete;
159 #else
160  finished = paContinue;
161 #endif
162  }
163  else
164  {
165 #if 1
166  yCDebug(PORTAUDIO) << "Reading" << framesPerBuffer*2 << "bytes from the circular buffer";
167 #endif
168  for( i=0; i<framesPerBuffer; i++ )
169  {
170  *wptr++ = playdata->read(); // left
171  if (num_play_channels == 2) {
172  *wptr++ = playdata->read(); // right
173  }
174  for (int chs = 2; chs < num_play_channels; chs++) {
175  playdata->read(); //remove all additional channels > 2
176  }
177  }
178  //if we return paContinue, then the callback will be invoked again later
179  //method Pa_IsStreamActive() will return 0
180  finished = paContinue;
181  }
182  //note: you can record or play but not simultaneously (for now)
183  return finished;
184  }
185 
186  yCError(PORTAUDIO, "No read/write operations requested, aborting");
187  return paAbort;
188 }
189 
191  stream(nullptr),
192  err(paNoError),
193  numSamples(0),
194  numBytes(0),
195  m_system_resource(nullptr),
196  m_numPlaybackChannels(0),
197  m_numRecordChannels(0),
198  m_frequency(0),
199  m_loopBack(false),
200  m_getSoundIsNotBlocking(true),
201  renderMode(RENDER_APPEND)
202 {
203  memset(&inputParameters, 0, sizeof(PaStreamParameters));
204  memset(&outputParameters, 0, sizeof(PaStreamParameters));
205  memset(&m_driverConfig, 0, sizeof(PortAudioDeviceDriverSettings));
206 }
207 
209 {
210  close();
211 }
212 
213 
215 {
216  m_driverConfig.rate = config.check("rate",Value(0),"audio sample rate (0=automatic)").asInt32();
217  m_driverConfig.samples = 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();
218  m_driverConfig.playChannels = config.check("channels",Value(0),"number of audio channels (0=automatic, max is 2)").asInt32();
219  m_driverConfig.recChannels = config.check("channels", Value(0), "number of audio channels (0=automatic, max is 2)").asInt32();
220  m_driverConfig.wantRead = (bool)config.check("read","if present, just deal with reading audio (microphone)");
221  m_driverConfig.wantWrite = (bool)config.check("write","if present, just deal with writing audio (speaker)");
222  m_driverConfig.deviceNumber = config.check("id",Value(-1),"which portaudio index to use (-1=automatic)").asInt32();
223 
225  {
227  }
228 
229  if (config.check("loopback","if present, send audio read from microphone immediately back to speaker"))
230  {
231  yCError(PORTAUDIO, "loopback not yet implemented");
232  m_loopBack = true;
233  }
234 
235  if (config.check("render_mode_append"))
236  {
238  }
239  if (config.check("render_mode_immediate"))
240  {
242  }
243 
244  return open(m_driverConfig);
245 }
246 
248 {
249  int rate = config.rate;
250  int samples = config.samples;
251  int playChannels = config.playChannels;
252  int recChannels = config.recChannels;
253  bool wantRead = config.wantRead;
254  bool wantWrite = config.wantWrite;
255  int deviceNumber = config.deviceNumber;
256 
257  if (playChannels == 0) {
258  playChannels = DEFAULT_NUM_CHANNELS;
259  }
260  if (recChannels == 0) {
261  recChannels = DEFAULT_NUM_CHANNELS;
262  }
263  m_numPlaybackChannels = playChannels;
264  m_numRecordChannels = recChannels;
265 
266  if (rate == 0) {
267  rate = DEFAULT_SAMPLE_RATE;
268  }
269  m_frequency = rate;
270 
271  if (samples == 0) {
272  numSamples = m_frequency; // by default let's stream chunks of 1 second
273  } else {
274  numSamples = samples;
275  }
276 
277 // size_t numPlayBytes = numSamples * sizeof(SAMPLE) * m_numPlaybackChannels;
278 // size_t numRecBytes = numSamples * sizeof(SAMPLE) * m_numRecordChannels;
279 // int twiceTheBuffer = 2;
280  AudioBufferSize playback_buffer_size(numSamples, m_numPlaybackChannels, sizeof(SAMPLE));
281  AudioBufferSize rec_buffer_size (numSamples, m_numRecordChannels, sizeof(SAMPLE));
283  dataBuffers.numRecChannels = m_numRecordChannels;
284  if (dataBuffers.playData == nullptr) {
285  dataBuffers.playData = new CircularAudioBuffer_16t("portatudio_play", playback_buffer_size);
286  }
287  if (dataBuffers.recData == nullptr) {
288  dataBuffers.recData = new CircularAudioBuffer_16t("portatudio_rec", rec_buffer_size);
289  }
290  if (wantRead) {
291  dataBuffers.canRec = true;
292  }
293  if (wantWrite) {
294  dataBuffers.canPlay = true;
295  }
296 
297  err = Pa_Initialize();
298  if( err != paNoError )
299  {
300  yCError(PORTAUDIO, "portaudio system failed to initialize");
301  return false;
302  }
303 
304  inputParameters.device = (deviceNumber==-1)?Pa_GetDefaultInputDevice():deviceNumber;
305  yCInfo(PORTAUDIO, "Device number %d", inputParameters.device);
306  inputParameters.channelCount = m_numRecordChannels;
307  inputParameters.sampleFormat = PA_SAMPLE_TYPE;
308  if ((Pa_GetDeviceInfo( inputParameters.device ))!=nullptr) {
309  inputParameters.suggestedLatency = Pa_GetDeviceInfo( inputParameters.device )->defaultLowInputLatency;
310  }
311  inputParameters.hostApiSpecificStreamInfo = nullptr;
312 
313  outputParameters.device = (deviceNumber==-1)?Pa_GetDefaultOutputDevice():deviceNumber;
314  outputParameters.channelCount = m_numPlaybackChannels;
315  outputParameters.sampleFormat = PA_SAMPLE_TYPE;
316  outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;
317  outputParameters.hostApiSpecificStreamInfo = nullptr;
318 
319  err = Pa_OpenStream(
320  &stream,
321  wantRead?(&inputParameters):nullptr,
322  wantWrite?(&outputParameters):nullptr,
323  m_frequency,
325  paClipOff,
327  &dataBuffers );
328 
329  if( err != paNoError )
330  {
331  yCError(PORTAUDIO, "An error occurred while using the portaudio stream");
332  yCError(PORTAUDIO, "Error number: %d", err);
333  yCError(PORTAUDIO, "Error message: %s", Pa_GetErrorText(err));
334  }
335 
336  //start the thread
337  pThread.stream = stream;
338  pThread.start();
339 
340  return (err==paNoError);
341 }
342 
343 void streamThread::handleError()
344 {
345  Pa_Terminate();
346  if( err != paNoError )
347  {
348  yCError(PORTAUDIO, "An error occurred while using the portaudio stream");
349  yCError(PORTAUDIO, "Error number: %d", err);
350  yCError(PORTAUDIO, "Error message: %s\n", Pa_GetErrorText(err));
351  }
352 }
353 
355 {
356  //Pa_Terminate();
357  dataBuffers.playData->clear();
358 
359  if( err != paNoError )
360  {
361  yCError(PORTAUDIO, "An error occurred while using the portaudio stream");
362  yCError(PORTAUDIO, "Error number: %d", err);
363  yCError(PORTAUDIO, "Error message: %s", Pa_GetErrorText(err));
364  }
365 }
366 
368 {
369  pThread.stop();
370  if (stream != nullptr)
371  {
372  err = Pa_CloseStream( stream );
373  if( err != paNoError )
374  {
375  yCError(PORTAUDIO, "An error occurred while closing the portaudio stream");
376  yCError(PORTAUDIO, "Error number: %d", err);
377  yCError(PORTAUDIO, "Error message: %s", Pa_GetErrorText(err));
378  }
379  }
380 
381  if (this->dataBuffers.playData != nullptr)
382  {
383  delete this->dataBuffers.playData;
384  this->dataBuffers.playData = nullptr;
385  }
386  if (this->dataBuffers.recData != nullptr)
387  {
388  delete this->dataBuffers.recData;
389  this->dataBuffers.recData = nullptr;
390  }
391 
392  return (err==paNoError);
393 }
394 
396 {
397  pThread.something_to_record = true;
398  err = Pa_StartStream( stream );
399  if( err < 0 ) {handleError(); return false;}
400  return true;
401 }
402 
404 {
405  pThread.something_to_record = false;
406  err = Pa_StopStream( stream );
407  if( err < 0 ) {handleError(); return false;}
408  return true;
409 }
410 
411 bool PortAudioDeviceDriver::getSound(yarp::sig::Sound& sound, size_t min_number_of_samples, size_t max_number_of_samples, double max_samples_timeout_s)
412 {
413  if (pThread.something_to_record == false)
414  {
415  this->startRecording();
416  }
417 
418  size_t buff_size = 0;
419  int buff_size_wdt = 0;
420  do
421  {
422  buff_size = dataBuffers.recData->size().getSamples();
423 
424  if (buff_size_wdt > 100)
425  {
426  if (buff_size == 0)
427  {
428  yCError(PORTAUDIO) << "PortAudioDeviceDriver::getSound() Buffer size is still zero after 100 iterations, returning";
429  return false;
430  }
431  else
432  {
433  yCDebug(PORTAUDIO) << "PortAudioDeviceDriver::getSound() Buffer size is " << buff_size << "/" << this->numSamples <<" after 100 iterations";
435  {
436  yCError(PORTAUDIO) << "PortAudioDeviceDriver::getSound() is in not-blocking mode, returning";
437  return false;
438  }
439  }
440  }
441  buff_size_wdt++;
443  }
444  while (buff_size < this->numSamples);
445 
446  buff_size_wdt = 0;
447 
448  if (sound.getChannels()!=this->m_numRecordChannels && sound.getSamples() != this->numSamples)
449  {
450  sound.resize(this->numSamples,this->m_numRecordChannels);
451  }
452  sound.setFrequency(this->m_frequency);
453 
454  for (size_t i = 0; i < this->numSamples; i++) {
455  for (size_t j=0; j<this->m_numRecordChannels; j++)
456  {
457  SAMPLE s = dataBuffers.recData->read();
458  sound.set(s,i,j);
459  }
460  }
461  return true;
462 }
463 
465 {
466  yCInfo(PORTAUDIO, "=== Stopping and clearing stream.==="); fflush(stdout);
467  err = Pa_StopStream( stream );
468  if( err != paNoError )
469  {
470  yCError(PORTAUDIO, "abortSound: error occurred while stopping the portaudio stream" );
471  yCError(PORTAUDIO, "Error number: %d", err );
472  yCError(PORTAUDIO, "Error message: %s", Pa_GetErrorText( err ) );
473  }
474 
475  dataBuffers.playData->clear();
476 
477  return (err==paNoError);
478 }
479 
481 {
482 }
483 
485 {
486  something_to_play=false;
487  something_to_record=false;
488  err = paNoError;
489  return true;
490 }
491 
493 {
494  while(this->isStopping()==false)
495  {
496  if( something_to_play )
497  {
498  something_to_play = false;
499  err = Pa_StartStream( stream );
500  if( err != paNoError ) {handleError(); return;}
501 
502  while( ( err = Pa_IsStreamActive( stream ) ) == 1 )
503  {
505  }
506  if (err == 0)
507  {
508  yCDebug(PORTAUDIO) << "The playback stream has been stopped";
509  }
510  if( err < 0 )
511  {
512  handleError();
513  return;
514  }
515 
516  err = Pa_StopStream( stream );
517  //err = Pa_AbortStream( stream );
518  if( err < 0 )
519  {
520  handleError();
521  return;
522  }
523 
524  }
525 
527  {
528  while( ( err = Pa_IsStreamActive( stream ) ) == 1 )
529  {
531  }
532  if (err == 0)
533  {
534  Pa_StopStream(stream);
535  yCDebug(PORTAUDIO) << "The recording stream has been stopped";
536  something_to_record = false;
537  }
538  if( err < 0 )
539  {
540  handleError();
541  return;
542  }
543  }
544 
546  }
547  return;
548 }
549 
551 {
552  dataBuffers.playData->clear();
553 
554 // size_t num_bytes = sound.getBytesPerSample();
555  size_t num_channels = sound.getChannels();
556  size_t num_samples = sound.getSamples();
557 
558  for (size_t i = 0; i < num_samples; i++) {
559  for (size_t j = 0; j < num_channels; j++) {
560  dataBuffers.playData->write(sound.get(i, j));
561  }
562  }
563 
564  pThread.something_to_play = true;
565  return true;
566 }
567 
569 {
570  int freq = sound.getFrequency();
571  size_t chans = sound.getChannels();
572  if (freq != this->m_frequency ||
573  chans != this->m_numPlaybackChannels)
574  {
575  //wait for current playback to finish
576  while (Pa_IsStreamStopped(stream )==0)
577  {
579  }
580 
581  //reset the driver
582  yCInfo(PORTAUDIO, "***** audio driver configuration changed, resetting");
583  yCInfo(PORTAUDIO) << "changing from: " << this->m_numPlaybackChannels << "channels, " << this->m_frequency << " Hz, ->" <<
584  chans << "channels, " << freq << " Hz";
585  this->close();
586  m_driverConfig.playChannels = (int)(chans);
587  m_driverConfig.rate = (int)(freq);
588  bool ok = open(m_driverConfig);
589  if (ok == false)
590  {
591  yCError(PORTAUDIO, "error occurred during audio driver reconfiguration, aborting");
592  return false;
593  }
594  }
595 
596  if (renderMode == RENDER_IMMEDIATE) {
597  return immediateSound(sound);
598  } else if (renderMode == RENDER_APPEND) {
599  return appendSound(sound);
600  }
601 
602  return false;
603 }
604 
606 {
607 // size_t num_bytes = sound.getBytesPerSample();
608  size_t num_channels = sound.getChannels();
609  size_t num_samples = sound.getSamples();
610 
611  for (size_t i = 0; i < num_samples; i++) {
612  for (size_t j = 0; j < num_channels; j++) {
613  dataBuffers.playData->write(sound.get(i, j));
614  }
615  }
616 
617  pThread.something_to_play = true;
618  return true;
619 }
620 
622 {
623  size = this->dataBuffers.playData->size();
624  return true;
625 }
626 
628 {
629  size = this->dataBuffers.playData->getMaxSize();
630  return true;
631 }
632 
634 {
635  this->dataBuffers.playData->clear();
636  return true;
637 }
638 
640 {
641  size = this->dataBuffers.recData->size();
642  return true;
643 }
644 
646 {
647  size = this->dataBuffers.recData->getMaxSize();
648  return true;
649 }
650 
652 {
653  this->dataBuffers.recData->clear();
654  return true;
655 }
656 
658 {
659  pThread.something_to_play = true;
660  return true;
661 }
662 
664 {
665  pThread.something_to_play = false;
666  return true;
667 }
668 
670 {
671  yCError(PORTAUDIO,"Not yet implemented");
672  return false;
673 }
674 
676 {
677  yCError(PORTAUDIO, "Not yet implemented");
678  return false;
679 }
680 
681 bool PortAudioDeviceDriver::isPlaying(bool& playback_enabled)
682 {
683  playback_enabled = true;
684  return true;
685 }
686 
687 bool PortAudioDeviceDriver::isRecording(bool& recording_enabled)
688 {
689  recording_enabled = true;
690  return true;
691 }
#define DEFAULT_NUM_CHANNELS
#define DEFAULT_SAMPLE_RATE
int16_t * samples
#define PA_SAMPLE_TYPE
static int bufferIOCallback(const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo, PaStreamCallbackFlags statusFlags, void *userData)
#define SLEEP_TIME
short SAMPLE
#define DEFAULT_FRAMES_PER_BUFFER
bool getSound(yarp::sig::Sound &sound, size_t min_number_of_samples, size_t max_number_of_samples, double max_samples_timeout_s) override
Get a sound from a device.
bool startPlayback() override
Start the playback.
bool setSWGain(double gain) override
Sets a software gain for the grabbed audio.
bool open(yarp::os::Searchable &config) override
Open the DeviceDriver.
bool stopRecording() override
Stop the recording.
bool isPlaying(bool &playback_enabled) override
Check if the playback has been enabled (e.g.
bool getPlaybackAudioBufferCurrentSize(yarp::dev::AudioBufferSize &size) override
PortAudioDeviceDriverSettings m_driverConfig
bool resetRecordingAudioBuffer() override
bool getPlaybackAudioBufferMaxSize(yarp::dev::AudioBufferSize &size) override
bool setHWGain(double gain) override
Sets the hardware gain of the grabbing device (if supported by the hardware)
bool startRecording() override
Start the recording.
bool getRecordingAudioBufferCurrentSize(yarp::dev::AudioBufferSize &size) override
bool resetPlaybackAudioBuffer() override
bool renderSound(const yarp::sig::Sound &sound) override
Render a sound using a device (i.e.
bool isRecording(bool &recording_enabled) override
Check if the recording has been enabled (e.g.
bool appendSound(const yarp::sig::Sound &sound)
enum PortAudioDeviceDriver::@90 renderMode
bool close() override
Close the DeviceDriver.
bool stopPlayback() override
Stop the playback.
bool immediateSound(const yarp::sig::Sound &sound)
bool getRecordingAudioBufferMaxSize(yarp::dev::AudioBufferSize &size) override
void run() override
Main body of the new thread.
bool threadInit() override
Initialization method.
void threadRelease() override
Release method.
yarp::dev::AudioBufferSize getMaxSize()
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.
static void delaySystem(double seconds)
Definition: SystemClock.cpp:29
bool stop()
Stop the thread.
Definition: Thread.cpp:81
bool isStopping()
Returns true if the thread is stopping (Thread::stop has been called).
Definition: Thread.cpp:99
bool start()
Start the new thread running.
Definition: Thread.cpp:93
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
void setFrequency(int freq)
Set the frequency of the sound (i.e.
Definition: Sound.cpp:229
size_t getChannels() const
Get the number of channels of the sound.
Definition: Sound.cpp:424
void resize(size_t samples, size_t channels=1)
Set the sound size.
Definition: Sound.cpp:168
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
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:132
#define yCError(component,...)
Definition: LogComponent.h:154
#define yCTrace(component,...)
Definition: LogComponent.h:85
#define yCDebug(component,...)
Definition: LogComponent.h:109
#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.
yarp::dev::CircularAudioBuffer_16t * playData
yarp::dev::CircularAudioBuffer_16t * recData
#define YARP_UNUSED(var)
Definition: api.h:162