~ubuntu-branches/ubuntu/wily/sflphone/wily

« back to all changes in this revision

Viewing changes to daemon/libs/pjproject-2.2.1/third_party/portaudio/src/hostapi/alsa/pa_linux_alsa.c

  • Committer: Package Import Robot
  • Author(s): Francois Marier, Francois Marier, Mark Purcell
  • Date: 2014-10-18 15:08:50 UTC
  • mfrom: (1.1.12)
  • mto: This revision was merged to the branch mainline in revision 29.
  • Revision ID: package-import@ubuntu.com-20141018150850-2exfk34ckb15pcwi
Tags: 1.4.1-0.1
[ Francois Marier ]
* Non-maintainer upload
* New upstream release (closes: #759576, #741130)
  - debian/rules +PJPROJECT_VERSION := 2.2.1
  - add upstream patch to fix broken TLS support
  - add patch to fix pjproject regression

[ Mark Purcell ]
* Build-Depends:
  - sflphone-daemon + libavformat-dev, libavcodec-dev, libswscale-dev,
  libavdevice-dev, libavutil-dev
  - sflphone-gnome + libclutter-gtk-1.0-dev

Show diffs side-by-side

added added

removed removed

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