~ubuntu-branches/ubuntu/utopic/sflphone/utopic-proposed

« back to all changes in this revision

Viewing changes to daemon/libs/pjproject-2.0.1/third_party/portaudio/src/hostapi/oss/pa_unix_oss.c

  • Committer: Package Import Robot
  • Author(s): Mark Purcell
  • Date: 2013-06-30 11:40:56 UTC
  • mfrom: (4.1.18 saucy-proposed)
  • Revision ID: package-import@ubuntu.com-20130630114056-0np50jkyqo6vnmii
Tags: 1.2.3-2
* changeset_r92d62cfc54732bbbcfff2b1d36c096b120b981a5.diff 
  - fixes automatic endian detection 
* Update Vcs: fixes vcs-field-not-canonical

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * $Id: pa_unix_oss.c 1385 2008-06-05 21:13:54Z aknudsen $
 
3
 * PortAudio Portable Real-Time Audio Library
 
4
 * Latest Version at: http://www.portaudio.com
 
5
 * OSS implementation by:
 
6
 *   Douglas Repetto
 
7
 *   Phil Burk
 
8
 *   Dominic Mazzoni
 
9
 *   Arve Knudsen
 
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
#include <stdio.h>
 
51
#include <string.h>
 
52
#include <math.h>
 
53
#include <fcntl.h>
 
54
#include <sys/ioctl.h>
 
55
#include <unistd.h>
 
56
#include <pthread.h>
 
57
#include <stdlib.h>
 
58
#include <assert.h>
 
59
#include <errno.h>
 
60
#include <sys/types.h>
 
61
#include <sys/stat.h>
 
62
#include <sys/poll.h>
 
63
#include <limits.h>
 
64
#include <semaphore.h>
 
65
 
 
66
#ifdef HAVE_SYS_SOUNDCARD_H
 
67
# include <sys/soundcard.h>
 
68
# ifdef __NetBSD__
 
69
#  define DEVICE_NAME_BASE           "/dev/audio"
 
70
# else
 
71
#  define DEVICE_NAME_BASE           "/dev/dsp"
 
72
# endif
 
73
#elif defined(HAVE_LINUX_SOUNDCARD_H)
 
74
# include <linux/soundcard.h>
 
75
# define DEVICE_NAME_BASE            "/dev/dsp"
 
76
#elif defined(HAVE_MACHINE_SOUNDCARD_H)
 
77
# include <machine/soundcard.h> /* JH20010905 */
 
78
# define DEVICE_NAME_BASE            "/dev/audio"
 
79
#else
 
80
# error No sound card header file
 
81
#endif
 
82
 
 
83
#include "portaudio.h"
 
84
#include "pa_util.h"
 
85
#include "pa_allocation.h"
 
86
#include "pa_hostapi.h"
 
87
#include "pa_stream.h"
 
88
#include "pa_cpuload.h"
 
89
#include "pa_process.h"
 
90
#include "pa_unix_util.h"
 
91
#include "pa_debugprint.h"
 
92
 
 
93
static int sysErr_;
 
94
static pthread_t mainThread_;
 
95
 
 
96
/* Check return value of system call, and map it to PaError */
 
97
#define ENSURE_(expr, code) \
 
98
    do { \
 
99
        if( UNLIKELY( (sysErr_ = (expr)) < 0 ) ) \
 
100
        { \
 
101
            /* PaUtil_SetLastHostErrorInfo should only be used in the main thread */ \
 
102
            if( (code) == paUnanticipatedHostError && pthread_self() == mainThread_ ) \
 
103
            { \
 
104
                PaUtil_SetLastHostErrorInfo( paOSS, sysErr_, strerror( errno ) ); \
 
105
            } \
 
106
            \
 
107
            PaUtil_DebugPrint(( "Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" )); \
 
108
            result = (code); \
 
109
            goto error; \
 
110
        } \
 
111
    } while( 0 );
 
112
 
 
113
#ifndef AFMT_S16_NE
 
114
#define AFMT_S16_NE  Get_AFMT_S16_NE()
 
115
/*********************************************************************
 
116
 * Some versions of OSS do not define AFMT_S16_NE. So check CPU.
 
117
 * PowerPC is Big Endian. X86 is Little Endian.
 
118
 */
 
119
static int Get_AFMT_S16_NE( void )
 
120
{
 
121
    long testData = 1;
 
122
    char *ptr = (char *) &testData;
 
123
    int isLittle = ( *ptr == 1 ); /* Does address point to least significant byte? */
 
124
    return isLittle ? AFMT_S16_LE : AFMT_S16_BE;
 
125
}
 
126
#endif
 
127
 
 
128
/* PaOSSHostApiRepresentation - host api datastructure specific to this implementation */
 
129
 
 
130
typedef struct
 
131
{
 
132
    PaUtilHostApiRepresentation inheritedHostApiRep;
 
133
    PaUtilStreamInterface callbackStreamInterface;
 
134
    PaUtilStreamInterface blockingStreamInterface;
 
135
 
 
136
    PaUtilAllocationGroup *allocations;
 
137
 
 
138
    PaHostApiIndex hostApiIndex;
 
139
}
 
140
PaOSSHostApiRepresentation;
 
141
 
 
142
/** Per-direction structure for PaOssStream.
 
143
 *
 
144
 * Aspect StreamChannels: In case the user requests to open the same device for both capture and playback,
 
145
 * but with different number of channels we will have to adapt between the number of user and host
 
146
 * channels for at least one direction, since the configuration space is the same for both directions
 
147
 * of an OSS device.
 
148
 */
 
149
typedef struct
 
150
{
 
151
    int fd;
 
152
    const char *devName;
 
153
    int userChannelCount, hostChannelCount;
 
154
    int userInterleaved;
 
155
    void *buffer;
 
156
    PaSampleFormat userFormat, hostFormat;
 
157
    double latency;
 
158
    unsigned long hostFrames, numBufs;
 
159
    void **userBuffers; /* For non-interleaved blocking */
 
160
} PaOssStreamComponent;
 
161
 
 
162
/** Implementation specific representation of a PaStream.
 
163
 *
 
164
 */
 
165
typedef struct PaOssStream
 
166
{
 
167
    PaUtilStreamRepresentation streamRepresentation;
 
168
    PaUtilCpuLoadMeasurer cpuLoadMeasurer;
 
169
    PaUtilBufferProcessor bufferProcessor;
 
170
 
 
171
    PaUtilThreading threading;
 
172
 
 
173
    int sharedDevice;
 
174
    unsigned long framesPerHostBuffer;
 
175
    int triggered;  /* Have the devices been triggered yet (first start) */
 
176
 
 
177
    int isActive;
 
178
    int isStopped;
 
179
 
 
180
    int lastPosPtr;
 
181
    double lastStreamBytes;
 
182
 
 
183
    int framesProcessed;
 
184
 
 
185
    double sampleRate;
 
186
 
 
187
    int callbackMode;
 
188
    int callbackStop, callbackAbort;
 
189
 
 
190
    PaOssStreamComponent *capture, *playback;
 
191
    unsigned long pollTimeout;
 
192
    sem_t semaphore;
 
193
}
 
194
PaOssStream;
 
195
 
 
196
typedef enum {
 
197
    StreamMode_In,
 
198
    StreamMode_Out
 
199
} StreamMode;
 
200
 
 
201
/* prototypes for functions declared in this file */
 
202
 
 
203
static void Terminate( struct PaUtilHostApiRepresentation *hostApi );
 
204
static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
 
205
                                  const PaStreamParameters *inputParameters,
 
206
                                  const PaStreamParameters *outputParameters,
 
207
                                  double sampleRate );
 
208
static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
 
209
                           PaStream** s,
 
210
                           const PaStreamParameters *inputParameters,
 
211
                           const PaStreamParameters *outputParameters,
 
212
                           double sampleRate,
 
213
                           unsigned long framesPerBuffer,
 
214
                           PaStreamFlags streamFlags,
 
215
                           PaStreamCallback *streamCallback,
 
216
                           void *userData );
 
217
static PaError CloseStream( PaStream* stream );
 
218
static PaError StartStream( PaStream *stream );
 
219
static PaError StopStream( PaStream *stream );
 
220
static PaError AbortStream( PaStream *stream );
 
221
static PaError IsStreamStopped( PaStream *s );
 
222
static PaError IsStreamActive( PaStream *stream );
 
223
static PaTime GetStreamTime( PaStream *stream );
 
224
static double GetStreamCpuLoad( PaStream* stream );
 
225
static PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames );
 
226
static PaError WriteStream( PaStream* stream, const void *buffer, unsigned long frames );
 
227
static signed long GetStreamReadAvailable( PaStream* stream );
 
228
static signed long GetStreamWriteAvailable( PaStream* stream );
 
229
static PaError BuildDeviceList( PaOSSHostApiRepresentation *hostApi );
 
230
 
 
231
 
 
232
/** Initialize the OSS API implementation.
 
233
 *
 
234
 * This function will initialize host API datastructures and query host devices for information.
 
235
 *
 
236
 * Aspect DeviceCapabilities: Enumeration of host API devices is initiated from here
 
237
 *
 
238
 * Aspect FreeResources: If an error is encountered under way we have to free each resource allocated in this function,
 
239
 * this happens with the usual "error" label.
 
240
 */
 
241
PaError PaOSS_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
 
242
{
 
243
    PaError result = paNoError;
 
244
    PaOSSHostApiRepresentation *ossHostApi = NULL;
 
245
 
 
246
    PA_UNLESS( ossHostApi = (PaOSSHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaOSSHostApiRepresentation) ),
 
247
            paInsufficientMemory );
 
248
    PA_UNLESS( ossHostApi->allocations = PaUtil_CreateAllocationGroup(), paInsufficientMemory );
 
249
    ossHostApi->hostApiIndex = hostApiIndex;
 
250
 
 
251
    /* Initialize host API structure */
 
252
    *hostApi = &ossHostApi->inheritedHostApiRep;
 
253
    (*hostApi)->info.structVersion = 1;
 
254
    (*hostApi)->info.type = paOSS;
 
255
    (*hostApi)->info.name = "OSS";
 
256
    (*hostApi)->Terminate = Terminate;
 
257
    (*hostApi)->OpenStream = OpenStream;
 
258
    (*hostApi)->IsFormatSupported = IsFormatSupported;
 
259
 
 
260
    PA_ENSURE( BuildDeviceList( ossHostApi ) );
 
261
 
 
262
    PaUtil_InitializeStreamInterface( &ossHostApi->callbackStreamInterface, CloseStream, StartStream,
 
263
                                      StopStream, AbortStream, IsStreamStopped, IsStreamActive,
 
264
                                      GetStreamTime, GetStreamCpuLoad,
 
265
                                      PaUtil_DummyRead, PaUtil_DummyWrite,
 
266
                                      PaUtil_DummyGetReadAvailable,
 
267
                                      PaUtil_DummyGetWriteAvailable );
 
268
 
 
269
    PaUtil_InitializeStreamInterface( &ossHostApi->blockingStreamInterface, CloseStream, StartStream,
 
270
                                      StopStream, AbortStream, IsStreamStopped, IsStreamActive,
 
271
                                      GetStreamTime, PaUtil_DummyGetCpuLoad,
 
272
                                      ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable );
 
273
 
 
274
    mainThread_ = pthread_self();
 
275
 
 
276
    return result;
 
277
 
 
278
error:
 
