11 #include <portaudio.h>
22 #define SLEEP_TIME 0.005f
25 #define PA_SAMPLE_TYPE paFloat32
27 #define SAMPLE_SILENCE (0.0f)
29 #define PA_SAMPLE_TYPE paInt16
31 #define SAMPLE_SILENCE (0)
33 #define PA_SAMPLE_TYPE paInt8
35 #define SAMPLE_SILENCE (0)
37 #define PA_SAMPLE_TYPE paUInt8
38 typedef unsigned char SAMPLE;
39 #define SAMPLE_SILENCE (128)
40 #define SAMPLE_UNSIGNED
52 unsigned long framesPerBuffer,
53 const PaStreamCallbackTimeInfo* timeInfo,
54 PaStreamCallbackFlags statusFlags,
60 int num_rec_channels = dataBuffers->numRecChannels;
61 int num_play_channels = dataBuffers->numPlayChannels;
62 int finished = paComplete;
64 if (dataBuffers->canRec)
66 const auto* rptr = (
const SAMPLE*)inputBuffer;
67 unsigned int framesToCalc;
77 if( framesLeft/ num_rec_channels < framesPerBuffer )
79 framesToCalc = framesLeft/ num_rec_channels;
80 #ifdef STOP_REC_ON_EMPTY_BUFFER
84 finished = paComplete;
86 finished = paContinue;
91 framesToCalc = framesPerBuffer;
94 finished = paContinue;
97 if( inputBuffer ==
nullptr )
99 for( i=0; i<framesToCalc; i++ )
102 if (num_rec_channels == 2) {
109 yCTrace(PORTAUDIO) <<
"Writing" << framesToCalc*2*2 <<
"bytes in the circular buffer";
110 for( i=0; i<framesToCalc; i++ )
112 recdata->
write(*rptr++);
113 if (num_rec_channels == 2) {
114 recdata->
write(*rptr++);
122 if (dataBuffers->canPlay)
124 auto* wptr = (
SAMPLE*)outputBuffer;
134 if( framesLeft/ num_play_channels < framesPerBuffer )
137 for( i=0; i<framesLeft/ num_play_channels; i++ )
139 *wptr++ = playdata->
read();
140 if (num_play_channels == 2) {
141 *wptr++ = playdata->
read();
143 for (
int chs = 2; chs < num_play_channels; chs++) {
147 for( ; i<framesPerBuffer; i++ )
150 if (num_play_channels == 2) {
154 #ifdef STOP_PLAY_ON_EMPTY_BUFFER
158 finished = paComplete;
160 finished = paContinue;
166 yCDebug(PORTAUDIO) <<
"Reading" << framesPerBuffer*2 <<
"bytes from the circular buffer";
168 for( i=0; i<framesPerBuffer; i++ )
170 *wptr++ = playdata->
read();
171 if (num_play_channels == 2) {
172 *wptr++ = playdata->
read();
174 for (
int chs = 2; chs < num_play_channels; chs++) {
180 finished = paContinue;
186 yCError(PORTAUDIO,
"No read/write operations requested, aborting");
195 m_system_resource(nullptr),
196 m_numPlaybackChannels(0),
197 m_numRecordChannels(0),
200 m_getSoundIsNotBlocking(true),
201 renderMode(RENDER_APPEND)
203 memset(&inputParameters, 0,
sizeof(PaStreamParameters));
204 memset(&outputParameters, 0,
sizeof(PaStreamParameters));
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();
229 if (config.
check(
"loopback",
"if present, send audio read from microphone immediately back to speaker"))
231 yCError(PORTAUDIO,
"loopback not yet implemented");
235 if (config.
check(
"render_mode_append"))
239 if (config.
check(
"render_mode_immediate"))
249 int rate = config.
rate;
257 if (playChannels == 0) {
260 if (recChannels == 0) {
284 if (dataBuffers.
playData ==
nullptr) {
287 if (dataBuffers.
recData ==
nullptr) {
291 dataBuffers.
canRec =
true;
297 err = Pa_Initialize();
298 if( err != paNoError )
300 yCError(PORTAUDIO,
"portaudio system failed to initialize");
304 inputParameters.device = (deviceNumber==-1)?Pa_GetDefaultInputDevice():deviceNumber;
305 yCInfo(PORTAUDIO,
"Device number %d", inputParameters.device);
308 if ((Pa_GetDeviceInfo( inputParameters.device ))!=
nullptr) {
309 inputParameters.suggestedLatency = Pa_GetDeviceInfo( inputParameters.device )->defaultLowInputLatency;
311 inputParameters.hostApiSpecificStreamInfo =
nullptr;
313 outputParameters.device = (deviceNumber==-1)?Pa_GetDefaultOutputDevice():deviceNumber;
316 outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;
317 outputParameters.hostApiSpecificStreamInfo =
nullptr;
321 wantRead?(&inputParameters):
nullptr,
322 wantWrite?(&outputParameters):
nullptr,
329 if( err != paNoError )
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));
340 return (err==paNoError);
343 void streamThread::handleError()
346 if( err != paNoError )
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));
359 if( err != paNoError )
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));
370 if (stream !=
nullptr)
372 err = Pa_CloseStream( stream );
373 if( err != paNoError )
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));
381 if (this->dataBuffers.
playData !=
nullptr)
384 this->dataBuffers.
playData =
nullptr;
386 if (this->dataBuffers.
recData !=
nullptr)
388 delete this->dataBuffers.
recData;
389 this->dataBuffers.
recData =
nullptr;
392 return (err==paNoError);
398 err = Pa_StartStream( stream );
406 err = Pa_StopStream( stream );
418 size_t buff_size = 0;
419 int buff_size_wdt = 0;
424 if (buff_size_wdt > 100)
428 yCError(PORTAUDIO) <<
"PortAudioDeviceDriver::getSound() Buffer size is still zero after 100 iterations, returning";
433 yCDebug(PORTAUDIO) <<
"PortAudioDeviceDriver::getSound() Buffer size is " << buff_size <<
"/" << this->numSamples <<
" after 100 iterations";
436 yCError(PORTAUDIO) <<
"PortAudioDeviceDriver::getSound() is in not-blocking mode, returning";
444 while (buff_size < this->numSamples);
454 for (
size_t i = 0; i < this->numSamples; i++) {
466 yCInfo(PORTAUDIO,
"=== Stopping and clearing stream.==="); fflush(stdout);
467 err = Pa_StopStream( stream );
468 if( err != paNoError )
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 ) );
477 return (err==paNoError);
499 err = Pa_StartStream(
stream );
500 if( err != paNoError ) {handleError();
return;}
502 while( ( err = Pa_IsStreamActive(
stream ) ) == 1 )
508 yCDebug(PORTAUDIO) <<
"The playback stream has been stopped";
516 err = Pa_StopStream(
stream );
528 while( ( err = Pa_IsStreamActive(
stream ) ) == 1 )
535 yCDebug(PORTAUDIO) <<
"The recording stream has been stopped";
558 for (
size_t i = 0; i < num_samples; i++) {
559 for (
size_t j = 0; j < num_channels; j++) {
576 while (Pa_IsStreamStopped(stream )==0)
582 yCInfo(PORTAUDIO,
"***** audio driver configuration changed, resetting");
584 chans <<
"channels, " << freq <<
" Hz";
591 yCError(PORTAUDIO,
"error occurred during audio driver reconfiguration, aborting");
611 for (
size_t i = 0; i < num_samples; i++) {
612 for (
size_t j = 0; j < num_channels; j++) {
671 yCError(PORTAUDIO,
"Not yet implemented");
677 yCError(PORTAUDIO,
"Not yet implemented");
683 playback_enabled =
true;
689 recording_enabled =
true;
#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)
#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.
size_t m_numPlaybackChannels
bool setSWGain(double gain) override
Sets a software gain for the grabbed audio.
bool open(yarp::os::Searchable &config) override
Open the DeviceDriver.
bool m_getSoundIsNotBlocking
bool stopRecording() override
Stop the recording.
virtual ~PortAudioDeviceDriver()
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
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 See Audio in YARP for additional documentation on YARP audio.
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