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