279
    if( ossHostApi )
 
280
    {
 
281
        if( ossHostApi->allocations )
 
282
        {
 
283
            PaUtil_FreeAllAllocations( ossHostApi->allocations );
 
284
            PaUtil_DestroyAllocationGroup( ossHostApi->allocations );
 
285
        }
 
286
 
 
287
        PaUtil_FreeMemory( ossHostApi );
 
288
    }
 
289
    return result;
 
290
}
 
291
 
 
292
PaError PaUtil_InitializeDeviceInfo( PaDeviceInfo *deviceInfo, const char *name, PaHostApiIndex hostApiIndex, int maxInputChannels,
 
293
        int maxOutputChannels, PaTime defaultLowInputLatency, PaTime defaultLowOutputLatency, PaTime defaultHighInputLatency,
 
294
        PaTime defaultHighOutputLatency, double defaultSampleRate, PaUtilAllocationGroup *allocations  )
 
295
{
 
296
    PaError result = paNoError;
 
297
 
 
298
    deviceInfo->structVersion = 2;
 
299
    if( allocations )
 
300
    {
 
301
        size_t len = strlen( name ) + 1;
 
302
        PA_UNLESS( deviceInfo->name = PaUtil_GroupAllocateMemory( allocations, len ), paInsufficientMemory );
 
303
        strncpy( (char *)deviceInfo->name, name, len );
 
304
    }
 
305
    else
 
306
        deviceInfo->name = name;
 
307
 
 
308
    deviceInfo->hostApi = hostApiIndex;
 
309
    deviceInfo->maxInputChannels = maxInputChannels;
 
310
    deviceInfo->maxOutputChannels = maxOutputChannels;
 
311
    deviceInfo->defaultLowInputLatency = defaultLowInputLatency;
 
312
    deviceInfo->defaultLowOutputLatency = defaultLowOutputLatency;
 
313
    deviceInfo->defaultHighInputLatency = defaultHighInputLatency;
 
314
    deviceInfo->defaultHighOutputLatency = defaultHighOutputLatency;
 
315
    deviceInfo->defaultSampleRate = defaultSampleRate;
 
316
 
 
317
error:
 
318
    return result;
 
319
}
 
320
 
 
321
static PaError QueryDirection( const char *deviceName, StreamMode mode, double *defaultSampleRate, int *maxChannelCount,
 
322
        double *defaultLowLatency, double *defaultHighLatency )
 
323
{
 
324
    PaError result = paNoError;
 
325
    int numChannels, maxNumChannels;
 
326
    int busy = 0;
 
327
    int devHandle = -1;
 
328
    int sr;
 
329
    *maxChannelCount = 0;  /* Default value in case this fails */
 
330
 
 
331
    if ( (devHandle = open( deviceName, (mode == StreamMode_In ? O_RDONLY : O_WRONLY) | O_NONBLOCK ))  < 0 )
 
332
    {
 
333
        if( errno == EBUSY || errno == EAGAIN )
 
334
        {
 
335
            PA_DEBUG(( "%s: Device %s busy\n", __FUNCTION__, deviceName ));
 
336
        }
 
337
        else
 
338
        {
 
339
            /* Ignore ENOENT, which means we've tried a non-existent device */
 
340
            if( errno != ENOENT )
 
341
            {
 
342
                PA_DEBUG(( "%s: Can't access device %s: %s\n", __FUNCTION__, deviceName, strerror( errno ) ));
 
343
            }
 
344
        }
 
345
 
 
346
        return paDeviceUnavailable;
 
347
    }
 
348
 
 
349
    /* Negotiate for the maximum number of channels for this device. PLB20010927
 
350
     * Consider up to 16 as the upper number of channels.
 
351
     * Variable maxNumChannels should contain the actual upper limit after the call.
 
352
     * Thanks to John Lazzaro and Heiko Purnhagen for suggestions.
 
353
     */
 
354
    maxNumChannels = 0;
 
355
    for( numChannels = 1; numChannels <= 16; numChannels++ )
 
356
    {
 
357
        int temp = numChannels;
 
358
        if( ioctl( devHandle, SNDCTL_DSP_CHANNELS, &temp ) < 0 )
 
359
        {
 
360
            busy = EAGAIN == errno || EBUSY == errno;
 
361
            /* ioctl() failed so bail out if we already have stereo */
 
362
            if( maxNumChannels >= 2 )
 
363
                break;
 
364
        }
 
365
        else
 
366
        {
 
367
            /* ioctl() worked but bail out if it does not support numChannels.
 
368
             * We don't want to leave gaps in the numChannels supported.
 
369
             */
 
370
            if( (numChannels > 2) && (temp != numChannels) )
 
371
                break;
 
372
            if( temp > maxNumChannels )
 
373
                maxNumChannels = temp; /* Save maximum. */
 
374
        }
 
375
    }
 
376
    /* A: We're able to open a device for capture if it's busy playing back and vice versa,
 
377
     * but we can't configure anything */
 
378
    if( 0 == maxNumChannels && busy )
 
379
    {
 
380
        result = paDeviceUnavailable;
 
381
        goto error;
 
382
    }
 
383
 
 
384
    /* The above negotiation may fail for an old driver so try this older technique. */
 
385
    if( maxNumChannels < 1 )
 
386
    {
 
387
        int stereo = 1;
 
388
        if( ioctl( devHandle, SNDCTL_DSP_STEREO, &stereo ) < 0 )
 
389
        {
 
390
            maxNumChannels = 1;
 
391
        }
 
392
        else
 
393
        {
 
394
            maxNumChannels = (stereo) ? 2 : 1;
 
395
        }
 
396
        PA_DEBUG(( "%s: use SNDCTL_DSP_STEREO, maxNumChannels = %d\n", __FUNCTION__, maxNumChannels ));
 
397
    }
 
398
 
 
399
    /* During channel negotiation, the last ioctl() may have failed. This can
 
400
     * also cause sample rate negotiation to fail. Hence the following, to return
 
401
     * to a supported number of channels. SG20011005 */
 
402
    {
 
403
        /* use most reasonable default value */
 
404
        int temp = PA_MIN( maxNumChannels, 2 );
 
405
        ENSURE_( ioctl( devHandle, SNDCTL_DSP_CHANNELS, &temp ), paUnanticipatedHostError );
 
406
    }
 
407
 
 
408
    /* Get supported sample rate closest to 44100 Hz */
 
409
    if( *defaultSampleRate < 0 )
 
410
    {
 
411
        sr = 44100;
 
412
        ENSURE_( ioctl( devHandle, SNDCTL_DSP_SPEED, &sr ), paUnanticipatedHostError );
 
413
 
 
414
        *defaultSampleRate = sr;
 
415
    }
 
416
 
 
417
    *maxChannelCount = maxNumChannels;
 
418
    /* TODO */
 
419
    *defaultLowLatency = 512. / *defaultSampleRate;
 
420
    *defaultHighLatency = 2048. / *defaultSampleRate;
 
421
 
 
422
error:
 
423
    if( devHandle >= 0 )
 
424
        close( devHandle );
 
425
 
 
426
    return result;
 
427
}
 
428
 
 
429
/** Query OSS device.
 
430
 *
 
431
 * This is where PaDeviceInfo objects are constructed and filled in with relevant information.
 
432
 *
 
433
 * Aspect DeviceCapabilities: The inferred device capabilities are recorded in a PaDeviceInfo object that is constructed
 
434
 * in place.
 
435
 */
 
436
static PaError QueryDevice( char *deviceName, PaOSSHostApiRepresentation *ossApi, PaDeviceInfo **deviceInfo )
 
437
{
 
438
    PaError result = paNoError;
 
439
    double sampleRate = -1.;
 
440
    int maxInputChannels, maxOutputChannels;
 
441
    PaTime defaultLowInputLatency, defaultLowOutputLatency, defaultHighInputLatency, defaultHighOutputLatency;
 
442
    PaError tmpRes = paNoError;
 
443
    int busy = 0;
 
444
    *deviceInfo = NULL;
 
445
 
 
446
    /* douglas:
 
447
       we have to do this querying in a slightly different order. apparently
 
448
       some sound cards will give you different info based on their settins.
 
449
       e.g. a card might give you stereo at 22kHz but only mono at 44kHz.
 
450
       the correct order for OSS is: format, channels, sample rate
 
451
    */
 
452
 
 
453
    /* Aspect StreamChannels: The number of channels supported for a device may depend on the mode it is
 
454
     * opened in, it may have more channels available for capture than playback and vice versa. Therefore
 
455
     * we will open the device in both read- and write-only mode to determine the supported number.
 
456
     */
 
457
    if( (tmpRes = QueryDirection( deviceName, StreamMode_In, &sampleRate, &maxInputChannels, &defaultLowInputLatency,
 
458
                &defaultHighInputLatency )) != paNoError )
 
459
    {
 
460
        if( tmpRes != paDeviceUnavailable )
 
461
        {
 
462
            PA_DEBUG(( "%s: Querying device %s for capture failed!\n", __FUNCTION__, deviceName ));
 
463
            /* PA_ENSURE( tmpRes ); */
 
464
        }
 
465
        ++busy;
 
466
    }
 
467
    if( (tmpRes = QueryDirection( deviceName, StreamMode_Out, &sampleRate, &maxOutputChannels, &defaultLowOutputLatency,
 
468
                &defaultHighOutputLatency )) != paNoError )
 
469
    {
 
470
        if( tmpRes != paDeviceUnavailable )
 
471
        {
 
472
            PA_DEBUG(( "%s: Querying device %s for playback failed!\n", __FUNCTION__, deviceName ));
 
473
            /* PA_ENSURE( tmpRes ); */
 
474
        }
 
475
        ++busy;
 
476
    }
 
477
    assert( 0 <= busy && busy <= 2 );
 
478
    if( 2 == busy )     /* Both directions are unavailable to us */
 
479
    {
 
480
        result = paDeviceUnavailable;
 
481
        goto error;
 
482
    }
 
483
 
 
484
    PA_UNLESS( *deviceInfo = PaUtil_GroupAllocateMemory( ossApi->allocations, sizeof (PaDeviceInfo) ), paInsufficientMemory );
 
485
    PA_ENSURE( PaUtil_InitializeDeviceInfo( *deviceInfo, deviceName, ossApi->hostApiIndex, maxInputChannels, maxOutputChannels,
 
486
                defaultLowInputLatency, defaultLowOutputLatency, defaultHighInputLatency, defaultHighOutputLatency, sampleRate,
 
487
                ossApi->allocations ) );
 
488
 
 
489
error:
 
490
    return result;
 
491
}
 
492
 
 
493
/** Query host devices.
 
494
 *
 
495
 * Loop over host devices and query their capabilitiesu
 
496
 *
 
497
 * Aspect DeviceCapabilities: This function calls QueryDevice on each device entry and receives a filled in PaDeviceInfo object
 
498
 * per device, these are placed in the host api representation's deviceInfos array.
 
499
 */
 
500
static PaError BuildDeviceList( PaOSSHostApiRepresentation *ossApi )
 
