YARP
Yet Another Robot Platform
PortAudioRecorderDeviceDriver.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 
20 
21 #include <cstdio>
22 #include <cstdlib>
23 #include <cstring>
24 #include <yarp/dev/api.h>
25 
26 #include <yarp/os/Time.h>
27 #include <yarp/os/LogComponent.h>
28 #include <yarp/os/LogStream.h>
29 
30 using namespace yarp::os;
31 using namespace yarp::dev;
32 
33 #define SLEEP_TIME 0.005f
34 
35 #if 0
36 #define PA_SAMPLE_TYPE paFloat32
37 typedef float SAMPLE;
38 #define SAMPLE_SILENCE (0.0f)
39 #elif 1
40 #define PA_SAMPLE_TYPE paInt16
41 typedef short SAMPLE;
42 #define SAMPLE_SILENCE (0)
43 #elif 1
44 #define PA_SAMPLE_TYPE paInt8
45 typedef char SAMPLE;
46 #define SAMPLE_SILENCE (0)
47 #else
48 #define PA_SAMPLE_TYPE paUInt8
49 typedef unsigned char SAMPLE;
50 #define SAMPLE_SILENCE (128)
51 #define SAMPLE_UNSIGNED
52 #endif
53 
54 #define DEFAULT_SAMPLE_RATE (44100)
55 #define DEFAULT_NUM_CHANNELS (2)
56 #define DEFAULT_DITHER_FLAG (0)
57 #define DEFAULT_FRAMES_PER_BUFFER (512)
58 
59 namespace {
60 YARP_LOG_COMPONENT(PORTAUDIORECORDER, "yarp.devices.portaudioRecorder")
61 }
62 
63 
64 /* This routine will be called by the PortAudio engine when audio is needed.
65 ** It may be called at interrupt level on some machines so don't do anything
66 ** that could mess up the system like calling malloc() or free().
67 */
68 static int bufferIOCallback( const void *inputBuffer, void *outputBuffer,
69  unsigned long framesPerBuffer,
70  const PaStreamCallbackTimeInfo* timeInfo,
71  PaStreamCallbackFlags statusFlags,
72  void *userData )
73 {
74  CircularAudioBuffer_16t *recdata = (CircularAudioBuffer_16t*)(userData);
75  size_t num_rec_channels = recdata->getMaxSize().getChannels();
76  int finished = paComplete;
77 
78  if (1)
79  {
80  const auto* rptr = (const SAMPLE*)inputBuffer;
81  size_t framesToCalc;
82  unsigned int i;
83  size_t framesLeft = (recdata->getMaxSize().getSamples()* recdata->getMaxSize().getChannels()) -
84  (recdata->size().getSamples() * recdata->size().getChannels());
85 
86  YARP_UNUSED(outputBuffer); // just to prevent unused variable warnings
87  YARP_UNUSED(timeInfo);
88  YARP_UNUSED(statusFlags);
89  YARP_UNUSED(userData);
90 
91  if( framesLeft/ num_rec_channels < framesPerBuffer )
92  {
93  framesToCalc = framesLeft/ num_rec_channels;
94 #ifdef STOP_REC_ON_EMPTY_BUFFER
95  //if we return paComplete, then the callback is not called anymore.
96  //method Pa_IsStreamActive() will return 1.
97  //user needs to call Pa_StopStream() before starting a new recording session
98  finished = paComplete;
99 #else
100  finished = paContinue;
101 #endif
102  }
103  else
104  {
105  framesToCalc = framesPerBuffer;
106  //if we return paContinue, then the callback will be invoked again later
107  //method Pa_IsStreamActive() will return 0
108  finished = paContinue;
109  }
110 
111  if( inputBuffer == nullptr )
112  {
113  for( i=0; i<framesToCalc; i++ )
114  {
115  recdata->write(0); // left
116  if(num_rec_channels == 2 ) recdata->write(0); // right
117  }
118  }
119  else
120  {
121 #if 0
122  yCDebug(PORTAUDIORECORDER) << "Writing" << framesToCalc*2*2 << "bytes in the circular buffer";
123 #endif
124  for( i=0; i<framesToCalc; i++ )
125  {
126  recdata->write(*rptr++); // left
127  if(num_rec_channels == 2 ) recdata->write(*rptr++); // right
128  }
129  }
130  return finished;
131  }
132 
133  yCError(PORTAUDIORECORDER, "No write operations requested, aborting");
134  return paAbort;
135 }
136 
138  m_stream(nullptr),
139  m_err(paNoError),
140  m_system_resource(nullptr)
141 {
142  memset(&m_inputParameters, 0, sizeof(PaStreamParameters));
143 }
144 
146 {
147  close();
148 }
149 
150 
152 {
153  m_audiorecorder_cfg.frequency = config.check("rate",Value(0),"audio sample rate (0=automatic)").asInt32();
154  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();
155  m_audiorecorder_cfg.numChannels = config.check("channels", Value(0), "number of audio channels (0=automatic, max is 2)").asInt32();
156  m_device_id = config.check("id",Value(-1),"which portaudio index to use (-1=automatic)").asInt32();
157  int driver_frame_size = config.check("driver_frame_size", Value(0), "" ).asInt32();
158 
161  if (m_audiorecorder_cfg.numSamples == 0) m_audiorecorder_cfg.numSamples = m_audiorecorder_cfg.frequency; // by default let's use chunks of 1 second
162  if (driver_frame_size == 0) driver_frame_size = DEFAULT_FRAMES_PER_BUFFER;
163 
164 // size_t debug_numRecBytes = m_config.cfg_samples * sizeof(SAMPLE) * m_config.cfg_recChannels;
166  if (m_inputBuffer ==nullptr)
167  m_inputBuffer = new CircularAudioBuffer_16t("portatudio_rec", rec_buffer_size);
168 
169  m_err = Pa_Initialize();
170  if(m_err != paNoError )
171  {
172  yCError(PORTAUDIORECORDER, "portaudio system failed to initialize");
173  return false;
174  }
175 
176  m_inputParameters.device = (m_device_id ==-1)?Pa_GetDefaultInputDevice(): m_device_id;
177  yCInfo(PORTAUDIORECORDER, "Device number %d", m_inputParameters.device);
178  m_inputParameters.channelCount = static_cast<int>(m_audiorecorder_cfg.numChannels);
179  m_inputParameters.sampleFormat = PA_SAMPLE_TYPE;
180  if ((Pa_GetDeviceInfo(m_inputParameters.device ))!=nullptr) {
181  m_inputParameters.suggestedLatency = Pa_GetDeviceInfo(m_inputParameters.device )->defaultLowInputLatency;
182  }
183  m_inputParameters.hostApiSpecificStreamInfo = nullptr;
184 
185  m_err = Pa_OpenStream(
186  &m_stream,
187  &m_inputParameters,
188  nullptr,
190  driver_frame_size,
191  paClipOff,
193  m_inputBuffer);
194 
195  if(m_err != paNoError )
196  {
197  yCError(PORTAUDIORECORDER, "An error occurred while using the portaudio stream" );
198  yCError(PORTAUDIORECORDER, "Error number: %d", m_err );
199  yCError(PORTAUDIORECORDER, "Error message: %s", Pa_GetErrorText(m_err ) );
200  }
201 
202  //start the thread
203  bool ret = this->start();
204  YARP_UNUSED(ret);
205 
206  return (m_err==paNoError);
207 }
208 
210 {
211  //Pa_Terminate();
212  m_inputBuffer->clear();
213 
214  if(m_err != paNoError )
215  {
216  yCError(PORTAUDIORECORDER, "An error occurred while using the portaudio stream" );
217  yCError(PORTAUDIORECORDER, "Error number: %d", m_err );
218  yCError(PORTAUDIORECORDER, "Error message: %s", Pa_GetErrorText(m_err ) );
219  }
220 }
221 
223 {
224  this->stop();
225  if (m_stream != nullptr)
226  {
227  m_err = Pa_CloseStream(m_stream );
228  if(m_err != paNoError )
229  {
230  yCError(PORTAUDIORECORDER, "An error occurred while closing the portaudio stream" );
231  yCError(PORTAUDIORECORDER, "Error number: %d", m_err );
232  yCError(PORTAUDIORECORDER, "Error message: %s", Pa_GetErrorText(m_err ) );
233  }
234  }
235 
236  if (this->m_inputBuffer != nullptr)
237  {
238  delete this->m_inputBuffer;
239  this->m_inputBuffer = nullptr;
240  }
241 
242  return (m_err==paNoError);
243 }
244 
246 {
247  AudioRecorderDeviceBase::startRecording();
248  m_err = Pa_StartStream(m_stream );
249  if(m_err < 0 ) {handleError(); return false;}
250  yCInfo(PORTAUDIORECORDER) << "PortAudioRecorderDeviceDriver started recording";
251  return true;
252 }
253 
255 {
256  AudioRecorderDeviceBase::stopRecording();
257  m_err = Pa_StopStream(m_stream );
258  if(m_err < 0 ) {handleError(); return false;}
259  yCInfo(PORTAUDIORECORDER) << "PortAudioRecorderDeviceDriver stopped recording";
260  return true;
261 }
262 
264 {
265 }
266 
268 {
269  m_isRecording=false;
270  return true;
271 }
272 
274 {
275  while(this->isStopping()==false)
276  {
277  if (m_isRecording)
278  {
279  while( ( m_err = Pa_IsStreamActive(m_stream) ) == 1 )
280  {
282  }
283 
284  if (m_err == 0)
285  {
286  Pa_StopStream(m_stream);
287  yCDebug(PORTAUDIORECORDER) << "The recording stream has been stopped";
288  m_isRecording = false;
289  }
290  if(m_err < 0 )
291  {
292  handleError();
293  return;
294  }
295  }
296 
298  }
299  return;
300 }
LogStream.h
DEFAULT_SAMPLE_RATE
#define DEFAULT_SAMPLE_RATE
Definition: PortAudioRecorderDeviceDriver.cpp:54
yarp::dev::CircularAudioBuffer::write
void write(SAMPLE elem)
Definition: CircularAudioBuffer.h:48
PortAudioRecorderDeviceDriver::~PortAudioRecorderDeviceDriver
~PortAudioRecorderDeviceDriver() override
Definition: PortAudioRecorderDeviceDriver.cpp:145
yarp::dev::CircularAudioBuffer::clear
void clear()
Definition: CircularAudioBuffer.h:87
PortAudioRecorderDeviceDriver::startRecording
bool startRecording() override
Start the recording.
Definition: PortAudioRecorderDeviceDriver.cpp:245
yarp::os::Searchable
A base class for nested structures that can be searched.
Definition: Searchable.h:69
yarp::dev::AudioBufferSize
Definition: AudioBufferSize.h:26
yarp::dev::AudioBufferSize::getChannels
size_t getChannels()
Definition: AudioBufferSize.h:32
yarp::dev::AudioDeviceDriverSettings::numChannels
size_t numChannels
Definition: AudioRecorderDeviceBase.h:28
YARP_LOG_COMPONENT
#define YARP_LOG_COMPONENT(name,...)
Definition: LogComponent.h:80
SLEEP_TIME
#define SLEEP_TIME
Definition: PortAudioRecorderDeviceDriver.cpp:33
PortAudioRecorderDeviceDriver::run
void run() override
Main body of the new thread.
Definition: PortAudioRecorderDeviceDriver.cpp:273
yarp::dev::AudioDeviceDriverSettings::numSamples
size_t numSamples
Definition: AudioRecorderDeviceBase.h:27
DEFAULT_FRAMES_PER_BUFFER
#define DEFAULT_FRAMES_PER_BUFFER
Definition: PortAudioRecorderDeviceDriver.cpp:57
PA_SAMPLE_TYPE
#define PA_SAMPLE_TYPE
Definition: PortAudioRecorderDeviceDriver.cpp:40
YARP_UNUSED
#define YARP_UNUSED(var)
Definition: api.h:159
yarp::dev::AudioRecorderDeviceBase::m_isRecording
bool m_isRecording
Definition: AudioRecorderDeviceBase.h:36
ret
bool ret
Definition: ImplementAxisInfo.cpp:72
yarp::dev
An interface for the device drivers.
Definition: audioBufferSizeData.cpp:17
PortAudioRecorderDeviceDriver::open
bool open(yarp::os::Searchable &config) override
Open the DeviceDriver.
Definition: PortAudioRecorderDeviceDriver.cpp:151
yarp::dev::AudioDeviceDriverSettings::frequency
size_t frequency
Definition: AudioRecorderDeviceBase.h:29
PortAudioRecorderDeviceDriver::threadRelease
void threadRelease() override
Release method.
Definition: PortAudioRecorderDeviceDriver.cpp:263
yarp::dev::CircularAudioBuffer
Definition: CircularAudioBuffer.h:25
yarp::dev::CircularAudioBuffer::size
AudioBufferSize size()
Definition: CircularAudioBuffer.h:59
yarp::dev::AudioRecorderDeviceBase::m_audiorecorder_cfg
AudioDeviceDriverSettings m_audiorecorder_cfg
Definition: AudioRecorderDeviceBase.h:40
yarp::os::SystemClock::delaySystem
static void delaySystem(double seconds)
Definition: SystemClock.cpp:32
yarp::os::Searchable::check
virtual bool check(const std::string &key) const =0
Check if there exists a property of the given name.
PortAudioRecorderDeviceDriver::stopRecording
bool stopRecording() override
Stop the recording.
Definition: PortAudioRecorderDeviceDriver.cpp:254
PortAudioRecorderDeviceDriver::close
bool close() override
Close the DeviceDriver.
Definition: PortAudioRecorderDeviceDriver.cpp:222
bufferIOCallback
static int bufferIOCallback(const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo, PaStreamCallbackFlags statusFlags, void *userData)
Definition: PortAudioRecorderDeviceDriver.cpp:68
PortAudioRecorderDeviceDriver::threadInit
bool threadInit() override
Initialization method.
Definition: PortAudioRecorderDeviceDriver.cpp:267
yarp::dev::AudioBufferSize::getSamples
size_t getSamples()
Definition: AudioBufferSize.h:31
PortAudioRecorderDeviceDriver::PortAudioRecorderDeviceDriver
PortAudioRecorderDeviceDriver()
Definition: PortAudioRecorderDeviceDriver.cpp:137
LogComponent.h
yCError
#define yCError(component,...)
Definition: LogComponent.h:157
yarp::os::Thread::isStopping
bool isStopping()
Returns true if the thread is stopping (Thread::stop has been called).
Definition: Thread.cpp:102
yCInfo
#define yCInfo(component,...)
Definition: LogComponent.h:135
yarp::dev::AudioRecorderDeviceBase::m_inputBuffer
yarp::dev::CircularAudioBuffer_16t * m_inputBuffer
Definition: AudioRecorderDeviceBase.h:38
yarp::os
An interface to the operating system, including Port based communication.
Definition: AbstractCarrier.h:17
yCDebug
#define yCDebug(component,...)
Definition: LogComponent.h:112
SAMPLE
short SAMPLE
Definition: PortAudioRecorderDeviceDriver.cpp:41
yarp::dev::CircularAudioBuffer_16t
yarp::dev::CircularAudioBuffer< unsigned short int > CircularAudioBuffer_16t
Definition: CircularAudioBuffer.h:118
PortAudioRecorderDeviceDriver::m_device_id
int m_device_id
Definition: PortAudioRecorderDeviceDriver.h:80
DEFAULT_NUM_CHANNELS
#define DEFAULT_NUM_CHANNELS
Definition: PortAudioRecorderDeviceDriver.cpp:55
yarp::os::Thread::start
bool start()
Start the new thread running.
Definition: Thread.cpp:96
Time.h
yarp::os::Value
A single value (typically within a Bottle).
Definition: Value.h:47
yarp::os::Thread::stop
bool stop()
Stop the thread.
Definition: Thread.cpp:84
SAMPLE
short SAMPLE
Definition: PortAudioDeviceDriver.cpp:43
PortAudioRecorderDeviceDriver::handleError
void handleError()
Definition: PortAudioRecorderDeviceDriver.cpp:209
api.h
yarp::dev::CircularAudioBuffer::getMaxSize
yarp::dev::AudioBufferSize getMaxSize()
Definition: CircularAudioBuffer.h:82
yarp::os::Time::delay
void delay(double seconds)
Wait for a certain number of seconds.
Definition: Time.cpp:114
PortAudioRecorderDeviceDriver.h