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

  ViewVC Help
Powered by ViewVC 1.1.22