501
{
 
502
    PaError result = paNoError;
 
503
    PaUtilHostApiRepresentation *commonApi = &ossApi->inheritedHostApiRep;
 
504
    int i;
 
505
    int numDevices = 0, maxDeviceInfos = 1;
 
506
    PaDeviceInfo **deviceInfos = NULL;
 
507
 
 
508
    /* These two will be set to the first working input and output device, respectively */
 
509
    commonApi->info.defaultInputDevice = paNoDevice;
 
510
    commonApi->info.defaultOutputDevice = paNoDevice;
 
511
 
 
512
    /* Find devices by calling QueryDevice on each
 
513
     * potential device names.  When we find a valid one,
 
514
     * add it to a linked list.
 
515
     * A: Set an arbitrary of 100 devices, should probably be a smarter way. */
 
516
 
 
517
    for( i = 0; i < 100; i++ )
 
518
    {
 
519
       char deviceName[32];
 
520
       PaDeviceInfo *deviceInfo;
 
521
       int testResult;
 
522
 
 
523
       if( i == 0 )
 
524
          snprintf(deviceName, sizeof (deviceName), "%s", DEVICE_NAME_BASE);
 
525
       else
 
526
          snprintf(deviceName, sizeof (deviceName), "%s%d", DEVICE_NAME_BASE, i);
 
527
 
 
528
       /* PA_DEBUG(("%s: trying device %s\n", __FUNCTION__, deviceName )); */
 
529
       if( (testResult = QueryDevice( deviceName, ossApi, &deviceInfo )) != paNoError )
 
530
       {
 
531
           if( testResult != paDeviceUnavailable )
 
532
               PA_ENSURE( testResult );
 
533
 
 
534
           continue;
 
535
       }
 
536
 
 
537
       ++numDevices;
 
538
       if( !deviceInfos || numDevices > maxDeviceInfos )
 
539
       {
 
540
           maxDeviceInfos *= 2;
 
541
           PA_UNLESS( deviceInfos = (PaDeviceInfo **) realloc( deviceInfos, maxDeviceInfos * sizeof (PaDeviceInfo *) ),
 
542
                   paInsufficientMemory );
 
543
       }
 
544
       {
 
545
           int devIdx = numDevices - 1;
 
546
           deviceInfos[devIdx] = deviceInfo;
 
547
 
 
548
           if( commonApi->info.defaultInputDevice == paNoDevice && deviceInfo->maxInputChannels > 0 )
 
549
               commonApi->info.defaultInputDevice = devIdx;
 
550
           if( commonApi->info.defaultOutputDevice == paNoDevice && deviceInfo->maxOutputChannels > 0 )
 
551
               commonApi->info.defaultOutputDevice = devIdx;
 
552
       }
 
553
    }
 
554
 
 
555
    /* Make an array of PaDeviceInfo pointers out of the linked list */
 
556
 
 
557
    PA_DEBUG(("PaOSS %s: Total number of devices found: %d\n", __FUNCTION__, numDevices));
 
558
 
 
559
    commonApi->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(
 
560
        ossApi->allocations, sizeof(PaDeviceInfo*) * numDevices );
 
561
    memcpy( commonApi->deviceInfos, deviceInfos, numDevices * sizeof (PaDeviceInfo *) );
 
562
 
 
563
    commonApi->info.deviceCount = numDevices;
 
564
 
 
565
error:
 
566
    free( deviceInfos );
 
567
 
 
568
    return result;
 
569
}
 
570
 
 
571
static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
 
572
{
 
573
    PaOSSHostApiRepresentation *ossHostApi = (PaOSSHostApiRepresentation*)hostApi;
 
574
 
 
575
    if( ossHostApi->allocations )
 
576
    {
 
577
        PaUtil_FreeAllAllocations( ossHostApi->allocations );
 
578
        PaUtil_DestroyAllocationGroup( ossHostApi->allocations );
 
579
    }
 
580
 
 
581
    PaUtil_FreeMemory( ossHostApi );
 
582
}
 
583
 
 
584
static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
 
585
                                  const PaStreamParameters *inputParameters,
 
586
                                  const PaStreamParameters *outputParameters,
 
587
                                  double sampleRate )
 
588
{
 
589
    PaError result = paNoError;
 
590
    PaDeviceIndex device;
 
591
    PaDeviceInfo *deviceInfo;
 
592
    char *deviceName;
 
593
    int inputChannelCount, outputChannelCount;
 
594
    int tempDevHandle = -1;
 
595
    int flags;
 
596
    PaSampleFormat inputSampleFormat, outputSampleFormat;
 
597
 
 
598
    if( inputParameters )
 
599
    {
 
600
        inputChannelCount = inputParameters->channelCount;
 
601
        inputSampleFormat = inputParameters->sampleFormat;
 
602
 
 
603
        /* unless alternate device specification is supported, reject the use of
 
604
            paUseHostApiSpecificDeviceSpecification */
 
605
 
 
606
        if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
 
607
            return paInvalidDevice;
 
608
 
 
609
        /* check that input device can support inputChannelCount */
 
610
        if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels )
 
611
            return paInvalidChannelCount;
 
612
 
 
613
        /* validate inputStreamInfo */
 
614
        if( inputParameters->hostApiSpecificStreamInfo )
 
615
            return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
 
616
    }
 
617
    else
 
618
    {
 
619
        inputChannelCount = 0;
 
620
    }
 
621
 
 
622
    if( outputParameters )
 
623
    {
 
624
        outputChannelCount = outputParameters->channelCount;
 
625
        outputSampleFormat = outputParameters->sampleFormat;
 
626
 
 
627
        /* unless alternate device specification is supported, reject the use of
 
628
            paUseHostApiSpecificDeviceSpecification */
 
629
 
 
630
        if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
 
631
            return paInvalidDevice;
 
632
 
 
633
        /* check that output device can support inputChannelCount */
 
634
        if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels )
 
635
            return paInvalidChannelCount;
 
636
 
 
637
        /* validate outputStreamInfo */
 
638
        if( outputParameters->hostApiSpecificStreamInfo )
 
639
            return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
 
640
    }
 
641
    else
 
642
    {
 
643
        outputChannelCount = 0;
 
644
    }
 
645
 
 
646
    if (inputChannelCount == 0 && outputChannelCount == 0)
 
647
        return paInvalidChannelCount;
 
648
 
 
649
    /* if full duplex, make sure that they're the same device */
 
650
 
 
651
    if (inputChannelCount > 0 && outputChannelCount > 0 &&
 
652
        inputParameters->device != outputParameters->device)
 
653
        return paInvalidDevice;
 
654
 
 
655
    /* if full duplex, also make sure that they're the same number of channels */
 
656
 
 
657
    if (inputChannelCount > 0 && outputChannelCount > 0 &&
 
658
        inputChannelCount != outputChannelCount)
 
659
       return paInvalidChannelCount;
 
660
 
 
661
    /* open the device so we can do more tests */
 
662
 
 
663
    if( inputChannelCount > 0 )
 
664
    {
 
665
        result = PaUtil_DeviceIndexToHostApiDeviceIndex(&device, inputParameters->device, hostApi);
 
666
        if (result != paNoError)
 
667
            return result;
 
668
    }
 
669
    else
 
670
    {
 
671
        result = PaUtil_DeviceIndexToHostApiDeviceIndex(&device, outputParameters->device, hostApi);
 
672
        if (result != paNoError)
 
673
            return result;
 
674
    }
 
675
 
 
676
    deviceInfo = hostApi->deviceInfos[device];
 
677
    deviceName = (char *)deviceInfo->name;
 
678
 
 
679
    flags = O_NONBLOCK;
 
680
    if (inputChannelCount > 0 && outputChannelCount > 0)
 
681
       flags |= O_RDWR;
 
682
    else if (inputChannelCount > 0)
 
683
       flags |= O_RDONLY;
 
684
    else
 
685
       flags |= O_WRONLY;
 
686
 
 
687
    ENSURE_( tempDevHandle = open( deviceInfo->name, flags ), paDeviceUnavailable );
 
688
 
 
689
    /* PaOssStream_Configure will do the rest of the checking for us */
 
690
    /* PA_ENSURE( PaOssStream_Configure( tempDevHandle, deviceName, outputChannelCount, &sampleRate ) ); */
 
691
 
 
692
    /* everything succeeded! */
 
693
 
 
694
 error:
 
695
    if( tempDevHandle >= 0 )
 
696
        close( tempDevHandle );
 
697
 
 
698
    return result;
 
699
}
 
700
 
 
701
/** Validate stream parameters.
 
702
 *
 
703
 * Aspect StreamChannels: We verify that the number of channels is within the allowed range for the device
 
704
 */
 
705
static PaError ValidateParameters( const PaStreamParameters *parameters, const PaDeviceInfo *deviceInfo, StreamMode mode )
 
706
{
 
707
    int maxChans;
 
708
 
 
709
    assert( parameters );
 
710
 
 
711
    if( parameters->device == paUseHostApiSpecificDeviceSpecification )
 
712
    {
 
713
        return paInvalidDevice;
 
714
    }
 
715
 
 
716
    maxChans = (mode == StreamMode_In ? deviceInfo->maxInputChannels :
 
717
        deviceInfo->maxOutputChannels);
 
718
    if( parameters->channelCount > maxChans )
 
719
    {
 
720
        return paInvalidChannelCount;
 
721
    }
 
722
 
 
723
    return paNoError;
 
724
}
 
725
 
 
726
static PaError PaOssStreamComponent_Initialize( PaOssStreamComponent *component, const PaStreamParameters *parameters,
 
727
        int callbackMode, int fd, const char *deviceName )
 
728
{
 
729
    PaError result = paNoError;
 
730
    assert( component );
 
731
 
 
732
    memset( component, 0, sizeof (PaOssStreamComponent) );
 
733
 
 
734
    component->fd = fd;
 
735
    component->devName = deviceName;
 
736
    component->userChannelCount = parameters->channelCount;
 
737
    component->userFormat = parameters->sampleFormat;
 
738
    component->latency = parameters->suggestedLatency;
 
739
    component->userInterleaved = !(parameters->sampleFormat & paNonInterleaved);
 
740
 
 
741
    if( !callbackMode && !component->userInterleaved )
 
742
    {
 
743
        /* Pre-allocate non-interleaved user provided buffers */
 
744
        PA_UNLESS( component->userBuffers = PaUtil_AllocateMemory( sizeof (void *) * component->userChannelCount ),
 
745
                paInsufficientMemory );
 
746
    }
 
747
 
 
748
error:
 
749
    return result;
 
750
}
 
751
 
 
752
static void PaOssStreamComponent_Terminate( PaOssStreamComponent *component )
 
753
{
 
754
    assert( component );
 
755
 
 
756
    if( component->fd >= 0 )
 
757
        close( component->fd );
 
758
    if( component->buffer )
 
759
        PaUtil_FreeMemory( component->buffer );
 
760
 
 
761
    if( component->userBuffers )
 
762
        PaUtil_FreeMemory( component->userBuffers );
 
763
 
 
764
    PaUtil_FreeMemory( component );
 
765
}
 
766
 
 
767
static PaError ModifyBlocking( int fd, int blocking )
 
768
{
 
769
    PaError result = paNoError;
 
770
    int fflags;
 
771
 
 
772
    ENSURE_( fflags = fcntl( fd, F_GETFL ), paUnanticipatedHostError );
 
773
 
 
774
    if( blocking )
 
775
        fflags &= ~O_NONBLOCK;
 
776
    else
 
777
        fflags |= O_NONBLOCK;
 
778
 
 
779
    ENSURE_( fcntl( fd, F_SETFL, fflags ), paUnanticipatedHostError );
 
780
 
 
781
error:
 
782
    return result;
 
783
}
 
