~ubuntu-branches/ubuntu/precise/puredata/precise

« back to all changes in this revision

Viewing changes to portaudio/src/hostapi/oss/pa_unix_oss.c

  • Committer: Bazaar Package Importer
  • Author(s): Paul Brossier
  • Date: 2009-12-22 21:29:31 UTC
  • mfrom: (1.2.6 upstream) (4.1.2 squeeze)
  • Revision ID: james.westby@ubuntu.com-20091222212931-nhwkzapjwsmjao1l
Tags: 0.42.5-3
* debian/control:
  - add community site to homepage field
  - improve long description
  - remove Replaces and Conflicts fields
  - add Suggests on pd-csound, pd-pdp, pd-zexy, pd-aubio
* debian/rules: add per-arch configuration flags
* debian/patches/02_kfreebsd.diff:
  - also define pd_tilde_dllextent on FreeBSD
  - fix typo (really closing #414414 this time)
  - also add hurd glue
* debian/patches/04_hurd.diff:
  - add hurd glue and s_midi_dummy.c

Show diffs side-by-side

added added

removed removed

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