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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 273 - (show annotations) (download)
Fri Nov 12 01:10:22 2010 UTC (9 years, 2 months ago) by william
File MIME type: text/plain
File size: 139941 byte(s)
Auto Commited Import of: pcsx2-0.9.7-DEBUG (upstream: v0.9.7.4013 local: v0.9.7.197-latest) in ./trunk
1 /*
2 * $Id: pa_linux_alsa.c 1540 2010-09-20 16:23:30Z dmitrykos $
3 * PortAudio Portable Real-Time Audio Library
4 * Latest Version at: http://www.portaudio.com
5 * ALSA implementation by Joshua Haberman and Arve Knudsen
6 *
7 * Copyright (c) 2002 Joshua Haberman <joshua@haberman.com>
8 * Copyright (c) 2005-2009 Arve Knudsen <arve.knudsen@gmail.com>
9 * Copyright (c) 2008 Kevin Kofler <kevin.kofler@chello.at>
10 *
11 * Based on the Open Source API proposed by Ross Bencina
12 * Copyright (c) 1999-2002 Ross Bencina, Phil Burk
13 *
14 * Permission is hereby granted, free of charge, to any person obtaining
15 * a copy of this software and associated documentation files
16 * (the "Software"), to deal in the Software without restriction,
17 * including without limitation the rights to use, copy, modify, merge,
18 * publish, distribute, sublicense, and/or sell copies of the Software,
19 * and to permit persons to whom the Software is furnished to do so,
20 * subject to the following conditions:
21 *
22 * The above copyright notice and this permission notice shall be
23 * included in all copies or substantial portions of the Software.
24 *
25 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
28 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
29 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
30 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 */
33
34 /*
35 * The text above constitutes the entire PortAudio license; however,
36 * the PortAudio community also makes the following non-binding requests:
37 *
38 * Any person wishing to distribute modifications to the Software is
39 * requested to send the modifications to the original developer so that
40 * they can be incorporated into the canonical version. It is also
41 * requested that these non-binding requests be included along with the
42 * license above.
43 */
44
45 /**
46 @file
47 @ingroup hostapi_src
48 */
49
50 #define ALSA_PCM_NEW_HW_PARAMS_API
51 #define ALSA_PCM_NEW_SW_PARAMS_API
52 #include <alsa/asoundlib.h>
53 #undef ALSA_PCM_NEW_HW_PARAMS_API
54 #undef ALSA_PCM_NEW_SW_PARAMS_API
55
56 #include <sys/poll.h>
57 #include <string.h> /* strlen() */
58 #include <limits.h>
59 #include <math.h>
60 #include <pthread.h>
61 #include <signal.h>
62 #include <time.h>
63 #include <sys/mman.h>
64 #include <signal.h> /* For sig_atomic_t */
65
66 #include "portaudio.h"
67 #include "pa_util.h"
68 #include "pa_unix_util.h"
69 #include "pa_allocation.h"
70 #include "pa_hostapi.h"
71 #include "pa_stream.h"
72 #include "pa_cpuload.h"
73 #include "pa_process.h"
74 #include "pa_endianness.h"
75 #include "pa_debugprint.h"
76
77 #include "pa_linux_alsa.h"
78
79 /* Check return value of ALSA function, and map it to PaError */
80 #define ENSURE_(expr, code) \
81 do { \
82 if( UNLIKELY( (aErr_ = (expr)) < 0 ) ) \
83 { \
84 /* PaUtil_SetLastHostErrorInfo should only be used in the main thread */ \
85 if( (code) == paUnanticipatedHostError && pthread_equal( pthread_self(), paUnixMainThread) ) \
86 { \
87 PaUtil_SetLastHostErrorInfo( paALSA, aErr_, snd_strerror( aErr_ ) ); \
88 } \
89 PaUtil_DebugPrint( "Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" ); \
90 if( (code) == paUnanticipatedHostError ) \
91 PA_DEBUG(( "Host error description: %s\n", snd_strerror( aErr_ ) )); \
92 result = (code); \
93 goto error; \
94 } \
95 } while( 0 );
96
97 #define ASSERT_CALL_(expr, success) \
98 aErr_ = (expr); \
99 assert( success == aErr_ );
100
101 static int aErr_; /* Used with ENSURE_ */
102 static int numPeriods_ = 4;
103 static int busyRetries_ = 100;
104
105 int PaAlsa_SetNumPeriods( int numPeriods )
106 {
107 numPeriods_ = numPeriods;
108 return paNoError;
109 }
110
111 typedef enum
112 {
113 StreamDirection_In,
114 StreamDirection_Out
115 } StreamDirection;
116
117 typedef struct
118 {
119 PaSampleFormat hostSampleFormat;
120 unsigned long framesPerBuffer;
121 int numUserChannels, numHostChannels;
122 int userInterleaved, hostInterleaved;
123 int canMmap;
124 void *nonMmapBuffer;
125 unsigned int nonMmapBufferSize;
126 PaDeviceIndex device; /* Keep the device index */
127
128 snd_pcm_t *pcm;
129 snd_pcm_uframes_t bufferSize;
130 snd_pcm_format_t nativeFormat;
131 unsigned int nfds;
132 int ready; /* Marked ready from poll */
133 void **userBuffers;
134 snd_pcm_uframes_t offset;
135 StreamDirection streamDir;
136
137 snd_pcm_channel_area_t *channelAreas; /* Needed for channel adaption */
138 } PaAlsaStreamComponent;
139
140 /* Implementation specific stream structure */
141 typedef struct PaAlsaStream
142 {
143 PaUtilStreamRepresentation streamRepresentation;
144 PaUtilCpuLoadMeasurer cpuLoadMeasurer;
145 PaUtilBufferProcessor bufferProcessor;
146 PaUnixThread thread;
147
148 unsigned long framesPerUserBuffer, maxFramesPerHostBuffer;
149
150 int primeBuffers;
151 int callbackMode; /* bool: are we running in callback mode? */
152 int pcmsSynced; /* Have we successfully synced pcms */
153 int rtSched;
154
155 /* the callback thread uses these to poll the sound device(s), waiting
156 * for data to be ready/available */
157 struct pollfd* pfds;
158 int pollTimeout;
159
160 /* Used in communication between threads */
161 volatile sig_atomic_t callback_finished; /* bool: are we in the "callback finished" state? */
162 volatile sig_atomic_t callbackAbort; /* Drop frames? */
163 volatile sig_atomic_t isActive; /* Is stream in active state? (Between StartStream and StopStream || !paContinue) */
164 PaUnixMutex stateMtx; /* Used to synchronize access to stream state */
165
166 int neverDropInput;
167
168 PaTime underrun;
169 PaTime overrun;
170
171 PaAlsaStreamComponent capture, playback;
172 }
173 PaAlsaStream;
174
175 /* PaAlsaHostApiRepresentation - host api datastructure specific to this implementation */
176
177 typedef struct PaAlsaHostApiRepresentation
178 {
179 PaUtilHostApiRepresentation baseHostApiRep;
180 PaUtilStreamInterface callbackStreamInterface;
181 PaUtilStreamInterface blockingStreamInterface;
182
183 PaUtilAllocationGroup *allocations;
184
185 PaHostApiIndex hostApiIndex;
186 }
187 PaAlsaHostApiRepresentation;
188
189 typedef struct PaAlsaDeviceInfo
190 {
191 PaDeviceInfo baseDeviceInfo;
192 char *alsaName;
193 int isPlug;
194 int minInputChannels;
195 int minOutputChannels;
196 }
197 PaAlsaDeviceInfo;
198
199 /* prototypes for functions declared in this file */
200
201 static void Terminate( struct PaUtilHostApiRepresentation *hostApi );
202 static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
203 const PaStreamParameters *inputParameters,
204 const PaStreamParameters *outputParameters,
205 double sampleRate );
206 static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
207 PaStream** s,
208 const PaStreamParameters *inputParameters,
209 const PaStreamParameters *outputParameters,
210 double sampleRate,
211 unsigned long framesPerBuffer,
212 PaStreamFlags streamFlags,
213 PaStreamCallback *callback,
214 void *userData );
215 static PaError CloseStream( PaStream* stream );
216 static PaError StartStream( PaStream *stream );
217 static PaError StopStream( PaStream *stream );
218 static PaError AbortStream( PaStream *stream );
219 static PaError IsStreamStopped( PaStream *s );
220 static PaError IsStreamActive( PaStream *stream );
221 static PaTime GetStreamTime( PaStream *stream );
222 static double GetStreamCpuLoad( PaStream* stream );
223 static PaError BuildDeviceList( PaAlsaHostApiRepresentation *hostApi );
224 static int SetApproximateSampleRate( snd_pcm_t *pcm, snd_pcm_hw_params_t *hwParams, double sampleRate );
225 static int GetExactSampleRate( snd_pcm_hw_params_t *hwParams, double *sampleRate );
226
227 /* Callback prototypes */
228 static void *CallbackThreadFunc( void *userData );
229
230 /* Blocking prototypes */
231 static signed long GetStreamReadAvailable( PaStream* s );
232 static signed long GetStreamWriteAvailable( PaStream* s );
233 static PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames );
234 static PaError WriteStream( PaStream* stream, const void *buffer, unsigned long frames );
235
236
237 static const PaAlsaDeviceInfo *GetDeviceInfo( const PaUtilHostApiRepresentation *hostApi, int device )
238 {
239 return (const PaAlsaDeviceInfo *)hostApi->deviceInfos[device];
240 }
241
242 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 hwDevInfos[numDeviceNames - 1].hasPlayback = predefined->hasPlayback;
814 hwDevInfos[numDeviceNames - 1].hasCapture = predefined->hasCapture;
815 }
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 self->nonMmapBufferSize = 0;
1194
1195 if( !callbackMode && !self->userInterleaved )
1196 {
1197 /* Pre-allocate non-interleaved user provided buffers */
1198 PA_UNLESS( self->userBuffers = PaUtil_AllocateMemory( sizeof (void *) * self->numUserChannels ),
1199 paInsufficientMemory );
1200 }
1201
1202 error:
1203 return result;
1204 }
1205
1206 static void PaAlsaStreamComponent_Terminate( PaAlsaStreamComponent *self )
1207 {
1208 snd_pcm_close( self->pcm );
1209 if( self->userBuffers )
1210 PaUtil_FreeMemory( self->userBuffers );
1211 }
1212
1213 /*
1214 static int nearbyint_(float value) {
1215 if( value - (int)value > .5 )
1216 return (int)ceil( value );
1217 return (int)floor( value );
1218 }
1219 */
1220
1221 /** Initiate configuration, preparing for determining a period size suitable for both capture and playback components.
1222 *
1223 */
1224 static PaError PaAlsaStreamComponent_InitialConfigure( PaAlsaStreamComponent *self, const PaStreamParameters *params,
1225 int primeBuffers, snd_pcm_hw_params_t *hwParams, double *sampleRate )
1226 {
1227 /* Configuration consists of setting all of ALSA's parameters.
1228 * These parameters come in two flavors: hardware parameters
1229 * and software paramters. Hardware parameters will affect
1230 * the way the device is initialized, software parameters
1231 * affect the way ALSA interacts with me, the user-level client.
1232 */
1233
1234 PaError result = paNoError;
1235 snd_pcm_access_t accessMode, alternateAccessMode;
1236 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
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
1261 PA_DEBUG(("%s: device MMAP SND_PCM_ACCESS_MMAP_INTERLEAVED: %s\n", __FUNCTION__, (snd_pcm_hw_params_test_access( pcm, hwParams, accessMode ) >= 0 ? "YES" : "NO")));
1262 PA_DEBUG(("%s: device MMAP SND_PCM_ACCESS_MMAP_NONINTERLEAVED: %s\n", __FUNCTION__, (snd_pcm_hw_params_test_access( pcm, hwParams, alternateAccessMode ) >= 0 ? "YES" : "NO")));
1263
1264 if (!self->canMmap)
1265 {
1266 accessMode = SND_PCM_ACCESS_RW_INTERLEAVED;
1267 alternateAccessMode = SND_PCM_ACCESS_RW_NONINTERLEAVED;
1268 }
1269 }
1270 else
1271 {
1272 accessMode = SND_PCM_ACCESS_MMAP_NONINTERLEAVED;
1273 alternateAccessMode = SND_PCM_ACCESS_MMAP_INTERLEAVED;
1274
1275 /* test if MMAP supported */
1276 self->canMmap = snd_pcm_hw_params_test_access( pcm, hwParams, accessMode ) >= 0 ||
1277 snd_pcm_hw_params_test_access( pcm, hwParams, alternateAccessMode ) >= 0;
1278
1279 PA_DEBUG(("%s: device MMAP SND_PCM_ACCESS_MMAP_NONINTERLEAVED: %s\n", __FUNCTION__, (snd_pcm_hw_params_test_access( pcm, hwParams, accessMode ) >= 0 ? "YES" : "NO")));
1280 PA_DEBUG(("%s: device MMAP SND_PCM_ACCESS_MMAP_INTERLEAVED: %s\n", __FUNCTION__, (snd_pcm_hw_params_test_access( pcm, hwParams, alternateAccessMode ) >= 0 ? "YES" : "NO")));
1281
1282 if (!self->canMmap)
1283 {
1284 accessMode = SND_PCM_ACCESS_RW_NONINTERLEAVED;
1285 alternateAccessMode = SND_PCM_ACCESS_RW_INTERLEAVED;
1286 }
1287 }
1288
1289 PA_DEBUG(("%s: device can MMAP: %s\n", __FUNCTION__, (self->canMmap ? "YES" : "NO")));
1290
1291 /* If requested access mode fails, try alternate mode */
1292 if( snd_pcm_hw_params_set_access( pcm, hwParams, accessMode ) < 0 )
1293 {
1294 int err = 0;
1295 if( (err = snd_pcm_hw_params_set_access( pcm, hwParams, alternateAccessMode )) < 0)
1296 {
1297 result = paUnanticipatedHostError;
1298 PaUtil_SetLastHostErrorInfo( paALSA, err, snd_strerror( err ) );
1299 goto error;
1300 }
1301 /* Flip mode */
1302 self->hostInterleaved = !self->userInterleaved;
1303 }
1304
1305 ENSURE_( snd_pcm_hw_params_set_format( pcm, hwParams, self->nativeFormat ), paUnanticipatedHostError );
1306
1307 ENSURE_( SetApproximateSampleRate( pcm, hwParams, sr ), paInvalidSampleRate );
1308 ENSURE_( GetExactSampleRate( hwParams, &sr ), paUnanticipatedHostError );
1309 /* reject if there's no sample rate within 1% of the one requested */
1310 if( (fabs( *sampleRate - sr ) / *sampleRate) > 0.01 )
1311 {
1312 PA_DEBUG(("%s: Wanted %f, closest sample rate was %d\n", __FUNCTION__, sampleRate, sr ));
1313 PA_ENSURE( paInvalidSampleRate );
1314 }
1315
1316 ENSURE_( snd_pcm_hw_params_set_channels( pcm, hwParams, self->numHostChannels ), paInvalidChannelCount );
1317
1318 *sampleRate = sr;
1319
1320 end:
1321 return result;
1322
1323 error:
1324 /* No particular action */
1325 goto end;
1326 }
1327
1328 /** Finish the configuration of the component's ALSA device.
1329 *
1330 * As part of this method, the component's bufferSize attribute will be set.
1331 * @param latency: The latency for this component.
1332 */
1333 static PaError PaAlsaStreamComponent_FinishConfigure( PaAlsaStreamComponent *self, snd_pcm_hw_params_t* hwParams,
1334 const PaStreamParameters *params, int primeBuffers, double sampleRate, PaTime* latency )
1335 {
1336 PaError result = paNoError;
1337 snd_pcm_sw_params_t* swParams;
1338 snd_pcm_uframes_t bufSz = 0;
1339 *latency = -1.;
1340
1341 snd_pcm_sw_params_alloca( &swParams );
1342
1343 bufSz = params->suggestedLatency * sampleRate;
1344 ENSURE_( snd_pcm_hw_params_set_buffer_size_near( self->pcm, hwParams, &bufSz ), paUnanticipatedHostError );
1345
1346 /* Set the parameters! */
1347 {
1348 int r = snd_pcm_hw_params( self->pcm, hwParams );
1349 #ifdef PA_ENABLE_DEBUG_OUTPUT
1350 if( r < 0 )
1351 {
1352 snd_output_t *output = NULL;
1353 snd_output_stdio_attach( &output, stderr, 0 );
1354 snd_pcm_hw_params_dump( hwParams, output );
1355 }
1356 #endif
1357 ENSURE_(r, paUnanticipatedHostError );
1358 }
1359 ENSURE_( snd_pcm_hw_params_get_buffer_size( hwParams, &self->bufferSize ), paUnanticipatedHostError );
1360 /* Latency in seconds */
1361 *latency = self->bufferSize / sampleRate;
1362
1363 /* Now software parameters... */
1364 ENSURE_( snd_pcm_sw_params_current( self->pcm, swParams ), paUnanticipatedHostError );
1365
1366 ENSURE_( snd_pcm_sw_params_set_start_threshold( self->pcm, swParams, self->framesPerBuffer ), paUnanticipatedHostError );
1367 ENSURE_( snd_pcm_sw_params_set_stop_threshold( self->pcm, swParams, self->bufferSize ), paUnanticipatedHostError );
1368
1369 /* Silence buffer in the case of underrun */
1370 if( !primeBuffers ) /* XXX: Make sense? */
1371 {
1372 snd_pcm_uframes_t boundary;
1373 ENSURE_( snd_pcm_sw_params_get_boundary( swParams, &boundary ), paUnanticipatedHostError );
1374 ENSURE_( snd_pcm_sw_params_set_silence_threshold( self->pcm, swParams, 0 ), paUnanticipatedHostError );
1375 ENSURE_( snd_pcm_sw_params_set_silence_size( self->pcm, swParams, boundary ), paUnanticipatedHostError );
1376 }
1377
1378 ENSURE_( snd_pcm_sw_params_set_avail_min( self->pcm, swParams, self->framesPerBuffer ), paUnanticipatedHostError );
1379 ENSURE_( snd_pcm_sw_params_set_xfer_align( self->pcm, swParams, 1 ), paUnanticipatedHostError );
1380 ENSURE_( snd_pcm_sw_params_set_tstamp_mode( self->pcm, swParams, SND_PCM_TSTAMP_ENABLE ), paUnanticipatedHostError );
1381
1382 /* Set the parameters! */
1383 ENSURE_( snd_pcm_sw_params( self->pcm, swParams ), paUnanticipatedHostError );
1384
1385 error:
1386 return result;
1387 }
1388
1389 static PaError PaAlsaStream_Initialize( PaAlsaStream *self, PaAlsaHostApiRepresentation *alsaApi, const PaStreamParameters *inParams,
1390 const PaStreamParameters *outParams, double sampleRate, unsigned long framesPerUserBuffer, PaStreamCallback callback,
1391 PaStreamFlags streamFlags, void *userData )
1392 {
1393 PaError result = paNoError;
1394 assert( self );
1395
1396 memset( self, 0, sizeof (PaAlsaStream) );
1397
1398 if( NULL != callback )
1399 {
1400 PaUtil_InitializeStreamRepresentation( &self->streamRepresentation,
1401 &alsaApi->callbackStreamInterface,
1402 callback, userData );
1403 self->callbackMode = 1;
1404 }
1405 else
1406 {
1407 PaUtil_InitializeStreamRepresentation( &self->streamRepresentation,
1408 &alsaApi->blockingStreamInterface,
1409 NULL, userData );
1410 }
1411
1412 self->framesPerUserBuffer = framesPerUserBuffer;
1413 self->neverDropInput = streamFlags & paNeverDropInput;
1414 /* XXX: Ignore paPrimeOutputBuffersUsingStreamCallback untill buffer priming is fully supported in pa_process.c */
1415 /*
1416 if( outParams & streamFlags & paPrimeOutputBuffersUsingStreamCallback )
1417 self->primeBuffers = 1;
1418 */
1419 memset( &self->capture, 0, sizeof (PaAlsaStreamComponent) );
1420 memset( &self->playback, 0, sizeof (PaAlsaStreamComponent) );
1421 if( inParams )
1422 {
1423 PA_ENSURE( PaAlsaStreamComponent_Initialize( &self->capture, alsaApi, inParams, StreamDirection_In, NULL != callback ) );
1424 }
1425 if( outParams )
1426 {
1427 PA_ENSURE( PaAlsaStreamComponent_Initialize( &self->playback, alsaApi, outParams, StreamDirection_Out, NULL != callback ) );
1428 }
1429
1430 assert( self->capture.nfds || self->playback.nfds );
1431
1432 PA_UNLESS( self->pfds = (struct pollfd*)PaUtil_AllocateMemory( (self->capture.nfds +
1433 self->playback.nfds) * sizeof (struct pollfd) ), paInsufficientMemory );
1434
1435 PaUtil_InitializeCpuLoadMeasurer( &self->cpuLoadMeasurer, sampleRate );
1436 ASSERT_CALL_( PaUnixMutex_Initialize( &self->stateMtx ), paNoError );
1437
1438 error:
1439 return result;
1440 }
1441
1442 /** Free resources associated with stream, and eventually stream itself.
1443 *
1444 * Frees allocated memory, and terminates individual StreamComponents.
1445 */
1446 static void PaAlsaStream_Terminate( PaAlsaStream *self )
1447 {
1448 assert( self );
1449
1450 if( self->capture.pcm )
1451 {
1452 PaAlsaStreamComponent_Terminate( &self->capture );
1453 }
1454 if( self->playback.pcm )
1455 {
1456 PaAlsaStreamComponent_Terminate( &self->playback );
1457 }
1458
1459 PaUtil_FreeMemory( self->pfds );
1460 ASSERT_CALL_( PaUnixMutex_Terminate( &self->stateMtx ), paNoError );
1461
1462 PaUtil_FreeMemory( self );
1463 }
1464
1465 /** Calculate polling timeout
1466 *
1467 * @param frames Time to wait
1468 * @return Polling timeout in milliseconds
1469 */
1470 static int CalculatePollTimeout( const PaAlsaStream *stream, unsigned long frames )
1471 {
1472 assert( stream->streamRepresentation.streamInfo.sampleRate > 0.0 );
1473 /* Period in msecs, rounded up */
1474 return (int)ceil( 1000 * frames / stream->streamRepresentation.streamInfo.sampleRate );
1475 }
1476
1477 /** Align value in backward direction.
1478 *
1479 * @param v: Value to align.
1480 * @param align: Alignment.
1481 */
1482 static unsigned long PaAlsa_AlignBackward(unsigned long v, unsigned long align)
1483 {
1484 return ((v - (align ? v % align : 0)));
1485 }
1486
1487 /** Align value in forward direction.
1488 *
1489 * @param v: Value to align.
1490 * @param align: Alignment.
1491 */
1492 static unsigned long PaAlsa_AlignForward(unsigned long v, unsigned long align)
1493 {
1494 unsigned long remainder = (align ? (v % align) : 0);
1495 return (remainder != 0 ? v + (align - remainder) : v);
1496 }
1497
1498 /** Get size of host buffer maintained from the number of user frames, sample rate and suggested latency. Minimum double buffering
1499 * is maintained to allow 100% CPU usage inside user callback.
1500 *
1501 * @param userFramesPerBuffer: User buffer size in number of frames.
1502 * @param suggestedLatency: User provided desired latency.
1503 * @param sampleRate: Sample rate.
1504 */
1505 static unsigned long PaAlsa_GetFramesPerHostBuffer(unsigned long userFramesPerBuffer, PaTime suggestedLatency, double sampleRate)
1506 {
1507 unsigned long frames = userFramesPerBuffer + PA_MAX( userFramesPerBuffer, (unsigned long)(suggestedLatency * sampleRate) );
1508 return frames;
1509 }
1510
1511 /** Determine size per host buffer.
1512 *
1513 * During this method call, the component's framesPerBuffer attribute gets computed, and the corresponding period size
1514 * gets configured for the device.
1515 * @param accurate: If the configured period size is non-integer, this will be set to 0.
1516 */
1517 static PaError PaAlsaStreamComponent_DetermineFramesPerBuffer( PaAlsaStreamComponent* self, const PaStreamParameters* params,
1518 unsigned long framesPerUserBuffer, double sampleRate, snd_pcm_hw_params_t* hwParams, int* accurate )
1519 {
1520 PaError result = paNoError;
1521 unsigned long bufferSize, framesPerHostBuffer;
1522 int dir = 0;
1523
1524 /* Calculate host buffer size */
1525 bufferSize = PaAlsa_GetFramesPerHostBuffer(framesPerUserBuffer, params->suggestedLatency, sampleRate);
1526
1527 /* Log */
1528 PA_DEBUG(( "%s: user-buffer (frames) = %lu\n", __FUNCTION__, framesPerUserBuffer ));
1529 PA_DEBUG(( "%s: user-buffer (sec) = %f\n", __FUNCTION__, (double)(framesPerUserBuffer / sampleRate) ));
1530 PA_DEBUG(( "%s: suggested latency (sec) = %f\n", __FUNCTION__, params->suggestedLatency ));
1531 PA_DEBUG(( "%s: suggested host buffer (frames) = %lu\n", __FUNCTION__, bufferSize ));
1532 PA_DEBUG(( "%s: suggested host buffer (sec) = %f\n", __FUNCTION__, (double)(bufferSize / sampleRate) ));
1533
1534 #ifdef PA_ALSA_USE_OBSOLETE_HOST_BUFFER_CALC
1535
1536 if( framesPerUserBuffer != paFramesPerBufferUnspecified )
1537 {
1538 /* Preferably the host buffer size should be a multiple of the user buffer size */
1539
1540 if( bufferSize > framesPerUserBuffer )
1541 {
1542 snd_pcm_uframes_t remainder = bufferSize % framesPerUserBuffer;
1543 if( remainder > framesPerUserBuffer / 2. )
1544 bufferSize += framesPerUserBuffer - remainder;
1545 else
1546 bufferSize -= remainder;
1547
1548 assert( bufferSize % framesPerUserBuffer == 0 );
1549 }
1550 else if( framesPerUserBuffer % bufferSize != 0 )
1551 {
1552 /* Find a good compromise between user specified latency and buffer size */
1553 if( bufferSize > framesPerUserBuffer * .75 )
1554 {
1555 bufferSize = framesPerUserBuffer;
1556 }
1557 else
1558 {
1559 snd_pcm_uframes_t newSz = framesPerUserBuffer;
1560 while( newSz / 2 >= bufferSize )
1561 {
1562 if( framesPerUserBuffer % (newSz / 2) != 0 )
1563 {
1564 /* No use dividing any further */
1565 break;
1566 }
1567 newSz /= 2;
1568 }
1569 bufferSize = newSz;
1570 }
1571
1572 assert( framesPerUserBuffer % bufferSize == 0 );
1573 }
1574 }
1575
1576 #endif
1577
1578 {
1579 unsigned numPeriods = numPeriods_, maxPeriods = 0, minPeriods = numPeriods_;
1580
1581 /* It may be that the device only supports 2 periods for instance */
1582 dir = 0;
1583 ENSURE_( snd_pcm_hw_params_get_periods_min( hwParams, &minPeriods, &dir ), paUnanticipatedHostError )
1584 ENSURE_( snd_pcm_hw_params_get_periods_max( hwParams, &maxPeriods, &dir ), paUnanticipatedHostError );
1585 assert( maxPeriods > 1 );
1586
1587 /* Clamp to min/max */
1588 numPeriods = PA_MIN(maxPeriods, PA_MAX(minPeriods, numPeriods));
1589
1590 PA_DEBUG(( "%s: periods min = %lu, max = %lu, req = %lu \n", __FUNCTION__, minPeriods, maxPeriods, numPeriods ));
1591
1592 #ifndef PA_ALSA_USE_OBSOLETE_HOST_BUFFER_CALC
1593
1594 /* Calculate period size */
1595 framesPerHostBuffer = (bufferSize / numPeriods);
1596
1597 /* Align & test size */
1598 if( framesPerUserBuffer != paFramesPerBufferUnspecified )
1599 {
1600 /* Align to user buffer size */
1601 framesPerHostBuffer = PaAlsa_AlignForward(framesPerHostBuffer, framesPerUserBuffer);
1602
1603 /* Test (borrowed from older implementation) */
1604 if( framesPerHostBuffer < framesPerUserBuffer )
1605 {
1606 assert( framesPerUserBuffer % framesPerHostBuffer == 0 );
1607 if( snd_pcm_hw_params_test_period_size( self->pcm, hwParams, framesPerHostBuffer, 0 ) < 0 )
1608 {
1609 if( snd_pcm_hw_params_test_period_size( self->pcm, hwParams, framesPerHostBuffer * 2, 0 ) == 0 )
1610 framesPerHostBuffer *= 2;
1611 else
1612 if( snd_pcm_hw_params_test_period_size( self->pcm, hwParams, framesPerHostBuffer / 2, 0 ) == 0 )
1613 framesPerHostBuffer /= 2;
1614 }
1615 }
1616 else
1617 {
1618 assert( framesPerHostBuffer % framesPerUserBuffer == 0 );
1619 if( snd_pcm_hw_params_test_period_size( self->pcm, hwParams, framesPerHostBuffer, 0 ) < 0 )
1620 {
1621 if( snd_pcm_hw_params_test_period_size( self->pcm, hwParams, framesPerHostBuffer + framesPerUserBuffer, 0 ) == 0 )
1622 framesPerHostBuffer += framesPerUserBuffer;
1623 else
1624 if( snd_pcm_hw_params_test_period_size( self->pcm, hwParams, framesPerHostBuffer - framesPerUserBuffer, 0 ) == 0 )
1625 framesPerHostBuffer -= framesPerUserBuffer;
1626 }
1627 }
1628 }
1629 #endif
1630
1631 #ifdef PA_ALSA_USE_OBSOLETE_HOST_BUFFER_CALC
1632
1633 if( framesPerUserBuffer != paFramesPerBufferUnspecified )
1634 {
1635 /* Try to get a power-of-two of the user buffer size. */
1636 framesPerHostBuffer = framesPerUserBuffer;
1637 if( framesPerHostBuffer < bufferSize )
1638 {
1639 while( bufferSize / framesPerHostBuffer > numPeriods )
1640 {
1641 framesPerHostBuffer *= 2;
1642 }
1643 /* One extra period is preferrable to one less (should be more robust) */
1644 if( bufferSize / framesPerHostBuffer < numPeriods )
1645 {
1646 framesPerHostBuffer /= 2;
1647 }
1648 }
1649 else
1650 {
1651 while( bufferSize / framesPerHostBuffer < numPeriods )
1652 {
1653 if( framesPerUserBuffer % (framesPerHostBuffer / 2) != 0 )
1654 {
1655 /* Can't be divided any further */
1656 break;
1657 }
1658 framesPerHostBuffer /= 2;
1659 }
1660 }
1661
1662 if( framesPerHostBuffer < framesPerUserBuffer )
1663 {
1664 assert( framesPerUserBuffer % framesPerHostBuffer == 0 );
1665 if( snd_pcm_hw_params_test_period_size( self->pcm, hwParams, framesPerHostBuffer, 0 ) < 0 )
1666 {
1667 if( snd_pcm_hw_params_test_period_size( self->pcm, hwParams, framesPerHostBuffer * 2, 0 ) == 0 )
1668 framesPerHostBuffer *= 2;
1669 else if( snd_pcm_hw_params_test_period_size( self->pcm, hwParams, framesPerHostBuffer / 2, 0 ) == 0 )
1670 framesPerHostBuffer /= 2;
1671 }
1672 }
1673 else
1674 {
1675 assert( framesPerHostBuffer % framesPerUserBuffer == 0 );
1676 if( snd_pcm_hw_params_test_period_size( self->pcm, hwParams, framesPerHostBuffer, 0 ) < 0 )
1677 {
1678 if( snd_pcm_hw_params_test_period_size( self->pcm, hwParams, framesPerHostBuffer + framesPerUserBuffer, 0 ) == 0 )
1679 framesPerHostBuffer += framesPerUserBuffer;
1680 else if( snd_pcm_hw_params_test_period_size( self->pcm, hwParams, framesPerHostBuffer - framesPerUserBuffer, 0 ) == 0 )
1681 framesPerHostBuffer -= framesPerUserBuffer;
1682 }
1683 }
1684 }
1685 else
1686 {
1687 framesPerHostBuffer = bufferSize / numPeriods;
1688 }
1689
1690 /* non-mmap mode needs a reasonably-sized buffer or it'll stutter */
1691 if( !self->canMmap && framesPerHostBuffer < 2048 )
1692 framesPerHostBuffer = 2048;
1693 #endif
1694 PA_DEBUG(( "%s: suggested host buffer period = %lu \n", __FUNCTION__, framesPerHostBuffer ));
1695 }
1696
1697 {
1698 /* Get min/max period sizes and adjust our chosen */
1699 snd_pcm_uframes_t min = 0, max = 0, minmax_diff;
1700 ENSURE_( snd_pcm_hw_params_get_period_size_min( hwParams, &min, NULL ), paUnanticipatedHostError );
1701 ENSURE_( snd_pcm_hw_params_get_period_size_max( hwParams, &max, NULL ), paUnanticipatedHostError );
1702 minmax_diff = max - min;
1703
1704 if( framesPerHostBuffer < min )
1705 {
1706 PA_DEBUG(( "%s: The determined period size (%lu) is less than minimum (%lu)\n", __FUNCTION__, framesPerHostBuffer, min ));
1707 framesPerHostBuffer = ((minmax_diff == 2) ? min + 1 : min);
1708 }
1709 else
1710 if( framesPerHostBuffer > max )
1711 {
1712 PA_DEBUG(( "%s: The determined period size (%lu) is greater than maximum (%lu)\n", __FUNCTION__, framesPerHostBuffer, max ));
1713 framesPerHostBuffer = ((minmax_diff == 2) ? max - 1 : max);
1714 }
1715
1716 PA_DEBUG(( "%s: device period minimum = %lu\n", __FUNCTION__, min ));
1717 PA_DEBUG(( "%s: device period maximum = %lu\n", __FUNCTION__, max ));
1718 PA_DEBUG(( "%s: host buffer period = %lu\n", __FUNCTION__, framesPerHostBuffer ));
1719 PA_DEBUG(( "%s: host buffer period latency = %f\n", __FUNCTION__, (double)(framesPerHostBuffer / sampleRate) ));
1720
1721 /* Try setting period size */
1722 dir = 0;
1723 ENSURE_( snd_pcm_hw_params_set_period_size_near( self->pcm, hwParams, &framesPerHostBuffer, &dir ), paUnanticipatedHostError );
1724 if( dir != 0 )
1725 {
1726 PA_DEBUG(( "%s: The configured period size is non-integer.\n", __FUNCTION__, dir ));
1727 *accurate = 0;
1728 }
1729 }
1730
1731 /* Set result */
1732 self->framesPerBuffer = framesPerHostBuffer;
1733
1734 error:
1735 return result;
1736 }
1737
1738 /* We need to determine how many frames per host buffer (period) to use. Our
1739 * goals are to provide the best possible performance, but also to
1740 * honor the requested latency settings as closely as we can. Therefore this
1741 * decision is based on:
1742 *
1743 * - the period sizes that playback and/or capture support. The
1744 * host buffer size has to be one of these.
1745 * - the number of periods that playback and/or capture support.
1746 *
1747 * We want to make period_size*(num_periods-1) to be as close as possible
1748 * to latency*rate for both playback and capture.
1749 *
1750 * This method will determine suitable period sizes for capture and playback handles, and report the maximum number of
1751 * frames per host buffer. The latter is relevant, in case we should be so unfortunate that the period size differs
1752 * between capture and playback. If this should happen, the stream's hostBufferSizeMode attribute will be set to
1753 * paUtilBoundedHostBufferSize, because the best we can do is limit the size of individual host buffers to the upper
1754 * bound. The size of host buffers scheduled for processing should only matter if the user has specified a buffer size,
1755 * but when he/she does we must strive for an optimal configuration. By default we'll opt for a fixed host buffer size,
1756 * which should be fine if the period size is the same for capture and playback. In general, if there is a specified user
1757 * buffer size, this method tries it best to determine a period size which is a multiple of the user buffer size.
1758 *
1759 * The framesPerBuffer attributes of the individual capture and playback components of the stream are set to corresponding
1760 * values determined here. Since these should be reported as
1761 *
1762 * This is one of those blocks of code that will just take a lot of
1763 * refinement to be any good.
1764 *
1765 * In the full-duplex case it is possible that the routine was unable
1766 * to find a number of frames per buffer acceptable to both devices
1767 * TODO: Implement an algorithm to find the value closest to acceptance
1768 * by both devices, to minimize difference between period sizes?
1769 *
1770 * @param determinedFramesPerHostBuffer: The determined host buffer size.
1771 */
1772 static PaError PaAlsaStream_DetermineFramesPerBuffer( PaAlsaStream* self, double sampleRate, const PaStreamParameters* inputParameters,
1773 const PaStreamParameters* outputParameters, unsigned long framesPerUserBuffer, snd_pcm_hw_params_t* hwParamsCapture,
1774 snd_pcm_hw_params_t* hwParamsPlayback, PaUtilHostBufferSizeMode* hostBufferSizeMode )
1775 {
1776 PaError result = paNoError;
1777 unsigned long framesPerHostBuffer = 0;
1778 int dir = 0;
1779 int accurate = 1;
1780 unsigned numPeriods = numPeriods_;
1781
1782 if( self->capture.pcm && self->playback.pcm )
1783 {
1784 if( framesPerUserBuffer == paFramesPerBufferUnspecified )
1785 {
1786 /* Come up with a common desired latency */
1787 snd_pcm_uframes_t desiredBufSz, e, minPeriodSize, maxPeriodSize, optimalPeriodSize, periodSize,
1788 minCapture, minPlayback, maxCapture, maxPlayback;
1789
1790 dir = 0;
1791 ENSURE_( snd_pcm_hw_params_get_period_size_min( hwParamsCapture, &minCapture, &dir ), paUnanticipatedHostError );
1792 dir = 0;
1793 ENSURE_( snd_pcm_hw_params_get_period_size_min( hwParamsPlayback, &minPlayback, &dir ), paUnanticipatedHostError );
1794 dir = 0;
1795 ENSURE_( snd_pcm_hw_params_get_period_size_max( hwParamsCapture, &maxCapture, &dir ), paUnanticipatedHostError );
1796 dir = 0;
1797 ENSURE_( snd_pcm_hw_params_get_period_size_max( hwParamsPlayback, &maxPlayback, &dir ), paUnanticipatedHostError );
1798 minPeriodSize = PA_MAX( minPlayback, minCapture );
1799 maxPeriodSize = PA_MIN( maxPlayback, maxCapture );
1800 PA_UNLESS( minPeriodSize <= maxPeriodSize, paBadIODeviceCombination );
1801
1802 desiredBufSz = (snd_pcm_uframes_t)(PA_MIN( outputParameters->suggestedLatency, inputParameters->suggestedLatency )
1803 * sampleRate);
1804 /* Clamp desiredBufSz */
1805 {
1806 snd_pcm_uframes_t maxBufferSize;
1807 snd_pcm_uframes_t maxBufferSizeCapture, maxBufferSizePlayback;
1808 ENSURE_( snd_pcm_hw_params_get_buffer_size_max( hwParamsCapture, &maxBufferSizeCapture ), paUnanticipatedHostError );
1809 ENSURE_( snd_pcm_hw_params_get_buffer_size_max( hwParamsPlayback, &maxBufferSizePlayback ), paUnanticipatedHostError );
1810 maxBufferSize = PA_MIN( maxBufferSizeCapture, maxBufferSizePlayback );
1811
1812 desiredBufSz = PA_MIN( desiredBufSz, maxBufferSize );
1813 }
1814
1815 /* Find the closest power of 2 */
1816 e = ilogb( minPeriodSize );
1817 if( minPeriodSize & (minPeriodSize - 1) )
1818 e += 1;
1819 periodSize = (snd_pcm_uframes_t)pow( 2, e );
1820
1821 while( periodSize <= maxPeriodSize )
1822 {
1823 if( snd_pcm_hw_params_test_period_size( self->playback.pcm, hwParamsPlayback, periodSize, 0 ) >= 0 &&
1824 snd_pcm_hw_params_test_period_size( self->capture.pcm, hwParamsCapture, periodSize, 0 ) >= 0 )
1825 {
1826 /* OK! */
1827 break;
1828 }
1829
1830 periodSize *= 2;
1831 }
1832
1833 optimalPeriodSize = PA_MAX( desiredBufSz / numPeriods, minPeriodSize );
1834 optimalPeriodSize = PA_MIN( optimalPeriodSize, maxPeriodSize );
1835
1836 /* Find the closest power of 2 */
1837 e = ilogb( optimalPeriodSize );
1838 if( optimalPeriodSize & (optimalPeriodSize - 1) )
1839 e += 1;
1840 optimalPeriodSize = (snd_pcm_uframes_t)pow( 2, e );
1841
1842 while( optimalPeriodSize >= periodSize )
1843 {
1844 if( snd_pcm_hw_params_test_period_size( self->capture.pcm, hwParamsCapture, optimalPeriodSize, 0 )
1845 >= 0 && snd_pcm_hw_params_test_period_size( self->playback.pcm, hwParamsPlayback,
1846 optimalPeriodSize, 0 ) >= 0 )
1847 {
1848 break;
1849 }
1850 optimalPeriodSize /= 2;
1851 }
1852
1853 if( optimalPeriodSize > periodSize )
1854 periodSize = optimalPeriodSize;
1855
1856 if( periodSize <= maxPeriodSize )
1857 {
1858 /* Looks good, the periodSize _should_ be acceptable by both devices */
1859 ENSURE_( snd_pcm_hw_params_set_period_size( self->capture.pcm, hwParamsCapture, periodSize, 0 ),
1860 paUnanticipatedHostError );
1861 ENSURE_( snd_pcm_hw_params_set_period_size( self->playback.pcm, hwParamsPlayback, periodSize, 0 ),
1862 paUnanticipatedHostError );
1863 self->capture.framesPerBuffer = self->playback.framesPerBuffer = periodSize;
1864 framesPerHostBuffer = periodSize;
1865 }
1866 else
1867 {
1868 /* Unable to find a common period size, oh well */
1869 optimalPeriodSize = PA_MAX( desiredBufSz / numPeriods, minPeriodSize );
1870 optimalPeriodSize = PA_MIN( optimalPeriodSize, maxPeriodSize );
1871
1872 self->capture.framesPerBuffer = optimalPeriodSize;
1873 dir = 0;
1874 ENSURE_( snd_pcm_hw_params_set_period_size_near( self->capture.pcm, hwParamsCapture, &self->capture.framesPerBuffer, &dir ),
1875 paUnanticipatedHostError );
1876 self->playback.framesPerBuffer = optimalPeriodSize;
1877 dir = 0;
1878 ENSURE_( snd_pcm_hw_params_set_period_size_near( self->playback.pcm, hwParamsPlayback, &self->playback.framesPerBuffer, &dir ),
1879 paUnanticipatedHostError );
1880 framesPerHostBuffer = PA_MAX( self->capture.framesPerBuffer, self->playback.framesPerBuffer );
1881 *hostBufferSizeMode = paUtilBoundedHostBufferSize;
1882 }
1883 }
1884 else
1885 {
1886 /* We choose the simple route and determine a suitable number of frames per buffer for one component of
1887 * the stream, then we hope that this will work for the other component too (it should!).
1888 */
1889
1890 unsigned maxPeriods = 0;
1891 PaAlsaStreamComponent* first = &self->capture, * second = &self->playback;
1892 const PaStreamParameters* firstStreamParams = inputParameters;
1893 snd_pcm_hw_params_t* firstHwParams = hwParamsCapture, * secondHwParams = hwParamsPlayback;
1894
1895 dir = 0;
1896 ENSURE_( snd_pcm_hw_params_get_periods_max( hwParamsPlayback, &maxPeriods, &dir ), paUnanticipatedHostError );
1897 if( maxPeriods < numPeriods )
1898 {
1899 /* The playback component is trickier to get right, try that first */
1900 first = &self->playback;
1901 second = &self->capture;
1902 firstStreamParams = outputParameters;
1903 firstHwParams = hwParamsPlayback;
1904 secondHwParams = hwParamsCapture;
1905 }
1906
1907 PA_ENSURE( PaAlsaStreamComponent_DetermineFramesPerBuffer( first, firstStreamParams, framesPerUserBuffer,
1908 sampleRate, firstHwParams, &accurate ) );
1909
1910 second->framesPerBuffer = first->framesPerBuffer;
1911 dir = 0;
1912 ENSURE_( snd_pcm_hw_params_set_period_size_near( second->pcm, secondHwParams, &second->framesPerBuffer, &dir ),
1913 paUnanticipatedHostError );
1914 if( self->capture.framesPerBuffer == self->playback.framesPerBuffer )
1915 {
1916 framesPerHostBuffer = self->capture.framesPerBuffer;
1917 }
1918 else
1919 {
1920 framesPerHostBuffer = PA_MAX( self->capture.framesPerBuffer, self->playback.framesPerBuffer );
1921 *hostBufferSizeMode = paUtilBoundedHostBufferSize;
1922 }
1923 }
1924 }
1925 else /* half-duplex is a slightly simpler case */
1926 {
1927 if( self->capture.pcm )
1928 {
1929 PA_ENSURE( PaAlsaStreamComponent_DetermineFramesPerBuffer( &self->capture, inputParameters, framesPerUserBuffer,
1930 sampleRate, hwParamsCapture, &accurate) );
1931 framesPerHostBuffer = self->capture.framesPerBuffer;
1932 }
1933 else
1934 {
1935 assert( self->playback.pcm );
1936 PA_ENSURE( PaAlsaStreamComponent_DetermineFramesPerBuffer( &self->playback, outputParameters, framesPerUserBuffer,
1937 sampleRate, hwParamsPlayback, &accurate ) );
1938 framesPerHostBuffer = self->playback.framesPerBuffer;
1939 }
1940 }
1941
1942 PA_UNLESS( framesPerHostBuffer != 0, paInternalError );
1943 self->maxFramesPerHostBuffer = framesPerHostBuffer;
1944
1945 if( !self->playback.canMmap || !accurate )
1946 {
1947 /* Don't know the exact size per host buffer */
1948 *hostBufferSizeMode = paUtilBoundedHostBufferSize;
1949 /* Raise upper bound */
1950 if( !accurate )
1951 ++self->maxFramesPerHostBuffer;
1952 }
1953
1954 error:
1955 return result;
1956 }
1957
1958 /** Set up ALSA stream parameters.
1959 *
1960 */
1961 static PaError PaAlsaStream_Configure( PaAlsaStream *self, const PaStreamParameters *inParams, const PaStreamParameters*
1962 outParams, double sampleRate, unsigned long framesPerUserBuffer, double* inputLatency, double* outputLatency,
1963 PaUtilHostBufferSizeMode* hostBufferSizeMode )
1964 {
1965 PaError result = paNoError;
1966 double realSr = sampleRate;
1967 snd_pcm_hw_params_t* hwParamsCapture, * hwParamsPlayback;
1968
1969 snd_pcm_hw_params_alloca( &hwParamsCapture );
1970 snd_pcm_hw_params_alloca( &hwParamsPlayback );
1971
1972 if( self->capture.pcm )
1973 PA_ENSURE( PaAlsaStreamComponent_InitialConfigure( &self->capture, inParams, self->primeBuffers, hwParamsCapture,
1974 &realSr ) );
1975 if( self->playback.pcm )
1976 PA_ENSURE( PaAlsaStreamComponent_InitialConfigure( &self->playback, outParams, self->primeBuffers, hwParamsPlayback,
1977 &realSr ) );
1978
1979 PA_ENSURE( PaAlsaStream_DetermineFramesPerBuffer( self, realSr, inParams, outParams, framesPerUserBuffer,
1980 hwParamsCapture, hwParamsPlayback, hostBufferSizeMode ) );
1981
1982 if( self->capture.pcm )
1983 {
1984 assert( self->capture.framesPerBuffer != 0 );
1985 PA_ENSURE( PaAlsaStreamComponent_FinishConfigure( &self->capture, hwParamsCapture, inParams, self->primeBuffers, realSr,
1986 inputLatency ) );
1987 PA_DEBUG(( "%s: Capture period size: %lu, latency: %f\n", __FUNCTION__, self->capture.framesPerBuffer, *inputLatency ));
1988 }
1989 if( self->playback.pcm )
1990 {
1991 assert( self->playback.framesPerBuffer != 0 );
1992 PA_ENSURE( PaAlsaStreamComponent_FinishConfigure( &self->playback, hwParamsPlayback, outParams, self->primeBuffers, realSr,
1993 outputLatency ) );
1994 PA_DEBUG(( "%s: Playback period size: %lu, latency: %f\n", __FUNCTION__, self->playback.framesPerBuffer, *outputLatency ));
1995 }
1996
1997 /* Should be exact now */
1998 self->streamRepresentation.streamInfo.sampleRate = realSr;
1999
2000 /* this will cause the two streams to automatically start/stop/prepare in sync.
2001 * We only need to execute these operations on one of the pair.
2002 * A: We don't want to do this on a blocking stream.
2003 */
2004 if( self->callbackMode && self->capture.pcm && self->playback.pcm )
2005 {
2006 int err = snd_pcm_link( self->capture.pcm, self->playback.pcm );
2007 if( err == 0 )
2008 self->pcmsSynced = 1;
2009 else
2010 PA_DEBUG(( "%s: Unable to sync pcms: %s\n", __FUNCTION__, snd_strerror( err ) ));
2011 }
2012
2013 {
2014 unsigned long minFramesPerHostBuffer = PA_MIN( self->capture.pcm ? self->capture.framesPerBuffer : ULONG_MAX,
2015 self->playback.pcm ? self->playback.framesPerBuffer : ULONG_MAX );
2016 self->pollTimeout = CalculatePollTimeout( self, minFramesPerHostBuffer ); /* Period in msecs, rounded up */
2017
2018 /* Time before watchdog unthrottles realtime thread == 1/4 of period time in msecs */
2019 /* self->threading.throttledSleepTime = (unsigned long) (minFramesPerHostBuffer / sampleRate / 4 * 1000); */
2020 }
2021
2022 if( self->callbackMode )
2023 {
2024 /* If the user expects a certain number of frames per callback we will either have to rely on block adaption
2025 * (framesPerHostBuffer is not an integer multiple of framesPerBuffer) or we can simply align the number
2026 * of host buffer frames with what the user specified */
2027 if( self->framesPerUserBuffer != paFramesPerBufferUnspecified )
2028 {
2029 /* self->alignFrames = 1; */
2030
2031 /* Unless the ratio between number of host and user buffer frames is an integer we will have to rely
2032 * on block adaption */
2033 /*
2034 if( framesPerHostBuffer % framesPerBuffer != 0 || (self->capture.pcm && self->playback.pcm &&
2035 self->capture.framesPerBuffer != self->playback.framesPerBuffer) )
2036 self->useBlockAdaption = 1;
2037 else
2038 self->alignFrames = 1;
2039 */
2040 }
2041 }
2042
2043 error:
2044 return result;
2045 }
2046
2047 static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
2048 PaStream** s,
2049 const PaStreamParameters *inputParameters,
2050 const PaStreamParameters *outputParameters,
2051 double sampleRate,
2052 unsigned long framesPerBuffer,
2053 PaStreamFlags streamFlags,
2054 PaStreamCallback* callback,
2055 void *userData )
2056 {
2057 PaError result = paNoError;
2058 PaAlsaHostApiRepresentation *alsaHostApi = (PaAlsaHostApiRepresentation*)hostApi;
2059 PaAlsaStream *stream = NULL;
2060 PaSampleFormat hostInputSampleFormat = 0, hostOutputSampleFormat = 0;
2061 PaSampleFormat inputSampleFormat = 0, outputSampleFormat = 0;
2062 int numInputChannels = 0, numOutputChannels = 0;
2063 PaTime inputLatency, outputLatency;
2064 /* Operate with fixed host buffer size by default, since other modes will invariably lead to block adaption */
2065 /* XXX: Use Bounded by default? Output tends to get stuttery with Fixed ... */
2066 PaUtilHostBufferSizeMode hostBufferSizeMode = paUtilFixedHostBufferSize;
2067
2068 if( (streamFlags & paPlatformSpecificFlags) != 0 )
2069 return paInvalidFlag;
2070
2071 if( inputParameters )
2072 {
2073 PA_ENSURE( ValidateParameters( inputParameters, hostApi, StreamDirection_In ) );
2074
2075 numInputChannels = inputParameters->channelCount;
2076 inputSampleFormat = inputParameters->sampleFormat;
2077 }
2078 if( outputParameters )
2079 {
2080 PA_ENSURE( ValidateParameters( outputParameters, hostApi, StreamDirection_Out ) );
2081
2082 numOutputChannels = outputParameters->channelCount;
2083 outputSampleFormat = outputParameters->sampleFormat;
2084 }
2085
2086 /* XXX: Why do we support this anyway? */
2087 if( framesPerBuffer == paFramesPerBufferUnspecified && getenv( "PA_ALSA_PERIODSIZE" ) != NULL )
2088 {
2089 PA_DEBUG(( "%s: Getting framesPerBuffer from environment\n", __FUNCTION__ ));
2090 framesPerBuffer = atoi( getenv("PA_ALSA_PERIODSIZE") );
2091 }
2092
2093 PA_UNLESS( stream = (PaAlsaStream*)PaUtil_AllocateMemory( sizeof(PaAlsaStream) ), paInsufficientMemory );
2094 PA_ENSURE( PaAlsaStream_Initialize( stream, alsaHostApi, inputParameters, outputParameters, sampleRate,
2095 framesPerBuffer, callback, streamFlags, userData ) );
2096
2097 PA_ENSURE( PaAlsaStream_Configure( stream, inputParameters, outputParameters, sampleRate, framesPerBuffer,
2098 &inputLatency, &outputLatency, &hostBufferSizeMode ) );
2099 hostInputSampleFormat = stream->capture.hostSampleFormat | (!stream->capture.hostInterleaved ? paNonInterleaved : 0);
2100 hostOutputSampleFormat = stream->playback.hostSampleFormat | (!stream->playback.hostInterleaved ? paNonInterleaved : 0);
2101
2102 PA_ENSURE( PaUtil_InitializeBufferProcessor( &stream->bufferProcessor,
2103 numInputChannels, inputSampleFormat, hostInputSampleFormat,
2104 numOutputChannels, outputSampleFormat, hostOutputSampleFormat,
2105 sampleRate, streamFlags, framesPerBuffer, stream->maxFramesPerHostBuffer,
2106 hostBufferSizeMode, callback, userData ) );
2107
2108 /* Ok, buffer processor is initialized, now we can deduce it's latency */
2109 if( numInputChannels > 0 )
2110 stream->streamRepresentation.streamInfo.inputLatency = inputLatency + (PaTime)(
2111 PaUtil_GetBufferProcessorInputLatency( &stream->bufferProcessor ) / sampleRate);
2112 if( numOutputChannels > 0 )
2113 stream->streamRepresentation.streamInfo.outputLatency = outputLatency + (PaTime)(
2114 PaUtil_GetBufferProcessorOutputLatency( &stream->bufferProcessor ) / sampleRate);
2115
2116 PA_DEBUG(( "%s: Stream: framesPerBuffer = %lu, maxFramesPerHostBuffer = %lu, latency = i(%f)/o(%f), \n", __FUNCTION__, framesPerBuffer, stream->maxFramesPerHostBuffer, stream->streamRepresentation.streamInfo.inputLatency, stream->streamRepresentation.streamInfo.outputLatency));
2117
2118 *s = (PaStream*)stream;
2119
2120 return result;
2121
2122 error:
2123 if( stream )
2124 {
2125 PA_DEBUG(( "%s: Stream in error, terminating\n", __FUNCTION__ ));
2126 PaAlsaStream_Terminate( stream );
2127 }
2128
2129 return result;
2130 }
2131
2132 static PaError CloseStream( PaStream* s )
2133 {
2134 PaError result = paNoError;
2135 PaAlsaStream *stream = (PaAlsaStream*)s;
2136
2137 PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
2138 PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
2139
2140 PaAlsaStream_Terminate( stream );
2141
2142 return result;
2143 }
2144
2145 static void SilenceBuffer( PaAlsaStream *stream )
2146 {
2147 const snd_pcm_channel_area_t *areas;
2148 snd_pcm_uframes_t frames = (snd_pcm_uframes_t)snd_pcm_avail_update( stream->playback.pcm ), offset;
2149
2150 snd_pcm_mmap_begin( stream->playback.pcm, &areas, &offset, &frames );
2151 snd_pcm_areas_silence( areas, offset, stream->playback.numHostChannels, frames, stream->playback.nativeFormat );
2152 snd_pcm_mmap_commit( stream->playback.pcm, offset, frames );
2153 }
2154
2155 /** Start/prepare pcm(s) for streaming.
2156 *
2157 * Depending on wether the stream is in callback or blocking mode, we will respectively start or simply
2158 * prepare the playback pcm. If the buffer has _not_ been primed, we will in callback mode prepare and
2159 * silence the buffer before starting playback. In blocking mode we simply prepare, as the playback will
2160 * be started automatically as the user writes to output.
2161 *
2162 * The capture pcm, however, will simply be prepared and started.
2163 */
2164 static PaError AlsaStart( PaAlsaStream *stream, int priming )
2165 {
2166 PaError result = paNoError;
2167
2168 if( stream->playback.pcm )
2169 {
2170 if( stream->callbackMode )
2171 {
2172 if( !priming )
2173 {
2174 /* Buffer isn't primed, so prepare and silence */
2175 ENSURE_( snd_pcm_prepare( stream->playback.pcm ), paUnanticipatedHostError );
2176 if( stream->playback.canMmap )
2177 SilenceBuffer( stream );
2178 }
2179 if( stream->playback.canMmap )
2180 ENSURE_( snd_pcm_start( stream->playback.pcm ), paUnanticipatedHostError );
2181 }
2182 else
2183 ENSURE_( snd_pcm_prepare( stream->playback.pcm ), paUnanticipatedHostError );
2184 }
2185 if( stream->capture.pcm && !stream->pcmsSynced )
2186 {
2187 ENSURE_( snd_pcm_prepare( stream->capture.pcm ), paUnanticipatedHostError );
2188 /* For a blocking stream we want to start capture as well, since nothing will happen otherwise */
2189 ENSURE_( snd_pcm_start( stream->capture.pcm ), paUnanticipatedHostError );
2190 }
2191
2192 end:
2193 return result;
2194 error:
2195 goto end;
2196 }
2197
2198 /** Utility function for determining if pcms are in running state.
2199 *
2200 */
2201 #if 0
2202 static int IsRunning( PaAlsaStream *stream )
2203 {
2204 int result = 0;
2205
2206 PA_ENSURE( PaUnixMutex_Lock( &stream->stateMtx ) );
2207 if( stream->capture.pcm )
2208 {
2209 snd_pcm_state_t capture_state = snd_pcm_state( stream->capture.pcm );
2210
2211 if( capture_state == SND_PCM_STATE_RUNNING || capture_state == SND_PCM_STATE_XRUN
2212 || capture_state == SND_PCM_STATE_DRAINING )
2213 {
2214 result = 1;
2215 goto end;
2216 }
2217 }
2218
2219 if( stream->playback.pcm )
2220 {
2221 snd_pcm_state_t playback_state = snd_pcm_state( stream->playback.pcm );
2222
2223 if( playback_state == SND_PCM_STATE_RUNNING || playback_state == SND_PCM_STATE_XRUN
2224 || playback_state == SND_PCM_STATE_DRAINING )
2225 {
2226 result = 1;
2227 goto end;
2228 }
2229 }
2230
2231 end:
2232 ASSERT_CALL_( PaUnixMutex_Unlock( &stream->stateMtx ), paNoError );
2233 return result;
2234 error:
2235 goto error;
2236 }
2237 #endif
2238
2239 static PaError StartStream( PaStream *s )
2240 {
2241 PaError result = paNoError;
2242 PaAlsaStream* stream = (PaAlsaStream*)s;
2243 int streamStarted = 0; /* So we can know wether we need to take the stream down */
2244
2245 /* Ready the processor */
2246 PaUtil_ResetBufferProcessor( &stream->bufferProcessor );
2247
2248 /* Set now, so we can test for activity further down */
2249 stream->isActive = 1;
2250
2251 if( stream->callbackMode )
2252 {
2253 PA_ENSURE( PaUnixThread_New( &stream->thread, &CallbackThreadFunc, stream, 1., stream->rtSched ) );
2254 }
2255 else
2256 {
2257 PA_ENSURE( AlsaStart( stream, 0 ) );
2258 streamStarted = 1;
2259 }
2260
2261 end:
2262 return result;
2263 error:
2264 if( streamStarted )
2265 {
2266 AbortStream( stream );
2267 }
2268 stream->isActive = 0;
2269
2270 goto end;
2271 }
2272
2273 /** Stop PCM handle, either softly or abruptly.
2274 */
2275 static PaError AlsaStop( PaAlsaStream *stream, int abort )
2276 {
2277 PaError result = paNoError;
2278 /* XXX: snd_pcm_drain tends to lock up, avoid it until we find out more */
2279 abort = 1;
2280 /*
2281 if( stream->capture.pcm && !strcmp( Pa_GetDeviceInfo( stream->capture.device )->name,
2282 "dmix" ) )
2283 {
2284 abort = 1;
2285 }
2286 else if( stream->playback.pcm && !strcmp( Pa_GetDeviceInfo( stream->playback.device )->name,
2287 "dmix" ) )
2288 {
2289 abort = 1;
2290 }
2291 */
2292
2293 if( abort )
2294 {
2295 if( stream->playback.pcm )
2296 {
2297 ENSURE_( snd_pcm_drop( stream->playback.pcm ), paUnanticipatedHostError );
2298 }
2299 if( stream->capture.pcm && !stream->pcmsSynced )
2300 {
2301 ENSURE_( snd_pcm_drop( stream->capture.pcm ), paUnanticipatedHostError );
2302 }
2303
2304 PA_DEBUG(( "%s: Dropped frames\n", __FUNCTION__ ));
2305 }
2306 else
2307 {
2308 if( stream->playback.pcm )
2309 {
2310 ENSURE_( snd_pcm_nonblock( stream->playback.pcm, 0 ), paUnanticipatedHostError );
2311 if( snd_pcm_drain( stream->playback.pcm ) < 0 )
2312 {
2313 PA_DEBUG(( "%s: Draining playback handle failed!\n", __FUNCTION__ ));
2314 }
2315 }
2316 if( stream->capture.pcm && !stream->pcmsSynced )
2317 {
2318 /* We don't need to retrieve any remaining frames */
2319 if( snd_pcm_drain( stream->capture.pcm ) < 0 )
2320 {
2321 PA_DEBUG(( "%s: Draining capture handle failed!\n", __FUNCTION__ ));
2322 }
2323 }
2324 }
2325
2326 end:
2327 return result;
2328 error:
2329 goto end;
2330 }
2331
2332 /** Stop or abort stream.
2333 *
2334 * If a stream is in callback mode we will have to inspect wether the background thread has
2335 * finished, or we will have to take it out. In either case we join the thread before
2336 * returning. In blocking mode, we simply tell ALSA to stop abruptly (abort) or finish
2337 * buffers (drain)
2338 *
2339 * Stream will be considered inactive (!PaAlsaStream::isActive) after a call to this function
2340 */
2341 static PaError RealStop( PaAlsaStream *stream, int abort )
2342 {
2343 PaError result = paNoError;
2344
2345 /* First deal with the callback thread, cancelling and/or joining
2346 * it if necessary
2347 */
2348 if( stream->callbackMode )
2349 {
2350 PaError threadRes;
2351 stream->callbackAbort = abort;
2352
2353 if( !abort )
2354 {
2355 PA_DEBUG(( "Stopping callback\n" ));
2356 }
2357 PA_ENSURE( PaUnixThread_Terminate( &stream->thread, !abort, &threadRes ) );
2358 if( threadRes != paNoError )
2359 {
2360 PA_DEBUG(( "Callback thread returned: %d\n", threadRes ));
2361 }
2362 #if 0
2363 if( watchdogRes != paNoError )
2364 PA_DEBUG(( "Watchdog thread returned: %d\n", watchdogRes ));
2365 #endif
2366
2367 stream->callback_finished = 0;
2368 }
2369 else
2370 {
2371 PA_ENSURE( AlsaStop( stream, abort ) );
2372 }
2373
2374 stream->isActive = 0;
2375
2376 end:
2377 return result;
2378
2379 error:
2380 goto end;
2381 }
2382
2383 static PaError StopStream( PaStream *s )
2384 {
2385 return RealStop( (PaAlsaStream *) s, 0 );
2386 }
2387
2388 static PaError AbortStream( PaStream *s )
2389 {
2390 return RealStop( (PaAlsaStream * ) s, 1 );
2391 }
2392
2393 /** The stream is considered stopped before StartStream, or AFTER a call to Abort/StopStream (callback
2394 * returning !paContinue is not considered)
2395 *
2396 */
2397 static PaError IsStreamStopped( PaStream *s )
2398 {
2399 PaAlsaStream *stream = (PaAlsaStream *)s;
2400
2401 /* callback_finished indicates we need to join callback thread (ie. in Abort/StopStream) */
2402 return !IsStreamActive( s ) && !stream->callback_finished;
2403 }
2404
2405 static PaError IsStreamActive( PaStream *s )
2406 {
2407 PaAlsaStream *stream = (PaAlsaStream*)s;
2408 return stream->isActive;
2409 }
2410
2411 static PaTime GetStreamTime( PaStream *s )
2412 {
2413 PaAlsaStream *stream = (PaAlsaStream*)s;
2414
2415 snd_timestamp_t timestamp;
2416 snd_pcm_status_t* status;
2417 snd_pcm_status_alloca( &status );
2418
2419 /* TODO: what if we have both? does it really matter? */
2420
2421 /* TODO: if running in callback mode, this will mean
2422 * libasound routines are being called from multiple threads.
2423 * need to verify that libasound is thread-safe. */
2424
2425 if( stream->capture.pcm )
2426 {
2427 snd_pcm_status( stream->capture.pcm, status );
2428 }
2429 else if( stream->playback.pcm )
2430 {
2431 snd_pcm_status( stream->playback.pcm, status );
2432 }
2433
2434 snd_pcm_status_get_tstamp( status, &timestamp );
2435 return timestamp.tv_sec + (PaTime)timestamp.tv_usec / 1e6;
2436 }
2437
2438 static double GetStreamCpuLoad( PaStream* s )
2439 {
2440 PaAlsaStream *stream = (PaAlsaStream*)s;
2441
2442 return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer );
2443 }
2444
2445 static int SetApproximateSampleRate( snd_pcm_t *pcm, snd_pcm_hw_params_t *hwParams, double sampleRate )
2446 {
2447 PaError result = paNoError;
2448 unsigned long approx = (unsigned long) sampleRate;
2449 int dir = 0;
2450 double fraction = sampleRate - approx;
2451
2452 assert( pcm && hwParams );
2453
2454 if( fraction > 0.0 )
2455 {
2456 if( fraction > 0.5 )
2457 {
2458 ++approx;
2459 dir = -1;
2460 }
2461 else
2462 dir = 1;
2463 }
2464
2465 if( snd_pcm_hw_params_set_rate( pcm, hwParams, approx, dir ) < 0)
2466 result = paInvalidSampleRate;
2467
2468 end:
2469
2470 return result;
2471
2472 error:
2473
2474 /* Log */
2475 {
2476 unsigned int _min = 0, _max = 0; int _dir = 0;
2477 ENSURE_( snd_pcm_hw_params_get_rate_min( hwParams, &_min, &_dir ), paUnanticipatedHostError );
2478 ENSURE_( snd_pcm_hw_params_get_rate_max( hwParams, &_max, &_dir ), paUnanticipatedHostError );
2479 PA_DEBUG(( "%s: SR min = %d, max = %d, req = %lu\n", __FUNCTION__, _min, _max, approx ));
2480 }
2481
2482 goto end;
2483 }
2484
2485 /* Return exact sample rate in param sampleRate */
2486 static int GetExactSampleRate( snd_pcm_hw_params_t *hwParams, double *sampleRate )
2487 {
2488 unsigned int num, den;
2489 int err;
2490
2491 assert( hwParams );
2492
2493 err = snd_pcm_hw_params_get_rate_numden( hwParams, &num, &den );
2494 *sampleRate = (double) num / den;
2495
2496 return err;
2497 }
2498
2499 /* Utility functions for blocking/callback interfaces */
2500
2501 /* Atomic restart of stream (we don't want the intermediate state visible) */
2502 static PaError AlsaRestart( PaAlsaStream *stream )
2503 {
2504 PaError result = paNoError;
2505
2506 PA_ENSURE( PaUnixMutex_Lock( &stream->stateMtx ) );
2507 PA_ENSURE( AlsaStop( stream, 0 ) );
2508 PA_ENSURE( AlsaStart( stream, 0 ) );
2509
2510 PA_DEBUG(( "%s: Restarted audio\n", __FUNCTION__ ));
2511
2512 error:
2513 PA_ENSURE( PaUnixMutex_Unlock( &stream->stateMtx ) );
2514
2515 return result;
2516 }
2517
2518 /** Recover from xrun state.
2519 *
2520 */
2521 static PaError PaAlsaStream_HandleXrun( PaAlsaStream *self )
2522 {
2523 PaError result = paNoError;
2524 snd_pcm_status_t *st;
2525 PaTime now = PaUtil_GetTime();
2526 snd_timestamp_t t;
2527 int restartAlsa = 0; /* do not restart Alsa by default */
2528
2529 snd_pcm_status_alloca( &st );
2530
2531 if( self->playback.pcm )
2532 {
2533 snd_pcm_status( self->playback.pcm, st );
2534 if( snd_pcm_status_get_state( st ) == SND_PCM_STATE_XRUN )
2535 {
2536 snd_pcm_status_get_trigger_tstamp( st, &t );
2537 self->underrun = now * 1000 - ((PaTime) t.tv_sec * 1000 + (PaTime) t.tv_usec / 1000);
2538
2539 if (!self->playback.canMmap)
2540 {
2541 if (snd_pcm_recover( self->playback.pcm, -EPIPE, 0 ) < 0)
2542 {
2543 PA_DEBUG(( "%s: [playback] non-MMAP-PCM failed recovering from XRUN, will restart Alsa\n", __FUNCTION__ ));
2544 ++ restartAlsa; /* did not manage to recover */
2545 }
2546 }
2547 else
2548 ++ restartAlsa; /* always restart MMAPed device */
2549 }
2550 }
2551 if( self->capture.pcm )
2552 {
2553 snd_pcm_status( self->capture.pcm, st );
2554 if( snd_pcm_status_get_state( st ) == SND_PCM_STATE_XRUN )
2555 {
2556 snd_pcm_status_get_trigger_tstamp( st, &t );
2557 self->overrun = now * 1000 - ((PaTime) t.tv_sec * 1000 + (PaTime) t.tv_usec / 1000);
2558
2559 if (!self->capture.canMmap)
2560 {
2561 if (snd_pcm_recover( self->capture.pcm, -EPIPE, 0 ) < 0)
2562 {
2563 PA_DEBUG(( "%s: [capture] non-MMAP-PCM failed recovering from XRUN, will restart Alsa\n", __FUNCTION__ ));
2564 ++ restartAlsa; /* did not manage to recover */
2565 }
2566 }
2567 else
2568 ++ restartAlsa; /* always restart MMAPed device */
2569 }
2570 }
2571
2572 if( restartAlsa )
2573 {
2574 PA_DEBUG(( "%s: restarting Alsa to recover from XRUN\n", __FUNCTION__ ));
2575 PA_ENSURE( AlsaRestart( self ) );
2576 }
2577
2578 end:
2579 return result;
2580 error:
2581 goto end;
2582 }
2583
2584 /** Decide if we should continue polling for specified direction, eventually adjust the poll timeout.
2585 *
2586 */
2587 static PaError ContinuePoll( const PaAlsaStream *stream, StreamDirection streamDir, int *pollTimeout, int *continuePoll )
2588 {
2589 PaError result = paNoError;
2590 snd_pcm_sframes_t delay, margin;
2591 int err;
2592 const PaAlsaStreamComponent *component = NULL, *otherComponent = NULL;
2593
2594 *continuePoll = 1;
2595
2596 if( StreamDirection_In == streamDir )
2597 {
2598 component = &stream->capture;
2599 otherComponent = &stream->playback;
2600 }
2601 else
2602 {
2603 component = &stream->playback;
2604 otherComponent = &stream->capture;
2605 }
2606
2607 /* ALSA docs say that negative delay should indicate xrun, but in my experience snd_pcm_delay returns -EPIPE */
2608 if( (err = snd_pcm_delay( otherComponent->pcm, &delay )) < 0 )
2609 {
2610 if( err == -EPIPE )
2611 {
2612 /* Xrun */
2613 *continuePoll = 0;
2614 goto error;
2615 }
2616
2617 ENSURE_( err, paUnanticipatedHostError );
2618 }
2619
2620 if( StreamDirection_Out == streamDir )
2621 {
2622 /* Number of eligible frames before capture overrun */
2623 delay = otherComponent->bufferSize - delay;
2624 }
2625 margin = delay - otherComponent->framesPerBuffer / 2;
2626
2627 if( margin < 0 )
2628 {
2629 PA_DEBUG(( "%s: Stopping poll for %s\n", __FUNCTION__, StreamDirection_In == streamDir ? "capture" : "playback" ));
2630 *continuePoll = 0;
2631 }
2632 else if( margin < otherComponent->framesPerBuffer )
2633 {
2634 *pollTimeout = CalculatePollTimeout( stream, margin );
2635 PA_DEBUG(( "%s: Trying to poll again for %s frames, pollTimeout: %d\n",
2636 __FUNCTION__, StreamDirection_In == streamDir ? "capture" : "playback", *pollTimeout ));
2637 }
2638
2639 error:
2640 return result;
2641 }
2642
2643 /* Callback interface */
2644
2645 static void OnExit( void *data )
2646 {
2647 PaAlsaStream *stream = (PaAlsaStream *) data;
2648
2649 assert( data );
2650
2651 PaUtil_ResetCpuLoadMeasurer( &stream->cpuLoadMeasurer );
2652
2653 stream->callback_finished = 1; /* Let the outside world know stream was stopped in callback */
2654 PA_DEBUG(( "%s: Stopping ALSA handles\n", __FUNCTION__ ));
2655 AlsaStop( stream, stream->callbackAbort );
2656
2657 PA_DEBUG(( "%s: Stoppage\n", __FUNCTION__ ));
2658
2659 /* Eventually notify user all buffers have played */
2660 if( stream->streamRepresentation.streamFinishedCallback )
2661 {
2662 stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
2663 }
2664 stream->isActive = 0;
2665 }
2666
2667 static void CalculateTimeInfo( PaAlsaStream *stream, PaStreamCallbackTimeInfo *timeInfo )
2668 {
2669 snd_pcm_status_t *capture_status, *playback_status;
2670 snd_timestamp_t capture_timestamp, playback_timestamp;
2671 PaTime capture_time = 0., playback_time = 0.;
2672
2673 snd_pcm_status_alloca( &capture_status );
2674 snd_pcm_status_alloca( &playback_status );
2675
2676 if( stream->capture.pcm )
2677 {
2678 snd_pcm_sframes_t capture_delay;
2679
2680 snd_pcm_status( stream->capture.pcm, capture_status );
2681 snd_pcm_status_get_tstamp( capture_status, &capture_timestamp );
2682
2683 capture_time = capture_timestamp.tv_sec +
2684 ((PaTime)capture_timestamp.tv_usec / 1000000.0);
2685 timeInfo->currentTime = capture_time;
2686
2687 capture_delay = snd_pcm_status_get_delay( capture_status );
2688 timeInfo->inputBufferAdcTime = timeInfo->currentTime -
2689 (PaTime)capture_delay / stream->streamRepresentation.streamInfo.sampleRate;
2690 }
2691 if( stream->playback.pcm )
2692 {
2693 snd_pcm_sframes_t playback_delay;
2694
2695 snd_pcm_status( stream->playback.pcm, playback_status );
2696 snd_pcm_status_get_tstamp( playback_status, &playback_timestamp );
2697
2698 playback_time = playback_timestamp.tv_sec +
2699 ((PaTime)playback_timestamp.tv_usec / 1000000.0);
2700
2701 if( stream->capture.pcm ) /* Full duplex */
2702 {
2703 /* Hmm, we have both a playback and a capture timestamp.
2704 * Hopefully they are the same... */
2705 if( fabs( capture_time - playback_time ) > 0.01 )
2706 PA_DEBUG(("Capture time and playback time differ by %f\n", fabs(capture_time-playback_time)));
2707 }
2708 else
2709 timeInfo->currentTime = playback_time;
2710
2711 playback_delay = snd_pcm_status_get_delay( playback_status );
2712 timeInfo->outputBufferDacTime = timeInfo->currentTime +
2713 (PaTime)playback_delay / stream->streamRepresentation.streamInfo.sampleRate;
2714 }
2715 }
2716
2717 /** Called after buffer processing is finished.
2718 *
2719 * A number of mmapped frames is committed, it is possible that an xrun has occurred in the meantime.
2720 *
2721 * @param numFrames The number of frames that has been processed
2722 * @param xrun Return whether an xrun has occurred
2723 */
2724 static PaError PaAlsaStreamComponent_EndProcessing( PaAlsaStreamComponent *self, unsigned long numFrames, int *xrun )
2725 {
2726 PaError result = paNoError;
2727 int res = 0;
2728
2729 /* @concern FullDuplex It is possible that only one direction is marked ready after polling, and processed
2730 * afterwards
2731 */
2732 if( !self->ready )
2733 goto end;
2734
2735 if( !self->canMmap && StreamDirection_Out == self->streamDir )
2736 {
2737 /* Play sound */
2738 if( self->hostInterleaved )
2739 res = snd_pcm_writei( self->pcm, self->nonMmapBuffer, numFrames );
2740 else
2741 {
2742 void *bufs[self->numHostChannels];
2743 int bufsize = snd_pcm_format_size( self->nativeFormat, self->framesPerBuffer + 1 );
2744 unsigned char *buffer = self->nonMmapBuffer;
2745 int i;
2746 for( i = 0; i < self->numHostChannels; ++i )
2747 {
2748 bufs[i] = buffer;
2749 buffer += bufsize;
2750 }
2751 res = snd_pcm_writen( self->pcm, bufs, numFrames );
2752 }
2753 }
2754
2755 if( self->canMmap )
2756 res = snd_pcm_mmap_commit( self->pcm, self->offset, numFrames );
2757 else
2758 {
2759 /* using realloc for optimisation
2760 free( self->nonMmapBuffer );
2761 self->nonMmapBuffer = NULL;
2762 */
2763 }
2764
2765 if( res == -EPIPE || res == -ESTRPIPE )
2766 {
2767 *xrun = 1;
2768 }
2769 else
2770 {
2771 ENSURE_( res, paUnanticipatedHostError );
2772 }
2773
2774 end:
2775 error:
2776 return result;
2777 }
2778
2779 /* Extract buffer from channel area */
2780 static unsigned char *ExtractAddress( const snd_pcm_channel_area_t *area, snd_pcm_uframes_t offset )
2781 {
2782 return (unsigned char *) area->addr + (area->first + offset * area->step) / 8;
2783 }
2784
2785 /** Do necessary adaption between user and host channels.
2786 *
2787 @concern ChannelAdaption Adapting between user and host channels can involve silencing unused channels and
2788 duplicating mono information if host outputs come in pairs.
2789 */
2790 static PaError PaAlsaStreamComponent_DoChannelAdaption( PaAlsaStreamComponent *self, PaUtilBufferProcessor *bp, int numFrames )
2791 {
2792 PaError result = paNoError;
2793 unsigned char *p;
2794 int i;
2795 int unusedChans = self->numHostChannels - self->numUserChannels;
2796 unsigned char *src, *dst;
2797 int convertMono = (self->numHostChannels % 2) == 0 && (self->numUserChannels % 2) != 0;
2798
2799 assert( StreamDirection_Out == self->streamDir );
2800
2801 if( self->hostInterleaved )
2802 {
2803 int swidth = snd_pcm_format_size( self->nativeFormat, 1 );
2804 unsigned char *buffer = self->canMmap ? ExtractAddress( self->channelAreas, self->offset ) : self->nonMmapBuffer;
2805
2806 /* Start after the last user channel */
2807 p = buffer + self->numUserChannels * swidth;
2808
2809 if( convertMono )
2810 {
2811 /* Convert the last user channel into stereo pair */
2812 src = buffer + (self->numUserChannels - 1) * swidth;
2813 for( i = 0; i < numFrames; ++i )
2814 {
2815 dst = src + swidth;
2816 memcpy( dst, src, swidth );
2817 src += self->numHostChannels * swidth;
2818 }
2819
2820 /* Don't touch the channel we just wrote to */
2821 p += swidth;
2822 --unusedChans;
2823 }
2824
2825 if( unusedChans > 0 )
2826 {
2827 /* Silence unused output channels */
2828 for( i = 0; i < numFrames; ++i )
2829 {
2830 memset( p, 0, swidth * unusedChans );
2831 p += self->numHostChannels * swidth;
2832 }
2833 }
2834 }
2835 else
2836 {
2837 /* We extract the last user channel */
2838 if( convertMono )
2839 {
2840 ENSURE_( snd_pcm_area_copy( self->channelAreas + self->numUserChannels, self->offset, self->channelAreas +
2841 (self->numUserChannels - 1), self->offset, numFrames, self->nativeFormat ), paUnanticipatedHostError );
2842 --unusedChans;
2843 }
2844 if( unusedChans > 0 )
2845 {
2846 snd_pcm_areas_silence( self->channelAreas + (self->numHostChannels - unusedChans), self->offset, unusedChans, numFrames,
2847 self->nativeFormat );
2848 }
2849 }
2850
2851 error:
2852 return result;
2853 }
2854
2855 static PaError PaAlsaStream_EndProcessing( PaAlsaStream *self, unsigned long numFrames, int *xrunOccurred )
2856 {
2857 PaError result = paNoError;
2858 int xrun = 0;
2859
2860 if( self->capture.pcm )
2861 {
2862 PA_ENSURE( PaAlsaStreamComponent_EndProcessing( &self->capture, numFrames, &xrun ) );
2863 }
2864 if( self->playback.pcm )
2865 {
2866 if( self->playback.numHostChannels > self->playback.numUserChannels )
2867 {
2868 PA_ENSURE( PaAlsaStreamComponent_DoChannelAdaption( &self->playback, &self->bufferProcessor, numFrames ) );
2869 }
2870 PA_ENSURE( PaAlsaStreamComponent_EndProcessing( &self->playback, numFrames, &xrun ) );
2871 }
2872
2873 error:
2874 *xrunOccurred = xrun;
2875 return result;
2876 }
2877
2878 /** Update the number of available frames.
2879 *
2880 */
2881 static PaError PaAlsaStreamComponent_GetAvailableFrames( PaAlsaStreamComponent *self, unsigned long *numFrames, int *xrunOccurred )
2882 {
2883 PaError result = paNoError;
2884 snd_pcm_sframes_t framesAvail = snd_pcm_avail_update( self->pcm );
2885 *xrunOccurred = 0;
2886
2887 if( -EPIPE == framesAvail )
2888 {
2889 *xrunOccurred = 1;
2890 framesAvail = 0;
2891 }
2892 else
2893 {
2894 ENSURE_( framesAvail, paUnanticipatedHostError );
2895 }
2896
2897 *numFrames = framesAvail;
2898
2899 error:
2900 return result;
2901 }
2902
2903 /** Fill in pollfd objects.
2904 */
2905 static PaError PaAlsaStreamComponent_BeginPolling( PaAlsaStreamComponent* self, struct pollfd* pfds )
2906 {
2907 PaError result = paNoError;
2908 int ret = snd_pcm_poll_descriptors( self->pcm, pfds, self->nfds );
2909 (void)ret; /* Prevent unused variable warning if asserts are turned off */
2910 assert( ret == self->nfds );
2911
2912 self->ready = 0;
2913
2914 return result;
2915 }
2916
2917 /** Examine results from poll().
2918 *
2919 * @param pfds pollfds to inspect
2920 * @param shouldPoll Should we continue to poll
2921 * @param xrun Has an xrun occurred
2922 */
2923 static PaError PaAlsaStreamComponent_EndPolling( PaAlsaStreamComponent* self, struct pollfd* pfds, int* shouldPoll, int* xrun )
2924 {
2925 PaError result = paNoError;
2926 unsigned short revents;
2927
2928 ENSURE_( snd_pcm_poll_descriptors_revents( self->pcm, pfds, self->nfds, &revents ), paUnanticipatedHostError );
2929 if( revents != 0 )
2930 {
2931 if( revents & POLLERR )
2932 {
2933 *xrun = 1;
2934 }
2935 else
2936 if( revents & POLLHUP )
2937 {
2938 *xrun = 1;
2939 PA_DEBUG(( "%s: revents has POLLHUP, processing as XRUN\n", __FUNCTION__ ));
2940 }
2941 else
2942 self->ready = 1;
2943
2944 *shouldPoll = 0;
2945 }
2946
2947 error:
2948 return result;
2949 }
2950
2951 /** Return the number of available frames for this stream.
2952 *
2953 * @concern FullDuplex The minimum available for the two directions is calculated, it might be desirable to ignore
2954 * one direction however (not marked ready from poll), so this is controlled by queryCapture and queryPlayback.
2955 *
2956 * @param queryCapture Check available for capture
2957 * @param queryPlayback Check available for playback
2958 * @param available The returned number of frames
2959 * @param xrunOccurred Return whether an xrun has occurred
2960 */
2961 static PaError PaAlsaStream_GetAvailableFrames( PaAlsaStream *self, int queryCapture, int queryPlayback, unsigned long
2962 *available, int *xrunOccurred )
2963 {
2964 PaError result = paNoError;
2965 unsigned long captureFrames, playbackFrames;
2966 *xrunOccurred = 0;
2967
2968 assert( queryCapture || queryPlayback );
2969
2970 if( queryCapture )
2971 {
2972 assert( self->capture.pcm );
2973 PA_ENSURE( PaAlsaStreamComponent_GetAvailableFrames( &self->capture, &captureFrames, xrunOccurred ) );
2974 if( *xrunOccurred )
2975 {
2976 goto end;
2977 }
2978 }
2979 if( queryPlayback )
2980 {
2981 assert( self->playback.pcm );
2982 PA_ENSURE( PaAlsaStreamComponent_GetAvailableFrames( &self->playback, &playbackFrames, xrunOccurred ) );
2983 if( *xrunOccurred )
2984 {
2985 goto end;
2986 }
2987 }
2988
2989 if( queryCapture && queryPlayback )
2990 {
2991 *available = PA_MIN( captureFrames, playbackFrames );
2992 /*PA_DEBUG(("capture: %lu, playback: %lu, combined: %lu\n", captureFrames, playbackFrames, *available));*/
2993 }
2994 else if( queryCapture )
2995 {
2996 *available = captureFrames;
2997 }
2998 else
2999 {
3000 *available = playbackFrames;
3001 }
3002
3003 end:
3004 error:
3005 return result;
3006 }
3007
3008 /** Wait for and report available buffer space from ALSA.
3009 *
3010 * Unless ALSA reports a minimum of frames available for I/O, we poll the ALSA filedescriptors for more.
3011 * Both of these operations can uncover xrun conditions.
3012 *
3013 * @concern Xruns Both polling and querying available frames can report an xrun condition.
3014 *
3015 * @param framesAvail Return the number of available frames
3016 * @param xrunOccurred Return whether an xrun has occurred
3017 */
3018 static PaError PaAlsaStream_WaitForFrames( PaAlsaStream *self, unsigned long *framesAvail, int *xrunOccurred )
3019 {
3020 PaError result = paNoError;
3021 int pollPlayback = self->playback.pcm != NULL, pollCapture = self->capture.pcm != NULL;
3022 int pollTimeout = self->pollTimeout;
3023 int xrun = 0, timeouts = 0;
3024 int pollResults;
3025
3026 assert( self );
3027 assert( framesAvail );
3028
3029 if( !self->callbackMode )
3030 {
3031 /* In blocking mode we will only wait if necessary */
3032 PA_ENSURE( PaAlsaStream_GetAvailableFrames( self, self->capture.pcm != NULL, self->playback.pcm != NULL,
3033 framesAvail, &xrun ) );
3034 if( xrun )
3035 {
3036 goto end;
3037 }
3038
3039 if( *framesAvail > 0 )
3040 {
3041 /* Mark pcms ready from poll */
3042 if( self->capture.pcm )
3043 self->capture.ready = 1;
3044 if( self->playback.pcm )
3045 self->playback.ready = 1;
3046
3047 goto end;
3048 }
3049 }
3050
3051 while( pollPlayback || pollCapture )
3052 {
3053 int totalFds = 0;
3054 struct pollfd *capturePfds = NULL, *playbackPfds = NULL;
3055
3056 pthread_testcancel();
3057
3058 if( pollCapture )
3059 {
3060 capturePfds = self->pfds;
3061 PA_ENSURE( PaAlsaStreamComponent_BeginPolling( &self->capture, capturePfds ) );
3062 totalFds += self->capture.nfds;
3063 }
3064 if( pollPlayback )
3065 {
3066 playbackPfds = self->pfds + (self->capture.pcm ? self->capture.nfds : 0);
3067 PA_ENSURE( PaAlsaStreamComponent_BeginPolling( &self->playback, playbackPfds ) );
3068 totalFds += self->playback.nfds;
3069 }
3070
3071 pollResults = poll( self->pfds, totalFds, pollTimeout );
3072
3073 if( pollResults < 0 )
3074 {
3075 /* XXX: Depend on preprocessor condition? */
3076 if( errno == EINTR )
3077 {
3078 /* gdb */
3079 Pa_Sleep( 1 ); /* avoid hot loop */
3080 continue;
3081 }
3082
3083 /* TODO: Add macro for checking system calls */
3084 PA_ENSURE( paInternalError );
3085 }
3086 else
3087 if (pollResults == 0)
3088 {
3089
3090 /* Suspended, paused or failed device can provide 0 poll results. To avoid deadloop in such situation
3091 * we simply run counter 'timeouts' which detects 0 poll result and accumulates. As soon as 64 timouts
3092 * are achieved we simply fail function with paTimedOut to notify waiting methods that device is not capable
3093 * of providing audio data anymore and needs some corresponding recovery action.
3094 * Note that 'timeouts' is reset to 0 if poll() managed to return non 0 results.
3095 */
3096
3097 /*PA_DEBUG(( "%s: poll == 0 results, timed out, %d times left\n", __FUNCTION__, 64 - timeouts ));*/
3098
3099 ++ timeouts;
3100 if (timeouts > 1) /* sometimes device times out, but normally once, so we do not sleep any time */
3101 {
3102 Pa_Sleep( 1 ); /* avoid hot loop */
3103 }
3104 /* not else ! */
3105 if (timeouts >= 64) /* audio device not working, shall return error to notify waiters */
3106 {
3107 *framesAvail = 0; /* no frames available for processing */
3108
3109 PA_DEBUG(( "%s: poll timed out, returning error\n", __FUNCTION__, timeouts ));
3110 PA_ENSURE( paTimedOut );
3111 }
3112 }
3113 else
3114 if (pollResults > 0)
3115 {
3116 /* reset timouts counter */
3117 timeouts = 0;
3118
3119 /* check the return status of our pfds */
3120 if( pollCapture )
3121 {
3122 PA_ENSURE( PaAlsaStreamComponent_EndPolling( &self->capture, capturePfds, &pollCapture, &xrun ) );
3123 }
3124 if( pollPlayback )
3125 {
3126 PA_ENSURE( PaAlsaStreamComponent_EndPolling( &self->playback, playbackPfds, &pollPlayback, &xrun ) );
3127 }
3128 if( xrun )
3129 {
3130 break;
3131 }
3132 }
3133
3134 /* @concern FullDuplex If only one of two pcms is ready we may want to compromise between the two.
3135 * If there is less than half a period's worth of samples left of frames in the other pcm's buffer we will
3136 * stop polling.
3137 */
3138 if( self->capture.pcm && self->playback.pcm )
3139 {
3140 if( pollCapture && !pollPlayback )
3141 {
3142 PA_ENSURE( ContinuePoll( self, StreamDirection_In, &pollTimeout, &pollCapture ) );
3143 }
3144 else if( pollPlayback && !pollCapture )
3145 {
3146 PA_ENSURE( ContinuePoll( self, StreamDirection_Out, &pollTimeout, &pollPlayback ) );
3147 }
3148 }
3149 }
3150
3151 if( !xrun )
3152 {
3153 /* Get the number of available frames for the pcms that are marked ready.
3154 * @concern FullDuplex If only one direction is marked ready (from poll), the number of frames available for
3155 * the other direction is returned. Output is normally preferred over capture however, so capture frames may be
3156 * discarded to avoid overrun unless paNeverDropInput is specified.
3157 */
3158 int captureReady = self->capture.pcm ? self->capture.ready : 0,
3159 playbackReady = self->playback.pcm ? self->playback.ready : 0;
3160 PA_ENSURE( PaAlsaStream_GetAvailableFrames( self, captureReady, playbackReady, framesAvail, &xrun ) );
3161
3162 if( self->capture.pcm && self->playback.pcm )
3163 {
3164 if( !self->playback.ready && !self->neverDropInput )
3165 {
3166 /* Drop input, a period's worth */
3167 assert( self->capture.ready );
3168 PaAlsaStreamComponent_EndProcessing( &self->capture, PA_MIN( self->capture.framesPerBuffer,
3169 *framesAvail ), &xrun );
3170 *framesAvail = 0;
3171 self->capture.ready = 0;
3172 }
3173 }
3174 else if( self->capture.pcm )
3175 assert( self->capture.ready );
3176 else
3177 assert( self->playback.ready );
3178 }
3179
3180 end:
3181 error:
3182 if( xrun )
3183 {
3184 /* Recover from the xrun state */
3185 PA_ENSURE( PaAlsaStream_HandleXrun( self ) );
3186 *framesAvail = 0;
3187 }
3188 else
3189 {
3190 if( 0 != *framesAvail )
3191 {
3192 /* If we're reporting frames eligible for processing, one of the handles better be ready */
3193 PA_UNLESS( self->capture.ready || self->playback.ready, paInternalError );
3194 }
3195 }
3196 *xrunOccurred = xrun;
3197
3198 return result;
3199 }
3200
3201 /** Register per-channel ALSA buffer information with buffer processor.
3202 *
3203 * Mmapped buffer space is acquired from ALSA, and registered with the buffer processor. Differences between the
3204 * number of host and user channels is taken into account.
3205 *
3206 * @param numFrames On entrance the number of requested frames, on exit the number of contiguously accessible frames.
3207 */
3208 static PaError PaAlsaStreamComponent_RegisterChannels( PaAlsaStreamComponent* self, PaUtilBufferProcessor* bp,
3209 unsigned long* numFrames, int* xrun )
3210 {
3211 PaError result = paNoError;
3212 const snd_pcm_channel_area_t *areas, *area;
3213 void (*setChannel)(PaUtilBufferProcessor *, unsigned int, void *, unsigned int) =
3214 StreamDirection_In == self->streamDir ? PaUtil_SetInputChannel : PaUtil_SetOutputChannel;
3215 unsigned char *buffer, *p;
3216 int i;
3217 unsigned long framesAvail;
3218
3219 /* This _must_ be called before mmap_begin */
3220 PA_ENSURE( PaAlsaStreamComponent_GetAvailableFrames( self, &framesAvail, xrun ) );
3221 if( *xrun )
3222 {
3223 *numFrames = 0;
3224 goto end;
3225 }
3226
3227 if( self->canMmap )
3228 {
3229 ENSURE_( snd_pcm_mmap_begin( self->pcm, &areas, &self->offset, numFrames ), paUnanticipatedHostError );
3230 /* @concern ChannelAdaption Buffer address is recorded so we can do some channel adaption later */
3231 self->channelAreas = (snd_pcm_channel_area_t *)areas;
3232 }
3233 else
3234 {
3235 unsigned int bufferSize = self->numHostChannels * snd_pcm_format_size( self->nativeFormat, *numFrames );
3236 if (bufferSize > self->nonMmapBufferSize)
3237 {
3238 self->nonMmapBuffer = realloc(self->nonMmapBuffer, (self->nonMmapBufferSize = bufferSize));
3239 if (!self->nonMmapBuffer)
3240 {
3241 result = paInsufficientMemory;
3242 goto error;
3243 }
3244 }
3245 }
3246
3247 if( self->hostInterleaved )
3248 {
3249 int swidth = snd_pcm_format_size( self->nativeFormat, 1 );
3250
3251 p = buffer = self->canMmap ? ExtractAddress( areas, self->offset ) : self->nonMmapBuffer;
3252 for( i = 0; i < self->numUserChannels; ++i )
3253 {
3254 /* We're setting the channels up to userChannels, but the stride will be hostChannels samples */
3255 setChannel( bp, i, p, self->numHostChannels );
3256 p += swidth;
3257 }
3258 }
3259 else
3260 {
3261 if( self->canMmap )
3262 {
3263 for( i = 0; i < self->numUserChannels; ++i )
3264 {
3265 area = areas + i;
3266 buffer = ExtractAddress( area, self->offset );
3267 setChannel( bp, i, buffer, 1 );
3268 }
3269 }
3270 else
3271 {
3272 unsigned int buf_per_ch_size = self->nonMmapBufferSize / self->numHostChannels;
3273 buffer = self->nonMmapBuffer;
3274 for( i = 0; i < self->numUserChannels; ++i )
3275 {
3276 setChannel( bp, i, buffer, 1 );
3277 buffer += buf_per_ch_size;
3278 }
3279 }
3280 }
3281
3282 if( !self->canMmap && StreamDirection_In == self->streamDir )
3283 {
3284 /* Read sound */
3285 int res;
3286 if( self->hostInterleaved )
3287 res = snd_pcm_readi( self->pcm, self->nonMmapBuffer, *numFrames );
3288 else
3289 {
3290 void *bufs[self->numHostChannels];
3291 unsigned int buf_per_ch_size = self->nonMmapBufferSize / self->numHostChannels;
3292 unsigned char *buffer = self->nonMmapBuffer;
3293 int i;
3294 for( i = 0; i < self->numHostChannels; ++i )
3295 {
3296 bufs[i] = buffer;
3297 buffer += buf_per_ch_size;
3298 }
3299 res = snd_pcm_readn( self->pcm, bufs, *numFrames );
3300 }
3301 if( res == -EPIPE || res == -ESTRPIPE )
3302 {
3303 *xrun = 1;
3304 *numFrames = 0;
3305 }
3306 }
3307
3308 end:
3309 error:
3310 return result;
3311 }
3312
3313 /** Initiate buffer processing.
3314 *
3315 * ALSA buffers are registered with the PA buffer processor and the buffer size (in frames) set.
3316 *
3317 * @concern FullDuplex If both directions are being processed, the minimum amount of frames for the two directions is
3318 * calculated.
3319 *
3320 * @param numFrames On entrance the number of available frames, on exit the number of received frames
3321 * @param xrunOccurred Return whether an xrun has occurred
3322 */
3323 static PaError PaAlsaStream_SetUpBuffers( PaAlsaStream* self, unsigned long* numFrames, int* xrunOccurred )
3324 {
3325 PaError result = paNoError;
3326 unsigned long captureFrames = ULONG_MAX, playbackFrames = ULONG_MAX, commonFrames = 0;
3327 int xrun = 0;
3328
3329 if( *xrunOccurred )
3330 {
3331 *numFrames = 0;
3332 return result;
3333 }
3334 /* If we got here at least one of the pcm's should be marked ready */
3335 PA_UNLESS( self->capture.ready || self->playback.ready, paInternalError );
3336
3337 /* Extract per-channel ALSA buffer pointers and register them with the buffer processor.
3338 * It is possible that a direction is not marked ready however, because it is out of sync with the other.
3339 */
3340 if( self->capture.pcm && self->capture.ready )
3341 {
3342 captureFrames = *numFrames;
3343 PA_ENSURE( PaAlsaStreamComponent_RegisterChannels( &self->capture, &self->bufferProcessor, &captureFrames,
3344 &xrun ) );
3345 }
3346 if( self->playback.pcm && self->playback.ready )
3347 {
3348 playbackFrames = *numFrames;
3349 PA_ENSURE( PaAlsaStreamComponent_RegisterChannels( &self->playback, &self->bufferProcessor, &playbackFrames,
3350 &xrun ) );
3351 }
3352 if( xrun )
3353 {
3354 /* Nothing more to do */
3355 assert( 0 == commonFrames );
3356 goto end;
3357 }
3358
3359 commonFrames = PA_MIN( captureFrames, playbackFrames );
3360 /* assert( commonFrames <= *numFrames ); */
3361 if( commonFrames > *numFrames )
3362 {
3363 /* Hmmm ... how come there are more frames available than we requested!? Blah. */
3364 PA_DEBUG(( "%s: Common available frames are reported to be more than number requested: %lu, %lu, callbackMode: %d\n", __FUNCTION__,
3365 commonFrames, *numFrames, self->callbackMode ));
3366 if( self->capture.pcm )
3367 {
3368 PA_DEBUG(( "%s: captureFrames: %lu, capture.ready: %d\n", __FUNCTION__, captureFrames, self->capture.ready ));
3369 }
3370 if( self->playback.pcm )
3371 {
3372 PA_DEBUG(( "%s: playbackFrames: %lu, playback.ready: %d\n", __FUNCTION__, playbackFrames, self->playback.ready ));
3373 }
3374
3375 commonFrames = 0;
3376 goto end;
3377 }
3378
3379 /* Inform PortAudio of the number of frames we got.
3380 * @concern FullDuplex We might be experiencing underflow in either end; if its an input underflow, we go on
3381 * with output. If its output underflow however, depending on the paNeverDropInput flag, we may want to simply
3382 * discard the excess input or call the callback with paOutputOverflow flagged.
3383 */
3384 if( self->capture.pcm )
3385 {
3386 if( self->capture.ready )
3387 {
3388 PaUtil_SetInputFrameCount( &self->bufferProcessor, commonFrames );
3389 }
3390 else
3391 {
3392 /* We have input underflow */
3393 PaUtil_SetNoInput( &self->bufferProcessor );
3394 }
3395 }
3396 if( self->playback.pcm )
3397 {
3398 if( self->playback.ready )
3399 {
3400 PaUtil_SetOutputFrameCount( &self->bufferProcessor, commonFrames );
3401 }
3402 else
3403 {
3404 /* We have output underflow, but keeping input data (paNeverDropInput) */
3405 assert( self->neverDropInput );
3406 assert( self->capture.pcm != NULL );
3407 PA_DEBUG(( "%s: Setting output buffers to NULL\n", __FUNCTION__ ));
3408 PaUtil_SetNoOutput( &self->bufferProcessor );
3409 }
3410 }
3411
3412 end:
3413 *numFrames = commonFrames;
3414 error:
3415 if( xrun )
3416 {
3417 PA_ENSURE( PaAlsaStream_HandleXrun( self ) );
3418 *numFrames = 0;
3419 }
3420 *xrunOccurred = xrun;
3421
3422 return result;
3423 }
3424
3425 /** Callback thread's function.
3426 *
3427 * Roughly, the workflow can be described in the following way: The number of available frames that can be processed
3428 * directly is obtained from ALSA, we then request as much directly accessible memory as possible within this amount
3429 * from ALSA. The buffer memory is registered with the PA buffer processor and processing is carried out with
3430 * PaUtil_EndBufferProcessing. Finally, the number of processed frames is reported to ALSA. The processing can
3431 * happen in several iterations untill we have consumed the known number of available frames (or an xrun is detected).
3432 */
3433 static void *CallbackThreadFunc( void *userData )
3434 {
3435 PaError result = paNoError;
3436 PaAlsaStream *stream = (PaAlsaStream*) userData;
3437 PaStreamCallbackTimeInfo timeInfo = {0, 0, 0};
3438 snd_pcm_sframes_t startThreshold = 0;
3439 int callbackResult = paContinue;
3440 PaStreamCallbackFlags cbFlags = 0; /* We might want to keep state across iterations */
3441 int streamStarted = 0;
3442
3443 assert( stream );
3444
3445 /* Execute OnExit when exiting */
3446 pthread_cleanup_push( &OnExit, stream );
3447
3448 /* Not implemented */
3449 assert( !stream->primeBuffers );
3450
3451 /* @concern StreamStart If the output is being primed the output pcm needs to be prepared, otherwise the
3452 * stream is started immediately. The latter involves signaling the waiting main thread.
3453 */
3454 if( stream->primeBuffers )
3455 {
3456 snd_pcm_sframes_t avail;
3457
3458 if( stream->playback.pcm )
3459 ENSURE_( snd_pcm_prepare( stream->playback.pcm ), paUnanticipatedHostError );
3460 if( stream->capture.pcm && !stream->pcmsSynced )
3461 ENSURE_( snd_pcm_prepare( stream->capture.pcm ), paUnanticipatedHostError );
3462
3463 /* We can't be certain that the whole ring buffer is available for priming, but there should be
3464 * at least one period */
3465 avail = snd_pcm_avail_update( stream->playback.pcm );
3466 startThreshold = avail - (avail % stream->playback.framesPerBuffer);
3467 assert( startThreshold >= stream->playback.framesPerBuffer );
3468 }
3469 else
3470 {
3471 PA_ENSURE( PaUnixThread_PrepareNotify( &stream->thread ) );
3472 /* Buffer will be zeroed */
3473 PA_ENSURE( AlsaStart( stream, 0 ) );
3474 PA_ENSURE( PaUnixThread_NotifyParent( &stream->thread ) );
3475
3476 streamStarted = 1;
3477 }
3478
3479 while( 1 )
3480 {
3481 unsigned long framesAvail, framesGot;
3482 int xrun = 0;
3483
3484 pthread_testcancel();
3485
3486 /* @concern StreamStop if the main thread has requested a stop and the stream has not been effectively
3487 * stopped we signal this condition by modifying callbackResult (we'll want to flush buffered output).
3488 */
3489 if( PaUnixThread_StopRequested( &stream->thread ) && paContinue == callbackResult )
3490 {
3491 PA_DEBUG(( "Setting callbackResult to paComplete\n" ));
3492 callbackResult = paComplete;
3493 }
3494
3495 if( paContinue != callbackResult )
3496 {
3497 stream->callbackAbort = (paAbort == callbackResult);
3498 if( stream->callbackAbort ||
3499 /** @concern BlockAdaption: Go on if adaption buffers are empty */
3500 PaUtil_IsBufferProcessorOutputEmpty( &stream->bufferProcessor ) )
3501 {
3502 goto end;
3503 }
3504
3505 PA_DEBUG(( "%s: Flushing buffer processor\n", __FUNCTION__ ));
3506 /* There is still buffered output that needs to be processed */
3507 }
3508
3509 /* Wait for data to become available, this comes down to polling the ALSA file descriptors untill we have
3510 * a number of available frames.
3511 */
3512 PA_ENSURE( PaAlsaStream_WaitForFrames( stream, &framesAvail, &xrun ) );
3513 if( xrun )
3514 {
3515 assert( 0 == framesAvail );
3516 continue;
3517
3518 /* XXX: Report xruns to the user? A situation is conceivable where the callback is never invoked due
3519 * to constant xruns, it might be desirable to notify the user of this.
3520 */
3521 }
3522
3523 /* Consume buffer space. Once we have a number of frames available for consumption we must retrieve the
3524 * mmapped buffers from ALSA, this is contiguously accessible memory however, so we may receive smaller
3525 * portions at a time than is available as a whole. Therefore we should be prepared to process several
3526 * chunks successively. The buffers are passed to the PA buffer processor.
3527 */
3528 while( framesAvail > 0 )
3529 {
3530 xrun = 0;
3531
3532 pthread_testcancel();
3533
3534 /** @concern Xruns Under/overflows are to be reported to the callback */
3535 if( stream->underrun > 0.0 )
3536 {
3537 cbFlags |= paOutputUnderflow;
3538 stream->underrun = 0.0;
3539 }
3540 if( stream->overrun > 0.0 )
3541 {
3542 cbFlags |= paInputOverflow;
3543 stream->overrun = 0.0;
3544 }
3545 if( stream->capture.pcm && stream->playback.pcm )
3546 {
3547 /** @concern FullDuplex It's possible that only one direction is being processed to avoid an
3548 * under- or overflow, this should be reported correspondingly */
3549 if( !stream->capture.ready )
3550 {
3551 cbFlags |= paInputUnderflow;
3552 PA_DEBUG(( "%s: Input underflow\n", __FUNCTION__ ));
3553 }
3554 else if( !stream->playback.ready )
3555 {
3556 cbFlags |= paOutputOverflow;
3557 PA_DEBUG(( "%s: Output overflow\n", __FUNCTION__ ));
3558 }
3559 }
3560
3561 #if 0
3562 CallbackUpdate( &stream->threading );
3563 #endif
3564 CalculateTimeInfo( stream, &timeInfo );
3565 PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo, cbFlags );
3566 cbFlags = 0;
3567
3568 /* CPU load measurement should include processing activivity external to the stream callback */
3569 PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer );
3570
3571 framesGot = framesAvail;
3572 if( paUtilFixedHostBufferSize == stream->bufferProcessor.hostBufferSizeMode )
3573 {
3574 /* We've committed to a fixed host buffer size, stick to that */
3575 framesGot = framesGot >= stream->maxFramesPerHostBuffer ? stream->maxFramesPerHostBuffer : 0;
3576 }
3577 else
3578 {
3579 /* We've committed to an upper bound on the size of host buffers */
3580 assert( paUtilBoundedHostBufferSize == stream->bufferProcessor.hostBufferSizeMode );
3581 framesGot = PA_MIN( framesGot, stream->maxFramesPerHostBuffer );
3582 }
3583 PA_ENSURE( PaAlsaStream_SetUpBuffers( stream, &framesGot, &xrun ) );
3584 /* Check the host buffer size against the buffer processor configuration */
3585 framesAvail -= framesGot;
3586
3587 if( framesGot > 0 )
3588 {
3589 assert( !xrun );
3590 PaUtil_EndBufferProcessing( &stream->bufferProcessor, &callbackResult );
3591 PA_ENSURE( PaAlsaStream_EndProcessing( stream, framesGot, &xrun ) );
3592 }
3593 PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesGot );
3594
3595 if( 0 == framesGot )
3596 {
3597 /* Go back to polling for more frames */
3598 break;
3599
3600 }
3601
3602 if( paContinue != callbackResult )
3603 break;
3604 }
3605 }
3606
3607 /* Match pthread_cleanup_push */
3608 pthread_cleanup_pop( 1 );
3609
3610 end:
3611 PA_DEBUG(( "%s: Thread %d exiting\n ", __FUNCTION__, pthread_self() ));
3612 PaUnixThreading_EXIT( result );
3613 error:
3614 goto end;
3615 }
3616
3617 /* Blocking interface */
3618
3619 static PaError ReadStream( PaStream* s, void *buffer, unsigned long frames )
3620 {
3621 PaError result = paNoError;
3622 PaAlsaStream *stream = (PaAlsaStream*)s;
3623 unsigned long framesGot, framesAvail;
3624 void *userBuffer;
3625 snd_pcm_t *save = stream->playback.pcm;
3626
3627 assert( stream );
3628
3629 PA_UNLESS( stream->capture.pcm, paCanNotReadFromAnOutputOnlyStream );
3630
3631 /* Disregard playback */
3632 stream->playback.pcm = NULL;
3633
3634 if( stream->overrun > 0. )
3635 {
3636 result = paInputOverflowed;
3637 stream->overrun = 0.0;
3638 }
3639
3640 if( stream->capture.userInterleaved )
3641 {
3642 userBuffer = buffer;
3643 }
3644 else
3645 {
3646 /* Copy channels into local array */
3647 userBuffer = stream->capture.userBuffers;
3648 memcpy( userBuffer, buffer, sizeof (void *) * stream->capture.numUserChannels );
3649 }
3650
3651 /* Start stream if in prepared state */
3652 if( snd_pcm_state( stream->capture.pcm ) == SND_PCM_STATE_PREPARED )
3653 {
3654 ENSURE_( snd_pcm_start( stream->capture.pcm ), paUnanticipatedHostError );
3655 }
3656
3657 while( frames > 0 )
3658 {
3659 int xrun = 0;
3660 PA_ENSURE( PaAlsaStream_WaitForFrames( stream, &framesAvail, &xrun ) );
3661 framesGot = PA_MIN( framesAvail, frames );
3662
3663 PA_ENSURE( PaAlsaStream_SetUpBuffers( stream, &framesGot, &xrun ) );
3664 if( framesGot > 0 )
3665 {
3666 framesGot = PaUtil_CopyInput( &stream->bufferProcessor, &userBuffer, framesGot );
3667 PA_ENSURE( PaAlsaStream_EndProcessing( stream, framesGot, &xrun ) );
3668 frames -= framesGot;
3669 }
3670 }
3671
3672 end:
3673 stream->playback.pcm = save;
3674 return result;
3675 error:
3676 goto end;
3677 }
3678
3679 static PaError WriteStream( PaStream* s, const void *buffer, unsigned long frames )
3680 {
3681 PaError result = paNoError;
3682 signed long err;
3683 PaAlsaStream *stream = (PaAlsaStream*)s;
3684 snd_pcm_uframes_t framesGot, framesAvail;
3685 const void *userBuffer;
3686 snd_pcm_t *save = stream->capture.pcm;
3687
3688 assert( stream );
3689
3690 PA_UNLESS( stream->playback.pcm, paCanNotWriteToAnInputOnlyStream );
3691
3692 /* Disregard capture */
3693 stream->capture.pcm = NULL;
3694
3695 if( stream->underrun > 0. )
3696 {
3697 result = paOutputUnderflowed;
3698 stream->underrun = 0.0;
3699 }
3700
3701 if( stream->playback.userInterleaved )
3702 userBuffer = buffer;
3703 else /* Copy channels into local array */
3704 {
3705 userBuffer = stream->playback.userBuffers;
3706 memcpy( (void *)userBuffer, buffer, sizeof (void *) * stream->playback.numUserChannels );
3707 }
3708
3709 while( frames > 0 )
3710 {
3711 int xrun = 0;
3712 snd_pcm_uframes_t hwAvail;
3713
3714 PA_ENSURE( PaAlsaStream_WaitForFrames( stream, &framesAvail, &xrun ) );
3715 framesGot = PA_MIN( framesAvail, frames );
3716
3717 PA_ENSURE( PaAlsaStream_SetUpBuffers( stream, &framesGot, &xrun ) );
3718 if( framesGot > 0 )
3719 {
3720 framesGot = PaUtil_CopyOutput( &stream->bufferProcessor, &userBuffer, framesGot );
3721 PA_ENSURE( PaAlsaStream_EndProcessing( stream, framesGot, &xrun ) );
3722 frames -= framesGot;
3723 }
3724
3725 /* Start stream after one period of samples worth */
3726
3727 /* Frames residing in buffer */
3728 PA_ENSURE( err = GetStreamWriteAvailable( stream ) );
3729 framesAvail = err;
3730 hwAvail = stream->playback.bufferSize - framesAvail;
3731
3732 if( snd_pcm_state( stream->playback.pcm ) == SND_PCM_STATE_PREPARED &&
3733 hwAvail >= stream->playback.framesPerBuffer )
3734 {
3735 ENSURE_( snd_pcm_start( stream->playback.pcm ), paUnanticipatedHostError );
3736 }
3737 }
3738
3739 end:
3740 stream->capture.pcm = save;
3741 return result;
3742 error:
3743 goto end;
3744 }
3745
3746 /* Return frames available for reading. In the event of an overflow, the capture pcm will be restarted */
3747 static signed long GetStreamReadAvailable( PaStream* s )
3748 {
3749 PaError result = paNoError;
3750 PaAlsaStream *stream = (PaAlsaStream*)s;
3751 unsigned long avail;
3752 int xrun;
3753
3754 PA_ENSURE( PaAlsaStreamComponent_GetAvailableFrames( &stream->capture, &avail, &xrun ) );
3755 if( xrun )
3756 {
3757 PA_ENSURE( PaAlsaStream_HandleXrun( stream ) );
3758 PA_ENSURE( PaAlsaStreamComponent_GetAvailableFrames( &stream->capture, &avail, &xrun ) );
3759 if( xrun )
3760 PA_ENSURE( paInputOverflowed );
3761 }
3762
3763 return (signed long)avail;
3764
3765 error:
3766 return result;
3767 }
3768
3769 static signed long GetStreamWriteAvailable( PaStream* s )
3770 {
3771 PaError result = paNoError;
3772 PaAlsaStream *stream = (PaAlsaStream*)s;
3773 unsigned long avail;
3774 int xrun;
3775
3776 PA_ENSURE( PaAlsaStreamComponent_GetAvailableFrames( &stream->playback, &avail, &xrun ) );
3777 if( xrun )
3778 {
3779 snd_pcm_sframes_t savail;
3780
3781 PA_ENSURE( PaAlsaStream_HandleXrun( stream ) );
3782 savail = snd_pcm_avail_update( stream->playback.pcm );
3783
3784 /* savail should not contain -EPIPE now, since PaAlsaStream_HandleXrun will only prepare the pcm */
3785 ENSURE_( savail, paUnanticipatedHostError );
3786
3787 avail = (unsigned long) savail;
3788 }
3789
3790 return (signed long)avail;
3791
3792 error:
3793 return result;
3794 }
3795
3796 /* Extensions */
3797
3798 void PaAlsa_InitializeStreamInfo( PaAlsaStreamInfo *info )
3799 {
3800 info->size = sizeof (PaAlsaStreamInfo);
3801 info->hostApiType = paALSA;
3802 info->version = 1;
3803 info->deviceString = NULL;
3804 }
3805
3806 void PaAlsa_EnableRealtimeScheduling( PaStream *s, int enable )
3807 {
3808 PaAlsaStream *stream = (PaAlsaStream *) s;
3809 stream->rtSched = enable;
3810 }
3811
3812 #if 0
3813 void PaAlsa_EnableWatchdog( PaStream *s, int enable )
3814 {
3815 PaAlsaStream *stream = (PaAlsaStream *) s;
3816 stream->thread.useWatchdog = enable;
3817 }
3818 #endif
3819
3820 static PaError GetAlsaStreamPointer( PaStream* s, PaAlsaStream** stream )
3821 {
3822 PaError result = paNoError;
3823 PaUtilHostApiRepresentation* hostApi;
3824 PaAlsaHostApiRepresentation* alsaHostApi;
3825
3826 PA_ENSURE( PaUtil_ValidateStreamPointer( s ) );
3827 PA_ENSURE( PaUtil_GetHostApiRepresentation( &hostApi, paALSA ) );
3828 alsaHostApi = (PaAlsaHostApiRepresentation*)hostApi;
3829
3830 PA_UNLESS( PA_STREAM_REP( s )->streamInterface == &alsaHostApi->callbackStreamInterface
3831 || PA_STREAM_REP( s )->streamInterface == &alsaHostApi->blockingStreamInterface,
3832 paIncompatibleStreamHostApi );
3833
3834 *stream = (PaAlsaStream*)s;
3835 error:
3836 return paNoError;
3837 }
3838
3839 PaError PaAlsa_GetStreamInputCard(PaStream* s, int* card) {
3840 PaAlsaStream *stream;
3841 PaError result = paNoError;
3842 snd_pcm_info_t* pcmInfo;
3843
3844 PA_ENSURE( GetAlsaStreamPointer( s, &stream ) );
3845
3846 /* XXX: More descriptive error? */
3847 PA_UNLESS( stream->capture.pcm, paDeviceUnavailable );
3848
3849 snd_pcm_info_alloca( &pcmInfo );
3850 PA_ENSURE( snd_pcm_info( stream->capture.pcm, pcmInfo ) );
3851 *card = snd_pcm_info_get_card( pcmInfo );
3852
3853 error:
3854 return result;
3855 }
3856
3857 PaError PaAlsa_GetStreamOutputCard(PaStream* s, int* card) {
3858 PaAlsaStream *stream;
3859 PaError result = paNoError;
3860 snd_pcm_info_t* pcmInfo;
3861
3862 PA_ENSURE( GetAlsaStreamPointer( s, &stream ) );
3863
3864 /* XXX: More descriptive error? */
3865 PA_UNLESS( stream->playback.pcm, paDeviceUnavailable );
3866
3867 snd_pcm_info_alloca( &pcmInfo );
3868 PA_ENSURE( snd_pcm_info( stream->playback.pcm, pcmInfo ) );
3869 *card = snd_pcm_info_get_card( pcmInfo );
3870
3871 error:
3872 return result;
3873 }
3874
3875 PaError PaAlsa_SetRetriesBusy( int retries )
3876 {
3877 busyRetries_ = retries;
3878 return paNoError;
3879 }

  ViewVC Help
Powered by ViewVC 1.1.22