784
 
 
785
/** Open input and output devices.
 
786
 *
 
787
 * @param idev: Returned input device file descriptor.
 
788
 * @param odev: Returned output device file descriptor.
 
789
 */
 
790
static PaError OpenDevices( const char *idevName, const char *odevName, int *idev, int *odev )
 
791
{
 
792
    PaError result = paNoError;
 
793
    int flags = O_NONBLOCK, duplex = 0;
 
794
    *idev = *odev = -1;
 
795
 
 
796
    if( idevName && odevName )
 
797
    {
 
798
        duplex = 1;
 
799
        flags |= O_RDWR;
 
800
    }
 
801
    else if( idevName )
 
802
        flags |= O_RDONLY;
 
803
    else
 
804
        flags |= O_WRONLY;
 
805
 
 
806
    /* open first in nonblocking mode, in case it's busy...
 
807
     * A: then unset the non-blocking attribute */
 
808
    assert( flags & O_NONBLOCK );
 
809
    if( idevName )
 
810
    {
 
811
        ENSURE_( *idev = open( idevName, flags ), paDeviceUnavailable );
 
812
        PA_ENSURE( ModifyBlocking( *idev, 1 ) ); /* Blocking */
 
813
    }
 
814
    if( odevName )
 
815
    {
 
816
        if( !idevName )
 
817
        {
 
818
            ENSURE_( *odev = open( odevName, flags ), paDeviceUnavailable );
 
819
            PA_ENSURE( ModifyBlocking( *odev, 1 ) ); /* Blocking */
 
820
        }
 
821
        else
 
822
        {
 
823
            ENSURE_( *odev = dup( *idev ), paUnanticipatedHostError );
 
824
        }
 
825
    }
 
826
 
 
827
error:
 
828
    return result;
 
829
}
 
830
 
 
831
static PaError PaOssStream_Initialize( PaOssStream *stream, const PaStreamParameters *inputParameters, const PaStreamParameters *outputParameters,
 
832
        PaStreamCallback callback, void *userData, PaStreamFlags streamFlags,
 
833
        PaOSSHostApiRepresentation *ossApi )
 
834
{
 
835
    PaError result = paNoError;
 
836
    int idev, odev;
 
837
    PaUtilHostApiRepresentation *hostApi = &ossApi->inheritedHostApiRep;
 
838
    const char *idevName = NULL, *odevName = NULL;
 
839
 
 
840
    assert( stream );
 
841
 
 
842
    memset( stream, 0, sizeof (PaOssStream) );
 
843
    stream->isStopped = 1;
 
844
 
 
845
    PA_ENSURE( PaUtil_InitializeThreading( &stream->threading ) );
 
846
 
 
847
    if( inputParameters && outputParameters )
 
848
    {
 
849
        if( inputParameters->device == outputParameters->device )
 
850
            stream->sharedDevice = 1;
 
851
    }
 
852
 
 
853
    if( inputParameters )
 
854
        idevName = hostApi->deviceInfos[inputParameters->device]->name;
 
855
    if( outputParameters )
 
856
        odevName = hostApi->deviceInfos[outputParameters->device]->name;
 
857
    PA_ENSURE( OpenDevices( idevName, odevName, &idev, &odev ) );
 
858
    if( inputParameters )
 
859
    {
 
860
        PA_UNLESS( stream->capture = PaUtil_AllocateMemory( sizeof (PaOssStreamComponent) ), paInsufficientMemory );
 
861
        PA_ENSURE( PaOssStreamComponent_Initialize( stream->capture, inputParameters, callback != NULL, idev, idevName ) );
 
862
    }
 
863
    if( outputParameters )
 
864
    {
 
865
        PA_UNLESS( stream->playback = PaUtil_AllocateMemory( sizeof (PaOssStreamComponent) ), paInsufficientMemory );
 
866
        PA_ENSURE( PaOssStreamComponent_Initialize( stream->playback, outputParameters, callback != NULL, odev, odevName ) );
 
867
    }
 
868
 
 
869
    if( callback != NULL )
 
870
    {
 
871
        PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
 
872
                                               &ossApi->callbackStreamInterface, callback, userData );
 
873
        stream->callbackMode = 1;
 
874
    }
 
875
    else
 
876
    {
 
877
        PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
 
878
                                               &ossApi->blockingStreamInterface, callback, userData );
 
879
    }
 
880
 
 
881
    ENSURE_( sem_init( &stream->semaphore, 0, 0 ), paInternalError );
 
882
 
 
883
error:
 
884
    return result;
 
885
}
 
886
 
 
887
static void PaOssStream_Terminate( PaOssStream *stream )
 
888
{
 
889
    assert( stream );
 
890
 
 
891
    PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
 
892
    PaUtil_TerminateThreading( &stream->threading );
 
893
 
 
894
    if( stream->capture )
 
895
        PaOssStreamComponent_Terminate( stream->capture );
 
896
    if( stream->playback )
 
897
        PaOssStreamComponent_Terminate( stream->playback );
 
898
 
 
899
    sem_destroy( &stream->semaphore );
 
900
 
 
901
    PaUtil_FreeMemory( stream );
 
902
}
 
903
 
 
904
/** Translate from PA format to OSS native.
 
905
 *
 
906
 */
 
907
static PaError Pa2OssFormat( PaSampleFormat paFormat, int *ossFormat )
 
908
{
 
909
    switch( paFormat )
 
910
    {
 
911
        case paUInt8:
 
912
            *ossFormat = AFMT_U8;
 
913
            break;
 
914
        case paInt8:
 
915
            *ossFormat = AFMT_S8;
 
916
            break;
 
917
        case paInt16:
 
918
            *ossFormat = AFMT_S16_NE;
 
919
            break;
 
920
        default:
 
921
            return paInternalError;     /* This shouldn't happen */
 
922
    }
 
923
 
 
924
    return paNoError;
 
925
}
 
926
 
 
927
/** Return the PA-compatible formats that this device can support.
 
928
 *
 
929
 */
 
930
static PaError GetAvailableFormats( PaOssStreamComponent *component, PaSampleFormat *availableFormats )
 
931
{
 
932
    PaError result = paNoError;
 
933
    int mask = 0;
 
934
    PaSampleFormat frmts = 0;
 
935
 
 
936
    ENSURE_( ioctl( component->fd, SNDCTL_DSP_GETFMTS, &mask ), paUnanticipatedHostError );
 
937
    if( mask & AFMT_U8 )
 
938
        frmts |= paUInt8;
 
939
    if( mask & AFMT_S8 )
 
940
        frmts |= paInt8;
 
941
    if( mask & AFMT_S16_NE )
 
942
        frmts |= paInt16;
 
943
    else
 
944
        result = paSampleFormatNotSupported;
 
945
 
 
946
    *availableFormats = frmts;
 
947
 
 
948
error:
 
949
    return result;
 
950
}
 
951
 
 
952
static unsigned int PaOssStreamComponent_FrameSize( PaOssStreamComponent *component )
 
953
{
 
954
    return Pa_GetSampleSize( component->hostFormat ) * component->hostChannelCount;
 
955
}
 
956
 
 
957
/** Buffer size in bytes.
 
958
 *
 
959
 */
 
960
static unsigned long PaOssStreamComponent_BufferSize( PaOssStreamComponent *component )
 
961
{
 
962
    return PaOssStreamComponent_FrameSize( component ) * component->hostFrames * component->numBufs;
 
963
}
 
964
 
 
965
static int CalcHigherLogTwo( int n )
 
966
{
 
967
    int log2 = 0;
 
968
    while( (1<<log2) < n ) log2++;
 
969
    return log2;
 
970
}
 
971
 
 
972
/** Configure stream component device parameters.
 
973
 */
 
974
static PaError PaOssStreamComponent_Configure( PaOssStreamComponent *component, double sampleRate, unsigned long
 
975
        framesPerBuffer, StreamMode streamMode, PaOssStreamComponent *master )
 
976
{
 
977
    PaError result = paNoError;
 
978
    int temp, nativeFormat;
 
979
    int sr = (int)sampleRate;
 
980
    PaSampleFormat availableFormats = 0, hostFormat = 0;
 
981
    int chans = component->userChannelCount;
 
982
    int frgmt;
 
983
    int numBufs;
 
984
    int bytesPerBuf;
 
985
    unsigned long bufSz;
 
986
    unsigned long fragSz;
 
987
    audio_buf_info bufInfo;
 
988
 
 
989
    /* We may have a situation where only one component (the master) is configured, if both point to the same device.
 
990
     * In that case, the second component will copy settings from the other */
 
991
    if( !master )
 
992
    {
 
993
        /* Aspect BufferSettings: If framesPerBuffer is unspecified we have to infer a suitable fragment size.
 
994
         * The hardware need not respect the requested fragment size, so we may have to adapt.
 
995
         */
 
996
        if( framesPerBuffer == paFramesPerBufferUnspecified )
 
997
        {
 
998
            bufSz = (unsigned long)(component->latency * sampleRate);
 
999
            fragSz = bufSz / 4;
 
1000
        }
 
1001
        else
 
1002
        {
 
1003
            fragSz = framesPerBuffer;
 
1004
            bufSz = (unsigned long)(component->latency * sampleRate) + fragSz; /* Latency + 1 buffer */
 
1005
        }
 
1006
 
 
1007
        PA_ENSURE( GetAvailableFormats( component, &availableFormats ) );
 
1008
        hostFormat = PaUtil_SelectClosestAvailableFormat( availableFormats, component->userFormat );
 
1009
 
 
1010
        /* OSS demands at least 2 buffers, and 16 bytes per buffer */
 
1011
        numBufs = (int)PA_MAX( bufSz / fragSz, 2 );
 
1012
        bytesPerBuf = PA_MAX( fragSz * Pa_GetSampleSize( hostFormat ) * chans, 16 );
 
1013
 
 
1014
        /* The fragment parameters are encoded like this:
 
1015
         * Most significant byte: number of fragments
 
1016
         * Least significant byte: exponent of fragment size (i.e., for 256, 8)
 
1017
         */
 
1018
        frgmt = (numBufs << 16) + (CalcHigherLogTwo( bytesPerBuf ) & 0xffff);
 
1019
        ENSURE_( ioctl( component->fd, SNDCTL_DSP_SETFRAGMENT, &frgmt ), paUnanticipatedHostError );
 
1020
 
 
1021
        /* A: according to the OSS programmer's guide parameters should be set in this order:
 
1022
         * format, channels, rate */
 
1023
 
 
1024
        /* This format should be deemed good before we get this far */
 
1025
        PA_ENSURE( Pa2OssFormat( hostFormat, &temp ) );
 
1026
        nativeFormat = temp;
 
1027
        ENSURE_( ioctl( component->fd, SNDCTL_DSP_SETFMT, &temp ), paUnanticipatedHostError );
 
1028
        PA_UNLESS( temp == nativeFormat, paInternalError );
 
1029
 
 
1030
        /* try to set the number of channels */
 
1031
        ENSURE_( ioctl( component->fd, SNDCTL_DSP_CHANNELS, &chans ), paSampleFormatNotSupported );   /* XXX: Should be paInvalidChannelCount? */
 
1032
        /* It's possible that the minimum number of host channels is greater than what the user requested */
 
1033
        PA_UNLESS( chans >= component->userChannelCount, paInvalidChannelCount );
 
1034
 
 
1035
        /* try to set the sample rate */
 
1036
        ENSURE_( ioctl( component->fd, SNDCTL_DSP_SPEED, &sr ), paInvalidSampleRate );
 
1037
 
 
1038
        /* reject if there's no sample rate within 1% of the one requested */
 
1039
        if( (fabs( sampleRate - sr ) / sampleRate) > 0.01 )
 
1040
        {
 
1041
            PA_DEBUG(("%s: Wanted %f, closest sample rate was %d\n", __FUNCTION__, sampleRate, sr ));
 
1042
            PA_ENSURE( paInvalidSampleRate );
 
1043
        }
 
1044
 
 
1045
        ENSURE_( ioctl( component->fd, streamMode == StreamMode_In ? SNDCTL_DSP_GETISPACE : SNDCTL_DSP_GETOSPACE, &bufInfo ),
 
1046
                paUnanticipatedHostError );
 
1047
        component->numBufs = bufInfo.fragstotal;
 
1048
 
 
1049
        /* This needs to be the last ioctl call before the first read/write, according to the OSS programmer's guide */
 
1050
        ENSURE_( ioctl( component->fd, SNDCTL_DSP_GETBLKSIZE, &bytesPerBuf ), paUnanticipatedHostError );
 
1051
 
 
1052
        component->hostFrames = bytesPerBuf / Pa_GetSampleSize( hostFormat ) / chans;
 
1053
        component->hostChannelCount = chans;
 
1054
        component->hostFormat = hostFormat;
 
1055
    }
 
1056
    else
 
1057
    {
 
1058
        component->hostFormat = master->hostFormat;
 
1059
        component->hostFrames = master->hostFrames;
 
1060
        component->hostChannelCount = master->hostChannelCount;
 
1061
        component->numBufs = master->numBufs;
 
1062
    }
 
1063
 
 
1064
    PA_UNLESS( component->buffer = PaUtil_AllocateMemory( PaOssStreamComponent_BufferSize( component ) ),
 
1065
            paInsufficientMemory );
 
1066
 
 
1067
error:
 
1068
    return result;
 
1069
}
 
