gearmulator

Emulation of classic VA synths of the late 90s/2000s that are based on Motorola 56300 family DSPs
Log | Files | Refs | Submodules | README | LICENSE

pa_unix_oss.c (71395B)


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