/[pcsx2_0.9.7]/trunk/3rdparty/portaudio/src/hostapi/alsa/pa_linux_alsa.c
ViewVC logotype

Contents of /trunk/3rdparty/portaudio/src/hostapi/alsa/pa_linux_alsa.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 401 - (show annotations) (download)
Fri Feb 25 17:31:09 2011 UTC (8 years, 11 months ago) by william
File MIME type: text/plain
File size: 140397 byte(s)
Auto Commited Import of: pcsx2-0.9.7-DEBUG (upstream: v0.9.7.4358 local: v0.9.7.313-latest) in ./trunk
1 /*
2 * $Id: pa_linux_alsa.c 1540 2010-09-20 16:23:30Z dmitrykos $
3 * PortAudio Portable Real-Time Audio Library
4 * Latest Version at: http://www.portaudio.com
5 * ALSA implementation by Joshua Haberman and Arve Knudsen
6 *
7 * Copyright (c) 2002 Joshua Haberman <joshua@haberman.com>
8 * Copyright (c) 2005-2009 Arve Knudsen <arve.knudsen@gmail.com>
9 * Copyright (c) 2008 Kevin Kofler <kevin.kofler@chello.at>
10 *
11 * Based on the Open Source API proposed by Ross Bencina
12 * Copyright (c) 1999-2002 Ross Bencina, Phil Burk
13 *
14 * Permission is hereby granted, free of charge, to any person obtaining
15 * a copy of this software and associated documentation files
16 * (the "Software"), to deal in the Software without restriction,
17 * including without limitation the rights to use, copy, modify, merge,
18 * publish, distribute, sublicense, and/or sell copies of the Software,
19 * and to permit persons to whom the Software is furnished to do so,
20 * subject to the following conditions:
21 *
22 * The above copyright notice and this permission notice shall be
23 * included in all copies or substantial portions of the Software.
24 *
25 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
28 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
29 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
30 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 */
33
34 /*
35 * The text above constitutes the entire PortAudio license; however,
36 * the PortAudio community also makes the following non-binding requests:
37 *
38 * Any person wishing to distribute modifications to the Software is
39 * requested to send the modifications to the original developer so that
40 * they can be incorporated into the canonical version. It is also
41 * requested that these non-binding requests be included along with the
42 * license above.
43 */
44
45 /**
46 @file
47 @ingroup hostapi_src
48 */
49
50 #define ALSA_PCM_NEW_HW_PARAMS_API
51 #define ALSA_PCM_NEW_SW_PARAMS_API
52 #include <alsa/asoundlib.h>
53 #undef ALSA_PCM_NEW_HW_PARAMS_API
54 #undef ALSA_PCM_NEW_SW_PARAMS_API
55
56 #include <sys/poll.h>
57 #include <string.h> /* strlen() */
58 #include <limits.h>
59 #include <math.h>
60 #include <pthread.h>
61 #include <signal.h>
62 #include <time.h>
63 #include <sys/mman.h>
64 #include <signal.h> /* For sig_atomic_t */
65
66 #include "portaudio.h"
67 #include "pa_util.h"
68 #include "pa_unix_util.h"
69 #include "pa_allocation.h"
70 #include "pa_hostapi.h"
71 #include "pa_stream.h"
72 #include "pa_cpuload.h"
73 #include "pa_process.h"
74 #include "pa_endianness.h"
75 #include "pa_debugprint.h"
76
77 #include "pa_linux_alsa.h"
78
79 /* Check return value of ALSA function, and map it to PaError */
80 #define ENSURE_(expr, code) \
81 do { \
82 if( UNLIKELY( (aErr_ = (expr)) < 0 ) ) \
83 { \
84 /* PaUtil_SetLastHostErrorInfo should only be used in the main thread */ \
85 if( (code) == paUnanticipatedHostError && pthread_equal( pthread_self(), paUnixMainThread) ) \
86 { \
87 PaUtil_SetLastHostErrorInfo( paALSA, aErr_, snd_strerror( aErr_ ) ); \
88 } \
89 PaUtil_DebugPrint( "Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" ); \
90 if( (code) == paUnanticipatedHostError ) \
91 PA_DEBUG(( "Host error description: %s\n", snd_strerror( aErr_ ) )); \
92 result = (code); \
93 goto error; \
94 } \
95 } while( 0 );
96
97 #define ASSERT_CALL_(expr, success) \
98 aErr_ = (expr); \
99 assert( success == aErr_ );
100
101 static int aErr_; /* Used with ENSURE_ */
102 static int numPeriods_ = 4;
103 static int busyRetries_ = 100;
104
105 int PaAlsa_SetNumPeriods( int numPeriods )
106 {
107 numPeriods_ = numPeriods;
108 return paNoError;
109 }
110
111 typedef enum
112 {
113 StreamDirection_In,
114 StreamDirection_Out
115 } StreamDirection;
116
117 typedef struct
118 {
119 PaSampleFormat hostSampleFormat;
120 unsigned long framesPerBuffer;
121 int numUserChannels, numHostChannels;
122 int userInterleaved, hostInterleaved;
123 int canMmap;
124 void *nonMmapBuffer;
125 unsigned int nonMmapBufferSize;
126 PaDeviceIndex device; /* Keep the device index */
127
128 snd_pcm_t *pcm;
129 snd_pcm_uframes_t bufferSize;
130 snd_pcm_format_t nativeFormat;
131 unsigned int nfds;
132 int ready; /* Marked ready from poll */
133 void **userBuffers;
134 snd_pcm_uframes_t offset;
135 StreamDirection streamDir;
136
137 snd_pcm_channel_area_t *channelAreas; /* Needed for channel adaption */
138 } PaAlsaStreamComponent;
139
140 /* Implementation specific stream structure */
141 typedef struct PaAlsaStream
142 {
143 PaUtilStreamRepresentation streamRepresentation;
144 PaUtilCpuLoadMeasurer cpuLoadMeasurer;
145 PaUtilBufferProcessor bufferProcessor;
146 PaUnixThread thread;
147
148 unsigned long framesPerUserBuffer, maxFramesPerHostBuffer;
149
150 int primeBuffers;
151 int callbackMode; /* bool: are we running in callback mode? */
152 int pcmsSynced; /* Have we successfully synced pcms */
153 int rtSched;
154
155 /* the callback thread uses these to poll the sound device(s), waiting
156 * for data to be ready/available */
157 struct pollfd* pfds;
158 int pollTimeout;
159
160 /* Used in communication between threads */
161 volatile sig_atomic_t callback_finished; /* bool: are we in the "callback finished" state? */
162 volatile sig_atomic_t callbackAbort; /* Drop frames? */
163 volatile sig_atomic_t isActive; /* Is stream in active state? (Between StartStream and StopStream || !paContinue) */
164 PaUnixMutex stateMtx; /* Used to synchronize access to stream state */
165
166 int neverDropInput;
167
168 PaTime underrun;
169 PaTime overrun;
170
171 PaAlsaStreamComponent capture, playback;
172 }
173 PaAlsaStream;
174
175 /* PaAlsaHostApiRepresentation - host api datastructure specific to this implementation */
176
177 typedef struct PaAlsaHostApiRepresentation
178 {
179 PaUtilHostApiRepresentation baseHostApiRep;
180 PaUtilStreamInterface callbackStreamInterface;
181 PaUtilStreamInterface blockingStreamInterface;
182
183 PaUtilAllocationGroup *allocations;
184
185 PaHostApiIndex hostApiIndex;
186 }
187 PaAlsaHostApiRepresentation;
188
189 typedef struct PaAlsaDeviceInfo
190 {
191 PaDeviceInfo baseDeviceInfo;
192 char *alsaName;
193 int isPlug;
194 int minInputChannels;
195 int minOutputChannels;
196 }
197 PaAlsaDeviceInfo;
198
199 /* prototypes for functions declared in this file */
200
201 static void Terminate( struct PaUtilHostApiRepresentation *hostApi );
202 static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
203 const PaStreamParameters *inputParameters,
204 const PaStreamParameters *outputParameters,
205 double sampleRate );
206 static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
207 PaStream** s,
208 const PaStreamParameters *inputParameters,
209 const PaStreamParameters *outputParameters,
210 double sampleRate,
211 unsigned long framesPerBuffer,
212 PaStreamFlags streamFlags,
213 PaStreamCallback *callback,
214 void *userData );
215 static PaError CloseStream( PaStream* stream );
216 static PaError StartStream( PaStream *stream );
217 static PaError StopStream( PaStream *stream );
218 static PaError AbortStream( PaStream *stream );
219 static PaError IsStreamStopped( PaStream *s );
220 static PaError IsStreamActive( PaStream *stream );
221 static PaTime GetStreamTime( PaStream *stream );
222 static double GetStreamCpuLoad( PaStream* stream );
223 static PaError BuildDeviceList( PaAlsaHostApiRepresentation *hostApi );
224 static int SetApproximateSampleRate( snd_pcm_t *pcm, snd_pcm_hw_params_t *hwParams, double sampleRate );
225 static int GetExactSampleRate( snd_pcm_hw_params_t *hwParams, double *sampleRate );
226
227 /* Callback prototypes */
228 static void *CallbackThreadFunc( void *userData );
229
230 /* Blocking prototypes */
231 static signed long GetStreamReadAvailable( PaStream* s );
232 static signed long GetStreamWriteAvailable( PaStream* s );
233 static PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames );
234 static PaError WriteStream( PaStream* stream, const void *buffer, unsigned long frames );
235
236
237 static const PaAlsaDeviceInfo *GetDeviceInfo( const PaUtilHostApiRepresentation *hostApi, int device )
238 {
239 return (const PaAlsaDeviceInfo *)hostApi->deviceInfos[device];
240 }
241
242 /** Uncommented because AlsaErrorHandler is unused for anything good yet. If AlsaErrorHandler is
243 to be used, do not forget to register this callback in PaAlsa_Initialize, and unregister in Terminate.
244 */
245 /*static void AlsaErrorHandler(const char *file, int line, const char *function, int err, const char *fmt, ...)
246 {
247 }*/
248
249 PaError PaAlsa_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
250 {
251 PaError result = paNoError;
252 PaAlsaHostApiRepresentation *alsaHostApi = NULL;
253
254 PA_UNLESS( alsaHostApi = (PaAlsaHostApiRepresentation*) PaUtil_AllocateMemory(
255 sizeof(PaAlsaHostApiRepresentation) ), paInsufficientMemory );
256 PA_UNLESS( alsaHostApi->allocations = PaUtil_CreateAllocationGroup(), paInsufficientMemory );
257 alsaHostApi->hostApiIndex = hostApiIndex;
258
259 *hostApi = (PaUtilHostApiRepresentation*)alsaHostApi;
260 (*hostApi)->info.structVersion = 1;
261 (*hostApi)->info.type = paALSA;
262 (*hostApi)->info.name = "ALSA";
263
264 (*hostApi)->Terminate = Terminate;
265 (*hostApi)->OpenStream = OpenStream;
266 (*hostApi)->IsFormatSupported = IsFormatSupported;
267
268 /** If AlsaErrorHandler is to be used, do not forget to unregister callback pointer in
269 Terminate function.
270 */
271 /*ENSURE_( snd_lib_error_set_handler(AlsaErrorHandler), paUnanticipatedHostError );*/
272
273 PA_ENSURE( BuildDeviceList( alsaHostApi ) );
274
275 PaUtil_InitializeStreamInterface( &alsaHostApi->callbackStreamInterface,
276 CloseStream, StartStream,
277 StopStream, AbortStream,
278 IsStreamStopped, IsStreamActive,
279 GetStreamTime, GetStreamCpuLoad,
280 PaUtil_DummyRead, PaUtil_DummyWrite,
281 PaUtil_DummyGetReadAvailable,
282 PaUtil_DummyGetWriteAvailable );
283
284 PaUtil_InitializeStreamInterface( &alsaHostApi->blockingStreamInterface,
285 CloseStream, StartStream,
286 StopStream, AbortStream,
287 IsStreamStopped, IsStreamActive,
288 GetStreamTime, PaUtil_DummyGetCpuLoad,
289 ReadStream, WriteStream,
290 GetStreamReadAvailable,
291 GetStreamWriteAvailable );
292
293 PA_ENSURE( PaUnixThreading_Initialize() );
294
295 return result;
296
297 error:
298 if( alsaHostApi )
299 {
300 if( alsaHostApi->allocations )
301 {
302 PaUtil_FreeAllAllocations( alsaHostApi->allocations );
303 PaUtil_DestroyAllocationGroup( alsaHostApi->allocations );
304 }
305
306 PaUtil_FreeMemory( alsaHostApi );
307 }
308
309 return result;
310 }
311
312 static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
313 {
314 PaAlsaHostApiRepresentation *alsaHostApi = (PaAlsaHostApiRepresentation*)hostApi;
315
316 assert( hostApi );
317
318 /** See AlsaErrorHandler and PaAlsa_Initialize for details.
319 */
320 /*snd_lib_error_set_handler(NULL);*/
321
322 if( alsaHostApi->allocations )
323 {
324 PaUtil_FreeAllAllocations( alsaHostApi->allocations );
325 PaUtil_DestroyAllocationGroup( alsaHostApi->allocations );
326 }
327
328 PaUtil_FreeMemory( alsaHostApi );
329 snd_config_update_free_global();
330 }
331
332 /** Determine max channels and default latencies.
333 *
334 * This function provides functionality to grope an opened (might be opened for capture or playback) pcm device for
335 * traits like max channels, suitable default latencies and default sample rate. Upon error, max channels is set to zero,
336 * and a suitable result returned. The device is closed before returning.
337 */
338 static PaError GropeDevice( snd_pcm_t* pcm, int isPlug, StreamDirection mode, int openBlocking,
339 PaAlsaDeviceInfo* devInfo )
340 {
341 PaError result = paNoError;
342 snd_pcm_hw_params_t *hwParams;
343 snd_pcm_uframes_t lowLatency = 512, highLatency = 2048;
344 unsigned int minChans, maxChans;
345 int* minChannels, * maxChannels;
346 double * defaultLowLatency, * defaultHighLatency, * defaultSampleRate =
347 &devInfo->baseDeviceInfo.defaultSampleRate;
348 double defaultSr = *defaultSampleRate;
349
350 assert( pcm );
351
352 if( StreamDirection_In == mode )
353 {
354 minChannels = &devInfo->minInputChannels;
355 maxChannels = &devInfo->baseDeviceInfo.maxInputChannels;
356 defaultLowLatency = &devInfo->baseDeviceInfo.defaultLowInputLatency;
357 defaultHighLatency = &devInfo->baseDeviceInfo.defaultHighInputLatency;
358 }
359 else
360 {
361 minChannels = &devInfo->minOutputChannels;
362 maxChannels = &devInfo->baseDeviceInfo.maxOutputChannels;
363 defaultLowLatency = &devInfo->baseDeviceInfo.defaultLowOutputLatency;
364 defaultHighLatency = &devInfo->baseDeviceInfo.defaultHighOutputLatency;
365 }
366
367 ENSURE_( snd_pcm_nonblock( pcm, 0 ), paUnanticipatedHostError );
368
369 snd_pcm_hw_params_alloca( &hwParams );
370 snd_pcm_hw_params_any( pcm, hwParams );
371
372 if( defaultSr >= 0 )
373 {
374 /* Could be that the device opened in one mode supports samplerates that the other mode wont have,
375 * so try again .. */
376 if( SetApproximateSampleRate( pcm, hwParams, defaultSr ) < 0 )
377 {
378 defaultSr = -1.;
379 PA_DEBUG(( "%s: Original default samplerate failed, trying again ..\n", __FUNCTION__ ));
380 }
381 }
382
383 if( defaultSr < 0. ) /* Default sample rate not set */
384 {
385 unsigned int sampleRate = 44100; /* Will contain approximate rate returned by alsa-lib */
386 if( snd_pcm_hw_params_set_rate_near( pcm, hwParams, &sampleRate, NULL ) < 0)
387 {
388 result = paUnanticipatedHostError;
389 goto error;
390 }
391 ENSURE_( GetExactSampleRate( hwParams, &defaultSr ), paUnanticipatedHostError );
392 }
393
394 ENSURE_( snd_pcm_hw_params_get_channels_min( hwParams, &minChans ), paUnanticipatedHostError );
395 ENSURE_( snd_pcm_hw_params_get_channels_max( hwParams, &maxChans ), paUnanticipatedHostError );
396 assert( maxChans <= INT_MAX );
397 assert( maxChans > 0 ); /* Weird linking issue could cause wrong version of ALSA symbols to be called,
398 resulting in zeroed values */
399
400 /* XXX: Limit to sensible number (ALSA plugins accept a crazy amount of channels)? */
401 if( isPlug && maxChans > 128 )
402 {
403 maxChans = 128;
404 PA_DEBUG(( "%s: Limiting number of plugin channels to %u\n", __FUNCTION__, maxChans ));
405 }
406
407 /* TWEAKME:
408 *
409 * Giving values for default min and max latency is not
410 * straightforward. Here are our objectives:
411 *
412 * * for low latency, we want to give the lowest value
413 * that will work reliably. This varies based on the
414 * sound card, kernel, CPU, etc. I think it is better
415 * to give sub-optimal latency than to give a number
416 * too low and cause dropouts. My conservative
417 * estimate at this point is to base it on 4096-sample
418 * latency at 44.1 kHz, which gives a latency of 23ms.
419 * * for high latency we want to give a large enough
420 * value that dropouts are basically impossible. This
421 * doesn't really require as much tweaking, since
422 * providing too large a number will just cause us to
423 * select the nearest setting that will work at stream
424 * config time.
425 */
426 ENSURE_( snd_pcm_hw_params_set_buffer_size_near( pcm, hwParams, &lowLatency ), paUnanticipatedHostError );
427
428 /* Have to reset hwParams, to set new buffer size */
429 ENSURE_( snd_pcm_hw_params_any( pcm, hwParams ), paUnanticipatedHostError );
430 ENSURE_( snd_pcm_hw_params_set_buffer_size_near( pcm, hwParams, &highLatency ), paUnanticipatedHostError );
431
432 *minChannels = (int)minChans;
433 *maxChannels = (int)maxChans;
434 *defaultSampleRate = defaultSr;
435 *defaultLowLatency = (double) lowLatency / *defaultSampleRate;
436 *defaultHighLatency = (double) highLatency / *defaultSampleRate;
437
438 end:
439 snd_pcm_close( pcm );
440 return result;
441
442 error:
443 goto end;
444 }
445
446 /* Initialize device info with invalid values (maxInputChannels and maxOutputChannels are set to zero since these indicate
447 * wether input/output is available) */
448 static void InitializeDeviceInfo( PaDeviceInfo *deviceInfo )
449 {
450 deviceInfo->structVersion = -1;
451 deviceInfo->name = NULL;
452 deviceInfo->hostApi = -1;
453 deviceInfo->maxInputChannels = 0;
454 deviceInfo->maxOutputChannels = 0;
455 deviceInfo->defaultLowInputLatency = -1.;
456 deviceInfo->defaultLowOutputLatency = -1.;
457 deviceInfo->defaultHighInputLatency = -1.;
458 deviceInfo->defaultHighOutputLatency = -1.;
459 deviceInfo->defaultSampleRate = -1.;
460 }
461
462 /* Helper struct */
463 typedef struct
464 {
465 char *alsaName;
466 char *name;
467 int isPlug;
468 int hasPlayback;
469 int hasCapture;
470 } HwDevInfo;
471
472
473 HwDevInfo predefinedNames[] = {
474 { "center_lfe", NULL, 0, 1, 0 },
475 /* { "default", NULL, 0, 1, 0 }, */
476 /* { "dmix", NULL, 0, 1, 0 }, */
477 /* { "dpl", NULL, 0, 1, 0 }, */
478 /* { "dsnoop", NULL, 0, 1, 0 }, */
479 { "front", NULL, 0, 1, 0 },
480 { "iec958", NULL, 0, 1, 0 },
481 /* { "modem", NULL, 0, 1, 0 }, */
482 { "rear", NULL, 0, 1, 0 },
483 { "side", NULL, 0, 1, 0 },
484 /* { "spdif", NULL, 0, 0, 0 }, */
485 { "surround40", NULL, 0, 1, 0 },
486 { "surround41", NULL, 0, 1, 0 },
487 { "surround50", NULL, 0, 1, 0 },
488 { "surround51", NULL, 0, 1, 0 },
489 { "surround71", NULL, 0, 1, 0 },
490 { NULL, NULL, 0, 1, 0 }
491 };
492
493 static const HwDevInfo *FindDeviceName( const char *name )
494 {
495 int i;
496
497 for( i = 0; predefinedNames[i].alsaName; i++ )
498 {
499 if( strcmp( name, predefinedNames[i].alsaName ) == 0 )
500 {
501 return &predefinedNames[i];
502 }
503 }
504
505 return NULL;
506 }
507
508 static PaError PaAlsa_StrDup( PaAlsaHostApiRepresentation *alsaApi,
509 char **dst,
510 const char *src)
511 {
512 PaError result = paNoError;
513 int len = strlen( src ) + 1;
514
515 /* PA_DEBUG(("PaStrDup %s %d\n", src, len)); */
516
517 PA_UNLESS( *dst = (char *)PaUtil_GroupAllocateMemory( alsaApi->allocations, len ),
518 paInsufficientMemory );
519 strncpy( *dst, src, len );
520
521 error:
522 return result;
523 }
524
525 /* Disregard some standard plugins
526 */
527 static int IgnorePlugin( const char *pluginId )
528 {
529 static const char *ignoredPlugins[] = {"hw", "plughw", "plug", "dsnoop", "tee",
530 "file", "null", "shm", "cards", "rate_convert", NULL};
531 int i = 0;
532 while( ignoredPlugins[i] )
533 {
534 if( !strcmp( pluginId, ignoredPlugins[i] ) )
535 {
536 return 1;
537 }
538 ++i;
539 }
540
541 return 0;
542 }
543
544 /** Open PCM device.
545 *
546 * Wrapper around snd_pcm_open which may repeatedly retry opening a device if it is busy, for
547 * a certain time. This is because dmix may temporarily hold on to a device after it (dmix)
548 * has been opened and closed.
549 * @param mode: Open mode (e.g., SND_PCM_BLOCKING).
550 * @param waitOnBusy: Retry opening busy device for up to one second?
551 **/
552 static int OpenPcm( snd_pcm_t **pcmp, const char *name, snd_pcm_stream_t stream, int mode, int waitOnBusy )
553 {
554 int tries = 0, maxTries = waitOnBusy ? busyRetries_ : 0;
555 int ret = snd_pcm_open( pcmp, name, stream, mode );
556 for( tries = 0; tries < maxTries && -EBUSY == ret; ++tries )
557 {
558 Pa_Sleep( 10 );
559 ret = snd_pcm_open( pcmp, name, stream, mode );
560 if( -EBUSY != ret )
561 {
562 PA_DEBUG(( "%s: Successfully opened initially busy device after %d tries\n",
563 __FUNCTION__, tries ));
564 }
565 }
566 if( -EBUSY == ret )
567 {
568 PA_DEBUG(( "%s: Failed to open busy device '%s'\n",
569 __FUNCTION__, name ));
570 }
571
572 return ret;
573 }
574
575 static PaError FillInDevInfo( PaAlsaHostApiRepresentation *alsaApi, HwDevInfo* deviceName, int blocking,
576 PaAlsaDeviceInfo* devInfo, int* devIdx )
577 {
578 PaError result = 0;
579 PaDeviceInfo *baseDeviceInfo = &devInfo->baseDeviceInfo;
580 snd_pcm_t *pcm;
581 PaUtilHostApiRepresentation *baseApi = &alsaApi->baseHostApiRep;
582
583 /* Zero fields */
584 InitializeDeviceInfo( baseDeviceInfo );
585
586 /* to determine device capabilities, we must open the device and query the
587 * hardware parameter configuration space */
588
589 /* Query capture */
590 if( deviceName->hasCapture &&
591 OpenPcm( &pcm, deviceName->alsaName, SND_PCM_STREAM_CAPTURE, blocking, 0 )
592 >= 0 )
593 {
594 if( GropeDevice( pcm, deviceName->isPlug, StreamDirection_In, blocking, devInfo ) != paNoError )
595 {
596 /* Error */
597 PA_DEBUG(("%s: Failed groping %s for capture\n", __FUNCTION__, deviceName->alsaName));
598 goto end;
599 }
600 }
601
602 /* Query playback */
603 if( deviceName->hasPlayback &&
604 OpenPcm( &pcm, deviceName->alsaName, SND_PCM_STREAM_PLAYBACK, blocking, 0 )
605 >= 0 )
606 {
607 if( GropeDevice( pcm, deviceName->isPlug, StreamDirection_Out, blocking, devInfo ) != paNoError )
608 {
609 /* Error */
610 PA_DEBUG(("%s: Failed groping %s for playback\n", __FUNCTION__, deviceName->alsaName));
611 goto end;
612 }
613 }
614
615 baseDeviceInfo->structVersion = 2;
616 baseDeviceInfo->hostApi = alsaApi->hostApiIndex;
617 baseDeviceInfo->name = deviceName->name;
618 devInfo->alsaName = deviceName->alsaName;
619 devInfo->isPlug = deviceName->isPlug;
620
621 /* A: Storing pointer to PaAlsaDeviceInfo object as pointer to PaDeviceInfo object.
622 * Should now be safe to add device info, unless the device supports neither capture nor playback
623 */
624 if( baseDeviceInfo->maxInputChannels > 0 || baseDeviceInfo->maxOutputChannels > 0 )
625 {
626 /* Make device default if there isn't already one or it is the ALSA "default" device */
627 if( (baseApi->info.defaultInputDevice == paNoDevice || !strcmp(deviceName->alsaName,
628 "default" )) && baseDeviceInfo->maxInputChannels > 0 )
629 {
630 baseApi->info.defaultInputDevice = *devIdx;
631 PA_DEBUG(("Default input device: %s\n", deviceName->name));
632 }
633 if( (baseApi->info.defaultOutputDevice == paNoDevice || !strcmp(deviceName->alsaName,
634 "default" )) && baseDeviceInfo->maxOutputChannels > 0 )
635 {
636 baseApi->info.defaultOutputDevice = *devIdx;
637 PA_DEBUG(("Default output device: %s\n", deviceName->name));
638 }
639 PA_DEBUG(("%s: Adding device %s: %d\n", __FUNCTION__, deviceName->name, *devIdx));
640 baseApi->deviceInfos[*devIdx] = (PaDeviceInfo *) devInfo;
641 (*devIdx) += 1;
642 }
643
644 end:
645 return result;
646 }
647
648 /* Build PaDeviceInfo list, ignore devices for which we cannot determine capabilities (possibly busy, sigh) */
649 static PaError BuildDeviceList( PaAlsaHostApiRepresentation *alsaApi )
650 {
651 PaUtilHostApiRepresentation *baseApi = &alsaApi->baseHostApiRep;
652 PaAlsaDeviceInfo *deviceInfoArray;
653 int cardIdx = -1, devIdx = 0;
654 snd_ctl_card_info_t *cardInfo;
655 PaError result = paNoError;
656 size_t numDeviceNames = 0, maxDeviceNames = 1, i;
657 HwDevInfo *hwDevInfos = NULL;
658 snd_config_t *topNode = NULL;
659 snd_pcm_info_t *pcmInfo;
660 int res;
661 int blocking = SND_PCM_NONBLOCK;
662 char alsaCardName[50];
663 #ifdef PA_ENABLE_DEBUG_OUTPUT
664 PaTime startTime = PaUtil_GetTime();
665 #endif
666
667 if( getenv( "PA_ALSA_INITIALIZE_BLOCK" ) && atoi( getenv( "PA_ALSA_INITIALIZE_BLOCK" ) ) )
668 blocking = 0;
669
670 /* These two will be set to the first working input and output device, respectively */
671 baseApi->info.defaultInputDevice = paNoDevice;
672 baseApi->info.defaultOutputDevice = paNoDevice;
673
674 /* Gather info about hw devices
675
676 * snd_card_next() modifies the integer passed to it to be:
677 * the index of the first card if the parameter is -1
678 * the index of the next card if the parameter is the index of a card
679 * -1 if there are no more cards
680 *
681 * The function itself returns 0 if it succeeded. */
682 cardIdx = -1;
683 snd_ctl_card_info_alloca( &cardInfo );
684 snd_pcm_info_alloca( &pcmInfo );
685 while( snd_card_next( &cardIdx ) == 0 && cardIdx >= 0 )
686 {
687 char *cardName;
688 int devIdx = -1;
689 snd_ctl_t *ctl;
690 char buf[50];
691
692 snprintf( alsaCardName, sizeof (alsaCardName), "hw:%d", cardIdx );
693
694 /* Acquire name of card */
695 if( snd_ctl_open( &ctl, alsaCardName, 0 ) < 0 )
696 {
697 /* Unable to open card :( */
698 PA_DEBUG(( "%s: Unable to open device %s\n", __FUNCTION__, alsaCardName ));
699 continue;
700 }
701 snd_ctl_card_info( ctl, cardInfo );
702
703 PA_ENSURE( PaAlsa_StrDup( alsaApi, &cardName, snd_ctl_card_info_get_name( cardInfo )) );
704
705 while( snd_ctl_pcm_next_device( ctl, &devIdx ) == 0 && devIdx >= 0 )
706 {
707 char *alsaDeviceName, *deviceName;
708 size_t len;
709 int hasPlayback = 0, hasCapture = 0;
710 snprintf( buf, sizeof (buf), "hw:%d,%d", cardIdx, devIdx );
711
712 /* Obtain info about this particular device */
713 snd_pcm_info_set_device( pcmInfo, devIdx );
714 snd_pcm_info_set_subdevice( pcmInfo, 0 );
715 snd_pcm_info_set_stream( pcmInfo, SND_PCM_STREAM_CAPTURE );
716 if( snd_ctl_pcm_info( ctl, pcmInfo ) >= 0 )
717 {
718 hasCapture = 1;
719 }
720
721 snd_pcm_info_set_stream( pcmInfo, SND_PCM_STREAM_PLAYBACK );
722 if( snd_ctl_pcm_info( ctl, pcmInfo ) >= 0 )
723 {
724 hasPlayback = 1;
725 }
726
727 if( !hasPlayback && !hasCapture )
728 {
729 /* Error */
730 continue;
731 }
732
733 /* The length of the string written by snprintf plus terminating 0 */
734 len = snprintf( NULL, 0, "%s: %s (%s)", cardName, snd_pcm_info_get_name( pcmInfo ), buf ) + 1;
735 PA_UNLESS( deviceName = (char *)PaUtil_GroupAllocateMemory( alsaApi->allocations, len ),
736 paInsufficientMemory );
737 snprintf( deviceName, len, "%s: %s (%s)", cardName,
738 snd_pcm_info_get_name( pcmInfo ), buf );
739
740 ++numDeviceNames;
741 if( !hwDevInfos || numDeviceNames > maxDeviceNames )
742 {
743 maxDeviceNames *= 2;
744 PA_UNLESS( hwDevInfos = (HwDevInfo *) realloc( hwDevInfos, maxDeviceNames * sizeof (HwDevInfo) ),
745 paInsufficientMemory );
746 }
747
748 PA_ENSURE( PaAlsa_StrDup( alsaApi, &alsaDeviceName, buf ) );
749
750 hwDevInfos[ numDeviceNames - 1 ].alsaName = alsaDeviceName;
751 hwDevInfos[ numDeviceNames - 1 ].name = deviceName;
752 hwDevInfos[ numDeviceNames - 1 ].isPlug = 0;
753 hwDevInfos[ numDeviceNames - 1 ].hasPlayback = hasPlayback;
754 hwDevInfos[ numDeviceNames - 1 ].hasCapture = hasCapture;
755 }
756 snd_ctl_close( ctl );
757 }
758
759 /* Iterate over plugin devices */
760
761 if( NULL == snd_config )
762 {
763 /* snd_config_update is called implicitly by some functions, if this hasn't happened snd_config will be NULL (bleh) */
764 ENSURE_( snd_config_update(), paUnanticipatedHostError );
765 PA_DEBUG(( "Updating snd_config\n" ));
766 }
767 assert( snd_config );
768 if( (res = snd_config_search( snd_config, "pcm", &topNode )) >= 0 )
769 {
770 snd_config_iterator_t i, next;
771
772 snd_config_for_each( i, next, topNode )
773 {
774 const char *tpStr = "unknown", *idStr = NULL;
775 int err = 0;
776
777 char *alsaDeviceName, *deviceName;
778 const HwDevInfo *predefined = NULL;
779 snd_config_t *n = snd_config_iterator_entry( i ), * tp = NULL;;
780
781 if( (err = snd_config_search( n, "type", &tp )) < 0 )
782 {
783 if( -ENOENT != err )
784 {
785 ENSURE_(err, paUnanticipatedHostError);
786 }
787 }
788 else
789 {
790 ENSURE_( snd_config_get_string( tp, &tpStr ), paUnanticipatedHostError );
791 }
792 ENSURE_( snd_config_get_id( n, &idStr ), paUnanticipatedHostError );
793 if( IgnorePlugin( idStr ) )
794 {
795 PA_DEBUG(( "%s: Ignoring ALSA plugin device %s of type %s\n", __FUNCTION__, idStr, tpStr ));
796 continue;
797 }
798 PA_DEBUG(( "%s: Found plugin %s of type %s\n", __FUNCTION__, idStr, tpStr ));
799
800 PA_UNLESS( alsaDeviceName = (char*)PaUtil_GroupAllocateMemory( alsaApi->allocations,
801 strlen(idStr) + 6 ), paInsufficientMemory );
802 strcpy( alsaDeviceName, idStr );
803 PA_UNLESS( deviceName = (char*)PaUtil_GroupAllocateMemory( alsaApi->allocations,
804 strlen(idStr) + 1 ), paInsufficientMemory );
805 strcpy( deviceName, idStr );
806
807 ++numDeviceNames;
808 if( !hwDevInfos || numDeviceNames > maxDeviceNames )
809 {
810 maxDeviceNames *= 2;
811 PA_UNLESS( hwDevInfos = (HwDevInfo *) realloc( hwDevInfos, maxDeviceNames * sizeof (HwDevInfo) ),
812 paInsufficientMemory );
813 }
814
815 predefined = FindDeviceName( alsaDeviceName );
816
817 hwDevInfos[numDeviceNames - 1].alsaName = alsaDeviceName;
818 hwDevInfos[numDeviceNames - 1].name = deviceName;
819 hwDevInfos[numDeviceNames - 1].isPlug = 1;
820
821 if( predefined )
822 {
823 hwDevInfos[numDeviceNames - 1].hasPlayback = predefined->hasPlayback;
824 hwDevInfos[numDeviceNames - 1].hasCapture = predefined->hasCapture;
825 }
826 else
827 {
828 hwDevInfos[numDeviceNames - 1].hasPlayback = 1;
829 hwDevInfos[numDeviceNames - 1].hasCapture = 1;
830 }
831 }
832 }
833 else
834 PA_DEBUG(( "%s: Iterating over ALSA plugins failed: %s\n", __FUNCTION__, snd_strerror( res ) ));
835
836 /* allocate deviceInfo memory based on the number of devices */
837 PA_UNLESS( baseApi->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(
838 alsaApi->allocations, sizeof(PaDeviceInfo*) * (numDeviceNames) ), paInsufficientMemory );
839
840 /* allocate all device info structs in a contiguous block */
841 PA_UNLESS( deviceInfoArray = (PaAlsaDeviceInfo*)PaUtil_GroupAllocateMemory(
842 alsaApi->allocations, sizeof(PaAlsaDeviceInfo) * numDeviceNames ), paInsufficientMemory );
843
844 /* Loop over list of cards, filling in info. If a device is deemed unavailable (can't get name),
845 * it's ignored.
846 *
847 * Note that we do this in two stages. This is a workaround owing to the fact that the 'dmix'
848 * plugin may cause the underlying hardware device to be busy for a short while even after it
849 * (dmix) is closed. The 'default' plugin may also point to the dmix plugin, so the same goes
850 * for this.
851 */
852
853 for( i = 0, devIdx = 0; i < numDeviceNames; ++i )
854 {
855 PaAlsaDeviceInfo* devInfo = &deviceInfoArray[i];
856 HwDevInfo* hwInfo = &hwDevInfos[i];
857 if( !strcmp( hwInfo->name, "dmix" ) || !strcmp( hwInfo->name, "default" ) )
858 {
859 continue;
860 }
861
862 PA_ENSURE( FillInDevInfo( alsaApi, hwInfo, blocking, devInfo, &devIdx ) );
863 }
864 assert( devIdx < numDeviceNames );
865 /* Now inspect 'dmix' and 'default' plugins */
866 for( i = 0; i < numDeviceNames; ++i )
867 {
868 PaAlsaDeviceInfo* devInfo = &deviceInfoArray[i];
869 HwDevInfo* hwInfo = &hwDevInfos[i];
870 if( strcmp( hwInfo->name, "dmix" ) && strcmp( hwInfo->name, "default" ) )
871 {
872 continue;
873 }
874
875 PA_ENSURE( FillInDevInfo( alsaApi, hwInfo, blocking, devInfo,
876 &devIdx ) );
877 }
878 free( hwDevInfos );
879
880 baseApi->info.deviceCount = devIdx; /* Number of successfully queried devices */
881
882 #ifdef PA_ENABLE_DEBUG_OUTPUT
883 PA_DEBUG(( "%s: Building device list took %f seconds\n", __FUNCTION__, PaUtil_GetTime() - startTime ));
884 #endif
885
886 end:
887 return result;
888
889 error:
890 /* No particular action */
891 goto end;
892 }
893
894 /* Check against known device capabilities */
895 static PaError ValidateParameters( const PaStreamParameters *parameters, PaUtilHostApiRepresentation *hostApi, StreamDirection mode )
896 {
897 PaError result = paNoError;
898 int maxChans;
899 const PaAlsaDeviceInfo *deviceInfo = NULL;
900 assert( parameters );
901
902 if( parameters->device != paUseHostApiSpecificDeviceSpecification )
903 {
904 assert( parameters->device < hostApi->info.deviceCount );
905 PA_UNLESS( parameters->hostApiSpecificStreamInfo == NULL, paBadIODeviceCombination );
906 deviceInfo = GetDeviceInfo( hostApi, parameters->device );
907 }
908 else
909 {
910 const PaAlsaStreamInfo *streamInfo = parameters->hostApiSpecificStreamInfo;
911
912 PA_UNLESS( parameters->device == paUseHostApiSpecificDeviceSpecification, paInvalidDevice );
913 PA_UNLESS( streamInfo->size == sizeof (PaAlsaStreamInfo) && streamInfo->version == 1,
914 paIncompatibleHostApiSpecificStreamInfo );
915 PA_UNLESS( streamInfo->deviceString != NULL, paInvalidDevice );
916
917 /* Skip further checking */
918 return paNoError;
919 }
920
921 assert( deviceInfo );
922 assert( parameters->hostApiSpecificStreamInfo == NULL );
923 maxChans = (StreamDirection_In == mode ? deviceInfo->baseDeviceInfo.maxInputChannels :
924 deviceInfo->baseDeviceInfo.maxOutputChannels);
925 PA_UNLESS( parameters->channelCount <= maxChans, paInvalidChannelCount );
926
927 error:
928 return result;
929 }
930
931 /* Given an open stream, what sample formats are available? */
932 static PaSampleFormat GetAvailableFormats( snd_pcm_t *pcm )
933 {
934 PaSampleFormat available = 0;
935 snd_pcm_hw_params_t *hwParams;
936 snd_pcm_hw_params_alloca( &hwParams );
937
938 snd_pcm_hw_params_any( pcm, hwParams );
939
940 if( snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_FLOAT ) >= 0)
941 available |= paFloat32;
942
943 if( snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S32 ) >= 0)
944 available |= paInt32;
945
946 #ifdef PA_LITTLE_ENDIAN
947 if( snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S24_3LE ) >= 0)
948 available |= paInt24;
949 #elif defined PA_BIG_ENDIAN
950 if( snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S24_3BE ) >= 0)
951 available |= paInt24;
952 #endif
953
954 if( snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S16 ) >= 0)
955 available |= paInt16;
956
957 if( snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_U8 ) >= 0)
958 available |= paUInt8;
959
960 if( snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S8 ) >= 0)
961 available |= paInt8;
962
963 return available;
964 }
965
966 static snd_pcm_format_t Pa2AlsaFormat( PaSampleFormat paFormat )
967 {
968 switch( paFormat )
969 {
970 case paFloat32:
971 return SND_PCM_FORMAT_FLOAT;
972
973 case paInt16:
974 return SND_PCM_FORMAT_S16;
975
976 case paInt24:
977 #ifdef PA_LITTLE_ENDIAN
978 return SND_PCM_FORMAT_S24_3LE;
979 #elif defined PA_BIG_ENDIAN
980 return SND_PCM_FORMAT_S24_3BE;
981 #endif
982
983 case paInt32:
984 return SND_PCM_FORMAT_S32;
985
986 case paInt8:
987 return SND_PCM_FORMAT_S8;
988
989 case paUInt8:
990 return SND_PCM_FORMAT_U8;
991
992 default:
993 return SND_PCM_FORMAT_UNKNOWN;
994 }
995 }
996
997 /** Open an ALSA pcm handle.
998 *
999 * The device to be open can be specified in a custom PaAlsaStreamInfo struct, or it will be a device number. In case of a
1000 * device number, it maybe specified through an env variable (PA_ALSA_PLUGHW) that we should open the corresponding plugin
1001 * device.
1002 */
1003 static PaError AlsaOpen( const PaUtilHostApiRepresentation *hostApi, const PaStreamParameters *params, StreamDirection
1004 streamDir, snd_pcm_t **pcm )
1005 {
1006 PaError result = paNoError;
1007 int ret;
1008 char dnameArray[50];
1009 const char* deviceName = dnameArray;
1010 const PaAlsaDeviceInfo *deviceInfo = NULL;
1011 PaAlsaStreamInfo *streamInfo = (PaAlsaStreamInfo *)params->hostApiSpecificStreamInfo;
1012
1013 if( !streamInfo )
1014 {
1015 int usePlug = 0;
1016 deviceInfo = GetDeviceInfo( hostApi, params->device );
1017
1018 /* If device name starts with hw: and PA_ALSA_PLUGHW is 1, we open the plughw device instead */
1019 if( !strncmp( "hw:", deviceInfo->alsaName, 3 ) && getenv( "PA_ALSA_PLUGHW" ) )
1020 usePlug = atoi( getenv( "PA_ALSA_PLUGHW" ) );
1021 if( usePlug )
1022 snprintf( dnameArray, 50, "plug%s", deviceInfo->alsaName );
1023 else
1024 deviceName = deviceInfo->alsaName;
1025 }
1026 else
1027 deviceName = streamInfo->deviceString;
1028
1029 PA_DEBUG(( "%s: Opening device %s\n", __FUNCTION__, deviceName ));
1030 if( (ret = OpenPcm( pcm, deviceName, streamDir == StreamDirection_In ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK,
1031 SND_PCM_NONBLOCK, 1 )) < 0 )
1032 {
1033 /* Not to be closed */
1034 *pcm = NULL;
1035 ENSURE_( ret, -EBUSY == ret ? paDeviceUnavailable : paBadIODeviceCombination );
1036 }
1037 ENSURE_( snd_pcm_nonblock( *pcm, 0 ), paUnanticipatedHostError );
1038
1039 end:
1040 return result;
1041
1042 error:
1043 goto end;
1044 }
1045
1046 static PaError TestParameters( const PaUtilHostApiRepresentation *hostApi, const PaStreamParameters *parameters,
1047 double sampleRate, StreamDirection streamDir )
1048 {
1049 PaError result = paNoError;
1050 snd_pcm_t *pcm = NULL;
1051 PaSampleFormat availableFormats;
1052 /* We are able to adapt to a number of channels less than what the device supports */
1053 unsigned int numHostChannels;
1054 PaSampleFormat hostFormat;
1055 snd_pcm_hw_params_t *hwParams;
1056 snd_pcm_hw_params_alloca( &hwParams );
1057
1058 if( !parameters->hostApiSpecificStreamInfo )
1059 {
1060 const PaAlsaDeviceInfo *devInfo = GetDeviceInfo( hostApi, parameters->device );
1061 numHostChannels = PA_MAX( parameters->channelCount, StreamDirection_In == streamDir ?
1062 devInfo->minInputChannels : devInfo->minOutputChannels );
1063 }
1064 else
1065 numHostChannels = parameters->channelCount;
1066
1067 PA_ENSURE( AlsaOpen( hostApi, parameters, streamDir, &pcm ) );
1068
1069 snd_pcm_hw_params_any( pcm, hwParams );
1070
1071 if( SetApproximateSampleRate( pcm, hwParams, sampleRate ) < 0 )
1072 {
1073 result = paInvalidSampleRate;
1074 goto error;
1075 }
1076
1077 if( snd_pcm_hw_params_set_channels( pcm, hwParams, numHostChannels ) < 0 )
1078 {
1079 result = paInvalidChannelCount;
1080 goto error;
1081 }
1082
1083 /* See if we can find a best possible match */
1084 availableFormats = GetAvailableFormats( pcm );
1085 PA_ENSURE( hostFormat = PaUtil_SelectClosestAvailableFormat( availableFormats, parameters->sampleFormat ) );
1086 ENSURE_( snd_pcm_hw_params_set_format( pcm, hwParams, Pa2AlsaFormat( hostFormat ) ), paUnanticipatedHostError );
1087
1088 {
1089 /* It happens that this call fails because the device is busy */
1090 int ret = 0;
1091 if( (ret = snd_pcm_hw_params( pcm, hwParams )) < 0)
1092 {
1093 if( -EINVAL == ret )
1094 {
1095 /* Don't know what to return here */
1096 result = paBadIODeviceCombination;
1097 goto error;
1098 }
1099 else if( -EBUSY == ret )
1100 {
1101 result = paDeviceUnavailable;
1102 PA_DEBUG(( "%s: Device is busy\n", __FUNCTION__ ));
1103 }
1104 else
1105 {
1106 result = paUnanticipatedHostError;
1107 }
1108
1109 ENSURE_( ret, result );
1110 }
1111 }
1112
1113 end:
1114 if( pcm )
1115 {
1116 snd_pcm_close( pcm );
1117 }
1118 return result;
1119
1120 error:
1121 goto end;
1122 }
1123
1124 static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
1125 const PaStreamParameters *inputParameters,
1126 const PaStreamParameters *outputParameters,
1127 double sampleRate )
1128 {
1129 int inputChannelCount = 0, outputChannelCount = 0;
1130 PaSampleFormat inputSampleFormat, outputSampleFormat;
1131 PaError result = paFormatIsSupported;
1132
1133 if( inputParameters )
1134 {
1135 PA_ENSURE( ValidateParameters( inputParameters, hostApi, StreamDirection_In ) );
1136
1137 inputChannelCount = inputParameters->channelCount;
1138 inputSampleFormat = inputParameters->sampleFormat;
1139 }
1140
1141 if( outputParameters )
1142 {
1143 PA_ENSURE( ValidateParameters( outputParameters, hostApi, StreamDirection_Out ) );
1144
1145 outputChannelCount = outputParameters->channelCount;
1146 outputSampleFormat = outputParameters->sampleFormat;
1147 }
1148
1149 if( inputChannelCount )
1150 {
1151 if( (result = TestParameters( hostApi, inputParameters, sampleRate, StreamDirection_In ))
1152 != paNoError )
1153 goto error;
1154 }
1155 if ( outputChannelCount )
1156 {
1157 if( (result = TestParameters( hostApi, outputParameters, sampleRate, StreamDirection_Out ))
1158 != paNoError )
1159 goto error;
1160 }
1161
1162 return paFormatIsSupported;
1163
1164 error:
1165 return result;
1166 }
1167
1168 static PaError PaAlsaStreamComponent_Initialize( PaAlsaStreamComponent *self, PaAlsaHostApiRepresentation *alsaApi,
1169 const PaStreamParameters *params, StreamDirection streamDir, int callbackMode )
1170 {
1171 PaError result = paNoError;
1172 PaSampleFormat userSampleFormat = params->sampleFormat, hostSampleFormat;
1173 assert( params->channelCount > 0 );
1174
1175 /* Make sure things have an initial value */
1176 memset( self, 0, sizeof (PaAlsaStreamComponent) );
1177
1178 if( NULL == params->hostApiSpecificStreamInfo )
1179 {
1180 const PaAlsaDeviceInfo *devInfo = GetDeviceInfo( &alsaApi->baseHostApiRep, params->device );
1181 self->numHostChannels = PA_MAX( params->channelCount, StreamDirection_In == streamDir ? devInfo->minInputChannels
1182 : devInfo->minOutputChannels );
1183 }
1184 else
1185 {
1186 /* We're blissfully unaware of the minimum channelCount */
1187 self->numHostChannels = params->channelCount;
1188 }
1189
1190 self->device = params->device;
1191
1192 PA_ENSURE( AlsaOpen( &alsaApi->baseHostApiRep, params, streamDir, &self->pcm ) );
1193 self->nfds = snd_pcm_poll_descriptors_count( self->pcm );
1194 hostSampleFormat = PaUtil_SelectClosestAvailableFormat( GetAvailableFormats( self->pcm ), userSampleFormat );
1195
1196 self->hostSampleFormat = hostSampleFormat;
1197 self->nativeFormat = Pa2AlsaFormat( hostSampleFormat );
1198 self->hostInterleaved = self->userInterleaved = !(userSampleFormat & paNonInterleaved);
1199 self->numUserChannels = params->channelCount;
1200 self->streamDir = streamDir;
1201 self->canMmap = 0;
1202 self->nonMmapBuffer = NULL;
1203 self->nonMmapBufferSize = 0;
1204
1205 if( !callbackMode && !self->userInterleaved )
1206 {
1207 /* Pre-allocate non-interleaved user provided buffers */
1208 PA_UNLESS( self->userBuffers = PaUtil_AllocateMemory( sizeof (void *) * self->numUserChannels ),
1209 paInsufficientMemory );
1210 }
1211
1212 error:
1213 return result;
1214 }
1215
1216 static void PaAlsaStreamComponent_Terminate( PaAlsaStreamComponent *self )
1217 {
1218 snd_pcm_close( self->pcm );
1219 if( self->userBuffers )
1220 PaUtil_FreeMemory( self->userBuffers );
1221 }
1222
1223 /*
1224 static int nearbyint_(float value) {
1225 if( value - (int)value > .5 )
1226 return (int)ceil( value );
1227 return (int)floor( value );
1228 }
1229 */
1230
1231 /** Initiate configuration, preparing for determining a period size suitable for both capture and playback components.
1232 *
1233 */
1234 static PaError PaAlsaStreamComponent_InitialConfigure( PaAlsaStreamComponent *self, const PaStreamParameters *params,
1235 int primeBuffers, snd_pcm_hw_params_t *hwParams, double *sampleRate )
1236 {
1237 /* Configuration consists of setting all of ALSA's parameters.
1238 * These parameters come in two flavors: hardware parameters
1239 * and software paramters. Hardware parameters will affect
1240 * the way the device is initialized, software parameters
1241 * affect the way ALSA interacts with me, the user-level client.
1242 */
1243
1244 PaError result = paNoError;
1245 snd_pcm_access_t accessMode, alternateAccessMode;
1246 int dir = 0;
1247 snd_pcm_t *pcm = self->pcm;
1248 double sr = *sampleRate;
1249 unsigned int minPeriods = 2;
1250
1251 /* self->framesPerBuffer = framesPerHostBuffer; */
1252
1253 /* ... fill up the configuration space with all possibile
1254 * combinations of parameters this device will accept */
1255 ENSURE_( snd_pcm_hw_params_any( pcm, hwParams ), paUnanticipatedHostError );
1256
1257 ENSURE_( snd_pcm_hw_params_set_periods_integer( pcm, hwParams ), paUnanticipatedHostError );
1258 /* I think there should be at least 2 periods (even though ALSA doesn't appear to enforce this) */
1259 dir = 0;
1260 ENSURE_( snd_pcm_hw_params_set_periods_min( pcm, hwParams, &minPeriods, &dir ), paUnanticipatedHostError );
1261
1262 if( self->userInterleaved )
1263 {
1264 accessMode = SND_PCM_ACCESS_MMAP_INTERLEAVED;
1265 alternateAccessMode = SND_PCM_ACCESS_MMAP_NONINTERLEAVED;
1266
1267 /* test if MMAP supported */
1268 self->canMmap = snd_pcm_hw_params_test_access( pcm, hwParams, accessMode ) >= 0 ||
1269 snd_pcm_hw_params_test_access( pcm, hwParams, alternateAccessMode ) >= 0;
1270
1271 PA_DEBUG(("%s: device MMAP SND_PCM_ACCESS_MMAP_INTERLEAVED: %s\n", __FUNCTION__, (snd_pcm_hw_params_test_access( pcm, hwParams, accessMode ) >= 0 ? "YES" : "NO")));
1272 PA_DEBUG(("%s: device MMAP SND_PCM_ACCESS_MMAP_NONINTERLEAVED: %s\n", __FUNCTION__, (snd_pcm_hw_params_test_access( pcm, hwParams, alternateAccessMode ) >= 0 ? "YES" : "NO")));
1273
1274 if (!self->canMmap)
1275 {
1276 accessMode = SND_PCM_ACCESS_RW_INTERLEAVED;
1277 alternateAccessMode = SND_PCM_ACCESS_RW_NONINTERLEAVED;
1278 }
1279 }
1280 else
1281 {
1282 accessMode = SND_PCM_ACCESS_MMAP_NONINTERLEAVED;
1283 alternateAccessMode = SND_PCM_ACCESS_MMAP_INTERLEAVED;
1284
1285 /* test if MMAP supported */
1286 self->canMmap = snd_pcm_hw_params_test_access( pcm, hwParams, accessMode ) >= 0 ||
1287 snd_pcm_hw_params_test_access( pcm, hwParams, alternateAccessMode ) >= 0;
1288
1289 PA_DEBUG(("%s: device MMAP SND_PCM_ACCESS_MMAP_NONINTERLEAVED: %s\n", __FUNCTION__, (snd_pcm_hw_params_test_access( pcm, hwParams, accessMode ) >= 0 ? "YES" : "NO")));
1290 PA_DEBUG(("%s: device MMAP SND_PCM_ACCESS_MMAP_INTERLEAVED: %s\n", __FUNCTION__, (snd_pcm_hw_params_test_access( pcm, hwParams, alternateAccessMode ) >= 0 ? "YES" : "NO")));
1291
1292 if (!self->canMmap)
1293 {
1294 accessMode = SND_PCM_ACCESS_RW_NONINTERLEAVED;
1295 alternateAccessMode = SND_PCM_ACCESS_RW_INTERLEAVED;
1296 }
1297 }
1298
1299 PA_DEBUG(("%s: device can MMAP: %s\n", __FUNCTION__, (self->canMmap ? "YES" : "NO")));
1300
1301 /* If requested access mode fails, try alternate mode */
1302 if( snd_pcm_hw_params_set_access( pcm, hwParams, accessMode ) < 0 )
1303 {
1304 int err = 0;
1305 if( (err = snd_pcm_hw_params_set_access( pcm, hwParams, alternateAccessMode )) < 0)
1306 {
1307 result = paUnanticipatedHostError;
1308 PaUtil_SetLastHostErrorInfo( paALSA, err, snd_strerror( err ) );
1309 goto error;
1310 }
1311 /* Flip mode */
1312 self->hostInterleaved = !self->userInterleaved;
1313 }
1314
1315 ENSURE_( snd_pcm_hw_params_set_format( pcm, hwParams, self->nativeFormat ), paUnanticipatedHostError );
1316
1317 ENSURE_( SetApproximateSampleRate( pcm, hwParams, sr ), paInvalidSampleRate );
1318 ENSURE_( GetExactSampleRate( hwParams, &sr ), paUnanticipatedHostError );
1319 /* reject if there's no sample rate within 1% of the one requested */
1320 if( (fabs( *sampleRate - sr ) / *sampleRate) > 0.01 )
1321 {
1322 PA_DEBUG(("%s: Wanted %f, closest sample rate was %d\n", __FUNCTION__, sampleRate, sr ));
1323 PA_ENSURE( paInvalidSampleRate );
1324 }
1325
1326 ENSURE_( snd_pcm_hw_params_set_channels( pcm, hwParams, self->numHostChannels ), paInvalidChannelCount );
1327
1328 *sampleRate = sr;
1329
1330 end:
1331 return result;
1332
1333 error:
1334 /* No particular action */
1335 goto end;
1336 }
1337
1338 /** Finish the configuration of the component's ALSA device.
1339 *
1340 * As part of this method, the component's bufferSize attribute will be set.
1341 * @param latency: The latency for this component.
1342 */
1343 static PaError PaAlsaStreamComponent_FinishConfigure( PaAlsaStreamComponent *self, snd_pcm_hw_params_t* hwParams,
1344 const PaStreamParameters *params, int primeBuffers, double sampleRate, PaTime* latency )
1345 {
1346 PaError result = paNoError;
1347 snd_pcm_sw_params_t* swParams;
1348 snd_pcm_uframes_t bufSz = 0;
1349 *latency = -1.;
1350
1351 snd_pcm_sw_params_alloca( &swParams );
1352
1353 bufSz = params->suggestedLatency * sampleRate;
1354 ENSURE_( snd_pcm_hw_params_set_buffer_size_near( self->pcm, hwParams, &bufSz ), paUnanticipatedHostError );
1355
1356 /* Set the parameters! */
1357 {
1358 int r = snd_pcm_hw_params( self->pcm, hwParams );
1359 #ifdef PA_ENABLE_DEBUG_OUTPUT
1360 if( r < 0 )
1361 {
1362 snd_output_t *output = NULL;
1363 snd_output_stdio_attach( &output, stderr, 0 );
1364 snd_pcm_hw_params_dump( hwParams, output );
1365 }
1366 #endif
1367 ENSURE_(r, paUnanticipatedHostError );
1368 }
1369 ENSURE_( snd_pcm_hw_params_get_buffer_size( hwParams, &self->bufferSize ), paUnanticipatedHostError );
1370 /* Latency in seconds */
1371 *latency = self->bufferSize / sampleRate;
1372
1373 /* Now software parameters... */
1374 ENSURE_( snd_pcm_sw_params_current( self->pcm, swParams ), paUnanticipatedHostError );
1375
1376 ENSURE_( snd_pcm_sw_params_set_start_threshold( self->pcm, swParams, self->framesPerBuffer ), paUnanticipatedHostError );
1377 ENSURE_( snd_pcm_sw_params_set_stop_threshold( self->pcm, swParams, self->bufferSize ), paUnanticipatedHostError );
1378
1379 /* Silence buffer in the case of underrun */
1380 if( !primeBuffers ) /* XXX: Make sense? */
1381 {
1382 snd_pcm_uframes_t boundary;
1383 ENSURE_( snd_pcm_sw_params_get_boundary( swParams, &boundary ), paUnanticipatedHostError );
1384 ENSURE_( snd_pcm_sw_params_set_silence_threshold( self->pcm, swParams, 0 ), paUnanticipatedHostError );
1385 ENSURE_( snd_pcm_sw_params_set_silence_size( self->pcm, swParams, boundary ), paUnanticipatedHostError );
1386 }
1387
1388 ENSURE_( snd_pcm_sw_params_set_avail_min( self->pcm, swParams, self->framesPerBuffer ), paUnanticipatedHostError );
1389 ENSURE_( snd_pcm_sw_params_set_xfer_align( self->pcm, swParams, 1 ), paUnanticipatedHostError );
1390 ENSURE_( snd_pcm_sw_params_set_tstamp_mode( self->pcm, swParams, SND_PCM_TSTAMP_ENABLE ), paUnanticipatedHostError );
1391
1392 /* Set the parameters! */
1393 ENSURE_( snd_pcm_sw_params( self->pcm, swParams ), paUnanticipatedHostError );
1394
1395 error:
1396 return result;
1397 }
1398
1399 static PaError PaAlsaStream_Initialize( PaAlsaStream *self, PaAlsaHostApiRepresentation *alsaApi, const PaStreamParameters *inParams,
1400 const PaStreamParameters *outParams, double sampleRate, unsigned long framesPerUserBuffer, PaStreamCallback callback,
1401 PaStreamFlags streamFlags, void *userData )
1402 {
1403 PaError result = paNoError;
1404 assert( self );
1405
1406 memset( self, 0, sizeof (PaAlsaStream) );
1407
1408 if( NULL != callback )
1409 {
1410 PaUtil_InitializeStreamRepresentation( &self->streamRepresentation,
1411 &alsaApi->callbackStreamInterface,
1412 callback, userData );
1413 self->callbackMode = 1;
1414 }
1415 else
1416 {
1417 PaUtil_InitializeStreamRepresentation( &self->streamRepresentation,
1418 &alsaApi->blockingStreamInterface,
1419 NULL, userData );
1420 }
1421
1422 self->framesPerUserBuffer = framesPerUserBuffer;
1423 self->neverDropInput = streamFlags & paNeverDropInput;
1424 /* XXX: Ignore paPrimeOutputBuffersUsingStreamCallback untill buffer priming is fully supported in pa_process.c */
1425 /*
1426 if( outParams & streamFlags & paPrimeOutputBuffersUsingStreamCallback )
1427 self->primeBuffers = 1;
1428 */
1429 memset( &self->capture, 0, sizeof (PaAlsaStreamComponent) );
1430 memset( &self->playback, 0, sizeof (PaAlsaStreamComponent) );
1431 if( inParams )
1432 {
1433 PA_ENSURE( PaAlsaStreamComponent_Initialize( &self->capture, alsaApi, inParams, StreamDirection_In, NULL != callback ) );
1434 }
1435 if( outParams )
1436 {
1437 PA_ENSURE( PaAlsaStreamComponent_Initialize( &self->playback, alsaApi, outParams, StreamDirection_Out, NULL != callback ) );
1438 }
1439
1440 assert( self->capture.nfds || self->playback.nfds );
1441
1442 PA_UNLESS( self->pfds = (struct pollfd*)PaUtil_AllocateMemory( (self->capture.nfds +
1443 self->playback.nfds) * sizeof (struct pollfd) ), paInsufficientMemory );
1444
1445 PaUtil_InitializeCpuLoadMeasurer( &self->cpuLoadMeasurer, sampleRate );
1446 ASSERT_CALL_( PaUnixMutex_Initialize( &self->stateMtx ), paNoError );
1447
1448 error:
1449 return result;
1450 }
1451
1452 /** Free resources associated with stream, and eventually stream itself.
1453 *
1454 * Frees allocated memory, and terminates individual StreamComponents.
1455 */
1456 static void PaAlsaStream_Terminate( PaAlsaStream *self )
1457 {
1458 assert( self );
1459
1460 if( self->capture.pcm )
1461 {
1462 PaAlsaStreamComponent_Terminate( &self->capture );
1463 }
1464 if( self->playback.pcm )
1465 {
1466 PaAlsaStreamComponent_Terminate( &self->playback );
1467 }
1468
1469 PaUtil_FreeMemory( self->pfds );
1470 ASSERT_CALL_( PaUnixMutex_Terminate( &self->stateMtx ), paNoError );
1471
1472 PaUtil_FreeMemory( self );
1473 }
1474
1475 /** Calculate polling timeout
1476 *
1477 * @param frames Time to wait
1478 * @return Polling timeout in milliseconds
1479 */
1480 static int CalculatePollTimeout( const PaAlsaStream *stream, unsigned long frames )
1481 {
1482 assert( stream->streamRepresentation.streamInfo.sampleRate > 0.0 );
1483 /* Period in msecs, rounded up */
1484 return (int)ceil( 1000 * frames / stream->streamRepresentation.streamInfo.sampleRate );
1485 }
1486
1487 /** Align value in backward direction.
1488 *
1489 * @param v: Value to align.
1490 * @param align: Alignment.
1491 */
1492 static unsigned long PaAlsa_AlignBackward(unsigned long v, unsigned long align)
1493 {
1494 return ((v - (align ? v % align : 0)));
1495 }
1496
1497 /** Align value in forward direction.
1498 *
1499 * @param v: Value to align.
1500 * @param align: Alignment.
1501 */
1502 static unsigned long PaAlsa_AlignForward(unsigned long v, unsigned long align)
1503 {
1504 unsigned long remainder = (align ? (v % align) : 0);
1505 return (remainder != 0 ? v + (align - remainder) : v);
1506 }
1507
1508 /** Get size of host buffer maintained from the number of user frames, sample rate and suggested latency. Minimum double buffering
1509 * is maintained to allow 100% CPU usage inside user callback.
1510 *
1511 * @param userFramesPerBuffer: User buffer size in number of frames.
1512 * @param suggestedLatency: User provided desired latency.
1513 * @param sampleRate: Sample rate.
1514 */
1515 static unsigned long PaAlsa_GetFramesPerHostBuffer(unsigned long userFramesPerBuffer, PaTime suggestedLatency, double sampleRate)
1516 {
1517 unsigned long frames = userFramesPerBuffer + PA_MAX( userFramesPerBuffer, (unsigned long)(suggestedLatency * sampleRate) );
1518 return frames;
1519 }
1520
1521 /** Determine size per host buffer.
1522 *
1523 * During this method call, the component's framesPerBuffer attribute gets computed, and the corresponding period size
1524 * gets configured for the device.
1525 * @param accurate: If the configured period size is non-integer, this will be set to 0.
1526 */
1527 static PaError PaAlsaStreamComponent_DetermineFramesPerBuffer( PaAlsaStreamComponent* self, const PaStreamParameters* params,
1528 unsigned long framesPerUserBuffer, double sampleRate, snd_pcm_hw_params_t* hwParams, int* accurate )
1529 {
1530 PaError result = paNoError;
1531 unsigned long bufferSize, framesPerHostBuffer;
1532 int dir = 0;
1533
1534 /* Calculate host buffer size */
1535 bufferSize = PaAlsa_GetFramesPerHostBuffer(framesPerUserBuffer, params->suggestedLatency, sampleRate);
1536
1537 /* Log */
1538 PA_DEBUG(( "%s: user-buffer (frames) = %lu\n", __FUNCTION__, framesPerUserBuffer ));
1539 PA_DEBUG(( "%s: user-buffer (sec) = %f\n", __FUNCTION__, (double)(framesPerUserBuffer / sampleRate) ));
1540 PA_DEBUG(( "%s: suggested latency (sec) = %f\n", __FUNCTION__, params->suggestedLatency ));
1541 PA_DEBUG(( "%s: suggested host buffer (frames) = %lu\n", __FUNCTION__, bufferSize ));
1542 PA_DEBUG(( "%s: suggested host buffer (sec) = %f\n", __FUNCTION__, (double)(bufferSize / sampleRate) ));
1543
1544 #ifdef PA_ALSA_USE_OBSOLETE_HOST_BUFFER_CALC
1545
1546 if( framesPerUserBuffer != paFramesPerBufferUnspecified )
1547 {
1548 /* Preferably the host buffer size should be a multiple of the user buffer size */
1549
1550 if( bufferSize > framesPerUserBuffer )
1551 {
1552 snd_pcm_uframes_t remainder = bufferSize % framesPerUserBuffer;
1553 if( remainder > framesPerUserBuffer / 2. )
1554 bufferSize += framesPerUserBuffer - remainder;
1555 else
1556 bufferSize -= remainder;
1557
1558 assert( bufferSize % framesPerUserBuffer == 0 );
1559 }
1560 else if( framesPerUserBuffer % bufferSize != 0 )
1561 {
1562 /* Find a good compromise between user specified latency and buffer size */
1563 if( bufferSize > framesPerUserBuffer * .75 )
1564 {
1565 bufferSize = framesPerUserBuffer;
1566 }
1567 else
1568 {
1569 snd_pcm_uframes_t newSz = framesPerUserBuffer;
1570 while( newSz / 2 >= bufferSize )
1571 {
1572 if( framesPerUserBuffer % (newSz / 2) != 0 )
1573 {
1574 /* No use dividing any further */
1575 break;
1576 }
1577 newSz /= 2;
1578 }
1579 bufferSize = newSz;
1580 }
1581
1582 assert( framesPerUserBuffer % bufferSize == 0 );
1583 }
1584 }
1585
1586 #endif
1587
1588 {
1589 unsigned numPeriods = numPeriods_, maxPeriods = 0, minPeriods = numPeriods_;
1590
1591 /* It may be that the device only supports 2 periods for instance */
1592 dir = 0;
1593 ENSURE_( snd_pcm_hw_params_get_periods_min( hwParams, &minPeriods, &dir ), paUnanticipatedHostError )
1594 ENSURE_( snd_pcm_hw_params_get_periods_max( hwParams, &maxPeriods, &dir ), paUnanticipatedHostError );
1595 assert( maxPeriods > 1 );
1596
1597 /* Clamp to min/max */
1598 numPeriods = PA_MIN(maxPeriods, PA_MAX(minPeriods, numPeriods));
1599
1600 PA_DEBUG(( "%s: periods min = %lu, max = %lu, req = %lu \n", __FUNCTION__, minPeriods, maxPeriods, numPeriods ));
1601
1602 #ifndef PA_ALSA_USE_OBSOLETE_HOST_BUFFER_CALC
1603
1604 /* Calculate period size */
1605 framesPerHostBuffer = (bufferSize / numPeriods);
1606
1607 /* Align & test size */
1608 if( framesPerUserBuffer != paFramesPerBufferUnspecified )
1609 {
1610 /* Align to user buffer size */
1611 framesPerHostBuffer = PaAlsa_AlignForward(framesPerHostBuffer, framesPerUserBuffer);
1612
1613 /* Test (borrowed from older implementation) */
1614 if( framesPerHostBuffer < framesPerUserBuffer )
1615 {
1616 assert( framesPerUserBuffer % framesPerHostBuffer == 0 );
1617 if( snd_pcm_hw_params_test_period_size( self->pcm, hwParams, framesPerHostBuffer, 0 ) < 0 )
1618 {
1619 if( snd_pcm_hw_params_test_period_size( self->pcm, hwParams, framesPerHostBuffer * 2, 0 ) == 0 )
1620 framesPerHostBuffer *= 2;
1621 else
1622 if( snd_pcm_hw_params_test_period_size( self->pcm, hwParams, framesPerHostBuffer / 2, 0 ) == 0 )
1623 framesPerHostBuffer /= 2;
1624 }
1625 }
1626 else
1627 {
1628 assert( framesPerHostBuffer % framesPerUserBuffer == 0 );
1629 if( snd_pcm_hw_params_test_period_size( self->pcm, hwParams, framesPerHostBuffer, 0 ) < 0 )
1630 {
1631 if( snd_pcm_hw_params_test_period_size( self->pcm, hwParams, framesPerHostBuffer + framesPerUserBuffer, 0 ) == 0 )
1632 framesPerHostBuffer += framesPerUserBuffer;
1633 else
1634 if( snd_pcm_hw_params_test_period_size( self->pcm, hwParams, framesPerHostBuffer - framesPerUserBuffer, 0 ) == 0 )
1635 framesPerHostBuffer -= framesPerUserBuffer;
1636 }
1637 }
1638 }
1639 #endif
1640
1641 #ifdef PA_ALSA_USE_OBSOLETE_HOST_BUFFER_CALC
1642
1643 if( framesPerUserBuffer != paFramesPerBufferUnspecified )
1644 {
1645 /* Try to get a power-of-two of the user buffer size. */
1646 framesPerHostBuffer = framesPerUserBuffer;
1647 if( framesPerHostBuffer < bufferSize )
1648 {
1649 while( bufferSize / framesPerHostBuffer > numPeriods )
1650 {
1651 framesPerHostBuffer *= 2;
1652 }
1653 /* One extra period is preferrable to one less (should be more robust) */
1654 if( bufferSize / framesPerHostBuffer < numPeriods )
1655 {
1656 framesPerHostBuffer /= 2;
1657 }
1658 }
1659 else
1660 {
1661 while( bufferSize / framesPerHostBuffer < numPeriods )
1662 {
1663 if( framesPerUserBuffer % (framesPerHostBuffer / 2) != 0 )
1664 {
1665 /* Can't be divided any further */
1666 break;
1667 }
1668 framesPerHostBuffer /= 2;
1669 }
1670 }
1671
1672 if( framesPerHostBuffer < framesPerUserBuffer )
1673 {
1674 assert( framesPerUserBuffer % framesPerHostBuffer == 0 );
1675 if( snd_pcm_hw_params_test_period_size( self->pcm, hwParams, framesPerHostBuffer, 0 ) < 0 )
1676 {
1677 if( snd_pcm_hw_params_test_period_size( self->pcm, hwParams, framesPerHostBuffer * 2, 0 ) == 0 )
1678 framesPerHostBuffer *= 2;
1679 else if( snd_pcm_hw_params_test_period_size( self->pcm, hwParams, framesPerHostBuffer / 2, 0 ) == 0 )
1680 framesPerHostBuffer /= 2;
1681 }
1682 }
1683 else
1684 {
1685 assert( framesPerHostBuffer % framesPerUserBuffer == 0 );
1686 if( snd_pcm_hw_params_test_period_size( self->pcm, hwParams, framesPerHostBuffer, 0 ) < 0 )
1687 {
1688 if( snd_pcm_hw_params_test_period_size( self->pcm, hwParams, framesPerHostBuffer + framesPerUserBuffer, 0 ) == 0 )
1689 framesPerHostBuffer += framesPerUserBuffer;
1690 else if( snd_pcm_hw_params_test_period_size( self->pcm, hwParams, framesPerHostBuffer - framesPerUserBuffer, 0 ) == 0 )
1691 framesPerHostBuffer -= framesPerUserBuffer;
1692 }
1693 }
1694 }
1695 else
1696 {
1697 framesPerHostBuffer = bufferSize / numPeriods;
1698 }
1699
1700 /* non-mmap mode needs a reasonably-sized buffer or it'll stutter */
1701 if( !self->canMmap && framesPerHostBuffer < 2048 )
1702 framesPerHostBuffer = 2048;
1703 #endif
1704 PA_DEBUG(( "%s: suggested host buffer period = %lu \n", __FUNCTION__, framesPerHostBuffer ));
1705 }
1706
1707 {
1708 /* Get min/max period sizes and adjust our chosen */
1709 snd_pcm_uframes_t min = 0, max = 0, minmax_diff;
1710 ENSURE_( snd_pcm_hw_params_get_period_size_min( hwParams, &min, NULL ), paUnanticipatedHostError );
1711 ENSURE_( snd_pcm_hw_params_get_period_size_max( hwParams, &max, NULL ), paUnanticipatedHostError );
1712 minmax_diff = max - min;
1713
1714 if( framesPerHostBuffer < min )
1715 {
1716 PA_DEBUG(( "%s: The determined period size (%lu) is less than minimum (%lu)\n", __FUNCTION__, framesPerHostBuffer, min ));
1717 framesPerHostBuffer = ((minmax_diff == 2) ? min + 1 : min);
1718 }
1719 else
1720 if( framesPerHostBuffer > max )
1721 {
1722 PA_DEBUG(( "%s: The determined period size (%lu) is greater than maximum (%lu)\n", __FUNCTION__, framesPerHostBuffer, max ));
1723 framesPerHostBuffer = ((minmax_diff == 2) ? max - 1 : max);
1724 }
1725
1726 PA_DEBUG(( "%s: device period minimum = %lu\n", __FUNCTION__, min ));
1727 PA_DEBUG(( "%s: device period maximum = %lu\n", __FUNCTION__, max ));
1728 PA_DEBUG(( "%s: host buffer period = %lu\n", __FUNCTION__, framesPerHostBuffer ));
1729 PA_DEBUG(( "%s: host buffer period latency = %f\n", __FUNCTION__, (double)(framesPerHostBuffer / sampleRate) ));
1730
1731 /* Try setting period size */
1732 dir = 0;
1733 ENSURE_( snd_pcm_hw_params_set_period_size_near( self->pcm, hwParams, &framesPerHostBuffer, &dir ), paUnanticipatedHostError );
1734 if( dir != 0 )
1735 {
1736 PA_DEBUG(( "%s: The configured period size is non-integer.\n", __FUNCTION__, dir ));
1737 *accurate = 0;
1738 }
1739 }
1740
1741 /* Set result */
1742 self->framesPerBuffer = framesPerHostBuffer;
1743
1744 error:
1745 return result;
1746 }
1747
1748 /* We need to determine how many frames per host buffer (period) to use. Our
1749 * goals are to provide the best possible performance, but also to
1750 * honor the requested latency settings as closely as we can. Therefore this
1751 * decision is based on:
1752 *
1753 * - the period sizes that playback and/or capture support. The
1754 * host buffer size has to be one of these.
1755 * - the number of periods that playback and/or capture support.
1756 *
1757 * We want to make period_size*(num_periods-1) to be as close as possible
1758 * to latency*rate for both playback and capture.
1759 *
1760 * This method will determine suitable period sizes for capture and playback handles, and report the maximum number of
1761 * frames per host buffer. The latter is relevant, in case we should be so unfortunate that the period size differs
1762 * between capture and playback. If this should happen, the stream's hostBufferSizeMode attribute will be set to
1763 * paUtilBoundedHostBufferSize, because the best we can do is limit the size of individual host buffers to the upper
1764 * bound. The size of host buffers scheduled for processing should only matter if the user has specified a buffer size,
1765 * but when he/she does we must strive for an optimal configuration. By default we'll opt for a fixed host buffer size,
1766 * which should be fine if the period size is the same for capture and playback. In general, if there is a specified user
1767 * buffer size, this method tries it best to determine a period size which is a multiple of the user buffer size.
1768 *
1769 * The framesPerBuffer attributes of the individual capture and playback components of the stream are set to corresponding
1770 * values determined here. Since these should be reported as
1771 *
1772 * This is one of those blocks of code that will just take a lot of
1773 * refinement to be any good.
1774 *
1775 * In the full-duplex case it is possible that the routine was unable
1776 * to find a number of frames per buffer acceptable to both devices
1777 * TODO: Implement an algorithm to find the value closest to acceptance
1778 * by both devices, to minimize difference between period sizes?
1779 *
1780 * @param determinedFramesPerHostBuffer: The determined host buffer size.
1781 */
1782 static PaError PaAlsaStream_DetermineFramesPerBuffer( PaAlsaStream* self, double sampleRate, const PaStreamParameters* inputParameters,
1783 const PaStreamParameters* outputParameters, unsigned long framesPerUserBuffer, snd_pcm_hw_params_t* hwParamsCapture,
1784 snd_pcm_hw_params_t* hwParamsPlayback, PaUtilHostBufferSizeMode* hostBufferSizeMode )
1785 {
1786 PaError result = paNoError;
1787 unsigned long framesPerHostBuffer = 0;
1788 int dir = 0;
1789 int accurate = 1;
1790 unsigned numPeriods = numPeriods_;
1791
1792 if( self->capture.pcm && self->playback.pcm )
1793 {
1794 if( framesPerUserBuffer == paFramesPerBufferUnspecified )
1795 {
1796 /* Come up with a common desired latency */
1797 snd_pcm_uframes_t desiredBufSz, e, minPeriodSize, maxPeriodSize, optimalPeriodSize, periodSize,
1798 minCapture, minPlayback, maxCapture, maxPlayback;
1799
1800 dir = 0;
1801 ENSURE_( snd_pcm_hw_params_get_period_size_min( hwParamsCapture, &minCapture, &dir ), paUnanticipatedHostError );
1802 dir = 0;
1803 ENSURE_( snd_pcm_hw_params_get_period_size_min( hwParamsPlayback, &minPlayback, &dir ), paUnanticipatedHostError );
1804 dir = 0;
1805 ENSURE_( snd_pcm_hw_params_get_period_size_max( hwParamsCapture, &maxCapture, &dir ), paUnanticipatedHostError );
1806 dir = 0;
1807 ENSURE_( snd_pcm_hw_params_get_period_size_max( hwParamsPlayback, &maxPlayback, &dir ), paUnanticipatedHostError );
1808 minPeriodSize = PA_MAX( minPlayback, minCapture );
1809 maxPeriodSize = PA_MIN( maxPlayback, maxCapture );
1810 PA_UNLESS( minPeriodSize <= maxPeriodSize, paBadIODeviceCombination );
1811
1812 desiredBufSz = (snd_pcm_uframes_t)(PA_MIN( outputParameters->suggestedLatency, inputParameters->suggestedLatency )
1813 * sampleRate);
1814 /* Clamp desiredBufSz */
1815 {
1816 snd_pcm_uframes_t maxBufferSize;
1817 snd_pcm_uframes_t maxBufferSizeCapture, maxBufferSizePlayback;
1818 ENSURE_( snd_pcm_hw_params_get_buffer_size_max( hwParamsCapture, &maxBufferSizeCapture ), paUnanticipatedHostError );
1819 ENSURE_( snd_pcm_hw_params_get_buffer_size_max( hwParamsPlayback, &maxBufferSizePlayback ), paUnanticipatedHostError );
1820 maxBufferSize = PA_MIN( maxBufferSizeCapture, maxBufferSizePlayback );
1821
1822 desiredBufSz = PA_MIN( desiredBufSz, maxBufferSize );
1823 }
1824
1825 /* Find the closest power of 2 */
1826 e = ilogb( minPeriodSize );
1827 if( minPeriodSize & (minPeriodSize - 1) )
1828 e += 1;
1829 periodSize = (snd_pcm_uframes_t)pow( 2, e );
1830
1831 while( periodSize <= maxPeriodSize )
1832 {
1833 if( snd_pcm_hw_params_test_period_size( self->playback.pcm, hwParamsPlayback, periodSize, 0 ) >= 0 &&
1834 snd_pcm_hw_params_test_period_size( self->capture.pcm, hwParamsCapture, periodSize, 0 ) >= 0 )
1835 {
1836 /* OK! */
1837 break;
1838 }
1839
1840 periodSize *= 2;
1841 }
1842
1843 optimalPeriodSize = PA_MAX( desiredBufSz / numPeriods, minPeriodSize );
1844 optimalPeriodSize = PA_MIN( optimalPeriodSize, maxPeriodSize );
1845
1846 /* Find the closest power of 2 */
1847 e = ilogb( optimalPeriodSize );
1848 if( optimalPeriodSize & (optimalPeriodSize - 1) )
1849 e += 1;
1850 optimalPeriodSize = (snd_pcm_uframes_t)pow( 2, e );
1851
1852 while( optimalPeriodSize >= periodSize )
1853 {
1854 if( snd_pcm_hw_params_test_period_size( self->capture.pcm, hwParamsCapture, optimalPeriodSize, 0 )
1855 >= 0 && snd_pcm_hw_params_test_period_size( self->playback.pcm, hwParamsPlayback,
1856 optimalPeriodSize, 0 ) >= 0 )
1857 {
1858 break;
1859 }
1860 optimalPeriodSize /= 2;
1861 }
1862
1863 if( optimalPeriodSize > periodSize )
1864 periodSize = optimalPeriodSize;
1865
1866 if( periodSize <= maxPeriodSize )
1867 {
1868 /* Looks good, the periodSize _should_ be acceptable by both devices */
1869 ENSURE_( snd_pcm_hw_params_set_period_size( self->capture.pcm, hwParamsCapture, periodSize, 0 ),
1870 paUnanticipatedHostError );
1871 ENSURE_( snd_pcm_hw_params_set_period_size( self->playback.pcm, hwParamsPlayback, periodSize, 0 ),
1872 paUnanticipatedHostError );
1873 self->capture.framesPerBuffer = self->playback.framesPerBuffer = periodSize;
1874 framesPerHostBuffer = periodSize;
1875 }
1876 else
1877 {
1878 /* Unable to find a common period size, oh well */
1879 optimalPeriodSize = PA_MAX( desiredBufSz / numPeriods, minPeriodSize );
1880 optimalPeriodSize = PA_MIN( optimalPeriodSize, maxPeriodSize );
1881
1882 self->capture.framesPerBuffer = optimalPeriodSize;
1883 dir = 0;
1884 ENSURE_( snd_pcm_hw_params_set_period_size_near( self->capture.pcm, hwParamsCapture, &self->capture.framesPerBuffer, &dir ),
1885 paUnanticipatedHostError );
1886 self->playback.framesPerBuffer = optimalPeriodSize;
1887 dir = 0;
1888 ENSURE_( snd_pcm_hw_params_set_period_size_near( self->playback.pcm, hwParamsPlayback, &self->playback.framesPerBuffer, &dir ),
1889 paUnanticipatedHostError );
1890 framesPerHostBuffer = PA_MAX( self->capture.framesPerBuffer, self->playback.framesPerBuffer );
1891 *hostBufferSizeMode = paUtilBoundedHostBufferSize;
1892 }
1893 }
1894 else
1895 {
1896 /* We choose the simple route and determine a suitable number of frames per buffer for one component of
1897 * the stream, then we hope that this will work for the other component too (it should!).
1898 */
1899
1900 unsigned maxPeriods = 0;
1901 PaAlsaStreamComponent* first = &self->capture, * second = &self->playback;
1902 const PaStreamParameters* firstStreamParams = inputParameters;
1903 snd_pcm_hw_params_t* firstHwParams = hwParamsCapture, * secondHwParams = hwParamsPlayback;
1904
1905 dir = 0;
1906 ENSURE_( snd_pcm_hw_params_get_periods_max( hwParamsPlayback, &maxPeriods, &dir ), paUnanticipatedHostError );
1907 if( maxPeriods < numPeriods )
1908 {
1909 /* The playback component is trickier to get right, try that first */
1910 first = &self->playback;
1911 second = &self->capture;
1912 firstStreamParams = outputParameters;
1913 firstHwParams = hwParamsPlayback;
1914 secondHwParams = hwParamsCapture;
1915 }
1916
1917 PA_ENSURE( PaAlsaStreamComponent_DetermineFramesPerBuffer( first, firstStreamParams, framesPerUserBuffer,
1918 sampleRate, firstHwParams, &accurate ) );
1919
1920 second->framesPerBuffer = first->framesPerBuffer;
1921 dir = 0;
1922 ENSURE_( snd_pcm_hw_params_set_period_size_near( second->pcm, secondHwParams, &second->framesPerBuffer, &dir ),
1923 paUnanticipatedHostError );
1924 if( self->capture.framesPerBuffer == self->playback.framesPerBuffer )
1925 {
1926 framesPerHostBuffer = self->capture.framesPerBuffer;
1927 }
1928 else
1929 {
1930 framesPerHostBuffer = PA_MAX( self->capture.framesPerBuffer, self->playback.framesPerBuffer );
1931 *hostBufferSizeMode = paUtilBoundedHostBufferSize;
1932 }
1933 }
1934 }
1935 else /* half-duplex is a slightly simpler case */
1936 {
1937 if( self->capture.pcm )
1938 {
1939 PA_ENSURE( PaAlsaStreamComponent_DetermineFramesPerBuffer( &self->capture, inputParameters, framesPerUserBuffer,
1940 sampleRate, hwParamsCapture, &accurate) );
1941 framesPerHostBuffer = self->capture.framesPerBuffer;
1942 }
1943 else
1944 {
1945 assert( self->playback.pcm );
1946 PA_ENSURE( PaAlsaStreamComponent_DetermineFramesPerBuffer( &self->playback, outputParameters, framesPerUserBuffer,
1947 sampleRate, hwParamsPlayback, &accurate ) );
1948 framesPerHostBuffer = self->playback.framesPerBuffer;
1949 }
1950 }
1951
1952 PA_UNLESS( framesPerHostBuffer != 0, paInternalError );
1953 self->maxFramesPerHostBuffer = framesPerHostBuffer;
1954
1955 if( !self->playback.canMmap || !accurate )
1956 {
1957 /* Don't know the exact size per host buffer */
1958 *hostBufferSizeMode = paUtilBoundedHostBufferSize;
1959 /* Raise upper bound */
1960 if( !accurate )
1961 ++self->maxFramesPerHostBuffer;
1962 }
1963
1964 error:
1965 return result;
1966 }
1967
1968 /** Set up ALSA stream parameters.
1969 *
1970 */
1971 static PaError PaAlsaStream_Configure( PaAlsaStream *self, const PaStreamParameters *inParams, const PaStreamParameters*
1972 outParams, double sampleRate, unsigned long framesPerUserBuffer, double* inputLatency, double* outputLatency,
1973 PaUtilHostBufferSizeMode* hostBufferSizeMode )
1974 {
1975 PaError result = paNoError;
1976 double realSr = sampleRate;
1977 snd_pcm_hw_params_t* hwParamsCapture, * hwParamsPlayback;
1978
1979 snd_pcm_hw_params_alloca( &hwParamsCapture );
1980 snd_pcm_hw_params_alloca( &hwParamsPlayback );
1981
1982 if( self->capture.pcm )
1983 PA_ENSURE( PaAlsaStreamComponent_InitialConfigure( &self->capture, inParams, self->primeBuffers, hwParamsCapture,
1984 &realSr ) );
1985 if( self->playback.pcm )
1986 PA_ENSURE( PaAlsaStreamComponent_InitialConfigure( &self->playback, outParams, self->primeBuffers, hwParamsPlayback,
1987 &realSr ) );
1988
1989 PA_ENSURE( PaAlsaStream_DetermineFramesPerBuffer( self, realSr, inParams, outParams, framesPerUserBuffer,
1990 hwParamsCapture, hwParamsPlayback, hostBufferSizeMode ) );
1991
1992 if( self->capture.pcm )
1993 {
1994 assert( self->capture.framesPerBuffer != 0 );
1995 PA_ENSURE( PaAlsaStreamComponent_FinishConfigure( &self->capture, hwParamsCapture, inParams, self->primeBuffers, realSr,
1996 inputLatency ) );
1997 PA_DEBUG(( "%s: Capture period size: %lu, latency: %f\n", __FUNCTION__, self->capture.framesPerBuffer, *inputLatency ));
1998 }
1999 if( self->playback.pcm )
2000 {
2001 assert( self->playback.framesPerBuffer != 0 );
2002 PA_ENSURE( PaAlsaStreamComponent_FinishConfigure( &self->playback, hwParamsPlayback, outParams, self->primeBuffers, realSr,
2003 outputLatency ) );
2004 PA_DEBUG(( "%s: Playback period size: %lu, latency: %f\n", __FUNCTION__, self->playback.framesPerBuffer, *outputLatency ));
2005 }
2006
2007 /* Should be exact now */
2008 self->streamRepresentation.streamInfo.sampleRate = realSr;
2009
2010 /* this will cause the two streams to automatically start/stop/prepare in sync.
2011 * We only need to execute these operations on one of the pair.
2012 * A: We don't want to do this on a blocking stream.
2013 */
2014 if( self->callbackMode && self->capture.pcm && self->playback.pcm )
2015 {
2016 int err = snd_pcm_link( self->capture.pcm, self->playback.pcm );
2017 if( err == 0 )
2018 self->pcmsSynced = 1;
2019 else
2020 PA_DEBUG(( "%s: Unable to sync pcms: %s\n", __FUNCTION__, snd_strerror( err ) ));
2021 }
2022
2023 {
2024 unsigned long minFramesPerHostBuffer = PA_MIN( self->capture.pcm ? self->capture.framesPerBuffer : ULONG_MAX,
2025 self->playback.pcm ? self->playback.framesPerBuffer : ULONG_MAX );
2026 self->pollTimeout = CalculatePollTimeout( self, minFramesPerHostBuffer ); /* Period in msecs, rounded up */
2027
2028 /* Time before watchdog unthrottles realtime thread == 1/4 of period time in msecs */
2029 /* self->threading.throttledSleepTime = (unsigned long) (minFramesPerHostBuffer / sampleRate / 4 * 1000); */
2030 }
2031
2032 if( self->callbackMode )
2033 {
2034 /* If the user expects a certain number of frames per callback we will either have to rely on block adaption
2035 * (framesPerHostBuffer is not an integer multiple of framesPerBuffer) or we can simply align the number
2036 * of host buffer frames with what the user specified */
2037 if( self->framesPerUserBuffer != paFramesPerBufferUnspecified )
2038 {
2039 /* self->alignFrames = 1; */
2040
2041 /* Unless the ratio between number of host and user buffer frames is an integer we will have to rely
2042 * on block adaption */
2043 /*
2044 if( framesPerHostBuffer % framesPerBuffer != 0 || (self->capture.pcm && self->playback.pcm &&
2045 self->capture.framesPerBuffer != self->playback.framesPerBuffer) )
2046 self->useBlockAdaption = 1;
2047 else
2048 self->alignFrames = 1;
2049 */
2050 }
2051 }
2052
2053 error:
2054 return result;
2055 }
2056
2057 static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
2058 PaStream** s,
2059 const PaStreamParameters *inputParameters,
2060 const PaStreamParameters *outputParameters,
2061 double sampleRate,
2062 unsigned long framesPerBuffer,
2063 PaStreamFlags streamFlags,
2064 PaStreamCallback* callback,
2065 void *userData )
2066 {
2067 PaError result = paNoError;
2068 PaAlsaHostApiRepresentation *alsaHostApi = (PaAlsaHostApiRepresentation*)hostApi;
2069 PaAlsaStream *stream = NULL;
2070 PaSampleFormat hostInputSampleFormat = 0, hostOutputSampleFormat = 0;
2071 PaSampleFormat inputSampleFormat = 0, outputSampleFormat = 0;
2072 int numInputChannels = 0, numOutputChannels = 0;
2073 PaTime inputLatency, outputLatency;
2074 /* Operate with fixed host buffer size by default, since other modes will invariably lead to block adaption */
2075 /* XXX: Use Bounded by default? Output tends to get stuttery with Fixed ... */
2076 PaUtilHostBufferSizeMode hostBufferSizeMode = paUtilFixedHostBufferSize;
2077
2078 if( (streamFlags & paPlatformSpecificFlags) != 0 )
2079 return paInvalidFlag;
2080
2081 if( inputParameters )
2082 {
2083 PA_ENSURE( ValidateParameters( inputParameters, hostApi, StreamDirection_In ) );
2084
2085 numInputChannels = inputParameters->channelCount;
2086 inputSampleFormat = inputParameters->sampleFormat;
2087 }
2088 if( outputParameters )
2089 {
2090 PA_ENSURE( ValidateParameters( outputParameters, hostApi, StreamDirection_Out ) );
2091
2092 numOutputChannels = outputParameters->channelCount;
2093 outputSampleFormat = outputParameters->sampleFormat;
2094 }
2095
2096 /* XXX: Why do we support this anyway? */
2097 if( framesPerBuffer == paFramesPerBufferUnspecified && getenv( "PA_ALSA_PERIODSIZE" ) != NULL )
2098 {
2099 PA_DEBUG(( "%s: Getting framesPerBuffer from environment\n", __FUNCTION__ ));
2100 framesPerBuffer = atoi( getenv("PA_ALSA_PERIODSIZE") );
2101 }
2102
2103 PA_UNLESS( stream = (PaAlsaStream*)PaUtil_AllocateMemory( sizeof(PaAlsaStream) ), paInsufficientMemory );
2104 PA_ENSURE( PaAlsaStream_Initialize( stream, alsaHostApi, inputParameters, outputParameters, sampleRate,
2105 framesPerBuffer, callback, streamFlags, userData ) );
2106
2107 PA_ENSURE( PaAlsaStream_Configure( stream, inputParameters, outputParameters, sampleRate, framesPerBuffer,
2108 &inputLatency, &outputLatency, &hostBufferSizeMode ) );
2109 hostInputSampleFormat = stream->capture.hostSampleFormat | (!stream->capture.hostInterleaved ? paNonInterleaved : 0);
2110 hostOutputSampleFormat = stream->playback.hostSampleFormat | (!stream->playback.hostInterleaved ? paNonInterleaved : 0);
2111
2112 PA_ENSURE( PaUtil_InitializeBufferProcessor( &stream->bufferProcessor,
2113 numInputChannels, inputSampleFormat, hostInputSampleFormat,
2114 numOutputChannels, outputSampleFormat, hostOutputSampleFormat,
2115 sampleRate, streamFlags, framesPerBuffer, stream->maxFramesPerHostBuffer,
2116 hostBufferSizeMode, callback, userData ) );
2117
2118 /* Ok, buffer processor is initialized, now we can deduce it's latency */
2119 if( numInputChannels > 0 )
2120 stream->streamRepresentation.streamInfo.inputLatency = inputLatency + (PaTime)(
2121 PaUtil_GetBufferProcessorInputLatency( &stream->bufferProcessor ) / sampleRate);
2122 if( numOutputChannels > 0 )
2123 stream->streamRepresentation.streamInfo.outputLatency = outputLatency + (PaTime)(
2124 PaUtil_GetBufferProcessorOutputLatency( &stream->bufferProcessor ) / sampleRate);
2125
2126 PA_DEBUG(( "%s: Stream: framesPerBuffer = %lu, maxFramesPerHostBuffer = %lu, latency = i(%f)/o(%f), \n", __FUNCTION__, framesPerBuffer, stream->maxFramesPerHostBuffer, stream->streamRepresentation.streamInfo.inputLatency, stream->streamRepresentation.streamInfo.outputLatency));
2127
2128 *s = (PaStream*)stream;
2129
2130 return result;
2131
2132 error:
2133 if( stream )
2134 {
2135 PA_DEBUG(( "%s: Stream in error, terminating\n", __FUNCTION__ ));
2136 PaAlsaStream_Terminate( stream );
2137 }
2138
2139 return result;
2140 }
2141
2142 static PaError CloseStream( PaStream* s )
2143 {
2144 PaError result = paNoError;
2145 PaAlsaStream *stream = (PaAlsaStream*)s;
2146
2147 PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
2148 PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
2149
2150 PaAlsaStream_Terminate( stream );
2151
2152 return result;
2153 }
2154
2155 static void SilenceBuffer( PaAlsaStream *stream )
2156 {
2157 const snd_pcm_channel_area_t *areas;
2158 snd_pcm_uframes_t frames = (snd_pcm_uframes_t)snd_pcm_avail_update( stream->playback.pcm ), offset;
2159
2160 snd_pcm_mmap_begin( stream->playback.pcm, &areas, &offset, &frames );
2161 snd_pcm_areas_silence( areas, offset, stream->playback.numHostChannels, frames, stream->playback.nativeFormat );
2162 snd_pcm_mmap_commit( stream->playback.pcm, offset, frames );
2163 }
2164
2165 /** Start/prepare pcm(s) for streaming.
2166 *
2167 * Depending on wether the stream is in callback or blocking mode, we will respectively start or simply
2168 * prepare the playback pcm. If the buffer has _not_ been primed, we will in callback mode prepare and
2169 * silence the buffer before starting playback. In blocking mode we simply prepare, as the playback will
2170 * be started automatically as the user writes to output.
2171 *
2172 * The capture pcm, however, will simply be prepared and started.
2173 */
2174 static PaError AlsaStart( PaAlsaStream *stream, int priming )
2175 {
2176 PaError result = paNoError;
2177
2178 if( stream->playback.pcm )
2179 {
2180 if( stream->callbackMode )
2181 {
2182 if( !priming )
2183 {
2184 /* Buffer isn't primed, so prepare and silence */
2185 ENSURE_( snd_pcm_prepare( stream->playback.pcm ), paUnanticipatedHostError );
2186 if( stream->playback.canMmap )
2187 SilenceBuffer( stream );
2188 }
2189 if( stream->playback.canMmap )
2190 ENSURE_( snd_pcm_start( stream->playback.pcm ), paUnanticipatedHostError );
2191 }
2192 else
2193 ENSURE_( snd_pcm_prepare( stream->playback.pcm ), paUnanticipatedHostError );
2194 }
2195 if( stream->capture.pcm && !stream->pcmsSynced )
2196 {
2197 ENSURE_( snd_pcm_prepare( stream->capture.pcm ), paUnanticipatedHostError );
2198 /* For a blocking stream we want to start capture as well, since nothing will happen otherwise */
2199 ENSURE_( snd_pcm_start( stream->capture.pcm ), paUnanticipatedHostError );
2200 }
2201
2202 end:
2203 return result;
2204 error:
2205 goto end;
2206 }
2207
2208 /** Utility function for determining if pcms are in running state.
2209 *
2210 */
2211 #if 0
2212 static int IsRunning( PaAlsaStream *stream )
2213 {
2214 int result = 0;
2215
2216 PA_ENSURE( PaUnixMutex_Lock( &stream->stateMtx ) );
2217 if( stream->capture.pcm )
2218 {
2219 snd_pcm_state_t capture_state = snd_pcm_state( stream->capture.pcm );
2220
2221 if( capture_state == SND_PCM_STATE_RUNNING || capture_state == SND_PCM_STATE_XRUN
2222 || capture_state == SND_PCM_STATE_DRAINING )
2223 {
2224 result = 1;
2225 goto end;
2226 }
2227 }
2228
2229 if( stream->playback.pcm )
2230 {
2231 snd_pcm_state_t playback_state = snd_pcm_state( stream->playback.pcm );
2232
2233 if( playback_state == SND_PCM_STATE_RUNNING || playback_state == SND_PCM_STATE_XRUN
2234 || playback_state == SND_PCM_STATE_DRAINING )
2235 {
2236 result = 1;
2237 goto end;
2238 }
2239 }
2240
2241 end:
2242 ASSERT_CALL_( PaUnixMutex_Unlock( &stream->stateMtx ), paNoError );
2243 return result;
2244 error:
2245 goto error;
2246 }
2247 #endif
2248
2249 static PaError StartStream( PaStream *s )
2250 {
2251 PaError result = paNoError;
2252 PaAlsaStream* stream = (PaAlsaStream*)s;
2253 int streamStarted = 0; /* So we can know wether we need to take the stream down */
2254
2255 /* Ready the processor */
2256 PaUtil_ResetBufferProcessor( &stream->bufferProcessor );
2257
2258 /* Set now, so we can test for activity further down */
2259 stream->isActive = 1;
2260
2261 if( stream->callbackMode )
2262 {
2263 PA_ENSURE( PaUnixThread_New( &stream->thread, &CallbackThreadFunc, stream, 1., stream->rtSched ) );
2264 }
2265 else
2266 {
2267 PA_ENSURE( AlsaStart( stream, 0 ) );
2268 streamStarted = 1;
2269 }
2270
2271 end:
2272 return result;
2273 error:
2274 if( streamStarted )
2275 {
2276 AbortStream( stream );
2277 }
2278 stream->isActive = 0;
2279
2280 goto end;
2281 }
2282
2283 /** Stop PCM handle, either softly or abruptly.
2284 */
2285 static PaError AlsaStop( PaAlsaStream *stream, int abort )
2286 {
2287 PaError result = paNoError;
2288 /* XXX: snd_pcm_drain tends to lock up, avoid it until we find out more */
2289 abort = 1;
2290 /*
2291 if( stream->capture.pcm && !strcmp( Pa_GetDeviceInfo( stream->capture.device )->name,
2292 "dmix" ) )
2293 {
2294 abort = 1;
2295 }
2296 else if( stream->playback.pcm && !strcmp( Pa_GetDeviceInfo( stream->playback.device )->name,
2297 "dmix" ) )
2298 {
2299 abort = 1;
2300 }
2301 */
2302
2303 if( abort )
2304 {
2305 if( stream->playback.pcm )
2306 {
2307 ENSURE_( snd_pcm_drop( stream->playback.pcm ), paUnanticipatedHostError );
2308 }
2309 if( stream->capture.pcm && !stream->pcmsSynced )
2310 {
2311 ENSURE_( snd_pcm_drop( stream->capture.pcm ), paUnanticipatedHostError );
2312 }
2313
2314 PA_DEBUG(( "%s: Dropped frames\n", __FUNCTION__ ));
2315 }
2316 else
2317 {
2318 if( stream->playback.pcm )
2319 {
2320 ENSURE_( snd_pcm_nonblock( stream->playback.pcm, 0 ), paUnanticipatedHostError );
2321 if( snd_pcm_drain( stream->playback.pcm ) < 0 )
2322 {
2323 PA_DEBUG(( "%s: Draining playback handle failed!\n", __FUNCTION__ ));
2324 }
2325 }
2326 if( stream->capture.pcm && !stream->pcmsSynced )
2327 {
2328 /* We don't need to retrieve any remaining frames */
2329 if( snd_pcm_drain( stream->capture.pcm ) < 0 )
2330 {
2331 PA_DEBUG(( "%s: Draining capture handle failed!\n", __FUNCTION__ ));
2332 }
2333 }
2334 }
2335
2336 end:
2337 return result;
2338 error:
2339 goto end;
2340 }
2341
2342 /** Stop or abort stream.
2343 *
2344 * If a stream is in callback mode we will have to inspect wether the background thread has
2345 * finished, or we will have to take it out. In either case we join the thread before
2346 * returning. In blocking mode, we simply tell ALSA to stop abruptly (abort) or finish
2347 * buffers (drain)
2348 *
2349 * Stream will be considered inactive (!PaAlsaStream::isActive) after a call to this function
2350 */
2351 static PaError RealStop( PaAlsaStream *stream, int abort )
2352 {
2353 PaError result = paNoError;
2354
2355 /* First deal with the callback thread, cancelling and/or joining
2356 * it if necessary
2357 */
2358 if( stream->callbackMode )
2359 {
2360 PaError threadRes;
2361 stream->callbackAbort = abort;
2362
2363 if( !abort )
2364 {
2365 PA_DEBUG(( "Stopping callback\n" ));
2366 }
2367 PA_ENSURE( PaUnixThread_Terminate( &stream->thread, !abort, &threadRes ) );
2368 if( threadRes != paNoError )
2369 {
2370 PA_DEBUG(( "Callback thread returned: %d\n", threadRes ));
2371 }
2372 #if 0
2373 if( watchdogRes != paNoError )
2374 PA_DEBUG(( "Watchdog thread returned: %d\n", watchdogRes ));
2375 #endif
2376
2377 stream->callback_finished = 0;
2378 }
2379 else
2380 {
2381 PA_ENSURE( AlsaStop( stream, abort ) );
2382 }
2383
2384 stream->isActive = 0;
2385
2386 end:
2387 return result;
2388
2389 error:
2390 goto end;
2391 }
2392
2393 static PaError StopStream( PaStream *s )
2394 {
2395 return RealStop( (PaAlsaStream *) s, 0 );
2396 }
2397
2398 static PaError AbortStream( PaStream *s )
2399 {
2400 return RealStop( (PaAlsaStream * ) s, 1 );
2401 }
2402
2403 /** The stream is considered stopped before StartStream, or AFTER a call to Abort/StopStream (callback
2404 * returning !paContinue is not considered)
2405 *
2406 */
2407 static PaError IsStreamStopped( PaStream *s )
2408 {
2409 PaAlsaStream *stream = (PaAlsaStream *)s;
2410
2411 /* callback_finished indicates we need to join callback thread (ie. in Abort/StopStream) */
2412 return !IsStreamActive( s ) && !stream->callback_finished;
2413 }
2414
2415 static PaError IsStreamActive( PaStream *s )
2416 {
2417 PaAlsaStream *stream = (PaAlsaStream*)s;
2418 return stream->isActive;
2419 }
2420
2421 static PaTime GetStreamTime( PaStream *s )
2422 {
2423 PaAlsaStream *stream = (PaAlsaStream*)s;
2424
2425 snd_timestamp_t timestamp;
2426 snd_pcm_status_t* status;
2427 snd_pcm_status_alloca( &status );
2428
2429 /* TODO: what if we have both? does it really matter? */
2430
2431 /* TODO: if running in callback mode, this will mean
2432 * libasound routines are being called from multiple threads.
2433 * need to verify that libasound is thread-safe. */
2434
2435 if( stream->capture.pcm )
2436 {
2437 snd_pcm_status( stream->capture.pcm, status );
2438 }
2439 else if( stream->playback.pcm )
2440 {
2441 snd_pcm_status( stream->playback.pcm, status );
2442 }
2443
2444 snd_pcm_status_get_tstamp( status, &timestamp );
2445 return timestamp.tv_sec + (PaTime)timestamp.tv_usec / 1e6;
2446 }
2447
2448 static double GetStreamCpuLoad( PaStream* s )
2449 {
2450 PaAlsaStream *stream = (PaAlsaStream*)s;
2451
2452 return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer );
2453 }
2454
2455 static int SetApproximateSampleRate( snd_pcm_t *pcm, snd_pcm_hw_params_t *hwParams, double sampleRate )
2456 {
2457 PaError result = paNoError;
2458 unsigned long approx = (unsigned long) sampleRate;
2459 int dir = 0;
2460 double fraction = sampleRate - approx;
2461
2462 assert( pcm && hwParams );
2463
2464 if( fraction > 0.0 )
2465 {
2466 if( fraction > 0.5 )
2467 {
2468 ++approx;
2469 dir = -1;
2470 }
2471 else
2472 dir = 1;
2473 }
2474
2475 if( snd_pcm_hw_params_set_rate( pcm, hwParams, approx, dir ) < 0)
2476 result = paInvalidSampleRate;
2477
2478 end:
2479
2480 return result;
2481
2482 error:
2483
2484 /* Log */
2485 {
2486 unsigned int _min = 0, _max = 0; int _dir = 0;
2487 ENSURE_( snd_pcm_hw_params_get_rate_min( hwParams, &_min, &_dir ), paUnanticipatedHostError );
2488 ENSURE_( snd_pcm_hw_params_get_rate_max( hwParams, &_max, &_dir ), paUnanticipatedHostError );
2489 PA_DEBUG(( "%s: SR min = %d, max = %d, req = %lu\n", __FUNCTION__, _min, _max, approx ));
2490 }
2491
2492 goto end;
2493 }
2494
2495 /* Return exact sample rate in param sampleRate */
2496 static int GetExactSampleRate( snd_pcm_hw_params_t *hwParams, double *sampleRate )
2497 {
2498 unsigned int num, den;
2499 int err;
2500
2501 assert( hwParams );
2502
2503 err = snd_pcm_hw_params_get_rate_numden( hwParams, &num, &den );
2504 *sampleRate = (double) num / den;
2505
2506 return err;
2507 }
2508
2509 /* Utility functions for blocking/callback interfaces */
2510
2511 /* Atomic restart of stream (we don't want the intermediate state visible) */
2512 static PaError AlsaRestart( PaAlsaStream *stream )
2513 {
2514 PaError result = paNoError;
2515
2516 PA_ENSURE( PaUnixMutex_Lock( &stream->stateMtx ) );
2517 PA_ENSURE( AlsaStop( stream, 0 ) );
2518 PA_ENSURE( AlsaStart( stream, 0 ) );
2519
2520 PA_DEBUG(( "%s: Restarted audio\n", __FUNCTION__ ));
2521
2522 error:
2523 PA_ENSURE( PaUnixMutex_Unlock( &stream->stateMtx ) );
2524
2525 return result;
2526 }
2527
2528 /** Recover from xrun state.
2529 *
2530 */
2531 static PaError PaAlsaStream_HandleXrun( PaAlsaStream *self )
2532 {
2533 PaError result = paNoError;
2534 snd_pcm_status_t *st;
2535 PaTime now = PaUtil_GetTime();
2536 snd_timestamp_t t;
2537 int restartAlsa = 0; /* do not restart Alsa by default */
2538
2539 snd_pcm_status_alloca( &st );
2540
2541 if( self->playback.pcm )
2542 {
2543 snd_pcm_status( self->playback.pcm, st );
2544 if( snd_pcm_status_get_state( st ) == SND_PCM_STATE_XRUN )
2545 {
2546 snd_pcm_status_get_trigger_tstamp( st, &t );
2547 self->underrun = now * 1000 - ((PaTime) t.tv_sec * 1000 + (PaTime) t.tv_usec / 1000);
2548
2549 if (!self->playback.canMmap)
2550 {
2551 if (snd_pcm_recover( self->playback.pcm, -EPIPE, 0 ) < 0)
2552 {
2553 PA_DEBUG(( "%s: [playback] non-MMAP-PCM failed recovering from XRUN, will restart Alsa\n", __FUNCTION__ ));
2554 ++ restartAlsa; /* did not manage to recover */
2555 }
2556 }
2557 else
2558 ++ restartAlsa; /* always restart MMAPed device */
2559 }
2560 }
2561 if( self->capture.pcm )
2562 {
2563 snd_pcm_status( self->capture.pcm, st );
2564 if( snd_pcm_status_get_state( st ) == SND_PCM_STATE_XRUN )
2565 {
2566 snd_pcm_status_get_trigger_tstamp( st, &t );
2567 self->overrun = now * 1000 - ((PaTime) t.tv_sec * 1000 + (PaTime) t.tv_usec / 1000);
2568
2569 if (!self->capture.canMmap)
2570 {
2571 if (snd_pcm_recover( self->capture.pcm, -EPIPE, 0 ) < 0)
2572 {
2573 PA_DEBUG(( "%s: [capture] non-MMAP-PCM failed recovering from XRUN, will restart Alsa\n", __FUNCTION__ ));
2574 ++ restartAlsa; /* did not manage to recover */
2575 }
2576 }
2577 else
2578 ++ restartAlsa; /* always restart MMAPed device */
2579 }
2580 }
2581
2582 if( restartAlsa )
2583 {
2584 PA_DEBUG(( "%s: restarting Alsa to recover from XRUN\n", __FUNCTION__ ));
2585 PA_ENSURE( AlsaRestart( self ) );
2586 }
2587
2588 end:
2589 return result;
2590 error:
2591 goto end;
2592 }
2593
2594 /** Decide if we should continue polling for specified direction, eventually adjust the poll timeout.
2595 *
2596 */
2597 static PaError ContinuePoll( const PaAlsaStream *stream, StreamDirection streamDir, int *pollTimeout, int *continuePoll )
2598 {
2599 PaError result = paNoError;
2600 snd_pcm_sframes_t delay, margin;
2601 int err;
2602 const PaAlsaStreamComponent *component = NULL, *otherComponent = NULL;
2603
2604 *continuePoll = 1;
2605
2606 if( StreamDirection_In == streamDir )
2607 {
2608 component = &stream->capture;
2609 otherComponent = &stream->playback;
2610 }
2611 else
2612 {
2613 component = &stream->playback;
2614 otherComponent = &stream->capture;
2615 }
2616
2617 /* ALSA docs say that negative delay should indicate xrun, but in my experience snd_pcm_delay returns -EPIPE */
2618 if( (err = snd_pcm_delay( otherComponent->pcm, &delay )) < 0 )
2619 {
2620 if( err == -EPIPE )
2621 {
2622 /* Xrun */
2623 *continuePoll = 0;
2624 goto error;
2625 }
2626
2627 ENSURE_( err, paUnanticipatedHostError );
2628 }
2629
2630 if( StreamDirection_Out == streamDir )
2631 {
2632 /* Number of eligible frames before capture overrun */
2633 delay = otherComponent->bufferSize - delay;
2634 }
2635 margin = delay - otherComponent->framesPerBuffer / 2;
2636
2637 if( margin < 0 )
2638 {
2639 PA_DEBUG(( "%s: Stopping poll for %s\n", __FUNCTION__, StreamDirection_In == streamDir ? "capture" : "playback" ));
2640 *continuePoll = 0;
2641 }
2642 else if( margin < otherComponent->framesPerBuffer )
2643 {
2644 *pollTimeout = CalculatePollTimeout( stream, margin );
2645 PA_DEBUG(( "%s: Trying to poll again for %s frames, pollTimeout: %d\n",
2646 __FUNCTION__, StreamDirection_In == streamDir ? "capture" : "playback", *pollTimeout ));
2647 }
2648
2649 error:
2650 return result;
2651 }
2652
2653 /* Callback interface */
2654
2655 static void OnExit( void *data )
2656 {
2657 PaAlsaStream *stream = (PaAlsaStream *) data;
2658
2659 assert( data );
2660
2661 PaUtil_ResetCpuLoadMeasurer( &stream->cpuLoadMeasurer );
2662
2663 stream->callback_finished = 1; /* Let the outside world know stream was stopped in callback */
2664 PA_DEBUG(( "%s: Stopping ALSA handles\n", __FUNCTION__ ));
2665 AlsaStop( stream, stream->callbackAbort );
2666
2667 PA_DEBUG(( "%s: Stoppage\n", __FUNCTION__ ));
2668
2669 /* Eventually notify user all buffers have played */
2670 if( stream->streamRepresentation.streamFinishedCallback )
2671 {
2672 stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
2673 }
2674 stream->isActive = 0;
2675 }
2676
2677 static void CalculateTimeInfo( PaAlsaStream *stream, PaStreamCallbackTimeInfo *timeInfo )
2678 {
2679 snd_pcm_status_t *capture_status, *playback_status;
2680 snd_timestamp_t capture_timestamp, playback_timestamp;
2681 PaTime capture_time = 0., playback_time = 0.;
2682
2683 snd_pcm_status_alloca( &capture_status );
2684 snd_pcm_status_alloca( &playback_status );
2685
2686 if( stream->capture.pcm )
2687 {
2688 snd_pcm_sframes_t capture_delay;
2689
2690 snd_pcm_status( stream->capture.pcm, capture_status );
2691 snd_pcm_status_get_tstamp( capture_status, &capture_timestamp );
2692
2693 capture_time = capture_timestamp.tv_sec +
2694 ((PaTime)capture_timestamp.tv_usec / 1000000.0);
2695 timeInfo->currentTime = capture_time;
2696
2697 capture_delay = snd_pcm_status_get_delay( capture_status );
2698 timeInfo->inputBufferAdcTime = timeInfo->currentTime -
2699 (PaTime)capture_delay / stream->streamRepresentation.streamInfo.sampleRate;
2700 }
2701 if( stream->playback.pcm )
2702 {
2703 snd_pcm_sframes_t playback_delay;
2704
2705 snd_pcm_status( stream->playback.pcm, playback_status );
2706 snd_pcm_status_get_tstamp( playback_status, &playback_timestamp );
2707
2708 playback_time = playback_timestamp.tv_sec +
2709 ((PaTime)playback_timestamp.tv_usec / 1000000.0);
2710
2711 if( stream->capture.pcm ) /* Full duplex */
2712 {
2713 /* Hmm, we have both a playback and a capture timestamp.
2714 * Hopefully they are the same... */
2715 if( fabs( capture_time - playback_time ) > 0.01 )
2716 PA_DEBUG(("Capture time and playback time differ by %f\n", fabs(capture_time-playback_time)));
2717 }
2718 else
2719 timeInfo->currentTime = playback_time;
2720
2721 playback_delay = snd_pcm_status_get_delay( playback_status );
2722 timeInfo->outputBufferDacTime = timeInfo->currentTime +
2723 (PaTime)playback_delay / stream->streamRepresentation.streamInfo.sampleRate;
2724 }
2725 }
2726
2727 /** Called after buffer processing is finished.
2728 *
2729 * A number of mmapped frames is committed, it is possible that an xrun has occurred in the meantime.
2730 *
2731 * @param numFrames The number of frames that has been processed
2732 * @param xrun Return whether an xrun has occurred
2733 */
2734 static PaError PaAlsaStreamComponent_EndProcessing( PaAlsaStreamComponent *self, unsigned long numFrames, int *xrun )
2735 {
2736 PaError result = paNoError;
2737 int res = 0;
2738
2739 /* @concern FullDuplex It is possible that only one direction is marked ready after polling, and processed
2740 * afterwards
2741 */
2742 if( !self->ready )
2743 goto end;
2744
2745 if( !self->canMmap && StreamDirection_Out == self->streamDir )
2746 {
2747 /* Play sound */
2748 if( self->hostInterleaved )
2749 res = snd_pcm_writei( self->pcm, self->nonMmapBuffer, numFrames );
2750 else
2751 {
2752 void *bufs[self->numHostChannels];
2753 int bufsize = snd_pcm_format_size( self->nativeFormat, self->framesPerBuffer + 1 );
2754 unsigned char *buffer = self->nonMmapBuffer;
2755 int i;
2756 for( i = 0; i < self->numHostChannels; ++i )
2757 {
2758 bufs[i] = buffer;
2759 buffer += bufsize;
2760 }
2761 res = snd_pcm_writen( self->pcm, bufs, numFrames );
2762 }
2763 }
2764
2765 if( self->canMmap )
2766 res = snd_pcm_mmap_commit( self->pcm, self->offset, numFrames );
2767 else
2768 {
2769 /* using realloc for optimisation
2770 free( self->nonMmapBuffer );
2771 self->nonMmapBuffer = NULL;
2772 */
2773 }
2774
2775 if( res == -EPIPE || res == -ESTRPIPE )
2776 {
2777 *xrun = 1;
2778 }
2779 else
2780 {
2781 ENSURE_( res, paUnanticipatedHostError );
2782 }
2783
2784 end:
2785 error:
2786 return result;
2787 }
2788
2789 /* Extract buffer from channel area */
2790 static unsigned char *ExtractAddress( const snd_pcm_channel_area_t *area, snd_pcm_uframes_t offset )
2791 {
2792 return (unsigned char *) area->addr + (area->first + offset * area->step) / 8;
2793 }
2794
2795 /** Do necessary adaption between user and host channels.
2796 *
2797 @concern ChannelAdaption Adapting between user and host channels can involve silencing unused channels and
2798 duplicating mono information if host outputs come in pairs.
2799 */
2800 static PaError PaAlsaStreamComponent_DoChannelAdaption( PaAlsaStreamComponent *self, PaUtilBufferProcessor *bp, int numFrames )
2801 {
2802 PaError result = paNoError;
2803 unsigned char *p;
2804 int i;
2805 int unusedChans = self->numHostChannels - self->numUserChannels;
2806 unsigned char *src, *dst;
2807 int convertMono = (self->numHostChannels % 2) == 0 && (self->numUserChannels % 2) != 0;
2808
2809 assert( StreamDirection_Out == self->streamDir );
2810
2811 if( self->hostInterleaved )
2812 {
2813 int swidth = snd_pcm_format_size( self->nativeFormat, 1 );
2814 unsigned char *buffer = self->canMmap ? ExtractAddress( self->channelAreas, self->offset ) : self->nonMmapBuffer;
2815
2816 /* Start after the last user channel */
2817 p = buffer + self->numUserChannels * swidth;
2818
2819 if( convertMono )
2820 {
2821 /* Convert the last user channel into stereo pair */
2822 src = buffer + (self->numUserChannels - 1) * swidth;
2823 for( i = 0; i < numFrames; ++i )
2824 {
2825 dst = src + swidth;
2826 memcpy( dst, src, swidth );
2827 src += self->numHostChannels * swidth;
2828 }
2829
2830 /* Don't touch the channel we just wrote to */
2831 p += swidth;
2832 --unusedChans;
2833 }
2834
2835 if( unusedChans > 0 )
2836 {
2837 /* Silence unused output channels */
2838 for( i = 0; i < numFrames; ++i )
2839 {
2840 memset( p, 0, swidth * unusedChans );
2841 p += self->numHostChannels * swidth;
2842 }
2843 }
2844 }
2845 else
2846 {
2847 /* We extract the last user channel */
2848 if( convertMono )
2849 {
2850 ENSURE_( snd_pcm_area_copy( self->channelAreas + self->numUserChannels, self->offset, self->channelAreas +
2851 (self->numUserChannels - 1), self->offset, numFrames, self->nativeFormat ), paUnanticipatedHostError );
2852 --unusedChans;
2853 }
2854 if( unusedChans > 0 )
2855 {
2856 snd_pcm_areas_silence( self->channelAreas + (self->numHostChannels - unusedChans), self->offset, unusedChans, numFrames,
2857 self->nativeFormat );
2858 }
2859 }
2860
2861 error:
2862 return result;
2863 }
2864
2865 static PaError PaAlsaStream_EndProcessing( PaAlsaStream *self, unsigned long numFrames, int *xrunOccurred )
2866 {
2867 PaError result = paNoError;
2868 int xrun = 0;
2869
2870 if( self->capture.pcm )
2871 {
2872 PA_ENSURE( PaAlsaStreamComponent_EndProcessing( &self->capture, numFrames, &xrun ) );
2873 }
2874 if( self->playback.pcm )
2875 {
2876 if( self->playback.numHostChannels > self->playback.numUserChannels )
2877 {
2878 PA_ENSURE( PaAlsaStreamComponent_DoChannelAdaption( &self->playback, &self->bufferProcessor, numFrames ) );
2879 }
2880 PA_ENSURE( PaAlsaStreamComponent_EndProcessing( &self->playback, numFrames, &xrun ) );
2881 }
2882
2883 error:
2884 *xrunOccurred = xrun;
2885 return result;
2886 }
2887
2888 /** Update the number of available frames.
2889 *
2890 */
2891 static PaError PaAlsaStreamComponent_GetAvailableFrames( PaAlsaStreamComponent *self, unsigned long *numFrames, int *xrunOccurred )
2892 {
2893 PaError result = paNoError;
2894 snd_pcm_sframes_t framesAvail = snd_pcm_avail_update( self->pcm );
2895 *xrunOccurred = 0;
2896
2897 if( -EPIPE == framesAvail )
2898 {
2899 *xrunOccurred = 1;
2900 framesAvail = 0;
2901 }
2902 else
2903 {
2904 ENSURE_( framesAvail, paUnanticipatedHostError );
2905 }
2906
2907 *numFrames = framesAvail;
2908
2909 error:
2910 return result;
2911 }
2912
2913 /** Fill in pollfd objects.
2914 */
2915 static PaError PaAlsaStreamComponent_BeginPolling( PaAlsaStreamComponent* self, struct pollfd* pfds )
2916 {
2917 PaError result = paNoError;
2918 int ret = snd_pcm_poll_descriptors( self->pcm, pfds, self->nfds );
2919 (void)ret; /* Prevent unused variable warning if asserts are turned off */
2920 assert( ret == self->nfds );
2921
2922 self->ready = 0;
2923
2924 return result;
2925 }
2926
2927 /** Examine results from poll().
2928 *
2929 * @param pfds pollfds to inspect
2930 * @param shouldPoll Should we continue to poll
2931 * @param xrun Has an xrun occurred
2932 */
2933 static PaError PaAlsaStreamComponent_EndPolling( PaAlsaStreamComponent* self, struct pollfd* pfds, int* shouldPoll, int* xrun )
2934 {
2935 PaError result = paNoError;
2936 unsigned short revents;
2937
2938 ENSURE_( snd_pcm_poll_descriptors_revents( self->pcm, pfds, self->nfds, &revents ), paUnanticipatedHostError );
2939 if( revents != 0 )
2940 {
2941 if( revents & POLLERR )
2942 {
2943 *xrun = 1;
2944 }
2945 else
2946 if( revents & POLLHUP )
2947 {
2948 *xrun = 1;
2949 PA_DEBUG(( "%s: revents has POLLHUP, processing as XRUN\n", __FUNCTION__ ));
2950 }
2951 else
2952 self->ready = 1;
2953
2954 *shouldPoll = 0;
2955 }
2956
2957 error:
2958 return result;
2959 }
2960
2961 /** Return the number of available frames for this stream.
2962 *
2963 * @concern FullDuplex The minimum available for the two directions is calculated, it might be desirable to ignore
2964 * one direction however (not marked ready from poll), so this is controlled by queryCapture and queryPlayback.
2965 *
2966 * @param queryCapture Check available for capture
2967 * @param queryPlayback Check available for playback
2968 * @param available The returned number of frames
2969 * @param xrunOccurred Return whether an xrun has occurred
2970 */
2971 static PaError PaAlsaStream_GetAvailableFrames( PaAlsaStream *self, int queryCapture, int queryPlayback, unsigned long
2972 *available, int *xrunOccurred )
2973 {
2974 PaError result = paNoError;
2975 unsigned long captureFrames, playbackFrames;
2976 *xrunOccurred = 0;
2977
2978 assert( queryCapture || queryPlayback );
2979
2980 if( queryCapture )
2981 {
2982 assert( self->capture.pcm );
2983 PA_ENSURE( PaAlsaStreamComponent_GetAvailableFrames( &self->capture, &captureFrames, xrunOccurred ) );
2984 if( *xrunOccurred )
2985 {
2986 goto end;
2987 }
2988 }
2989 if( queryPlayback )
2990 {
2991 assert( self->playback.pcm );
2992 PA_ENSURE( PaAlsaStreamComponent_GetAvailableFrames( &self->playback, &playbackFrames, xrunOccurred ) );
2993 if( *xrunOccurred )
2994 {
2995 goto end;
2996 }
2997 }
2998
2999 if( queryCapture && queryPlayback )
3000 {
3001 *available = PA_MIN( captureFrames, playbackFrames );
3002 /*PA_DEBUG(("capture: %lu, playback: %lu, combined: %lu\n", captureFrames, playbackFrames, *available));*/
3003 }
3004 else if( queryCapture )
3005 {
3006 *available = captureFrames;
3007 }
3008 else
3009 {
3010 *available = playbackFrames;
3011 }
3012
3013 end:
3014 error:
3015 return result;
3016 }
3017
3018 /** Wait for and report available buffer space from ALSA.
3019 *
3020 * Unless ALSA reports a minimum of frames available for I/O, we poll the ALSA filedescriptors for more.
3021 * Both of these operations can uncover xrun conditions.
3022 *
3023 * @concern Xruns Both polling and querying available frames can report an xrun condition.
3024 *
3025 * @param framesAvail Return the number of available frames
3026 * @param xrunOccurred Return whether an xrun has occurred
3027 */
3028 static PaError PaAlsaStream_WaitForFrames( PaAlsaStream *self, unsigned long *framesAvail, int *xrunOccurred )
3029 {
3030 PaError result = paNoError;
3031 int pollPlayback = self->playback.pcm != NULL, pollCapture = self->capture.pcm != NULL;
3032 int pollTimeout = self->pollTimeout;
3033 int xrun = 0, timeouts = 0;
3034 int pollResults;
3035
3036 assert( self );
3037 assert( framesAvail );
3038
3039 if( !self->callbackMode )
3040 {
3041 /* In blocking mode we will only wait if necessary */
3042 PA_ENSURE( PaAlsaStream_GetAvailableFrames( self, self->capture.pcm != NULL, self->playback.pcm != NULL,
3043 framesAvail, &xrun ) );
3044 if( xrun )
3045 {
3046 goto end;
3047 }
3048
3049 if( *framesAvail > 0 )
3050 {
3051 /* Mark pcms ready from poll */
3052 if( self->capture.pcm )
3053 self->capture.ready = 1;
3054 if( self->playback.pcm )
3055 self->playback.ready = 1;
3056
3057 goto end;
3058 }
3059 }
3060
3061 while( pollPlayback || pollCapture )
3062 {
3063 int totalFds = 0;
3064 struct pollfd *capturePfds = NULL, *playbackPfds = NULL;
3065
3066 pthread_testcancel();
3067
3068 if( pollCapture )
3069 {
3070 capturePfds = self->pfds;
3071 PA_ENSURE( PaAlsaStreamComponent_BeginPolling( &self->capture, capturePfds ) );
3072 totalFds += self->capture.nfds;
3073 }
3074 if( pollPlayback )
3075 {
3076 playbackPfds = self->pfds + (self->capture.pcm ? self->capture.nfds : 0);
3077 PA_ENSURE( PaAlsaStreamComponent_BeginPolling( &self->playback, playbackPfds ) );
3078 totalFds += self->playback.nfds;
3079 }
3080
3081 pollResults = poll( self->pfds, totalFds, pollTimeout );
3082
3083 if( pollResults < 0 )
3084 {
3085 /* XXX: Depend on preprocessor condition? */
3086 if( errno == EINTR )
3087 {
3088 /* gdb */
3089 Pa_Sleep( 1 ); /* avoid hot loop */
3090 continue;
3091 }
3092
3093 /* TODO: Add macro for checking system calls */
3094 PA_ENSURE( paInternalError );
3095 }
3096 else
3097 if (pollResults == 0)
3098 {
3099
3100 /* Suspended, paused or failed device can provide 0 poll results. To avoid deadloop in such situation
3101 * we simply run counter 'timeouts' which detects 0 poll result and accumulates. As soon as 64 timouts
3102 * are achieved we simply fail function with paTimedOut to notify waiting methods that device is not capable
3103 * of providing audio data anymore and needs some corresponding recovery action.
3104 * Note that 'timeouts' is reset to 0 if poll() managed to return non 0 results.
3105 */
3106
3107 /*PA_DEBUG(( "%s: poll == 0 results, timed out, %d times left\n", __FUNCTION__, 64 - timeouts ));*/
3108
3109 ++ timeouts;
3110 if (timeouts > 1) /* sometimes device times out, but normally once, so we do not sleep any time */
3111 {
3112 Pa_Sleep( 1 ); /* avoid hot loop */
3113 }
3114 /* not else ! */
3115 if (timeouts >= 64) /* audio device not working, shall return error to notify waiters */
3116 {
3117 *framesAvail = 0; /* no frames available for processing */
3118
3119 PA_DEBUG(( "%s: poll timed out, returning error\n", __FUNCTION__, timeouts ));
3120 PA_ENSURE( paTimedOut );
3121 }
3122 }
3123 else
3124 if (pollResults > 0)
3125 {
3126 /* reset timouts counter */
3127 timeouts = 0;
3128
3129 /* check the return status of our pfds */
3130 if( pollCapture )
3131 {
3132 PA_ENSURE( PaAlsaStreamComponent_EndPolling( &self->capture, capturePfds, &pollCapture, &xrun ) );
3133 }
3134 if( pollPlayback )
3135 {
3136 PA_ENSURE( PaAlsaStreamComponent_EndPolling( &self->playback, playbackPfds, &pollPlayback, &xrun ) );
3137 }
3138 if( xrun )
3139 {
3140 break;
3141 }
3142 }
3143
3144 /* @concern FullDuplex If only one of two pcms is ready we may want to compromise between the two.
3145 * If there is less than half a period's worth of samples left of frames in the other pcm's buffer we will
3146 * stop polling.
3147 */
3148 if( self->capture.pcm && self->playback.pcm )
3149 {
3150 if( pollCapture && !pollPlayback )
3151 {
3152 PA_ENSURE( ContinuePoll( self, StreamDirection_In, &pollTimeout, &pollCapture ) );
3153 }
3154 else if( pollPlayback && !pollCapture )
3155 {
3156 PA_ENSURE( ContinuePoll( self, StreamDirection_Out, &pollTimeout, &pollPlayback ) );
3157 }
3158 }
3159 }
3160
3161 if( !xrun )
3162 {
3163 /* Get the number of available frames for the pcms that are marked ready.
3164 * @concern FullDuplex If only one direction is marked ready (from poll), the number of frames available for
3165 * the other direction is returned. Output is normally preferred over capture however, so capture frames may be
3166 * discarded to avoid overrun unless paNeverDropInput is specified.
3167 */
3168 int captureReady = self->capture.pcm ? self->capture.ready : 0,
3169 playbackReady = self->playback.pcm ? self->playback.ready : 0;
3170 PA_ENSURE( PaAlsaStream_GetAvailableFrames( self, captureReady, playbackReady, framesAvail, &xrun ) );
3171
3172 if( self->capture.pcm && self->playback.pcm )
3173 {
3174 if( !self->playback.ready && !self->neverDropInput )
3175 {
3176 /* Drop input, a period's worth */
3177 assert( self->capture.ready );
3178 PaAlsaStreamComponent_EndProcessing( &self->capture, PA_MIN( self->capture.framesPerBuffer,
3179 *framesAvail ), &xrun );
3180 *framesAvail = 0;
3181 self->capture.ready = 0;
3182 }
3183 }
3184 else if( self->capture.pcm )
3185 assert( self->capture.ready );
3186 else
3187 assert( self->playback.ready );
3188 }
3189
3190 end:
3191 error:
3192 if( xrun )
3193 {
3194 /* Recover from the xrun state */
3195 PA_ENSURE( PaAlsaStream_HandleXrun( self ) );
3196 *framesAvail = 0;
3197 }
3198 else
3199 {
3200 if( 0 != *framesAvail )
3201 {
3202 /* If we're reporting frames eligible for processing, one of the handles better be ready */
3203 PA_UNLESS( self->capture.ready || self->playback.ready, paInternalError );
3204 }
3205 }
3206 *xrunOccurred = xrun;
3207
3208 return result;
3209 }
3210
3211 /** Register per-channel ALSA buffer information with buffer processor.
3212 *
3213 * Mmapped buffer space is acquired from ALSA, and registered with the buffer processor. Differences between the
3214 * number of host and user channels is taken into account.
3215 *
3216 * @param numFrames On entrance the number of requested frames, on exit the number of contiguously accessible frames.
3217 */
3218 static PaError PaAlsaStreamComponent_RegisterChannels( PaAlsaStreamComponent* self, PaUtilBufferProcessor* bp,
3219 unsigned long* numFrames, int* xrun )
3220 {
3221 PaError result = paNoError;
3222 const snd_pcm_channel_area_t *areas, *area;
3223 void (*setChannel)(PaUtilBufferProcessor *, unsigned int, void *, unsigned int) =
3224 StreamDirection_In == self->streamDir ? PaUtil_SetInputChannel : PaUtil_SetOutputChannel;
3225 unsigned char *buffer, *p;
3226 int i;
3227 unsigned long framesAvail;
3228
3229 /* This _must_ be called before mmap_begin */
3230 PA_ENSURE( PaAlsaStreamComponent_GetAvailableFrames( self, &framesAvail, xrun ) );
3231 if( *xrun )
3232 {
3233 *numFrames = 0;
3234 goto end;
3235 }
3236
3237 if( self->canMmap )
3238 {
3239 ENSURE_( snd_pcm_mmap_begin( self->pcm, &areas, &self->offset, numFrames ), paUnanticipatedHostError );
3240 /* @concern ChannelAdaption Buffer address is recorded so we can do some channel adaption later */
3241 self->channelAreas = (snd_pcm_channel_area_t *)areas;
3242 }
3243 else
3244 {
3245 unsigned int bufferSize = self->numHostChannels * snd_pcm_format_size( self->nativeFormat, *numFrames );
3246 if (bufferSize > self->nonMmapBufferSize)
3247 {
3248 self->nonMmapBuffer = realloc(self->nonMmapBuffer, (self->nonMmapBufferSize = bufferSize));
3249 if (!self->nonMmapBuffer)
3250 {
3251 result = paInsufficientMemory;
3252 goto error;
3253 }
3254 }
3255 }
3256
3257 if( self->hostInterleaved )
3258 {
3259 int swidth = snd_pcm_format_size( self->nativeFormat, 1 );
3260
3261 p = buffer = self->canMmap ? ExtractAddress( areas, self->offset ) : self->nonMmapBuffer;
3262 for( i = 0; i < self->numUserChannels; ++i )
3263 {
3264 /* We're setting the channels up to userChannels, but the stride will be hostChannels samples */
3265 setChannel( bp, i, p, self->numHostChannels );
3266 p += swidth;
3267 }
3268 }
3269 else
3270 {
3271 if( self->canMmap )
3272 {
3273 for( i = 0; i < self->numUserChannels; ++i )
3274 {
3275 area = areas + i;
3276 buffer = ExtractAddress( area, self->offset );
3277 setChannel( bp, i, buffer, 1 );
3278 }
3279 }
3280 else
3281 {
3282 unsigned int buf_per_ch_size = self->nonMmapBufferSize / self->numHostChannels;
3283 buffer = self->nonMmapBuffer;
3284 for( i = 0; i < self->numUserChannels; ++i )
3285 {
3286 setChannel( bp, i, buffer, 1 );
3287 buffer += buf_per_ch_size;
3288 }
3289 }
3290 }
3291
3292 if( !self->canMmap && StreamDirection_In == self->streamDir )
3293 {
3294 /* Read sound */
3295 int res;
3296 if( self->hostInterleaved )
3297 res = snd_pcm_readi( self->pcm, self->nonMmapBuffer, *numFrames );
3298 else
3299 {
3300 void *bufs[self->numHostChannels];
3301 unsigned int buf_per_ch_size = self->nonMmapBufferSize / self->numHostChannels;
3302 unsigned char *buffer = self->nonMmapBuffer;
3303 int i;
3304 for( i = 0; i < self->numHostChannels; ++i )
3305 {
3306 bufs[i] = buffer;
3307 buffer += buf_per_ch_size;
3308 }
3309 res = snd_pcm_readn( self->pcm, bufs, *numFrames );
3310 }
3311 if( res == -EPIPE || res == -ESTRPIPE )
3312 {
3313 *xrun = 1;
3314 *numFrames = 0;
3315 }
3316 }
3317
3318 end:
3319 error:
3320 return result;
3321 }
3322
3323 /** Initiate buffer processing.
3324 *
3325 * ALSA buffers are registered with the PA buffer processor and the buffer size (in frames) set.
3326 *
3327 * @concern FullDuplex If both directions are being processed, the minimum amount of frames for the two directions is
3328 * calculated.
3329 *
3330 * @param numFrames On entrance the number of available frames, on exit the number of received frames
3331 * @param xrunOccurred Return whether an xrun has occurred
3332 */
3333 static PaError PaAlsaStream_SetUpBuffers( PaAlsaStream* self, unsigned long* numFrames, int* xrunOccurred )
3334 {
3335 PaError result = paNoError;
3336 unsigned long captureFrames = ULONG_MAX, playbackFrames = ULONG_MAX, commonFrames = 0;
3337 int xrun = 0;
3338
3339 if( *xrunOccurred )
3340 {
3341 *numFrames = 0;
3342 return result;
3343 }
3344 /* If we got here at least one of the pcm's should be marked ready */
3345 PA_UNLESS( self->capture.ready || self->playback.ready, paInternalError );
3346
3347 /* Extract per-channel ALSA buffer pointers and register them with the buffer processor.
3348 * It is possible that a direction is not marked ready however, because it is out of sync with the other.
3349 */
3350 if( self->capture.pcm && self->capture.ready )
3351 {
3352 captureFrames = *numFrames;
3353 PA_ENSURE( PaAlsaStreamComponent_RegisterChannels( &self->capture, &self->bufferProcessor, &captureFrames,
3354 &xrun ) );
3355 }
3356 if( self->playback.pcm && self->playback.ready )
3357 {
3358 playbackFrames = *numFrames;
3359 PA_ENSURE( PaAlsaStreamComponent_RegisterChannels( &self->playback, &self->bufferProcessor, &playbackFrames,
3360 &xrun ) );
3361 }
3362 if( xrun )
3363 {
3364 /* Nothing more to do */
3365 assert( 0 == commonFrames );
3366 goto end;
3367 }
3368
3369 commonFrames = PA_MIN( captureFrames, playbackFrames );
3370 /* assert( commonFrames <= *numFrames ); */
3371 if( commonFrames > *numFrames )
3372 {
3373 /* Hmmm ... how come there are more frames available than we requested!? Blah. */
3374 PA_DEBUG(( "%s: Common available frames are reported to be more than number requested: %lu, %lu, callbackMode: %d\n", __FUNCTION__,
3375 commonFrames, *numFrames, self->callbackMode ));
3376 if( self->capture.pcm )
3377 {
3378 PA_DEBUG(( "%s: captureFrames: %lu, capture.ready: %d\n", __FUNCTION__, captureFrames, self->capture.ready ));
3379 }
3380 if( self->playback.pcm )
3381 {
3382 PA_DEBUG(( "%s: playbackFrames: %lu, playback.ready: %d\n", __FUNCTION__, playbackFrames, self->playback.ready ));
3383 }
3384
3385 commonFrames = 0;
3386 goto end;
3387 }
3388
3389 /* Inform PortAudio of the number of frames we got.
3390 * @concern FullDuplex We might be experiencing underflow in either end; if its an input underflow, we go on
3391 * with output. If its output underflow however, depending on the paNeverDropInput flag, we may want to simply
3392 * discard the excess input or call the callback with paOutputOverflow flagged.
3393 */
3394 if( self->capture.pcm )
3395 {
3396 if( self->capture.ready )
3397 {
3398 PaUtil_SetInputFrameCount( &self->bufferProcessor, commonFrames );
3399 }
3400 else
3401 {
3402 /* We have input underflow */
3403 PaUtil_SetNoInput( &self->bufferProcessor );
3404 }
3405 }
3406 if( self->playback.pcm )
3407 {
3408 if( self->playback.ready )
3409 {
3410 PaUtil_SetOutputFrameCount( &self->bufferProcessor, commonFrames );
3411 }
3412 else
3413 {
3414 /* We have output underflow, but keeping input data (paNeverDropInput) */
3415 assert( self->neverDropInput );
3416 assert( self->capture.pcm != NULL );
3417 PA_DEBUG(( "%s: Setting output buffers to NULL\n", __FUNCTION__ ));
3418 PaUtil_SetNoOutput( &self->bufferProcessor );
3419 }
3420 }
3421
3422 end:
3423 *numFrames = commonFrames;
3424 error:
3425 if( xrun )
3426 {
3427 PA_ENSURE( PaAlsaStream_HandleXrun( self ) );
3428 *numFrames = 0;
3429 }
3430 *xrunOccurred = xrun;
3431
3432 return result;
3433 }
3434
3435 /** Callback thread's function.
3436 *
3437 * Roughly, the workflow can be described in the following way: The number of available frames that can be processed
3438 * directly is obtained from ALSA, we then request as much directly accessible memory as possible within this amount
3439 * from ALSA. The buffer memory is registered with the PA buffer processor and processing is carried out with
3440 * PaUtil_EndBufferProcessing. Finally, the number of processed frames is reported to ALSA. The processing can
3441 * happen in several iterations untill we have consumed the known number of available frames (or an xrun is detected).
3442 */
3443 static void *CallbackThreadFunc( void *userData )
3444 {
3445 PaError result = paNoError;
3446 PaAlsaStream *stream = (PaAlsaStream*) userData;
3447 PaStreamCallbackTimeInfo timeInfo = {0, 0, 0};
3448 snd_pcm_sframes_t startThreshold = 0;
3449 int callbackResult = paContinue;
3450 PaStreamCallbackFlags cbFlags = 0; /* We might want to keep state across iterations */
3451 int streamStarted = 0;
3452
3453 assert( stream );
3454
3455 /* Execute OnExit when exiting */
3456 pthread_cleanup_push( &OnExit, stream );
3457
3458 /* Not implemented */
3459 assert( !stream->primeBuffers );
3460
3461 /* @concern StreamStart If the output is being primed the output pcm needs to be prepared, otherwise the
3462 * stream is started immediately. The latter involves signaling the waiting main thread.
3463 */
3464 if( stream->primeBuffers )
3465 {
3466 snd_pcm_sframes_t avail;
3467
3468 if( stream->playback.pcm )
3469 ENSURE_( snd_pcm_prepare( stream->playback.pcm ), paUnanticipatedHostError );
3470 if( stream->capture.pcm && !stream->pcmsSynced )
3471 ENSURE_( snd_pcm_prepare( stream->capture.pcm ), paUnanticipatedHostError );
3472
3473 /* We can't be certain that the whole ring buffer is available for priming, but there should be
3474 * at least one period */
3475 avail = snd_pcm_avail_update( stream->playback.pcm );
3476 startThreshold = avail - (avail % stream->playback.framesPerBuffer);
3477 assert( startThreshold >= stream->playback.framesPerBuffer );
3478 }
3479 else
3480 {
3481 PA_ENSURE( PaUnixThread_PrepareNotify( &stream->thread ) );
3482 /* Buffer will be zeroed */
3483 PA_ENSURE( AlsaStart( stream, 0 ) );
3484 PA_ENSURE( PaUnixThread_NotifyParent( &stream->thread ) );
3485
3486 streamStarted = 1;
3487 }
3488
3489 while( 1 )
3490 {
3491 unsigned long framesAvail, framesGot;
3492 int xrun = 0;
3493
3494 pthread_testcancel();
3495
3496 /* @concern StreamStop if the main thread has requested a stop and the stream has not been effectively
3497 * stopped we signal this condition by modifying callbackResult (we'll want to flush buffered output).
3498 */
3499 if( PaUnixThread_StopRequested( &stream->thread ) && paContinue == callbackResult )
3500 {
3501 PA_DEBUG(( "Setting callbackResult to paComplete\n" ));
3502 callbackResult = paComplete;
3503 }
3504
3505 if( paContinue != callbackResult )
3506 {
3507 stream->callbackAbort = (paAbort == callbackResult);
3508 if( stream->callbackAbort ||
3509 /** @concern BlockAdaption: Go on if adaption buffers are empty */
3510 PaUtil_IsBufferProcessorOutputEmpty( &stream->bufferProcessor ) )
3511 {
3512 goto end;
3513 }
3514
3515 PA_DEBUG(( "%s: Flushing buffer processor\n", __FUNCTION__ ));
3516 /* There is still buffered output that needs to be processed */
3517 }
3518
3519 /* Wait for data to become available, this comes down to polling the ALSA file descriptors untill we have
3520 * a number of available frames.
3521 */
3522 PA_ENSURE( PaAlsaStream_WaitForFrames( stream, &framesAvail, &xrun ) );
3523 if( xrun )
3524 {
3525 assert( 0 == framesAvail );
3526 continue;
3527
3528 /* XXX: Report xruns to the user? A situation is conceivable where the callback is never invoked due
3529 * to constant xruns, it might be desirable to notify the user of this.
3530 */
3531 }
3532
3533 /* Consume buffer space. Once we have a number of frames available for consumption we must retrieve the
3534 * mmapped buffers from ALSA, this is contiguously accessible memory however, so we may receive smaller
3535 * portions at a time than is available as a whole. Therefore we should be prepared to process several
3536 * chunks successively. The buffers are passed to the PA buffer processor.
3537 */
3538 while( framesAvail > 0 )
3539 {
3540 xrun = 0;
3541
3542 pthread_testcancel();
3543
3544 /** @concern Xruns Under/overflows are to be reported to the callback */
3545 if( stream->underrun > 0.0 )
3546 {
3547 cbFlags |= paOutputUnderflow;
3548 stream->underrun = 0.0;
3549 }
3550 if( stream->overrun > 0.0 )
3551 {
3552 cbFlags |= paInputOverflow;
3553 stream->overrun = 0.0;
3554 }
3555 if( stream->capture.pcm && stream->playback.pcm )
3556 {
3557 /** @concern FullDuplex It's possible that only one direction is being processed to avoid an
3558 * under- or overflow, this should be reported correspondingly */
3559 if( !stream->capture.ready )
3560 {
3561 cbFlags |= paInputUnderflow;
3562 PA_DEBUG(( "%s: Input underflow\n", __FUNCTION__ ));
3563 }
3564 else if( !stream->playback.ready )
3565 {
3566 cbFlags |= paOutputOverflow;
3567 PA_DEBUG(( "%s: Output overflow\n", __FUNCTION__ ));
3568 }
3569 }
3570
3571 #if 0
3572 CallbackUpdate( &stream->threading );
3573 #endif
3574 CalculateTimeInfo( stream, &timeInfo );
3575 PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo, cbFlags );
3576 cbFlags = 0;
3577
3578 /* CPU load measurement should include processing activivity external to the stream callback */
3579 PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer );
3580
3581 framesGot = framesAvail;
3582 if( paUtilFixedHostBufferSize == stream->bufferProcessor.hostBufferSizeMode )
3583 {
3584 /* We've committed to a fixed host buffer size, stick to that */
3585 framesGot = framesGot >= stream->maxFramesPerHostBuffer ? stream->maxFramesPerHostBuffer : 0;
3586 }
3587 else
3588 {
3589 /* We've committed to an upper bound on the size of host buffers */
3590 assert( paUtilBoundedHostBufferSize == stream->bufferProcessor.hostBufferSizeMode );
3591 framesGot = PA_MIN( framesGot, stream->maxFramesPerHostBuffer );
3592 }
3593 PA_ENSURE( PaAlsaStream_SetUpBuffers( stream, &framesGot, &xrun ) );
3594 /* Check the host buffer size against the buffer processor configuration */
3595 framesAvail -= framesGot;
3596
3597 if( framesGot > 0 )
3598 {
3599 assert( !xrun );
3600 PaUtil_EndBufferProcessing( &stream->bufferProcessor, &callbackResult );
3601 PA_ENSURE( PaAlsaStream_EndProcessing( stream, framesGot, &xrun ) );
3602 }
3603 PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesGot );
3604
3605 if( 0 == framesGot )
3606 {
3607 /* Go back to polling for more frames */
3608 break;
3609
3610 }
3611
3612 if( paContinue != callbackResult )
3613 break;
3614 }
3615 }
3616
3617 /* Match pthread_cleanup_push */
3618 pthread_cleanup_pop( 1 );
3619
3620 end:
3621 PA_DEBUG(( "%s: Thread %d exiting\n ", __FUNCTION__, pthread_self() ));
3622 PaUnixThreading_EXIT( result );
3623 error:
3624 goto end;
3625 }
3626
3627 /* Blocking interface */
3628
3629 static PaError ReadStream( PaStream* s, void *buffer, unsigned long frames )
3630 {
3631 PaError result = paNoError;
3632 PaAlsaStream *stream = (PaAlsaStream*)s;
3633 unsigned long framesGot, framesAvail;
3634 void *userBuffer;
3635 snd_pcm_t *save = stream->playback.pcm;
3636
3637 assert( stream );
3638
3639 PA_UNLESS( stream->capture.pcm, paCanNotReadFromAnOutputOnlyStream );
3640
3641 /* Disregard playback */
3642 stream->playback.pcm = NULL;
3643
3644 if( stream->overrun > 0. )
3645 {
3646 result = paInputOverflowed;
3647 stream->overrun = 0.0;
3648 }
3649
3650 if( stream->capture.userInterleaved )
3651 {
3652 userBuffer = buffer;
3653 }
3654 else
3655 {
3656 /* Copy channels into local array */
3657 userBuffer = stream->capture.userBuffers;
3658 memcpy( userBuffer, buffer, sizeof (void *) * stream->capture.numUserChannels );
3659 }
3660
3661 /* Start stream if in prepared state */
3662 if( snd_pcm_state( stream->capture.pcm ) == SND_PCM_STATE_PREPARED )
3663 {
3664 ENSURE_( snd_pcm_start( stream->capture.pcm ), paUnanticipatedHostError );
3665 }
3666
3667 while( frames > 0 )
3668 {
3669 int xrun = 0;
3670 PA_ENSURE( PaAlsaStream_WaitForFrames( stream, &framesAvail, &xrun ) );
3671 framesGot = PA_MIN( framesAvail, frames );
3672
3673 PA_ENSURE( PaAlsaStream_SetUpBuffers( stream, &framesGot, &xrun ) );
3674 if( framesGot > 0 )
3675 {
3676 framesGot = PaUtil_CopyInput( &stream->bufferProcessor, &userBuffer, framesGot );
3677 PA_ENSURE( PaAlsaStream_EndProcessing( stream, framesGot, &xrun ) );
3678 frames -= framesGot;
3679 }
3680 }
3681
3682 end:
3683 stream->playback.pcm = save;
3684 return result;
3685 error:
3686 goto end;
3687 }
3688
3689 static PaError WriteStream( PaStream* s, const void *buffer, unsigned long frames )
3690 {
3691 PaError result = paNoError;
3692 signed long err;
3693 PaAlsaStream *stream = (PaAlsaStream*)s;
3694 snd_pcm_uframes_t framesGot, framesAvail;
3695 const void *userBuffer;
3696 snd_pcm_t *save = stream->capture.pcm;
3697
3698 assert( stream );
3699
3700 PA_UNLESS( stream->playback.pcm, paCanNotWriteToAnInputOnlyStream );
3701
3702 /* Disregard capture */
3703 stream->capture.pcm = NULL;
3704
3705 if( stream->underrun > 0. )
3706 {
3707 result = paOutputUnderflowed;
3708 stream->underrun = 0.0;
3709 }
3710
3711 if( stream->playback.userInterleaved )
3712 userBuffer = buffer;
3713 else /* Copy channels into local array */
3714 {
3715 userBuffer = stream->playback.userBuffers;
3716 memcpy( (void *)userBuffer, buffer, sizeof (void *) * stream->playback.numUserChannels );
3717 }
3718
3719 while( frames > 0 )
3720 {
3721 int xrun = 0;
3722 snd_pcm_uframes_t hwAvail;
3723
3724 PA_ENSURE( PaAlsaStream_WaitForFrames( stream, &framesAvail, &xrun ) );
3725 framesGot = PA_MIN( framesAvail, frames );
3726
3727 PA_ENSURE( PaAlsaStream_SetUpBuffers( stream, &framesGot, &xrun ) );
3728 if( framesGot > 0 )
3729 {
3730 framesGot = PaUtil_CopyOutput( &stream->bufferProcessor, &userBuffer, framesGot );
3731 PA_ENSURE( PaAlsaStream_EndProcessing( stream, framesGot, &xrun ) );
3732 frames -= framesGot;
3733 }
3734
3735 /* Start stream after one period of samples worth */
3736
3737 /* Frames residing in buffer */
3738 PA_ENSURE( err = GetStreamWriteAvailable( stream ) );
3739 framesAvail = err;
3740 hwAvail = stream->playback.bufferSize - framesAvail;
3741
3742 if( snd_pcm_state( stream->playback.pcm ) == SND_PCM_STATE_PREPARED &&
3743 hwAvail >= stream->playback.framesPerBuffer )
3744 {
3745 ENSURE_( snd_pcm_start( stream->playback.pcm ), paUnanticipatedHostError );
3746 }
3747 }
3748
3749 end:
3750 stream->capture.pcm = save;
3751 return result;
3752 error:
3753 goto end;
3754 }
3755
3756 /* Return frames available for reading. In the event of an overflow, the capture pcm will be restarted */
3757 static signed long GetStreamReadAvailable( PaStream* s )
3758 {
3759 PaError result = paNoError;
3760 PaAlsaStream *stream = (PaAlsaStream*)s;
3761 unsigned long avail;
3762 int xrun;
3763
3764 PA_ENSURE( PaAlsaStreamComponent_GetAvailableFrames( &stream->capture, &avail, &xrun ) );
3765 if( xrun )
3766 {
3767 PA_ENSURE( PaAlsaStream_HandleXrun( stream ) );
3768 PA_ENSURE( PaAlsaStreamComponent_GetAvailableFrames( &stream->capture, &avail, &xrun ) );
3769 if( xrun )
3770 PA_ENSURE( paInputOverflowed );
3771 }
3772
3773 return (signed long)avail;
3774
3775 error:
3776 return result;
3777 }
3778
3779 static signed long GetStreamWriteAvailable( PaStream* s )
3780 {
3781 PaError result = paNoError;
3782 PaAlsaStream *stream = (PaAlsaStream*)s;
3783 unsigned long avail;
3784 int xrun;
3785
3786 PA_ENSURE( PaAlsaStreamComponent_GetAvailableFrames( &stream->playback, &avail, &xrun ) );
3787 if( xrun )
3788 {
3789 snd_pcm_sframes_t savail;
3790
3791 PA_ENSURE( PaAlsaStream_HandleXrun( stream ) );
3792 savail = snd_pcm_avail_update( stream->playback.pcm );
3793
3794 /* savail should not contain -EPIPE now, since PaAlsaStream_HandleXrun will only prepare the pcm */
3795 ENSURE_( savail, paUnanticipatedHostError );
3796
3797 avail = (unsigned long) savail;
3798 }
3799
3800 return (signed long)avail;
3801
3802 error:
3803 return result;
3804 }
3805
3806 /* Extensions */
3807
3808 void PaAlsa_InitializeStreamInfo( PaAlsaStreamInfo *info )
3809 {
3810 info->size = sizeof (PaAlsaStreamInfo);
3811 info->hostApiType = paALSA;
3812 info->version = 1;
3813 info->deviceString = NULL;
3814 }
3815
3816 void PaAlsa_EnableRealtimeScheduling( PaStream *s, int enable )
3817 {
3818 PaAlsaStream *stream = (PaAlsaStream *) s;
3819 stream->rtSched = enable;
3820 }
3821
3822 #if 0
3823 void PaAlsa_EnableWatchdog( PaStream *s, int enable )
3824 {
3825 PaAlsaStream *stream = (PaAlsaStream *) s;
3826 stream->thread.useWatchdog = enable;
3827 }
3828 #endif
3829
3830 static PaError GetAlsaStreamPointer( PaStream* s, PaAlsaStream** stream )
3831 {
3832 PaError result = paNoError;
3833 PaUtilHostApiRepresentation* hostApi;
3834 PaAlsaHostApiRepresentation* alsaHostApi;
3835
3836 PA_ENSURE( PaUtil_ValidateStreamPointer( s ) );
3837 PA_ENSURE( PaUtil_GetHostApiRepresentation( &hostApi, paALSA ) );
3838 alsaHostApi = (PaAlsaHostApiRepresentation*)hostApi;
3839
3840 PA_UNLESS( PA_STREAM_REP( s )->streamInterface == &alsaHostApi->callbackStreamInterface
3841 || PA_STREAM_REP( s )->streamInterface == &alsaHostApi->blockingStreamInterface,
3842 paIncompatibleStreamHostApi );
3843
3844 *stream = (PaAlsaStream*)s;
3845 error:
3846 return paNoError;
3847 }
3848
3849 PaError PaAlsa_GetStreamInputCard(PaStream* s, int* card) {
3850 PaAlsaStream *stream;
3851 PaError result = paNoError;
3852 snd_pcm_info_t* pcmInfo;
3853
3854 PA_ENSURE( GetAlsaStreamPointer( s, &stream ) );
3855
3856 /* XXX: More descriptive error? */
3857 PA_UNLESS( stream->capture.pcm, paDeviceUnavailable );
3858
3859 snd_pcm_info_alloca( &pcmInfo );
3860 PA_ENSURE( snd_pcm_info( stream->capture.pcm, pcmInfo ) );
3861 *card = snd_pcm_info_get_card( pcmInfo );
3862
3863 error:
3864 return result;
3865 }
3866
3867 PaError PaAlsa_GetStreamOutputCard(PaStream* s, int* card) {
3868 PaAlsaStream *stream;
3869 PaError result = paNoError;
3870 snd_pcm_info_t* pcmInfo;
3871
3872 PA_ENSURE( GetAlsaStreamPointer( s, &stream ) );
3873
3874 /* XXX: More descriptive error? */
3875 PA_UNLESS( stream->playback.pcm, paDeviceUnavailable );
3876
3877 snd_pcm_info_alloca( &pcmInfo );
3878 PA_ENSURE( snd_pcm_info( stream->playback.pcm, pcmInfo ) );
3879 *card = snd_pcm_info_get_card( pcmInfo );
3880
3881 error:
3882 return result;
3883 }
3884
3885 PaError PaAlsa_SetRetriesBusy( int retries )
3886 {
3887 busyRetries_ = retries;
3888 return paNoError;
3889 }

  ViewVC Help
Powered by ViewVC 1.1.22