1070
 
 
1071
static PaError PaOssStreamComponent_Read( PaOssStreamComponent *component, unsigned long *frames )
 
1072
{
 
1073
    PaError result = paNoError;
 
1074
    size_t len = *frames * PaOssStreamComponent_FrameSize( component );
 
1075
    ssize_t bytesRead;
 
1076
 
 
1077
    ENSURE_( bytesRead = read( component->fd, component->buffer, len ), paUnanticipatedHostError );
 
1078
    *frames = bytesRead / PaOssStreamComponent_FrameSize( component );
 
1079
    /* TODO: Handle condition where number of frames read doesn't equal number of frames requested */
 
1080
 
 
1081
error:
 
1082
    return result;
 
1083
}
 
1084
 
 
1085
static PaError PaOssStreamComponent_Write( PaOssStreamComponent *component, unsigned long *frames )
 
1086
{
 
1087
    PaError result = paNoError;
 
1088
    size_t len = *frames * PaOssStreamComponent_FrameSize( component );
 
1089
    ssize_t bytesWritten;
 
1090
 
 
1091
    ENSURE_( bytesWritten = write( component->fd, component->buffer, len ), paUnanticipatedHostError );
 
1092
    *frames = bytesWritten / PaOssStreamComponent_FrameSize( component );
 
1093
    /* TODO: Handle condition where number of frames written doesn't equal number of frames requested */
 
1094
 
 
1095
error:
 
1096
    return result;
 
1097
}
 
1098
 
 
1099
/** Configure the stream according to input/output parameters.
 
1100
 *
 
1101
 * Aspect StreamChannels: The minimum number of channels supported by the device may exceed that requested by
 
1102
 * the user, if so we'll record the actual number of host channels and adapt later.
 
1103
 */
 
1104
static PaError PaOssStream_Configure( PaOssStream *stream, double sampleRate, unsigned long framesPerBuffer,
 
1105
        double *inputLatency, double *outputLatency )
 
1106
{
 
1107
    PaError result = paNoError;
 
1108
    int duplex = stream->capture && stream->playback;
 
1109
    unsigned long framesPerHostBuffer = 0;
 
1110
 
 
1111
    /* We should request full duplex first thing after opening the device */
 
1112
    if( duplex && stream->sharedDevice )
 
1113
        ENSURE_( ioctl( stream->capture->fd, SNDCTL_DSP_SETDUPLEX, 0 ), paUnanticipatedHostError );
 
1114
 
 
1115
    if( stream->capture )
 
1116
    {
 
1117
        PaOssStreamComponent *component = stream->capture;
 
1118
        PA_ENSURE( PaOssStreamComponent_Configure( component, sampleRate, framesPerBuffer, StreamMode_In,
 
1119
                    NULL ) );
 
1120
 
 
1121
        assert( component->hostChannelCount > 0 );
 
1122
        assert( component->hostFrames > 0 );
 
1123
 
 
1124
        *inputLatency = component->hostFrames * (component->numBufs - 1) / sampleRate;
 
1125
    }
 
1126
    if( stream->playback )
 
1127
    {
 
1128
        PaOssStreamComponent *component = stream->playback, *master = stream->sharedDevice ? stream->capture : NULL;
 
1129
        PA_ENSURE( PaOssStreamComponent_Configure( component, sampleRate, framesPerBuffer, StreamMode_Out,
 
1130
                    master ) );
 
1131
 
 
1132
        assert( component->hostChannelCount > 0 );
 
1133
        assert( component->hostFrames > 0 );
 
1134
 
 
1135
        *outputLatency = component->hostFrames * (component->numBufs - 1) / sampleRate;
 
1136
    }
 
1137
 
 
1138
    if( duplex )
 
1139
        framesPerHostBuffer = PA_MIN( stream->capture->hostFrames, stream->playback->hostFrames );
 
1140
    else if( stream->capture )
 
1141
        framesPerHostBuffer = stream->capture->hostFrames;
 
1142
    else if( stream->playback )
 
1143
        framesPerHostBuffer = stream->playback->hostFrames;
 
1144
 
 
1145
    stream->framesPerHostBuffer = framesPerHostBuffer;
 
1146
    stream->pollTimeout = (int) ceil( 1e6 * framesPerHostBuffer / sampleRate );    /* Period in usecs, rounded up */
 
1147
 
 
1148
    stream->sampleRate = stream->streamRepresentation.streamInfo.sampleRate = sampleRate;
 
1149
 
 
1150
error:
 
1151
    return result;
 
1152
}
 
1153
 
 
1154
/* see pa_hostapi.h for a list of validity guarantees made about OpenStream parameters */
 
1155
 
 
1156
/** Open a PA OSS stream.
 
1157
 *
 
1158
 * Aspect StreamChannels: The number of channels is specified per direction (in/out), and can differ between the
 
1159
 * two. However, OSS doesn't support separate configuration spaces for capture and playback so if both
 
1160
 * directions are the same device we will demand the same number of channels. The number of channels can range
 
1161
 * from 1 to the maximum supported by the device.
 
1162
 *
 
1163
 * Aspect BufferSettings: If framesPerBuffer != paFramesPerBufferUnspecified the number of frames per callback
 
1164
 * must reflect this, in addition the host latency per device should approximate the corresponding
 
1165
 * suggestedLatency. Based on these constraints we need to determine a number of frames per host buffer that
 
1166
 * both capture and playback can agree on (they can be different devices), the buffer processor can adapt
 
1167
 * between host and user buffer size, but the ratio should preferably be integral.
 
1168
 */
 
1169
static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
 
1170
                           PaStream** s,
 
1171
                           const PaStreamParameters *inputParameters,
 
1172
                           const PaStreamParameters *outputParameters,
 
1173
                           double sampleRate,
 
1174
                           unsigned long framesPerBuffer,
 
1175
                           PaStreamFlags streamFlags,
 
1176
                           PaStreamCallback *streamCallback,
 
1177
                           void *userData )
 
1178
{
 
1179
    PaError result = paNoError;
 
1180
    PaOSSHostApiRepresentation *ossHostApi = (PaOSSHostApiRepresentation*)hostApi;
 
1181
    PaOssStream *stream = NULL;
 
1182
    int inputChannelCount = 0, outputChannelCount = 0;
 
1183
    PaSampleFormat inputSampleFormat = 0, outputSampleFormat = 0, inputHostFormat = 0, outputHostFormat = 0;
 
1184
    const PaDeviceInfo *inputDeviceInfo = 0, *outputDeviceInfo = 0;
 
1185
    int bpInitialized = 0;
 
1186
    double inLatency = 0., outLatency = 0.;
 
1187
    int i = 0;
 
1188
 
 
1189
    /* validate platform specific flags */
 
1190
    if( (streamFlags & paPlatformSpecificFlags) != 0 )
 
1191
        return paInvalidFlag; /* unexpected platform specific flag */
 
1192
 
 
1193
    if( inputParameters )
 
1194
    {
 
1195
        /* unless alternate device specification is supported, reject the use of
 
1196
            paUseHostApiSpecificDeviceSpecification */
 
1197
        inputDeviceInfo = hostApi->deviceInfos[inputParameters->device];
 
1198
        PA_ENSURE( ValidateParameters( inputParameters, inputDeviceInfo, StreamMode_In ) );
 
1199
 
 
1200
        inputChannelCount = inputParameters->channelCount;
 
1201
        inputSampleFormat = inputParameters->sampleFormat;
 
1202
    }
 
1203
    if( outputParameters )
 
1204
    {
 
1205
        outputDeviceInfo = hostApi->deviceInfos[outputParameters->device];
 
1206
        PA_ENSURE( ValidateParameters( outputParameters, outputDeviceInfo, StreamMode_Out ) );
 
1207
 
 
1208
        outputChannelCount = outputParameters->channelCount;
 
1209
        outputSampleFormat = outputParameters->sampleFormat;
 
1210
    }
 
1211
 
 
1212
    /* Aspect StreamChannels: We currently demand that number of input and output channels are the same, if the same
 
1213
     * device is opened for both directions
 
1214
     */
 
1215
    if( inputChannelCount > 0 && outputChannelCount > 0 )
 
1216
    {
 
1217
        if( inputParameters->device == outputParameters->device )
 
1218
        {
 
1219
            if( inputParameters->channelCount != outputParameters->channelCount )
 
1220
                return paInvalidChannelCount;
 
1221
        }
 
1222
    }
 
1223
 
 
1224
    /* Round framesPerBuffer to the next power-of-two to make OSS happy. */
 
1225
    if( framesPerBuffer != paFramesPerBufferUnspecified )
 
1226
    {
 
1227
        framesPerBuffer &= INT_MAX;
 
1228
        for (i = 1; framesPerBuffer > i; i <<= 1) ;
 
1229
        framesPerBuffer = i;
 
1230
    }
 
1231
 
 
1232
    /* allocate and do basic initialization of the stream structure */
 
1233
    PA_UNLESS( stream = (PaOssStream*)PaUtil_AllocateMemory( sizeof(PaOssStream) ), paInsufficientMemory );
 
1234
    PA_ENSURE( PaOssStream_Initialize( stream, inputParameters, outputParameters, streamCallback, userData, streamFlags, ossHostApi ) );
 
1235
 
 
1236
    PA_ENSURE( PaOssStream_Configure( stream, sampleRate, framesPerBuffer, &inLatency, &outLatency ) );
 
1237
 
 
1238
    PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate );
 
