YARP
Yet Another Robot Platform
 
Loading...
Searching...
No Matches
PortAudioPlayerDeviceDriver.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 <cstdlib>
9#include <cstring>
10#include <portaudio.h>
12#include <yarp/dev/api.h>
13
14#include <yarp/os/Time.h>
16#include <yarp/os/LogStream.h>
17#include <mutex>
18
19using namespace yarp::os;
20using namespace yarp::dev;
21
22#define SLEEP_TIME 0.010f
23
24#if 0
25#define PA_SAMPLE_TYPE paFloat32
26typedef float SAMPLE;
27#define SAMPLE_SILENCE (0.0f)
28#elif 1
29#define PA_SAMPLE_TYPE paInt16
30typedef short SAMPLE;
31#define SAMPLE_SILENCE (0)
32#elif 1
33#define PA_SAMPLE_TYPE paInt8
34typedef char SAMPLE;
35#define SAMPLE_SILENCE (0)
36#else
37#define PA_SAMPLE_TYPE paUInt8
38typedef unsigned char SAMPLE;
39#define SAMPLE_SILENCE (128)
40#define SAMPLE_UNSIGNED
41#endif
42
43namespace {
44YARP_LOG_COMPONENT(PORTAUDIOPLAYER, "yarp.devices.portaudioPlayer")
45}
46
47
48/* This routine will be called by the PortAudio engine when audio is needed.
49** It may be called at interrupt level on some machines so don't do anything
50** that could mess up the system like calling malloc() or free().
51*/
52static int bufferIOCallback( const void *inputBuffer, void *outputBuffer,
53 unsigned long framesPerBuffer,
56 void *userData )
57{
59 size_t num_play_channels = playdata->getMaxSize().getChannels();
60 int finished = paComplete;
61
62 if (1)
63 {
64 auto* wptr = (SAMPLE*)outputBuffer;
65 unsigned int i;
66
67 size_t framesLeft = playdata->size().getSamples()* playdata->size().getChannels();
68
73
75 {
76 // final buffer
77 for( i=0; i<framesLeft/ num_play_channels; i++ )
78 {
79 *wptr++ = playdata->read(); // left
80 if (num_play_channels == 2) {
81 *wptr++ = playdata->read(); // right
82 }
83 for (size_t chs = 2; chs < num_play_channels; chs++) {
84 playdata->read(); //remove all additional channels > 2
85 }
86 }
87 for( ; i<framesPerBuffer; i++ )
88 {
89 *wptr++ = 0; // left
90 if (num_play_channels == 2) {
91 *wptr++ = 0; // right
92 }
93 }
94#ifdef STOP_PLAY_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#if 0
106 yCDebug(PORTAUDIOPLAYER) << "Reading" << framesPerBuffer*2 << "bytes from the circular buffer";
107#endif
108 for( i=0; i<framesPerBuffer; i++ )
109 {
110 *wptr++ = playdata->read(); // left
111 if (num_play_channels == 2) {
112 *wptr++ = playdata->read(); // right
113 }
114 for (size_t chs = 2; chs < num_play_channels; chs++) {
115 playdata->read(); //remove all additional channels > 2
116 }
117 }
118 //if we return paContinue, then the callback will be invoked again later
119 //method Pa_IsStreamActive() will return 0
120 finished = paContinue;
121 }
122 return finished;
123 }
124
125 yCError(PORTAUDIOPLAYER, "No read operations requested, aborting");
126 return paAbort;
127}
128
132
134{
135
136 return true;
137}
138
140{
141 while(this->isStopping()==false)
142 {
143 //The status of the buffer (i.e. the return value of Pa_IsStreamActive() depends on the returned value
144 //of the callback function bufferIOCallback() which may return paContinue or paComplete.
146 {
147 m_err = Pa_IsStreamActive(m_stream);
148 if (m_err < 0)
149 {
150 handleError();
151 yCError(PORTAUDIOPLAYER) << "Unhandled error. Calling abortSound()";
152 abortSound();
153 continue;
154 }
155 if (m_err == 1)
156 {
157 //already playing something
158 }
159 else if (m_err == 0)
160 {
161 //the playback is stopped
162 }
163 }
165 }
166 return;
167}
168
170 m_stream(nullptr),
171 m_err(paNoError),
172 m_system_resource(nullptr)
173{
174 memset(&m_outputParameters, 0, sizeof(PaStreamParameters));
175}
176
181
183{
184 this->stop();
185 if (m_stream != nullptr)
186 {
187 m_err = Pa_CloseStream(m_stream);
188 if (m_err != paNoError)
189 {
190 yCError(PORTAUDIOPLAYER, "An error occurred while closing the portaudio stream");
191 yCError(PORTAUDIOPLAYER, "Error number: %d", m_err);
192 yCError(PORTAUDIOPLAYER, "Error message: %s", Pa_GetErrorText(m_err));
193 }
194 }
195
196 if (this->m_outputBuffer != nullptr)
197 {
198 delete this->m_outputBuffer;
199 this->m_outputBuffer = nullptr;
200 }
201
202 return (m_err == paNoError);
203}
204
205bool PortAudioPlayerDeviceDriver::configureDeviceAndStart()
206{
208 if (m_outputBuffer == nullptr) {
210 }
211
212 m_err = Pa_Initialize();
213 if (m_err != paNoError)
214 {
215 yCError(PORTAUDIOPLAYER, "portaudio system failed to initialize");
216 return false;
217 }
218
219 m_outputParameters.device = (m_audio_device_id == -1) ? Pa_GetDefaultOutputDevice() : m_audio_device_id;
220 m_outputParameters.channelCount = static_cast<int>(m_audioplayer_cfg.numChannels);
221 m_outputParameters.sampleFormat = PA_SAMPLE_TYPE;
222 m_outputParameters.hostApiSpecificStreamInfo = nullptr;
223
224 const PaDeviceInfo* devinfo = Pa_GetDeviceInfo(m_outputParameters.device);
225 std::string devname = "unknown";
226 if (devinfo != nullptr)
227 {
228 m_outputParameters.suggestedLatency = devinfo->defaultLowOutputLatency;
229 devname = devinfo->name;
230 }
231 yCInfo(PORTAUDIOPLAYER, "Selected device: number: %d, name: %s", m_outputParameters.device, devname.c_str());
232
233 m_err = Pa_OpenStream(
234 &m_stream,
235 nullptr,
236 &m_outputParameters,
239 paClipOff,
242
243 if (m_err != paNoError)
244 {
245 yCError(PORTAUDIOPLAYER, "An error occurred while using the portaudio stream");
246 yCError(PORTAUDIOPLAYER, "Error number: %d", m_err);
247 yCError(PORTAUDIOPLAYER, "Error message: %s", Pa_GetErrorText(m_err));
248 }
249
250 //start the thread
251 this->start();
252
253 return true;
254}
255
257{
258 bool b = parseParams(config);
259 if (!b) { return false; }
260
261 b = configurePlayerAudioDevice(config.findGroup("AUDIO_BASE"), "portaudioPlayer");
262 if (!b) { return false; }
263
264 b = configureDeviceAndStart();
265 return (m_err==paNoError);
266}
267
269{
270 //Pa_Terminate();
272
273 if(m_err != paNoError )
274 {
275 yCError(PORTAUDIOPLAYER, "An error occurred while using the portaudio stream" );
276 yCError(PORTAUDIOPLAYER, "Error number: %d", m_err );
277 yCError(PORTAUDIOPLAYER, "Error message: %s", Pa_GetErrorText(m_err ) );
278 }
279}
280
285
286bool PortAudioPlayerDeviceDriver::abortSound()
287{
288 yCInfo(PORTAUDIOPLAYER, "=== Stopping and clearing stream.==="); fflush(stdout);
289 m_err = Pa_StopStream(m_stream );
290 if(m_err != paNoError )
291 {
292 yCError(PORTAUDIOPLAYER, "abortSound: error occurred while stopping the portaudio stream" );
293 yCError(PORTAUDIOPLAYER, "Error number: %d", m_err );
294 yCError(PORTAUDIOPLAYER, "Error message: %s", Pa_GetErrorText(m_err ) );
295 }
296
298
299 return (m_err==paNoError);
300}
301
303{
304 yCError(PORTAUDIOPLAYER, "Not yet implemented");
305 return ReturnValue::return_code::return_value_error_not_implemented_by_device;
306}
307
309{
310 while (Pa_IsStreamStopped(m_stream) == 0)
311 {
313 size_t tmp = m_outputBuffer->size().getSamples();
314 if (tmp == 0) {
315 break;
316 }
317 }
318}
319
321{
323 m_err = Pa_StartStream(m_stream);
324 if (m_err < 0) { handleError(); return ReturnValue::return_code::return_value_error_method_failed; }
325 yCInfo(PORTAUDIOPLAYER) << "started playback";
326 return ReturnValue_ok;
327}
328
330{
332 m_err = Pa_StopStream(m_stream);
333 if (m_err < 0) { handleError(); return ReturnValue::return_code::return_value_error_method_failed; }
334 yCInfo(PORTAUDIOPLAYER) << "stopped playback";
335 return ReturnValue_ok;
336}
#define PA_SAMPLE_TYPE
static int bufferIOCallback(const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo, PaStreamCallbackFlags statusFlags, void *userData)
#define SLEEP_TIME
static int bufferIOCallback(const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo, PaStreamCallbackFlags statusFlags, void *userData)
#define ReturnValue_ok
Definition ReturnValue.h:77
bool parseParams(const yarp::os::Searchable &config) override
Parse the DeviceDriver parameters.
yarp::dev::ReturnValue setHWGain(double gain) override
Sets the hardware gain of the playback device (if supported by the hardware)
bool threadInit() override
Initialization method.
yarp::dev::ReturnValue stopPlayback() override
Stop the playback.
void run() override
Main body of the new thread.
yarp::dev::ReturnValue startPlayback() override
Start the playback.
bool close() override
Close the DeviceDriver.
bool open(yarp::os::Searchable &config) override
Open the DeviceDriver.
void threadRelease() override
Release method.
virtual yarp::dev::ReturnValue stopPlayback() override
Stop the playback.
yarp::dev::CircularAudioBuffer_16t * m_outputBuffer
bool configurePlayerAudioDevice(yarp::os::Searchable &config, std::string device_name)
virtual yarp::dev::ReturnValue startPlayback() override
Start the playback.
AudioDeviceDriverSettings m_audioplayer_cfg
yarp::sig::AudioBufferSize size()
A mini-server for performing network communication in the background.
T * read(bool shouldWait=true) override
Read an available object from the port.
A base class for nested structures that can be searched.
Definition Searchable.h:31
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
#define yCInfo(component,...)
#define yCError(component,...)
#define yCDebug(component,...)
#define YARP_LOG_COMPONENT(name,...)
For streams capable of holding different kinds of content, check what they actually have.
Definition jointData.cpp:13
yarp::dev::CircularAudioBuffer< unsigned short int > CircularAudioBuffer_16t
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