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  */
Get_AFMT_S16_NE(void)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  */
PaOSS_Initialize(PaUtilHostApiRepresentation ** hostApi,PaHostApiIndex hostApiIndex)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 
PaUtil_InitializeDeviceInfo(PaDeviceInfo * deviceInfo,const char * name,PaHostApiIndex hostApiIndex,int maxInputChannels,int maxOutputChannels,PaTime defaultLowInputLatency,PaTime defaultLowOutputLatency,PaTime defaultHighInputLatency,PaTime defaultHighOutputLatency,double defaultSampleRate,PaUtilAllocationGroup * allocations)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 
CalcHigherLogTwo(int n)321 static int CalcHigherLogTwo( int n )
322 {
323     int log2 = 0;
324     while( (1<<log2) < n ) log2++;
325     return log2;
326 }
327 
QueryDirection(const char * deviceName,StreamMode mode,double * defaultSampleRate,int * maxChannelCount,double * defaultLowLatency,double * defaultHighLatency)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  */
QueryDevice(char * deviceName,PaOSSHostApiRepresentation * ossApi,PaDeviceInfo ** deviceInfo)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  */
BuildDeviceList(PaOSSHostApiRepresentation * ossApi)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 
Terminate(struct PaUtilHostApiRepresentation * hostApi)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 
IsFormatSupported(struct PaUtilHostApiRepresentation * hostApi,const PaStreamParameters * inputParameters,const PaStreamParameters * outputParameters,double sampleRate)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  */
ValidateParameters(const PaStreamParameters * parameters,const PaDeviceInfo * deviceInfo,StreamMode mode)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 
PaOssStreamComponent_Initialize(PaOssStreamComponent * component,const PaStreamParameters * parameters,int callbackMode,int fd,const char * deviceName)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 
PaOssStreamComponent_Terminate(PaOssStreamComponent * component)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 
ModifyBlocking(int fd,int blocking)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  */
OpenDevices(const char * idevName,const char * odevName,int * idev,int * odev)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 
PaOssStream_Initialize(PaOssStream * stream,const PaStreamParameters * inputParameters,const PaStreamParameters * outputParameters,PaStreamCallback callback,void * userData,PaStreamFlags streamFlags,PaOSSHostApiRepresentation * ossApi)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 
PaOssStream_Terminate(PaOssStream * stream)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  */
Pa2OssFormat(PaSampleFormat paFormat,int * ossFormat)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  */
GetAvailableFormats(PaOssStreamComponent * component,PaSampleFormat * availableFormats)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 
PaOssStreamComponent_FrameSize(PaOssStreamComponent * component)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  */
PaOssStreamComponent_BufferSize(PaOssStreamComponent * component)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  */
PaOssStreamComponent_Configure(PaOssStreamComponent * component,double sampleRate,unsigned long framesPerBuffer,StreamMode streamMode,PaOssStreamComponent * master)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 
PaOssStreamComponent_Read(PaOssStreamComponent * component,unsigned long * frames)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 
PaOssStreamComponent_Write(PaOssStreamComponent * component,unsigned long * frames)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  */
PaOssStream_Configure(PaOssStream * stream,double sampleRate,unsigned long framesPerBuffer,double * inputLatency,double * outputLatency)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  */
OpenStream(struct PaUtilHostApiRepresentation * hostApi,PaStream ** s,const PaStreamParameters * inputParameters,const PaStreamParameters * outputParameters,double sampleRate,unsigned long framesPerBuffer,PaStreamFlags streamFlags,PaStreamCallback * streamCallback,void * userData)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   */
PaOssStream_WaitForFrames(PaOssStream * stream,unsigned long * frames)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  */
PaOssStream_Prepare(PaOssStream * stream)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  */
PaOssStream_Stop(PaOssStream * stream,int abort)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  */
OnExit(void * data)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 
SetUpBuffers(PaOssStream * stream,unsigned long framesAvail)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  */
PaOSS_AudioThreadProc(void * userData)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  */
CloseStream(PaStream * s)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  */
StartStream(PaStream * s)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 
RealStop(PaOssStream * stream,int abort)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  */
StopStream(PaStream * s)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  */
AbortStream(PaStream * s)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  */
IsStreamStopped(PaStream * s)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  */
IsStreamActive(PaStream * s)1880 static PaError IsStreamActive( PaStream *s )
1881 {
1882     PaOssStream *stream = (PaOssStream*)s;
1883 
1884     return (stream->isActive);
1885 }
1886 
GetStreamTime(PaStream * s)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 
GetStreamCpuLoad(PaStream * s)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 
ReadStream(PaStream * s,void * buffer,unsigned long frames)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 
WriteStream(PaStream * s,const void * buffer,unsigned long frames)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 
GetStreamReadAvailable(PaStream * s)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 */
GetStreamWriteAvailable(PaStream * s)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 
2046