1239
 
 
1240
    if( inputParameters )
 
1241
    {
 
1242
        inputHostFormat = stream->capture->hostFormat;
 
1243
        stream->streamRepresentation.streamInfo.inputLatency = inLatency +
 
1244
            PaUtil_GetBufferProcessorInputLatency( &stream->bufferProcessor ) / sampleRate;
 
1245
    }
 
1246
    if( outputParameters )
 
1247
    {
 
1248
        outputHostFormat = stream->playback->hostFormat;
 
1249
        stream->streamRepresentation.streamInfo.outputLatency = outLatency +
 
1250
            PaUtil_GetBufferProcessorOutputLatency( &stream->bufferProcessor ) / sampleRate;
 
1251
    }
 
1252
 
 
1253
    /* Initialize buffer processor with fixed host buffer size.
 
1254
     * Aspect StreamSampleFormat: Here we commit the user and host sample formats, PA infrastructure will
 
1255
     * convert between the two.
 
1256
     */
 
1257
    PA_ENSURE( PaUtil_InitializeBufferProcessor( &stream->bufferProcessor,
 
1258
              inputChannelCount, inputSampleFormat, inputHostFormat, outputChannelCount, outputSampleFormat,
 
1259
              outputHostFormat, sampleRate, streamFlags, framesPerBuffer, stream->framesPerHostBuffer,
 
1260
              paUtilFixedHostBufferSize, streamCallback, userData ) );
 
1261
    bpInitialized = 1;
 
1262
 
 
1263
    *s = (PaStream*)stream;
 
1264
 
 
1265
    return result;
 
1266
 
 
1267
error:
 
1268
    if( bpInitialized )
 
1269
        PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
 
1270
    if( stream )
 
1271
        PaOssStream_Terminate( stream );
 
1272
 
 
1273
    return result;
 
1274
}
 
1275
 
 
1276
/*! Poll on I/O filedescriptors.
 
1277
 
 
1278
  Poll till we've determined there's data for read or write. In the full-duplex case,
 
1279
  we don't want to hang around forever waiting for either input or output frames, so
 
1280
  whenever we have a timed out filedescriptor we check if we're nearing under/overrun
 
1281
  for the other direction (critical limit set at one buffer). If so, we exit the waiting
 
1282
  state, and go on with what we got. We align the number of frames on a host buffer
 
1283
  boundary because it is possible that the buffer size differs for the two directions and
 
1284
  the host buffer size is a compromise between the two.
 
1285
  */
 
1286
static PaError PaOssStream_WaitForFrames( PaOssStream *stream, unsigned long *frames )
 
1287
{
 
1288
    PaError result = paNoError;
 
1289
    int pollPlayback = 0, pollCapture = 0;
 
1290
    int captureAvail = INT_MAX, playbackAvail = INT_MAX, commonAvail;
 
1291
    audio_buf_info bufInfo;
 
1292
    /* int ofs = 0, nfds = stream->nfds; */
 
1293
    fd_set readFds, writeFds;
 
1294
    int nfds = 0;
 
1295
    struct timeval selectTimeval = {0, 0};
 
1296
    unsigned long timeout = stream->pollTimeout;    /* In usecs */
 
1297
    int captureFd = -1, playbackFd = -1;
 
1298
 
 
1299
    assert( stream );
 
1300
    assert( frames );
 
1301
 
 
1302
    if( stream->capture )
 
1303
    {
 
1304
        pollCapture = 1;
 
1305
        captureFd = stream->capture->fd;
 
1306
        /* stream->capture->pfd->events = POLLIN; */
 
1307
    }
 
1308
    if( stream->playback )
 
1309
    {
 
1310
        pollPlayback = 1;
 
1311
        playbackFd = stream->playback->fd;
 
1312
        /* stream->playback->pfd->events = POLLOUT; */
 
1313
    }
 
1314
 
 
1315
    FD_ZERO( &readFds );
 
1316
    FD_ZERO( &writeFds );
 
1317
 
 
1318
    while( pollPlayback || pollCapture )
 
1319
    {
 
1320
        pthread_testcancel();
 
1321
 
 
1322
        /* select may modify the timeout parameter */
 
1323
        selectTimeval.tv_usec = timeout;
 
1324
        nfds = 0;
 
1325
 
 
1326
        if( pollCapture )
 
1327
        {
 
1328
            FD_SET( captureFd, &readFds );
 
1329
            nfds = captureFd + 1;
 
1330
        }
 
1331
        if( pollPlayback )
 
1332
        {
 
1333
            FD_SET( playbackFd, &writeFds );
 
1334
            nfds = PA_MAX( nfds, playbackFd + 1 );
 
1335
        }
 
1336
        ENSURE_( select( nfds, &readFds, &writeFds, NULL, &selectTimeval ), paUnanticipatedHostError );
 
1337
        /*
 
1338
        if( poll( stream->pfds + ofs, nfds, stream->pollTimeout ) < 0 )
 
1339
        {
 
1340
 
 
1341
            ENSURE_( -1, paUnanticipatedHostError );
 
1342
        }
 
1343
        */
 
1344
        pthread_testcancel();
 
1345
 
 
1346
        if( pollCapture )
 
1347
        {
 
1348
            if( FD_ISSET( captureFd, &readFds ) )
 
1349
            {
 
1350
                FD_CLR( captureFd, &readFds );
 
1351
                pollCapture = 0;
 
1352
            }
 
1353
            /*
 
1354
            if( stream->capture->pfd->revents & POLLIN )
 
1355
            {
 
1356
                --nfds;
 
1357
                ++ofs;
 
1358
                pollCapture = 0;
 
1359
            }
 
1360
            */
 
1361
            else if( stream->playback ) /* Timed out, go on with playback? */
 
1362
            {
 
1363
                /*PA_DEBUG(( "%s: Trying to poll again for capture frames, pollTimeout: %d\n",
 
1364
                            __FUNCTION__, stream->pollTimeout ));*/
 
1365
            }
 
1366
        }
 
1367
        if( pollPlayback )
 
1368
        {
 
1369
            if( FD_ISSET( playbackFd, &writeFds ) )
 
1370
            {
 
1371
                FD_CLR( playbackFd, &writeFds );
 
1372
                pollPlayback = 0;
 
1373
            }
 
1374
            /*
 
1375
            if( stream->playback->pfd->revents & POLLOUT )
 
1376
            {
 
1377
                --nfds;
 
1378
                pollPlayback = 0;
 
1379
            }
 
1380
            */
 
1381
            else if( stream->capture )  /* Timed out, go on with capture? */
 
1382
            {
 
1383
                /*PA_DEBUG(( "%s: Trying to poll again for playback frames, pollTimeout: %d\n\n",
 
1384
                            __FUNCTION__, stream->pollTimeout ));*/
 
1385
            }
 
1386
        }
 
1387
    }
 
1388
 
 
1389
    if( stream->capture )
 
1390
    {
 
1391
        ENSURE_( ioctl( captureFd, SNDCTL_DSP_GETISPACE, &bufInfo ), paUnanticipatedHostError );
 
1392
        captureAvail = bufInfo.fragments * stream->capture->hostFrames;
 
1393
        if( !captureAvail )
 
1394
            PA_DEBUG(( "%s: captureAvail: 0\n", __FUNCTION__ ));
 
1395
 
 
1396
        captureAvail = captureAvail == 0 ? INT_MAX : captureAvail;      /* Disregard if zero */
 
1397
    }
 
1398
    if( stream->playback )
 
1399
    {
 
1400
        ENSURE_( ioctl( playbackFd, SNDCTL_DSP_GETOSPACE, &bufInfo ), paUnanticipatedHostError );
 
1401
        playbackAvail = bufInfo.fragments * stream->playback->hostFrames;
 
1402
        if( !playbackAvail )
 
1403
        {
 
1404
            PA_DEBUG(( "%s: playbackAvail: 0\n", __FUNCTION__ ));
 
1405
        }
 
1406
 
 
1407
        playbackAvail = playbackAvail == 0 ? INT_MAX : playbackAvail;      /* Disregard if zero */
 
1408
    }
 
1409
 
 
1410
    commonAvail = PA_MIN( captureAvail, playbackAvail );
 
1411
    if( commonAvail == INT_MAX )
 
1412
        commonAvail = 0;
 
1413
    commonAvail -= commonAvail % stream->framesPerHostBuffer;
 
1414
 
 
1415
    assert( commonAvail != INT_MAX );
 
1416
    assert( commonAvail >= 0 );
 
1417
    *frames = commonAvail;
 
1418
 
 
1419
error:
 
1420
    return result;
 
1421
}
 
1422
 
 
1423
/** Prepare stream for capture/playback.
 
1424
 *
 
1425
 * In order to synchronize capture and playback properly we use the SETTRIGGER command.
 
1426
 */
 
1427
static PaError PaOssStream_Prepare( PaOssStream *stream )
 
1428
{
 
1429
    PaError result = paNoError;
 
1430
    int enableBits = 0;
 
1431
 
 
1432
    if( stream->triggered )
 
1433
        return result;
 
1434
 
 
1435
    /* The OSS reference instructs us to clear direction bits before setting them.*/
 
1436
    if( stream->playback )
 
1437
        ENSURE_( ioctl( stream->playback->fd, SNDCTL_DSP_SETTRIGGER, &enableBits ), paUnanticipatedHostError );
 
1438
    if( stream->capture )
 
1439
        ENSURE_( ioctl( stream->capture->fd, SNDCTL_DSP_SETTRIGGER, &enableBits ), paUnanticipatedHostError );
 
1440
 
 
1441
    if( stream->playback )
 
1442
    {
 
1443
        size_t bufSz = PaOssStreamComponent_BufferSize( stream->playback );
 
1444
        memset( stream->playback->buffer, 0, bufSz );
 
1445
 
 
1446
        /* Looks like we have to turn off blocking before we try this, but if we don't fill the buffer
 
1447
         * OSS will complain. */
 
1448
        PA_ENSURE( ModifyBlocking( stream->playback->fd, 0 ) );
 
1449
        while (1)
 
1450
        {
 
1451
            if( write( stream->playback->fd, stream->playback->buffer, bufSz ) < 0 )
 
1452
                break;
 
1453
        }
 
1454
        PA_ENSURE( ModifyBlocking( stream->playback->fd, 1 ) );
 
1455
    }
 
1456
 
 
1457
    if( stream->sharedDevice )
 
1458
    {
 
1459
        enableBits = PCM_ENABLE_INPUT | PCM_ENABLE_OUTPUT;
 
1460
        ENSURE_( ioctl( stream->capture->fd, SNDCTL_DSP_SETTRIGGER, &enableBits ), paUnanticipatedHostError );
 
1461
    }
 
1462
    else
 
1463
    {
 
1464
        if( stream->capture )
 
1465
        {
 
1466
            enableBits = PCM_ENABLE_INPUT;
 
1467
            ENSURE_( ioctl( stream->capture->fd, SNDCTL_DSP_SETTRIGGER, &enableBits ), paUnanticipatedHostError );
 
1468
        }
 
1469
        if( stream->playback )
 
1470
        {
 
1471
            enableBits = PCM_ENABLE_OUTPUT;
 
1472
            ENSURE_( ioctl( stream->playback->fd, SNDCTL_DSP_SETTRIGGER, &enableBits ), paUnanticipatedHostError );
 
1473
        }
 
1474
    }
 
1475
 
 
1476
    /* Ok, we have triggered the stream */
 
1477
    stream->triggered = 1;
 
1478
 
 
1479
error:
 
1480
    return result;
 
1481
}
 
