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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 31 - (hide annotations) (download)
Tue Sep 7 03:24:11 2010 UTC (9 years, 5 months ago) by william
File MIME type: text/plain
File size: 130852 byte(s)
committing r3113 initial commit again...
1 william 31 /*
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