24 #include <portaudio.h>
35 #define SLEEP_TIME 0.005f
38 #define PA_SAMPLE_TYPE paFloat32
40 #define SAMPLE_SILENCE (0.0f)
42 #define PA_SAMPLE_TYPE paInt16
44 #define SAMPLE_SILENCE (0)
46 #define PA_SAMPLE_TYPE paInt8
48 #define SAMPLE_SILENCE (0)
50 #define PA_SAMPLE_TYPE paUInt8
51 typedef unsigned char SAMPLE;
52 #define SAMPLE_SILENCE (128)
53 #define SAMPLE_UNSIGNED
65 unsigned long framesPerBuffer,
66 const PaStreamCallbackTimeInfo* timeInfo,
67 PaStreamCallbackFlags statusFlags,
73 int num_rec_channels = dataBuffers->numRecChannels;
74 int num_play_channels = dataBuffers->numPlayChannels;
75 int finished = paComplete;
77 if (dataBuffers->canRec)
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);
120 yCTrace(PORTAUDIO) <<
"Writing" << framesToCalc*2*2 <<
"bytes in the circular buffer";
121 for( i=0; i<framesToCalc; i++ )
123 recdata->
write(*rptr++);
124 if(num_rec_channels == 2 ) recdata->
write(*rptr++);
131 if (dataBuffers->canPlay)
133 auto* wptr = (
SAMPLE*)outputBuffer;
143 if( framesLeft/ num_play_channels < framesPerBuffer )
146 for( i=0; i<framesLeft/ num_play_channels; i++ )
148 *wptr++ = playdata->
read();
149 if( num_play_channels == 2 ) *wptr++ = playdata->
read();
150 for (
int chs=2; chs<num_play_channels; chs++) playdata->
read();
152 for( ; i<framesPerBuffer; i++ )
155 if(num_play_channels == 2 ) *wptr++ = 0;
157 #ifdef STOP_PLAY_ON_EMPTY_BUFFER
161 finished = paComplete;
163 finished = paContinue;
169 yCDebug(PORTAUDIO) <<
"Reading" << framesPerBuffer*2 <<
"bytes from the circular buffer";
171 for( i=0; i<framesPerBuffer; i++ )
173 *wptr++ = playdata->
read();
174 if( num_play_channels == 2 ) *wptr++ = playdata->
read();
175 for (
int chs=2; chs<num_play_channels; chs++) playdata->
read();
179 finished = paContinue;
185 yCError(PORTAUDIO,
"No read/write operations requested, aborting");
194 m_system_resource(nullptr),
195 m_numPlaybackChannels(0),
196 m_numRecordChannels(0),
199 m_getSoundIsNotBlocking(true),
200 renderMode(RENDER_APPEND)
202 memset(&inputParameters, 0,
sizeof(PaStreamParameters));
203 memset(&outputParameters, 0,
sizeof(PaStreamParameters));
216 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();
228 if (config.
check(
"loopback",
"if present, send audio read from microphone immediately back to speaker"))
230 yCError(PORTAUDIO,
"loopback not yet implemented");
234 if (config.
check(
"render_mode_append"))
238 if (config.
check(
"render_mode_immediate"))
248 int rate = config.
rate;
278 if (dataBuffers.
recData==
nullptr)
280 if (wantRead) dataBuffers.
canRec =
true;
281 if (wantWrite) dataBuffers.
canPlay =
true;
283 err = Pa_Initialize();
284 if( err != paNoError )
286 yCError(PORTAUDIO,
"portaudio system failed to initialize");
290 inputParameters.device = (deviceNumber==-1)?Pa_GetDefaultInputDevice():deviceNumber;
291 yCInfo(PORTAUDIO,
"Device number %d", inputParameters.device);
294 if ((Pa_GetDeviceInfo( inputParameters.device ))!=
nullptr) {
295 inputParameters.suggestedLatency = Pa_GetDeviceInfo( inputParameters.device )->defaultLowInputLatency;
297 inputParameters.hostApiSpecificStreamInfo =
nullptr;
299 outputParameters.device = (deviceNumber==-1)?Pa_GetDefaultOutputDevice():deviceNumber;
302 outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;
303 outputParameters.hostApiSpecificStreamInfo =
nullptr;
307 wantRead?(&inputParameters):
nullptr,
308 wantWrite?(&outputParameters):
nullptr,
315 if( err != paNoError )
317 yCError(PORTAUDIO,
"An error occurred while using the portaudio stream");
318 yCError(PORTAUDIO,
"Error number: %d", err);
319 yCError(PORTAUDIO,
"Error message: %s", Pa_GetErrorText(err));
326 return (err==paNoError);
329 void streamThread::handleError()
332 if( err != paNoError )
334 yCError(PORTAUDIO,
"An error occurred while using the portaudio stream");
335 yCError(PORTAUDIO,
"Error number: %d", err);
336 yCError(PORTAUDIO,
"Error message: %s\n", Pa_GetErrorText(err));
345 if( err != paNoError )
347 yCError(PORTAUDIO,
"An error occurred while using the portaudio stream");
348 yCError(PORTAUDIO,
"Error number: %d", err);
349 yCError(PORTAUDIO,
"Error message: %s", Pa_GetErrorText(err));
356 if (stream !=
nullptr)
358 err = Pa_CloseStream( stream );
359 if( err != paNoError )
361 yCError(PORTAUDIO,
"An error occurred while closing the portaudio stream");
362 yCError(PORTAUDIO,
"Error number: %d", err);
363 yCError(PORTAUDIO,
"Error message: %s", Pa_GetErrorText(err));
367 if (this->dataBuffers.
playData !=
nullptr)
370 this->dataBuffers.
playData =
nullptr;
372 if (this->dataBuffers.
recData !=
nullptr)
374 delete this->dataBuffers.
recData;
375 this->dataBuffers.
recData =
nullptr;
378 return (err==paNoError);
384 err = Pa_StartStream( stream );
392 err = Pa_StopStream( stream );
404 size_t buff_size = 0;
405 int buff_size_wdt = 0;
410 if (buff_size_wdt > 100)
414 yCError(PORTAUDIO) <<
"PortAudioDeviceDriver::getSound() Buffer size is still zero after 100 iterations, returning";
419 yCDebug(PORTAUDIO) <<
"PortAudioDeviceDriver::getSound() Buffer size is " << buff_size <<
"/" << this->numSamples <<
" after 100 iterations";
422 yCError(PORTAUDIO) <<
"PortAudioDeviceDriver::getSound() is in not-blocking mode, returning";
430 while (buff_size < this->numSamples);
440 for (
size_t i=0; i<this->numSamples; i++)
451 yCInfo(PORTAUDIO,
"=== Stopping and clearing stream.==="); fflush(stdout);
452 err = Pa_StopStream( stream );
453 if( err != paNoError )
455 yCError(PORTAUDIO,
"abortSound: error occurred while stopping the portaudio stream" );
456 yCError(PORTAUDIO,
"Error number: %d", err );
457 yCError(PORTAUDIO,
"Error message: %s", Pa_GetErrorText( err ) );
462 return (err==paNoError);
484 err = Pa_StartStream(
stream );
485 if( err != paNoError ) {handleError();
return;}
487 while( ( err = Pa_IsStreamActive(
stream ) ) == 1 )
493 yCDebug(PORTAUDIO) <<
"The playback stream has been stopped";
501 err = Pa_StopStream(
stream );
513 while( ( err = Pa_IsStreamActive(
stream ) ) == 1 )
520 yCDebug(PORTAUDIO) <<
"The recording stream has been stopped";
543 for (
size_t i=0; i<num_samples; i++)
544 for (
size_t j=0; j<num_channels; j++)
559 while (Pa_IsStreamStopped(stream )==0)
565 yCInfo(PORTAUDIO,
"***** audio driver configuration changed, resetting");
567 chans <<
"channels, " << freq <<
" Hz";
574 yCError(PORTAUDIO,
"error occurred during audio driver reconfiguration, aborting");
593 for (
size_t i=0; i<num_samples; i++)
594 for (
size_t j=0; j<num_channels; j++)
static int bufferIOCallback(const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo, PaStreamCallbackFlags statusFlags, void *userData)
#define DEFAULT_FRAMES_PER_BUFFER
#define DEFAULT_NUM_CHANNELS
#define DEFAULT_SAMPLE_RATE
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.
size_t m_numPlaybackChannels
bool open(yarp::os::Searchable &config) override
Open the DeviceDriver.
bool m_getSoundIsNotBlocking
bool stopRecording() override
Stop the recording.
virtual ~PortAudioDeviceDriver()
bool getPlaybackAudioBufferCurrentSize(yarp::dev::AudioBufferSize &size) override
PortAudioDeviceDriverSettings m_driverConfig
bool resetRecordingAudioBuffer() override
bool getPlaybackAudioBufferMaxSize(yarp::dev::AudioBufferSize &size) override
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 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
size_t m_numRecordChannels
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.
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.
int getFrequency() const
Get the frequency of the sound (i.e.
audio_sample get(size_t sample, size_t channel=0) const
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 yCTrace(component,...)
#define yCDebug(component,...)
#define YARP_LOG_COMPONENT(name,...)
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.
An interface to the operating system, including Port based communication.
yarp::dev::CircularAudioBuffer_16t * playData
yarp::dev::CircularAudioBuffer_16t * recData