1482
 
 
1483
/** Stop audio processing
 
1484
 *
 
1485
 */
 
1486
static PaError PaOssStream_Stop( PaOssStream *stream, int abort )
 
1487
{
 
1488
    PaError result = paNoError;
 
1489
 
 
1490
    /* Looks like the only safe way to stop audio without reopening the device is SNDCTL_DSP_POST.
 
1491
     * Also disable capture/playback till the stream is started again.
 
1492
     */
 
1493
    int captureErr = 0, playbackErr = 0;
 
1494
    if( stream->capture )
 
1495
    {
 
1496
        if( (captureErr = ioctl( stream->capture->fd, SNDCTL_DSP_POST, 0 )) < 0 )
 
1497
        {
 
1498
            PA_DEBUG(( "%s: Failed to stop capture device, error: %d\n", __FUNCTION__, captureErr ));
 
1499
        }
 
1500
    }
 
1501
    if( stream->playback && !stream->sharedDevice )
 
1502
    {
 
1503
        if( (playbackErr = ioctl( stream->playback->fd, SNDCTL_DSP_POST, 0 )) < 0 )
 
1504
        {
 
1505
            PA_DEBUG(( "%s: Failed to stop playback device, error: %d\n", __FUNCTION__, playbackErr ));
 
1506
        }
 
1507
    }
 
1508
 
 
1509
    if( captureErr || playbackErr )
 
1510
    {
 
1511
        result = paUnanticipatedHostError;
 
1512
    }
 
1513
 
 
1514
    return result;
 
1515
}
 
1516
 
 
1517
/** Clean up after thread exit.
 
1518
 *
 
1519
 * Aspect StreamState: If the user has registered a streamFinishedCallback it will be called here
 
1520
 */
 
1521
static void OnExit( void *data )
 
1522
{
 
1523
    PaOssStream *stream = (PaOssStream *) data;
 
1524
    assert( data );
 
1525
 
 
1526
    PaUtil_ResetCpuLoadMeasurer( &stream->cpuLoadMeasurer );
 
1527
 
 
1528
    PaOssStream_Stop( stream, stream->callbackAbort );
 
1529
 
 
1530
    PA_DEBUG(( "OnExit: Stoppage\n" ));
 
1531
 
 
1532
    /* Eventually notify user all buffers have played */
 
1533
    if( stream->streamRepresentation.streamFinishedCallback )
 
1534
        stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
 
1535
 
 
1536
    stream->callbackAbort = 0;      /* Clear state */
 
1537
    stream->isActive = 0;
 
1538
}
 
1539
 
 
1540
static PaError SetUpBuffers( PaOssStream *stream, unsigned long framesAvail )
 
1541
{
 
1542
    PaError result = paNoError;
 
1543
 
 
1544
    if( stream->capture )
 
1545
    {
 
1546
        PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, 0, stream->capture->buffer,
 
1547
                stream->capture->hostChannelCount );
 
1548
        PaUtil_SetInputFrameCount( &stream->bufferProcessor, framesAvail );
 
1549
    }
 
1550
    if( stream->playback )
 
1551
    {
 
1552
        PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, 0, stream->playback->buffer,
 
1553
                stream->playback->hostChannelCount );
 
1554
        PaUtil_SetOutputFrameCount( &stream->bufferProcessor, framesAvail );
 
1555
    }
 
1556
 
 
1557
    return result;
 
1558
}
 
1559
 
 
1560
/** Thread procedure for callback processing.
 
1561
 *
 
1562
 * Aspect StreamState: StartStream will wait on this to initiate audio processing, useful in case the
 
1563
 * callback should be used for buffer priming. When the stream is cancelled a separate function will
 
1564
 * take care of the transition to the Callback Finished state (the stream isn't considered Stopped
 
1565
 * before StopStream() or AbortStream() are called).
 
1566
 */
 
1567
static void *PaOSS_AudioThreadProc( void *userData )
 
1568
{
 
1569
    PaError result = paNoError;
 
1570
    PaOssStream *stream = (PaOssStream*)userData;
 
1571
    unsigned long framesAvail = 0, framesProcessed = 0;
 
1572
    int callbackResult = paContinue;
 
1573
    int triggered = stream->triggered;  /* See if SNDCTL_DSP_TRIGGER has been issued already */
 
1574
    int initiateProcessing = triggered;    /* Already triggered? */
 
1575
    PaStreamCallbackFlags cbFlags = 0;  /* We might want to keep state across iterations */
 
1576
    PaStreamCallbackTimeInfo timeInfo = {0,0,0}; /* TODO: IMPLEMENT ME */
 
1577
 
 
1578
    /*
 
1579
#if ( SOUND_VERSION > 0x030904 )
 
1580
        audio_errinfo errinfo;
 
1581
#endif
 
1582
*/
 
1583
 
 
1584
    assert( stream );
 
1585
 
 
1586
    pthread_cleanup_push( &OnExit, stream );    /* Execute OnExit when exiting */
 
1587
 
 
1588
    /* The first time the stream is started we use SNDCTL_DSP_TRIGGER to accurately start capture and
 
1589
     * playback in sync, when the stream is restarted after being stopped we simply start by reading/
 
1590
     * writing.
 
1591
     */
 
1592
    PA_ENSURE( PaOssStream_Prepare( stream ) );
 
1593
 
 
1594
    /* If we are to initiate processing implicitly by reading/writing data, we start off in blocking mode */
 
1595
    if( initiateProcessing )
 
1596
    {
 
1597
        /* Make sure devices are in blocking mode */
 
1598
        if( stream->capture )
 
1599
            ModifyBlocking( stream->capture->fd, 1 );
 
1600
        if( stream->playback )
 
1601
            ModifyBlocking( stream->playback->fd, 1 );
 
1602
    }
 
1603
 
 
1604
    while( 1 )
 
1605
    {
 
1606
        pthread_testcancel();
 
1607
 
 
1608
        if( stream->callbackStop && callbackResult == paContinue )
 
1609
        {
 
1610
            PA_DEBUG(( "Setting callbackResult to paComplete\n" ));
 
1611
            callbackResult = paComplete;
 
1612
        }
 
1613
 
 
1614
        /* Aspect StreamState: Because of the messy OSS scheme we can't explicitly trigger device start unless
 
1615
         * the stream has been recently started, we will have to go right ahead and read/write in blocking
 
1616
         * fashion to trigger operation. Therefore we begin with processing one host buffer before we switch
 
1617
         * to non-blocking mode.
 
1618
         */
 
1619
        if( !initiateProcessing )
 
1620
        {
 
1621
            /* Wait on available frames */
 
1622
            PA_ENSURE( PaOssStream_WaitForFrames( stream, &framesAvail ) );
 
1623
            assert( framesAvail % stream->framesPerHostBuffer == 0 );
 
1624
        }
 
1625
        else
 
1626
        {
 
1627
            framesAvail = stream->framesPerHostBuffer;
 
1628
        }
 
1629
 
 
1630
        while( framesAvail > 0 )
 
1631
        {
 
1632
            unsigned long frames = framesAvail;
 
1633
 
 
1634
            pthread_testcancel();
 
1635
 
 
1636
            PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer );
 
1637
 
 
1638
            /* Read data */
 
1639
            if ( stream->capture )
 
1640
            {
 
1641
                PA_ENSURE( PaOssStreamComponent_Read( stream->capture, &frames ) );
 
1642
                if( frames < framesAvail )
 
1643
                {
 
1644
                    PA_DEBUG(( "Read %lu less frames than requested\n", framesAvail - frames ));
 
1645
                    framesAvail = frames;
 
1646
                }
 
1647
            }
 
1648
 
 
1649
#if ( SOUND_VERSION >= 0x030904 )
 
1650
            /*
 
1651
               Check with OSS to see if there have been any under/overruns
 
1652
               since last time we checked.
 
1653
               */
 
1654
            /*
 
1655
            if( ioctl( stream->deviceHandle, SNDCTL_DSP_GETERROR, &errinfo ) >= 0 )
 
1656
            {
 
1657
                if( errinfo.play_underruns )
 
1658
                    cbFlags |= paOutputUnderflow ;
 
1659
                if( errinfo.record_underruns )
 
1660
                    cbFlags |= paInputUnderflow ;
 
1661
            }
 
1662
            else
 
1663
                PA_DEBUG(( "SNDCTL_DSP_GETERROR command failed: %s\n", strerror( errno ) ));
 
1664
                */
 
1665
#endif
 
1666
 
 
1667
            PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo,
 
1668
                    cbFlags );
 
1669
            cbFlags = 0;
 
1670
            PA_ENSURE( SetUpBuffers( stream, framesAvail ) );
 
1671
 
 
1672
            framesProcessed = PaUtil_EndBufferProcessing( &stream->bufferProcessor,
 
1673
                    &callbackResult );
 
1674
            assert( framesProcessed == framesAvail );
 
1675
            PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed );
 
1676
 
 
1677
            if ( stream->playback )
 
1678
            {
 
1679
                frames = framesAvail;
 
1680
 
 
1681
                PA_ENSURE( PaOssStreamComponent_Write( stream->playback, &frames ) );
 
1682
                if( frames < framesAvail )
 
1683
                {
 
1684
                    /* TODO: handle bytesWritten != bytesRequested (slippage?) */
 
1685
                    PA_DEBUG(( "Wrote %lu less frames than requested\n", framesAvail - frames ));
 
1686
                }
 
1687
            }
 
1688
 
 
1689
            framesAvail -= framesProcessed;
 
1690
            stream->framesProcessed += framesProcessed;
 
1691
 
 
1692
            if( callbackResult != paContinue )
 
1693
                break;
 
1694
        }
 
1695
 
 
1696
        if( initiateProcessing || !triggered )
 
1697
        {
 
1698
            /* Non-blocking */
 
1699
            if( stream->capture )
 
1700
                PA_ENSURE( ModifyBlocking( stream->capture->fd, 0 ) );
 
1701
            if( stream->playback && !stream->sharedDevice )
 
1702
                PA_ENSURE( ModifyBlocking( stream->playback->fd, 0 ) );
 
1703
 
 
1704
            initiateProcessing = 0;
 
1705
            sem_post( &stream->semaphore );
 
1706
        }
 
1707
 
 
1708
        if( callbackResult != paContinue )
 
1709
        {
 
1710
            stream->callbackAbort = callbackResult == paAbort;
 
1711
            if( stream->callbackAbort || PaUtil_IsBufferProcessorOutputEmpty( &stream->bufferProcessor ) )
 
1712
                break;
 
1713
        }
 
1714
    }
 
1715
 
 
1716
    pthread_cleanup_pop( 1 );
 
1717
 
 
1718
error:
 
1719
    pthread_exit( NULL );
 
1720
}
 
1721
 
 
1722
/** Close the stream.
 
1723
 *
 
1724
 */
 
1725
static PaError CloseStream( PaStream* s )
 
1726
{
 
1727
    PaError result = paNoError;
 
1728
    PaOssStream *stream = (PaOssStream*)s;
 
1729
 
 
1730
    assert( stream );
 
1731
 
 
1732
    PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
 
1733
    PaOssStream_Terminate( stream );
 
1734
 
 
1735
    return result;
 
1736
}
 
