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

  ViewVC Help
Powered by ViewVC 1.1.22