~ubuntu-branches/ubuntu/trusty/sflphone/trusty

« 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: 2014-01-28 18:23:36 UTC
  • mfrom: (4.3.4 sid)
  • Revision ID: package-import@ubuntu.com-20140128182336-jrsv0k9u6cawc068
Tags: 1.3.0-1
* New upstream release 
  - Fixes "New Upstream Release" (Closes: #735846)
  - Fixes "Ringtone does not stop" (Closes: #727164)
  - Fixes "[sflphone-kde] crash on startup" (Closes: #718178)
  - Fixes "sflphone GUI crashes when call is hung up" (Closes: #736583)
* Build-Depends: ensure GnuTLS 2.6
  - libucommon-dev (>= 6.0.7-1.1), libccrtp-dev (>= 2.0.6-3)
  - Fixes "FTBFS Build-Depends libgnutls{26,28}-dev" (Closes: #722040)
* Fix "boost 1.49 is going away" unversioned Build-Depends: (Closes: #736746)
* Add Build-Depends: libsndfile-dev, nepomuk-core-dev

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
 
}