1737
 
 
1738
/** Start the stream.
 
1739
 *
 
1740
 * Aspect StreamState: After returning, the stream shall be in the Active state, implying that an eventual
 
1741
 * callback will be repeatedly called in a separate thread. If a separate thread is started this function
 
1742
 * will block untill it has started processing audio, otherwise audio processing is started directly.
 
1743
 */
 
1744
static PaError StartStream( PaStream *s )
 
1745
{
 
1746
    PaError result = paNoError;
 
1747
    PaOssStream *stream = (PaOssStream*)s;
 
1748
 
 
1749
    stream->isActive = 1;
 
1750
    stream->isStopped = 0;
 
1751
    stream->lastPosPtr = 0;
 
1752
    stream->lastStreamBytes = 0;
 
1753
    stream->framesProcessed = 0;
 
1754
 
 
1755
    /* only use the thread for callback streams */
 
1756
    if( stream->bufferProcessor.streamCallback )
 
1757
    {
 
1758
        PA_ENSURE( PaUtil_StartThreading( &stream->threading, &PaOSS_AudioThreadProc, stream ) );
 
1759
        sem_wait( &stream->semaphore );
 
1760
    }
 
1761
    else
 
1762
        PA_ENSURE( PaOssStream_Prepare( stream ) );
 
1763
 
 
1764
error:
 
1765
    return result;
 
1766
}
 
1767
 
 
1768
static PaError RealStop( PaOssStream *stream, int abort )
 
1769
{
 
1770
    PaError result = paNoError;
 
1771
 
 
1772
    if( stream->callbackMode )
 
1773
    {
 
1774
        if( abort )
 
1775
            stream->callbackAbort = 1;
 
1776
        else
 
1777
            stream->callbackStop = 1;
 
1778
 
 
1779
        PA_ENSURE( PaUtil_CancelThreading( &stream->threading, !abort, NULL ) );
 
1780
 
 
1781
        stream->callbackStop = stream->callbackAbort = 0;
 
1782
    }
 
1783
    else
 
1784
        PA_ENSURE( PaOssStream_Stop( stream, abort ) );
 
1785
 
 
1786
    stream->isStopped = 1;
 
1787
 
 
1788
error:
 
1789
    return result;
 
1790
}
 
1791
 
 
1792
/** Stop the stream.
 
1793
 *
 
1794
 * Aspect StreamState: This will cause the stream to transition to the Stopped state, playing all enqueued
 
1795
 * buffers.
 
1796
 */
 
1797
static PaError StopStream( PaStream *s )
 
1798
{
 
1799
    return RealStop( (PaOssStream *)s, 0 );
 
1800
}
 
1801
 
 
1802
/** Abort the stream.
 
1803
 *
 
1804
 * Aspect StreamState: This will cause the stream to transition to the Stopped state, discarding all enqueued
 
1805
 * buffers. Note that the buffers are not currently correctly discarded, this is difficult without closing
 
1806
 * the OSS device.
 
1807
 */
 
1808
static PaError AbortStream( PaStream *s )
 
1809
{
 
1810
    return RealStop( (PaOssStream *)s, 1 );
 
1811
}
 
1812
 
 
1813
/** Is the stream in the Stopped state.
 
1814
 *
 
1815
 */
 
1816
static PaError IsStreamStopped( PaStream *s )
 
1817
{
 
1818
    PaOssStream *stream = (PaOssStream*)s;
 
1819
 
 
1820
    return (stream->isStopped);
 
1821
}
 
1822
 
 
1823
/** Is the stream in the Active state.
 
1824
 *
 
1825
 */
 
1826
static PaError IsStreamActive( PaStream *s )
 
1827
{
 
1828
    PaOssStream *stream = (PaOssStream*)s;
 
1829
 
 
1830
    return (stream->isActive);
 
1831
}
 
1832
 
 
1833
static PaTime GetStreamTime( PaStream *s )
 
1834
{
 
1835
    PaOssStream *stream = (PaOssStream*)s;
 
1836
    count_info info;
 
1837
    int delta;
 
1838
 
 
1839
    if( stream->playback ) {
 
1840
        if( ioctl( stream->playback->fd, SNDCTL_DSP_GETOPTR, &info) == 0 ) {
 
1841
            delta = ( info.bytes - stream->lastPosPtr ) /* & 0x000FFFFF*/;
 
1842
            return (float)(stream->lastStreamBytes + delta) / PaOssStreamComponent_FrameSize( stream->playback ) / stream->sampleRate;
 
1843
        }
 
1844
    }
 
1845
    else {
 
1846
        if (ioctl( stream->capture->fd, SNDCTL_DSP_GETIPTR, &info) == 0) {
 
1847
            delta = (info.bytes - stream->lastPosPtr) /*& 0x000FFFFF*/;
 
1848
            return (float)(stream->lastStreamBytes + delta) / PaOssStreamComponent_FrameSize( stream->capture ) / stream->sampleRate;
 
1849
        }
 
1850
    }
 
1851
 
 
1852
    /* the ioctl failed, but we can still give a coarse estimate */
 
1853
 
 
1854
    return stream->framesProcessed / stream->sampleRate;
 
1855
}
 
1856
 
 
1857
 
 
1858
static double GetStreamCpuLoad( PaStream* s )
 
1859
{
 
1860
    PaOssStream *stream = (PaOssStream*)s;
 
1861
 
 
1862
    return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer );
 
1863
}
 
1864
 
 
1865
 
 
1866
/*
 
1867
    As separate stream interfaces are used for blocking and callback
 
1868
    streams, the following functions can be guaranteed to only be called
 
1869
    for blocking streams.
 
1870
*/
 
1871
 
 
1872
 
 
1873
static PaError ReadStream( PaStream* s,
 
1874
                           void *buffer,
 
1875
                           unsigned long frames )
 
1876
{
 
1877
    PaError result = paNoError;
 
1878
    PaOssStream *stream = (PaOssStream*)s;
 
1879
    int bytesRequested, bytesRead;
 
1880
    unsigned long framesRequested;
 
1881
    void *userBuffer;
 
1882
 
 
1883
    /* If user input is non-interleaved, PaUtil_CopyInput will manipulate the channel pointers,
 
1884
     * so we copy the user provided pointers */
 
1885
    if( stream->bufferProcessor.userInputIsInterleaved )
 
1886
        userBuffer = buffer;
 
1887
    else /* Copy channels into local array */
 
1888
    {
 
1889
        userBuffer = stream->capture->userBuffers;
 
1890
        memcpy( (void *)userBuffer, buffer, sizeof (void *) * stream->capture->userChannelCount );
 
1891
    }
 
1892
 
 
1893
    while( frames )
 
1894
    {
 
1895
        framesRequested = PA_MIN( frames, stream->capture->hostFrames );
 
1896
 
 
1897
        bytesRequested = framesRequested * PaOssStreamComponent_FrameSize( stream->capture );
 
1898
        ENSURE_( (bytesRead = read( stream->capture->fd, stream->capture->buffer, bytesRequested )),
 
1899
                 paUnanticipatedHostError );
 
1900
        if ( bytesRequested != bytesRead )
 
1901
        {
 
1902
            PA_DEBUG(( "Requested %d bytes, read %d\n", bytesRequested, bytesRead ));
 
1903
            return paUnanticipatedHostError;
 
1904
        }
 
1905
 
 
1906
        PaUtil_SetInputFrameCount( &stream->bufferProcessor, stream->capture->hostFrames );
 
1907
        PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, 0, stream->capture->buffer, stream->capture->hostChannelCount );
 
1908
        PaUtil_CopyInput( &stream->bufferProcessor, &userBuffer, framesRequested );
 
1909
        frames -= framesRequested;
 
1910
    }
 
1911
 
 
1912
error:
 
1913
    return result;
 
1914
}
 
1915
 
 
1916
 
 
1917
static PaError WriteStream( PaStream *s, const void *buffer, unsigned long frames )
 
1918
{
 
1919
    PaError result = paNoError;
 
1920
    PaOssStream *stream = (PaOssStream*)s;
 
1921
    int bytesRequested, bytesWritten;
 
1922
    unsigned long framesConverted;
 
1923
    const void *userBuffer;
 
1924
 
 
1925
    /* If user output is non-interleaved, PaUtil_CopyOutput will manipulate the channel pointers,
 
1926
     * so we copy the user provided pointers */
 
1927
    if( stream->bufferProcessor.userOutputIsInterleaved )
 
1928
        userBuffer = buffer;
 
1929
    else
 
1930
    {
 
1931
        /* Copy channels into local array */
 
1932
        userBuffer = stream->playback->userBuffers;
 
1933
        memcpy( (void *)userBuffer, buffer, sizeof (void *) * stream->playback->userChannelCount );
 
1934
    }
 
1935
 
 
1936
    while( frames )
 
1937
    {
 
1938
        PaUtil_SetOutputFrameCount( &stream->bufferProcessor, stream->playback->hostFrames );
 
1939
        PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, 0, stream->playback->buffer, stream->playback->hostChannelCount );
 
1940
 
 
1941
        framesConverted = PaUtil_CopyOutput( &stream->bufferProcessor, &userBuffer, frames );
 
1942
        frames -= framesConverted;
 
1943
 
 
1944
        bytesRequested = framesConverted * PaOssStreamComponent_FrameSize( stream->playback );
 
1945
        ENSURE_( (bytesWritten = write( stream->playback->fd, stream->playback->buffer, bytesRequested )),
 
1946
                 paUnanticipatedHostError );
 
1947
 
 
1948
        if ( bytesRequested != bytesWritten )
 
1949
        {
 
1950
            PA_DEBUG(( "Requested %d bytes, wrote %d\n", bytesRequested, bytesWritten ));
 
1951
            return paUnanticipatedHostError;
 
1952
        }
 
1953
    }
 
1954
 
 
1955
error:
 
1956
    return result;
 
1957
}
 
1958
 
 
1959
 
 
1960
static signed long GetStreamReadAvailable( PaStream* s )
 
1961
{
 
1962
    PaError result = paNoError;
 
1963
    PaOssStream *stream = (PaOssStream*)s;
 
1964
    audio_buf_info info;
 
1965
 
 
1966
    ENSURE_( ioctl( stream->capture->fd, SNDCTL_DSP_GETISPACE, &info ), paUnanticipatedHostError );
 
1967
    return info.fragments * stream->capture->hostFrames;
 
1968
 
 
1969
error:
 
1970
    return result;
 
1971
}
 
1972
 
 
1973
 
 
1974
/* TODO: Compute number of allocated bytes somewhere else, can we use ODELAY with capture */
 
1975
static signed long GetStreamWriteAvailable( PaStream* s )
 
1976
{
 
1977
    PaError result = paNoError;
 
1978
    PaOssStream *stream = (PaOssStream*)s;
 
1979
    int delay = 0;
 
1980
#ifdef SNDCTL_DSP_GETODELAY
 
1981
    ENSURE_( ioctl( stream->playback->fd, SNDCTL_DSP_GETODELAY, &delay ), paUnanticipatedHostError );
 
1982
#endif
 
1983
    return (PaOssStreamComponent_BufferSize( stream->playback ) - delay) / PaOssStreamComponent_FrameSize( stream->playback );
 
1984
 
 
1985
/* Conditionally compile this to avoid warning about unused label */
 
1986
#ifdef SNDCTL_DSP_GETODELAY
 
1987
error:
 
1988
    return result;
 
1989
#endif
 
1990
}