24 #include <portaudio.h>
36 #define SLEEP_TIME 0.005f
39 #define PA_SAMPLE_TYPE paFloat32
41 #define SAMPLE_SILENCE (0.0f)
43 #define PA_SAMPLE_TYPE paInt16
45 #define SAMPLE_SILENCE (0)
47 #define PA_SAMPLE_TYPE paInt8
49 #define SAMPLE_SILENCE (0)
51 #define PA_SAMPLE_TYPE paUInt8
52 typedef unsigned char SAMPLE;
53 #define SAMPLE_SILENCE (128)
54 #define SAMPLE_UNSIGNED
68 unsigned long framesPerBuffer,
69 const PaStreamCallbackTimeInfo* timeInfo,
70 PaStreamCallbackFlags statusFlags,
75 int finished = paComplete;
79 const auto* rptr = (
const SAMPLE*)inputBuffer;
80 unsigned int framesToCalc;
90 if( framesLeft/ num_rec_channels < framesPerBuffer )
92 framesToCalc = framesLeft/ num_rec_channels;
93 #ifdef STOP_REC_ON_EMPTY_BUFFER
97 finished = paComplete;
99 finished = paContinue;
104 framesToCalc = framesPerBuffer;
107 finished = paContinue;
110 if( inputBuffer ==
nullptr )
112 for( i=0; i<framesToCalc; i++ )
115 if(num_rec_channels == 2 ) recdata->
write(0);
121 yCDebug(PORTAUDIORECORDER) <<
"Writing" << framesToCalc*2*2 <<
"bytes in the circular buffer";
123 for( i=0; i<framesToCalc; i++ )
125 recdata->
write(*rptr++);
126 if(num_rec_channels == 2 ) recdata->
write(*rptr++);
132 yCError(PORTAUDIORECORDER,
"No write operations requested, aborting");
139 m_recDataBuffer(nullptr),
140 m_isRecording(false),
141 m_system_resource(nullptr)
143 memset(&m_inputParameters, 0,
sizeof(PaStreamParameters));
155 m_driverConfig.
cfg_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();
174 if (m_recDataBuffer ==
nullptr)
177 m_err = Pa_Initialize();
178 if(m_err != paNoError )
180 yCError(PORTAUDIORECORDER,
"portaudio system failed to initialize");
185 yCInfo(PORTAUDIORECORDER,
"Device number %d", m_inputParameters.device);
188 if ((Pa_GetDeviceInfo(m_inputParameters.device ))!=
nullptr) {
189 m_inputParameters.suggestedLatency = Pa_GetDeviceInfo(m_inputParameters.device )->defaultLowInputLatency;
191 m_inputParameters.hostApiSpecificStreamInfo =
nullptr;
193 m_err = Pa_OpenStream(
203 if(m_err != paNoError )
205 yCError(PORTAUDIORECORDER,
"An error occurred while using the portaudio stream" );
206 yCError(PORTAUDIORECORDER,
"Error number: %d", m_err );
207 yCError(PORTAUDIORECORDER,
"Error message: %s", Pa_GetErrorText(m_err ) );
214 return (m_err==paNoError);
233 m_recDataBuffer->
clear();
235 if(m_err != paNoError )
237 yCError(PORTAUDIORECORDER,
"An error occurred while using the portaudio stream" );
238 yCError(PORTAUDIORECORDER,
"Error number: %d", m_err );
239 yCError(PORTAUDIORECORDER,
"Error message: %s", Pa_GetErrorText(m_err ) );
246 if (m_stream !=
nullptr)
248 m_err = Pa_CloseStream(m_stream );
249 if(m_err != paNoError )
251 yCError(PORTAUDIORECORDER,
"An error occurred while closing the portaudio stream" );
252 yCError(PORTAUDIORECORDER,
"Error number: %d", m_err );
253 yCError(PORTAUDIORECORDER,
"Error message: %s", Pa_GetErrorText(m_err ) );
257 if (this->m_recDataBuffer !=
nullptr)
259 delete this->m_recDataBuffer;
260 this->m_recDataBuffer =
nullptr;
263 return (m_err==paNoError);
268 if (m_isRecording ==
true)
return true;
269 std::lock_guard<std::mutex> lock(m_mutex);
270 m_isRecording =
true;
271 #ifdef BUFFER_AUTOCLEAR
272 this->m_recDataBuffer->
clear();
274 m_err = Pa_StartStream(m_stream );
276 yCInfo(PORTAUDIORECORDER) <<
"PortAudioRecorderDeviceDriver started recording";
282 if (m_isRecording ==
false)
return true;
283 std::lock_guard<std::mutex> lock(m_mutex);
284 m_isRecording =
false;
285 #ifdef BUFFER_AUTOCLEAR
286 this->m_recDataBuffer->
clear();
288 m_err = Pa_StopStream(m_stream );
290 yCInfo(PORTAUDIORECORDER) <<
"PortAudioRecorderDeviceDriver stopped recording";
298 #ifdef AUTOMATIC_REC_START
299 if (m_isRecording ==
false)
305 while (m_isRecording ==
false)
309 yCInfo(PORTAUDIORECORDER) <<
"getSound() is currently waiting. Use ::startRecording() to start the audio stream";
321 if (max_number_of_samples < min_number_of_samples)
323 yCError(PORTAUDIORECORDER) <<
"max_number_of_samples must be greater than min_number_of_samples!";
326 if (max_number_of_samples > this->m_config.
cfg_samples)
328 yCWarning(PORTAUDIORECORDER) <<
"max_number_of_samples bigger than the internal audio buffer! It will be truncated to:" << this->m_config.
cfg_samples;
329 max_number_of_samples = this->m_config.
cfg_samples;
333 size_t buff_size = 0;
339 if (buff_size >= max_number_of_samples) {
break; }
340 if (buff_size >= min_number_of_samples &&
yarp::os::Time::now() - start_time > max_samples_timeout_s) {
break; }
341 if (m_isRecording ==
false) {
break; }
346 yCDebug(PORTAUDIORECORDER) <<
"PortAudioRecorderDeviceDriver::getSound() Buffer size is " << buff_size <<
"/" << max_number_of_samples <<
" after 1s";
354 size_t samples_to_be_copied = buff_size;
355 if (samples_to_be_copied > max_number_of_samples) samples_to_be_copied = max_number_of_samples;
356 if (sound.
getChannels()!=this->m_config.cfg_recChannels && sound.
getSamples() != samples_to_be_copied)
363 for (
size_t i=0; i< samples_to_be_copied; i++)
388 while( ( m_err = Pa_IsStreamActive(m_stream) ) == 1 )
395 Pa_StopStream(m_stream);
396 yCDebug(PORTAUDIORECORDER) <<
"The recording stream has been stopped";
397 m_isRecording =
false;
414 size = this->m_recDataBuffer->
size();
427 std::lock_guard<std::mutex> lock(m_mutex);
428 this->m_recDataBuffer->
clear();
429 yCDebug(PORTAUDIORECORDER) <<
"PortAudioRecorderDeviceDriver::resetRecordingAudioBuffer";
#define DEFAULT_FRAMES_PER_BUFFER
#define DEFAULT_NUM_CHANNELS
#define DEFAULT_SAMPLE_RATE
static int bufferIOCallback(const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo, PaStreamCallbackFlags statusFlags, void *userData)
bool startRecording() override
Start the recording.
bool getRecordingAudioBufferCurrentSize(yarp::dev::AudioBufferSize &size) override
void run() override
Main body of the new thread.
bool threadInit() override
Initialization method.
bool resetRecordingAudioBuffer() override
bool getRecordingAudioBufferMaxSize(yarp::dev::AudioBufferSize &size) override
void threadRelease() override
Release method.
PortAudioRecorderDeviceDriverSettings m_driverConfig
bool close() override
Close the DeviceDriver.
~PortAudioRecorderDeviceDriver() override
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 stopRecording() override
Stop the recording.
PortAudioRecorderDeviceDriver()
bool open(yarp::os::Searchable &config) override
Open the DeviceDriver.
yarp::dev::AudioBufferSize getMaxSize()
A base class for nested structures that can be searched.
virtual bool check(const std::string &key) const =0
Check if there exists a property of the given name.
static void delaySystem(double seconds)
bool stop()
Stop the thread.
bool isStopping()
Returns true if the thread is stopping (Thread::stop has been called).
bool start()
Start the new thread running.
A single value (typically within a Bottle).
Class for storing sounds.
void setFrequency(int freq)
Set the frequency of the sound (i.e.
size_t getChannels() const
Get the number of channels of the sound.
void resize(size_t samples, size_t channels=1)
Set the sound size.
void set(audio_sample value, size_t sample, size_t channel=0)
size_t getSamples() const
Get the number of samples contained in the sound.
#define yCInfo(component,...)
#define yCError(component,...)
#define yCWarning(component,...)
#define yCDebug(component,...)
#define YARP_LOG_COMPONENT(name,...)
An interface for the device drivers.
yarp::dev::CircularAudioBuffer< unsigned short int > CircularAudioBuffer_16t
double now()
Return the current time in seconds, relative to an arbitrary starting point.
void delay(double seconds)
Wait for a certain number of seconds.
An interface to the operating system, including Port based communication.