YARP
Yet Another Robot Platform
PortAudioRecorderDeviceDriver.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 <yarp/dev/api.h>
12
13#include <yarp/os/Time.h>
15#include <yarp/os/LogStream.h>
16
17using namespace yarp::os;
18using namespace yarp::dev;
19
20#define SLEEP_TIME 0.005f
21
22#if 0
23#define PA_SAMPLE_TYPE paFloat32
24typedef float SAMPLE;
25#define SAMPLE_SILENCE (0.0f)
26#elif 1
27#define PA_SAMPLE_TYPE paInt16
28typedef short SAMPLE;
29#define SAMPLE_SILENCE (0)
30#elif 1
31#define PA_SAMPLE_TYPE paInt8
32typedef char SAMPLE;
33#define SAMPLE_SILENCE (0)
34#else
35#define PA_SAMPLE_TYPE paUInt8
36typedef unsigned char SAMPLE;
37#define SAMPLE_SILENCE (128)
38#define SAMPLE_UNSIGNED
39#endif
40
41namespace {
42YARP_LOG_COMPONENT(PORTAUDIORECORDER, "yarp.devices.portaudioRecorder")
43}
44
45#define DEFAULT_FRAMES_PER_BUFFER (512)
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*/
51static int bufferIOCallback( const void *inputBuffer, void *outputBuffer,
52 unsigned long framesPerBuffer,
53 const PaStreamCallbackTimeInfo* timeInfo,
54 PaStreamCallbackFlags statusFlags,
55 void *userData )
56{
58 size_t num_rec_channels = recdata->getMaxSize().getChannels();
59 int finished = paComplete;
60
61 if (1)
62 {
63 const auto* rptr = (const SAMPLE*)inputBuffer;
64 size_t framesToCalc;
65 size_t framesLeft = (recdata->getMaxSize().getSamples()* recdata->getMaxSize().getChannels()) -
66 (recdata->size().getSamples() * recdata->size().getChannels());
67
68 YARP_UNUSED(outputBuffer); // just to prevent unused variable warnings
69 YARP_UNUSED(timeInfo);
70 YARP_UNUSED(statusFlags);
71 YARP_UNUSED(userData);
72
73 if( framesLeft/ num_rec_channels < framesPerBuffer )
74 {
75 framesToCalc = framesLeft/ num_rec_channels;
76#ifdef STOP_REC_ON_EMPTY_BUFFER
77 //if we return paComplete, then the callback is not called anymore.
78 //method Pa_IsStreamActive() will return 1.
79 //user needs to call Pa_StopStream() before starting a new recording session
80 finished = paComplete;
81#else
82 finished = paContinue;
83#endif
84 }
85 else
86 {
87 framesToCalc = framesPerBuffer;
88 //if we return paContinue, then the callback will be invoked again later
89 //method Pa_IsStreamActive() will return 0
90 finished = paContinue;
91 }
92
93 if( inputBuffer == nullptr )
94 {
95 for( size_t i=0; i<framesToCalc; i++ )
96 {
97 for (size_t j=0; j < num_rec_channels; j++)
98 {
99 recdata->write(0);
100 }
101 }
102 }
103 else
104 {
105 for( size_t i=0; i<framesToCalc; i++ )
106 {
107 for (size_t j = 0; j < num_rec_channels; j++)
108 {
109 recdata->write(*rptr++);
110 }
111 }
112 }
113 return finished;
114 }
115
116 yCError(PORTAUDIORECORDER, "No write operations requested, aborting");
117 return paAbort;
118}
119
121 m_stream(nullptr),
122 m_err(paNoError),
123 m_system_resource(nullptr)
124{
125 memset(&m_inputParameters, 0, sizeof(PaStreamParameters));
126}
127
129{
130 close();
131}
132
133
135{
136 if (config.check("help"))
137 {
138 yCInfo(PORTAUDIORECORDER, "Some examples:");
139 yCInfo(PORTAUDIORECORDER, "yarpdev --device portaudioRecorder --help");
140 yCInfo(PORTAUDIORECORDER, "yarpdev --device AudioRecorderWrapper --subdevice portaudioRecorder --start");
141 return false;
142 }
143
144 bool b = configureRecorderAudioDevice(config.findGroup("AUDIO_BASE"),"portaudioRecorder");
145 if (!b) { return false; }
146
147 m_device_id = config.check("dev_id", Value(-1), "which portaudio index to use (-1=automatic)").asInt32();
148 m_driver_frame_size = config.check("driver_frame_size", Value(0), "").asInt32();
149 if (m_driver_frame_size == 0) {
151 }
152
153 m_err = Pa_Initialize();
154 if(m_err != paNoError )
155 {
156 yCError(PORTAUDIORECORDER, "portaudio system failed to initialize");
157 return false;
158 }
159
160 m_inputParameters.device = (m_device_id ==-1)?Pa_GetDefaultInputDevice(): m_device_id;
161 yCInfo(PORTAUDIORECORDER, "Device number %d", m_inputParameters.device);
162 m_inputParameters.channelCount = static_cast<int>(m_audiorecorder_cfg.numChannels);
163 m_inputParameters.sampleFormat = PA_SAMPLE_TYPE;
164 if ((Pa_GetDeviceInfo(m_inputParameters.device ))!=nullptr) {
165 m_inputParameters.suggestedLatency = Pa_GetDeviceInfo(m_inputParameters.device )->defaultLowInputLatency;
166 }
167 m_inputParameters.hostApiSpecificStreamInfo = nullptr;
168
169 m_err = Pa_OpenStream(
170 &m_stream,
171 &m_inputParameters,
172 nullptr,
175 paClipOff,
178
179 if(m_err != paNoError )
180 {
181 yCError(PORTAUDIORECORDER, "An error occurred while using the portaudio stream" );
182 yCError(PORTAUDIORECORDER, "Error number: %d", m_err );
183 yCError(PORTAUDIORECORDER, "Error message: %s", Pa_GetErrorText(m_err ) );
184 }
185
186 //start the thread
187 bool ret = this->start();
188 YARP_UNUSED(ret);
189
190 return (m_err==paNoError);
191}
192
194{
195 //Pa_Terminate();
197
198 if(m_err != paNoError )
199 {
200 yCError(PORTAUDIORECORDER, "An error occurred while using the portaudio stream" );
201 yCError(PORTAUDIORECORDER, "Error number: %d", m_err );
202 yCError(PORTAUDIORECORDER, "Error message: %s", Pa_GetErrorText(m_err ) );
203 }
204}
205
207{
208 this->stop();
209 if (m_stream != nullptr)
210 {
211 m_err = Pa_CloseStream(m_stream );
212 if(m_err != paNoError )
213 {
214 yCError(PORTAUDIORECORDER, "An error occurred while closing the portaudio stream" );
215 yCError(PORTAUDIORECORDER, "Error number: %d", m_err );
216 yCError(PORTAUDIORECORDER, "Error message: %s", Pa_GetErrorText(m_err ) );
217 }
218 }
219
220 if (this->m_inputBuffer != nullptr)
221 {
222 delete this->m_inputBuffer;
223 this->m_inputBuffer = nullptr;
224 }
225
226 return (m_err==paNoError);
227}
228
230{
231 AudioRecorderDeviceBase::startRecording();
232 m_err = Pa_StartStream(m_stream );
233 if(m_err < 0 ) {handleError(); return false;}
234 yCInfo(PORTAUDIORECORDER) << "started recording";
235 return true;
236}
237
239{
240 yCInfo(PORTAUDIORECORDER) << "not yet implemented recording";
241 return false;
242}
243
245{
246 AudioRecorderDeviceBase::stopRecording();
247 m_err = Pa_StopStream(m_stream );
248 if(m_err < 0 ) {handleError(); return false;}
249 yCInfo(PORTAUDIORECORDER) << "stopped recording";
250 return true;
251}
252
254{
255}
256
258{
259 return true;
260}
261
263{
264 while(this->isStopping()==false)
265 {
266 //The status of the buffer (i.e. the return value of Pa_IsStreamActive() depends on the returned value
267 //of the callback function bufferIOCallback() which may return paContinue or paComplete.
269 {
270 m_err = Pa_IsStreamActive(m_stream);
271 if (m_err < 0)
272 {
273 handleError();
274 yCError(PORTAUDIORECORDER) << "Unhandled error. Calling abortSound()";
275 //abortSound();
276 continue;
277 }
278 if (m_err == 1)
279 {
280 //already doing something
281 }
282 else if (m_err == 0)
283 {
284 //the recording is stopped
285 }
286 }
287
289 }
290 return;
291}
bool ret
short SAMPLE
#define PA_SAMPLE_TYPE
#define DEFAULT_FRAMES_PER_BUFFER
static int bufferIOCallback(const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo, PaStreamCallbackFlags statusFlags, void *userData)
bool startRecording() override
Start the recording.
void run() override
Main body of the new thread.
bool threadInit() override
Initialization method.
bool setHWGain(double gain) override
Sets the hardware gain of the grabbing device (if supported by the hardware)
void threadRelease() override
Release method.
bool close() override
Close the DeviceDriver.
bool stopRecording() override
Stop the recording.
bool open(yarp::os::Searchable &config) override
Open the DeviceDriver.
yarp::dev::CircularAudioBuffer_16t * m_inputBuffer
bool configureRecorderAudioDevice(yarp::os::Searchable &config, std::string device_name)
AudioDeviceDriverSettings m_audiorecorder_cfg
yarp::dev::AudioBufferSize getMaxSize()
A base class for nested structures that can be searched.
Definition: Searchable.h:63
virtual bool check(const std::string &key) const =0
Check if there exists a property of the given name.
virtual Bottle & findGroup(const std::string &key) const =0
Gets a list corresponding to a given keyword.
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:43
#define yCInfo(component,...)
Definition: LogComponent.h:171
#define yCError(component,...)
Definition: LogComponent.h:213
#define YARP_LOG_COMPONENT(name,...)
Definition: LogComponent.h:76
For streams capable of holding different kinds of content, check what they actually have.
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.
#define YARP_UNUSED(var)
Definition: api.h:162