2
* $Id: pa_unix_oss.c 1238 2007-07-15 16:58:50Z aknudsen $
3
* PortAudio Portable Real-Time Audio Library
4
* Latest Version at: http://www.portaudio.com
5
* OSS implementation by:
11
* Based on the Open Source API proposed by Ross Bencina
12
* Copyright (c) 1999-2002 Ross Bencina, Phil Burk
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:
22
* The above copyright notice and this permission notice shall be
23
* included in all copies or substantial portions of the Software.
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.
35
* The text above constitutes the entire PortAudio license; however,
36
* the PortAudio community also makes the following non-binding requests:
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
54
#include <sys/ioctl.h>
60
#include <sys/types.h>
64
#include <semaphore.h>
66
#ifdef HAVE_SYS_SOUNDCARD_H
67
# include <sys/soundcard.h>
68
# define DEVICE_NAME_BASE "/dev/dsp"
69
#elif defined(HAVE_LINUX_SOUNDCARD_H)
70
# include <linux/soundcard.h>
71
# define DEVICE_NAME_BASE "/dev/dsp"
72
#elif defined(HAVE_MACHINE_SOUNDCARD_H)
73
# include <machine/soundcard.h> /* JH20010905 */
74
# define DEVICE_NAME_BASE "/dev/audio"
76
# error No sound card header file
79
#include "portaudio.h"
81
#include "pa_allocation.h"
82
#include "pa_hostapi.h"
83
#include "pa_stream.h"
84
#include "pa_cpuload.h"
85
#include "pa_process.h"
86
#include "pa_unix_util.h"
87
#include "pa_debugprint.h"
90
static pthread_t mainThread_;
92
/* Check return value of system call, and map it to PaError */
93
#define ENSURE_(expr, code) \
95
if( UNLIKELY( (sysErr_ = (expr)) < 0 ) ) \
97
/* PaUtil_SetLastHostErrorInfo should only be used in the main thread */ \
98
if( (code) == paUnanticipatedHostError && pthread_self() == mainThread_ ) \
100
PaUtil_SetLastHostErrorInfo( paALSA, sysErr_, strerror( errno ) ); \
103
PaUtil_DebugPrint(( "Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" )); \
110
#define AFMT_S16_NE Get_AFMT_S16_NE()
111
/*********************************************************************
112
* Some versions of OSS do not define AFMT_S16_NE. So check CPU.
113
* PowerPC is Big Endian. X86 is Little Endian.
115
static int Get_AFMT_S16_NE( void )
118
char *ptr = (char *) &testData;
119
int isLittle = ( *ptr == 1 ); /* Does address point to least significant byte? */
120
return isLittle ? AFMT_S16_LE : AFMT_S16_BE;
124
/* PaOSSHostApiRepresentation - host api datastructure specific to this implementation */
128
PaUtilHostApiRepresentation inheritedHostApiRep;
129
PaUtilStreamInterface callbackStreamInterface;
130
PaUtilStreamInterface blockingStreamInterface;
132
PaUtilAllocationGroup *allocations;
134
PaHostApiIndex hostApiIndex;
136
PaOSSHostApiRepresentation;
138
/** Per-direction structure for PaOssStream.
140
* Aspect StreamChannels: In case the user requests to open the same device for both capture and playback,
141
* but with different number of channels we will have to adapt between the number of user and host
142
* channels for at least one direction, since the configuration space is the same for both directions
149
int userChannelCount, hostChannelCount;
152
PaSampleFormat userFormat, hostFormat;
154
unsigned long hostFrames, numBufs;
155
void **userBuffers; /* For non-interleaved blocking */
156
} PaOssStreamComponent;
158
/** Implementation specific representation of a PaStream.
161
typedef struct PaOssStream
163
PaUtilStreamRepresentation streamRepresentation;
164
PaUtilCpuLoadMeasurer cpuLoadMeasurer;
165
PaUtilBufferProcessor bufferProcessor;
167
PaUtilThreading threading;
170
unsigned long framesPerHostBuffer;
171
int triggered; /* Have the devices been triggered yet (first start) */
177
double lastStreamBytes;
184
int callbackStop, callbackAbort;
186
PaOssStreamComponent *capture, *playback;
187
unsigned long pollTimeout;
197
/* prototypes for functions declared in this file */
199
static void Terminate( struct PaUtilHostApiRepresentation *hostApi );
200
static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
201
const PaStreamParameters *inputParameters,
202
const PaStreamParameters *outputParameters,
204
static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
206
const PaStreamParameters *inputParameters,
207
const PaStreamParameters *outputParameters,
209
unsigned long framesPerBuffer,
210
PaStreamFlags streamFlags,
211
PaStreamCallback *streamCallback,
213
static PaError CloseStream( PaStream* stream );
214
static PaError StartStream( PaStream *stream );
215
static PaError StopStream( PaStream *stream );
216
static PaError AbortStream( PaStream *stream );
217
static PaError IsStreamStopped( PaStream *s );
218
static PaError IsStreamActive( PaStream *stream );
219
static PaTime GetStreamTime( PaStream *stream );
220
static double GetStreamCpuLoad( PaStream* stream );
221
static PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames );
222
static PaError WriteStream( PaStream* stream, const void *buffer, unsigned long frames );
223
static signed long GetStreamReadAvailable( PaStream* stream );
224
static signed long GetStreamWriteAvailable( PaStream* stream );
225
static PaError BuildDeviceList( PaOSSHostApiRepresentation *hostApi );
228
/** Initialize the OSS API implementation.
230
* This function will initialize host API datastructures and query host devices for information.
232
* Aspect DeviceCapabilities: Enumeration of host API devices is initiated from here
234
* Aspect FreeResources: If an error is encountered under way we have to free each resource allocated in this function,
235
* this happens with the usual "error" label.
237
PaError PaOSS_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
239
PaError result = paNoError;
240
PaOSSHostApiRepresentation *ossHostApi = NULL;
242
PA_UNLESS( ossHostApi = (PaOSSHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaOSSHostApiRepresentation) ),
243
paInsufficientMemory );
244
PA_UNLESS( ossHostApi->allocations = PaUtil_CreateAllocationGroup(), paInsufficientMemory );
245
ossHostApi->hostApiIndex = hostApiIndex;
247
/* Initialize host API structure */
248
*hostApi = &ossHostApi->inheritedHostApiRep;
249
(*hostApi)->info.structVersion = 1;
250
(*hostApi)->info.type = paOSS;
251
(*hostApi)->info.name = "OSS";
252
(*hostApi)->Terminate = Terminate;
253
(*hostApi)->OpenStream = OpenStream;
254
(*hostApi)->IsFormatSupported = IsFormatSupported;
256
PA_ENSURE( BuildDeviceList( ossHostApi ) );
258
PaUtil_InitializeStreamInterface( &ossHostApi->callbackStreamInterface, CloseStream, StartStream,
259
StopStream, AbortStream, IsStreamStopped, IsStreamActive,
260
GetStreamTime, GetStreamCpuLoad,
261
PaUtil_DummyRead, PaUtil_DummyWrite,
262
PaUtil_DummyGetReadAvailable,
263
PaUtil_DummyGetWriteAvailable );
265
PaUtil_InitializeStreamInterface( &ossHostApi->blockingStreamInterface, CloseStream, StartStream,
266
StopStream, AbortStream, IsStreamStopped, IsStreamActive,
267
GetStreamTime, PaUtil_DummyGetCpuLoad,
268
ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable );
270
mainThread_ = pthread_self();
277
if( ossHostApi->allocations )
279
PaUtil_FreeAllAllocations( ossHostApi->allocations );
280
PaUtil_DestroyAllocationGroup( ossHostApi->allocations );
283
PaUtil_FreeMemory( ossHostApi );
288
PaError PaUtil_InitializeDeviceInfo( PaDeviceInfo *deviceInfo, const char *name, PaHostApiIndex hostApiIndex, int maxInputChannels,
289
int maxOutputChannels, PaTime defaultLowInputLatency, PaTime defaultLowOutputLatency, PaTime defaultHighInputLatency,
290
PaTime defaultHighOutputLatency, double defaultSampleRate, PaUtilAllocationGroup *allocations )
292
PaError result = paNoError;
294
deviceInfo->structVersion = 2;
297
size_t len = strlen( name ) + 1;
298
PA_UNLESS( deviceInfo->name = PaUtil_GroupAllocateMemory( allocations, len ), paInsufficientMemory );
299
strncpy( (char *)deviceInfo->name, name, len );
302
deviceInfo->name = name;
304
deviceInfo->hostApi = hostApiIndex;
305
deviceInfo->maxInputChannels = maxInputChannels;
306
deviceInfo->maxOutputChannels = maxOutputChannels;
307
deviceInfo->defaultLowInputLatency = defaultLowInputLatency;
308
deviceInfo->defaultLowOutputLatency = defaultLowOutputLatency;
309
deviceInfo->defaultHighInputLatency = defaultHighInputLatency;
310
deviceInfo->defaultHighOutputLatency = defaultHighOutputLatency;
311
deviceInfo->defaultSampleRate = defaultSampleRate;
317
static PaError QueryDirection( const char *deviceName, StreamMode mode, double *defaultSampleRate, int *maxChannelCount,
318
double *defaultLowLatency, double *defaultHighLatency )
320
PaError result = paNoError;
321
int numChannels, maxNumChannels;
325
*maxChannelCount = 0; /* Default value in case this fails */
327
if ( (devHandle = open( deviceName, (mode == StreamMode_In ? O_RDONLY : O_WRONLY) | O_NONBLOCK )) < 0 )
329
if( errno == EBUSY || errno == EAGAIN )
331
PA_DEBUG(( "%s: Device %s busy\n", __FUNCTION__, deviceName ));
335
PA_DEBUG(( "%s: Can't access device: %s\n", __FUNCTION__, strerror( errno ) ));
338
return paDeviceUnavailable;
341
/* Negotiate for the maximum number of channels for this device. PLB20010927
342
* Consider up to 16 as the upper number of channels.
343
* Variable maxNumChannels should contain the actual upper limit after the call.
344
* Thanks to John Lazzaro and Heiko Purnhagen for suggestions.
347
for( numChannels = 1; numChannels <= 16; numChannels++ )
349
int temp = numChannels;
350
if( ioctl( devHandle, SNDCTL_DSP_CHANNELS, &temp ) < 0 )
352
busy = EAGAIN == errno || EBUSY == errno;
353
/* ioctl() failed so bail out if we already have stereo */
354
if( maxNumChannels >= 2 )
359
/* ioctl() worked but bail out if it does not support numChannels.
360
* We don't want to leave gaps in the numChannels supported.
362
if( (numChannels > 2) && (temp != numChannels) )
364
if( temp > maxNumChannels )
365
maxNumChannels = temp; /* Save maximum. */
368
/* A: We're able to open a device for capture if it's busy playing back and vice versa,
369
* but we can't configure anything */
370
if( 0 == maxNumChannels && busy )
372
result = paDeviceUnavailable;
376
/* The above negotiation may fail for an old driver so try this older technique. */
377
if( maxNumChannels < 1 )
380
if( ioctl( devHandle, SNDCTL_DSP_STEREO, &stereo ) < 0 )
386
maxNumChannels = (stereo) ? 2 : 1;
388
PA_DEBUG(( "%s: use SNDCTL_DSP_STEREO, maxNumChannels = %d\n", __FUNCTION__, maxNumChannels ));
391
/* During channel negotiation, the last ioctl() may have failed. This can
392
* also cause sample rate negotiation to fail. Hence the following, to return
393
* to a supported number of channels. SG20011005 */
395
/* use most reasonable default value */
396
int temp = PA_MIN( maxNumChannels, 2 );
397
ENSURE_( ioctl( devHandle, SNDCTL_DSP_CHANNELS, &temp ), paUnanticipatedHostError );
400
/* Get supported sample rate closest to 44100 Hz */
401
if( *defaultSampleRate < 0 )
404
if( ioctl( devHandle, SNDCTL_DSP_SPEED, &sr ) < 0 )
406
result = paUnanticipatedHostError;
410
*defaultSampleRate = sr;
413
*maxChannelCount = maxNumChannels;
415
*defaultLowLatency = 512. / *defaultSampleRate;
416
*defaultHighLatency = 2048. / *defaultSampleRate;
425
/** Query OSS device.
427
* This is where PaDeviceInfo objects are constructed and filled in with relevant information.
429
* Aspect DeviceCapabilities: The inferred device capabilities are recorded in a PaDeviceInfo object that is constructed
432
static PaError QueryDevice( char *deviceName, PaOSSHostApiRepresentation *ossApi, PaDeviceInfo **deviceInfo )
434
PaError result = paNoError;
435
double sampleRate = -1.;
436
int maxInputChannels, maxOutputChannels;
437
PaTime defaultLowInputLatency, defaultLowOutputLatency, defaultHighInputLatency, defaultHighOutputLatency;
438
PaError tmpRes = paNoError;
443
we have to do this querying in a slightly different order. apparently
444
some sound cards will give you different info based on their settins.
445
e.g. a card might give you stereo at 22kHz but only mono at 44kHz.
446
the correct order for OSS is: format, channels, sample rate
449
/* Aspect StreamChannels: The number of channels supported for a device may depend on the mode it is
450
* opened in, it may have more channels available for capture than playback and vice versa. Therefore
451
* we will open the device in both read- and write-only mode to determine the supported number.
453
if( (tmpRes = QueryDirection( deviceName, StreamMode_In, &sampleRate, &maxInputChannels, &defaultLowInputLatency,
454
&defaultHighInputLatency )) != paNoError )
456
if( tmpRes != paDeviceUnavailable )
458
PA_DEBUG(( "%s: Querying device %s for capture failed!\n", __FUNCTION__, deviceName ));
459
/* PA_ENSURE( tmpRes ); */
463
if( (tmpRes = QueryDirection( deviceName, StreamMode_Out, &sampleRate, &maxOutputChannels, &defaultLowOutputLatency,
464
&defaultHighOutputLatency )) != paNoError )
466
if( tmpRes != paDeviceUnavailable )
468
PA_DEBUG(( "%s: Querying device %s for playback failed!\n", __FUNCTION__, deviceName ));
469
/* PA_ENSURE( tmpRes ); */
473
assert( 0 <= busy && busy <= 2 );
474
if( 2 == busy ) /* Both directions are unavailable to us */
476
result = paDeviceUnavailable;
480
PA_UNLESS( *deviceInfo = PaUtil_GroupAllocateMemory( ossApi->allocations, sizeof (PaDeviceInfo) ), paInsufficientMemory );
481
PA_ENSURE( PaUtil_InitializeDeviceInfo( *deviceInfo, deviceName, ossApi->hostApiIndex, maxInputChannels, maxOutputChannels,
482
defaultLowInputLatency, defaultLowOutputLatency, defaultHighInputLatency, defaultHighOutputLatency, sampleRate,
483
ossApi->allocations ) );
489
/** Query host devices.
491
* Loop over host devices and query their capabilitiesu
493
* Aspect DeviceCapabilities: This function calls QueryDevice on each device entry and receives a filled in PaDeviceInfo object
494
* per device, these are placed in the host api representation's deviceInfos array.
496
static PaError BuildDeviceList( PaOSSHostApiRepresentation *ossApi )
498
PaError result = paNoError;
499
PaUtilHostApiRepresentation *commonApi = &ossApi->inheritedHostApiRep;
501
int numDevices = 0, maxDeviceInfos = 1;
502
PaDeviceInfo **deviceInfos = NULL;
504
/* These two will be set to the first working input and output device, respectively */
505
commonApi->info.defaultInputDevice = paNoDevice;
506
commonApi->info.defaultOutputDevice = paNoDevice;
508
/* Find devices by calling QueryDevice on each
509
* potential device names. When we find a valid one,
510
* add it to a linked list.
511
* A: Can there only be 10 devices? */
513
for( i = 0; i < 10; i++ )
516
PaDeviceInfo *deviceInfo;
521
snprintf(deviceName, sizeof (deviceName), "%s", DEVICE_NAME_BASE);
523
snprintf(deviceName, sizeof (deviceName), "%s%d", DEVICE_NAME_BASE, i);
525
/* PA_DEBUG(("PaOSS BuildDeviceList: trying device %s\n", deviceName )); */
526
if( stat( deviceName, &stbuf ) < 0 )
528
if( ENOENT != errno )
529
PA_DEBUG(( "%s: Error stat'ing %s: %s\n", __FUNCTION__, deviceName, strerror( errno ) ));
532
if( (testResult = QueryDevice( deviceName, ossApi, &deviceInfo )) != paNoError )
534
if( testResult != paDeviceUnavailable )
535
PA_ENSURE( testResult );
541
if( !deviceInfos || numDevices > maxDeviceInfos )
544
PA_UNLESS( deviceInfos = (PaDeviceInfo **) realloc( deviceInfos, maxDeviceInfos * sizeof (PaDeviceInfo *) ),
545
paInsufficientMemory );
548
int devIdx = numDevices - 1;
549
deviceInfos[devIdx] = deviceInfo;
551
if( commonApi->info.defaultInputDevice == paNoDevice && deviceInfo->maxInputChannels > 0 )
552
commonApi->info.defaultInputDevice = devIdx;
553
if( commonApi->info.defaultOutputDevice == paNoDevice && deviceInfo->maxOutputChannels > 0 )
554
commonApi->info.defaultOutputDevice = devIdx;
558
/* Make an array of PaDeviceInfo pointers out of the linked list */
560
PA_DEBUG(("PaOSS %s: Total number of devices found: %d\n", __FUNCTION__, numDevices));
562
commonApi->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(
563
ossApi->allocations, sizeof(PaDeviceInfo*) * numDevices );
564
memcpy( commonApi->deviceInfos, deviceInfos, numDevices * sizeof (PaDeviceInfo *) );
566
commonApi->info.deviceCount = numDevices;
574
static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
576
PaOSSHostApiRepresentation *ossHostApi = (PaOSSHostApiRepresentation*)hostApi;
578
if( ossHostApi->allocations )
580
PaUtil_FreeAllAllocations( ossHostApi->allocations );
581
PaUtil_DestroyAllocationGroup( ossHostApi->allocations );
584
PaUtil_FreeMemory( ossHostApi );
587
static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
588
const PaStreamParameters *inputParameters,
589
const PaStreamParameters *outputParameters,
592
PaError result = paNoError;
593
PaDeviceIndex device;
594
PaDeviceInfo *deviceInfo;
596
int inputChannelCount, outputChannelCount;
597
int tempDevHandle = -1;
599
PaSampleFormat inputSampleFormat, outputSampleFormat;
601
if( inputParameters )
603
inputChannelCount = inputParameters->channelCount;
604
inputSampleFormat = inputParameters->sampleFormat;
606
/* unless alternate device specification is supported, reject the use of
607
paUseHostApiSpecificDeviceSpecification */
609
if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
610
return paInvalidDevice;
612
/* check that input device can support inputChannelCount */
613
if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels )
614
return paInvalidChannelCount;
616
/* validate inputStreamInfo */
617
if( inputParameters->hostApiSpecificStreamInfo )
618
return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
622
inputChannelCount = 0;
625
if( outputParameters )
627
outputChannelCount = outputParameters->channelCount;
628
outputSampleFormat = outputParameters->sampleFormat;
630
/* unless alternate device specification is supported, reject the use of
631
paUseHostApiSpecificDeviceSpecification */
633
if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
634
return paInvalidDevice;
636
/* check that output device can support inputChannelCount */
637
if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels )
638
return paInvalidChannelCount;
640
/* validate outputStreamInfo */
641
if( outputParameters->hostApiSpecificStreamInfo )
642
return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
646
outputChannelCount = 0;
649
if (inputChannelCount == 0 && outputChannelCount == 0)
650
return paInvalidChannelCount;
652
/* if full duplex, make sure that they're the same device */
654
if (inputChannelCount > 0 && outputChannelCount > 0 &&
655
inputParameters->device != outputParameters->device)
656
return paInvalidDevice;
658
/* if full duplex, also make sure that they're the same number of channels */
660
if (inputChannelCount > 0 && outputChannelCount > 0 &&
661
inputChannelCount != outputChannelCount)
662
return paInvalidChannelCount;
664
/* open the device so we can do more tests */
666
if( inputChannelCount > 0 )
668
result = PaUtil_DeviceIndexToHostApiDeviceIndex(&device, inputParameters->device, hostApi);
669
if (result != paNoError)
674
result = PaUtil_DeviceIndexToHostApiDeviceIndex(&device, outputParameters->device, hostApi);
675
if (result != paNoError)
679
deviceInfo = hostApi->deviceInfos[device];
680
deviceName = (char *)deviceInfo->name;
683
if (inputChannelCount > 0 && outputChannelCount > 0)
685
else if (inputChannelCount > 0)
690
ENSURE_( tempDevHandle = open( deviceInfo->name, flags ), paDeviceUnavailable );
692
/* PaOssStream_Configure will do the rest of the checking for us */
693
/* PA_ENSURE( PaOssStream_Configure( tempDevHandle, deviceName, outputChannelCount, &sampleRate ) ); */
695
/* everything succeeded! */
698
if( tempDevHandle >= 0 )
699
close( tempDevHandle );
704
/** Validate stream parameters.
706
* Aspect StreamChannels: We verify that the number of channels is within the allowed range for the device
708
static PaError ValidateParameters( const PaStreamParameters *parameters, const PaDeviceInfo *deviceInfo, StreamMode mode )
712
assert( parameters );
714
if( parameters->device == paUseHostApiSpecificDeviceSpecification )
716
return paInvalidDevice;
719
maxChans = (mode == StreamMode_In ? deviceInfo->maxInputChannels :
720
deviceInfo->maxOutputChannels);
721
if( parameters->channelCount > maxChans )
723
return paInvalidChannelCount;
729
static PaError PaOssStreamComponent_Initialize( PaOssStreamComponent *component, const PaStreamParameters *parameters,
730
int callbackMode, int fd, const char *deviceName )
732
PaError result = paNoError;
735
memset( component, 0, sizeof (PaOssStreamComponent) );
738
component->devName = deviceName;
739
component->userChannelCount = parameters->channelCount;
740
component->userFormat = parameters->sampleFormat;
741
component->latency = parameters->suggestedLatency;
742
component->userInterleaved = !(parameters->sampleFormat & paNonInterleaved);
744
if( !callbackMode && !component->userInterleaved )
746
/* Pre-allocate non-interleaved user provided buffers */
747
PA_UNLESS( component->userBuffers = PaUtil_AllocateMemory( sizeof (void *) * component->userChannelCount ),
748
paInsufficientMemory );
755
static void PaOssStreamComponent_Terminate( PaOssStreamComponent *component )
759
if( component->fd >= 0 )
760
close( component->fd );
761
if( component->buffer )
762
PaUtil_FreeMemory( component->buffer );
764
if( component->userBuffers )
765
PaUtil_FreeMemory( component->userBuffers );
767
PaUtil_FreeMemory( component );
770
static PaError ModifyBlocking( int fd, int blocking )
772
PaError result = paNoError;
775
ENSURE_( fflags = fcntl( fd, F_GETFL ), paUnanticipatedHostError );
778
fflags &= ~O_NONBLOCK;
780
fflags |= O_NONBLOCK;
782
ENSURE_( fcntl( fd, F_SETFL, fflags ), paUnanticipatedHostError );
788
static PaError OpenDevices( const char *idevName, const char *odevName, int *idev, int *odev )
790
PaError result = paNoError;
791
int flags = O_NONBLOCK, duplex = 0;
795
if( idevName && odevName )
805
/* open first in nonblocking mode, in case it's busy...
806
* A: then unset the non-blocking attribute */
807
assert( flags & O_NONBLOCK );
810
ENSURE_( *idev = open( idevName, flags ), paDeviceUnavailable );
811
PA_ENSURE( ModifyBlocking( *idev, 1 ) ); /* Blocking */
813
/* Initially disable */
814
enableBits = ~PCM_ENABLE_INPUT;
815
ENSURE_( ioctl( *idev, SNDCTL_DSP_SETTRIGGER, &enableBits ), paUnanticipatedHostError );
821
ENSURE_( *odev = open( odevName, flags ), paDeviceUnavailable );
822
PA_ENSURE( ModifyBlocking( *odev, 1 ) ); /* Blocking */
824
/* Initially disable */
825
enableBits = ~PCM_ENABLE_OUTPUT;
826
ENSURE_( ioctl( *odev, SNDCTL_DSP_SETTRIGGER, &enableBits ), paUnanticipatedHostError );
830
ENSURE_( *odev = dup( *idev ), paUnanticipatedHostError );
838
static PaError PaOssStream_Initialize( PaOssStream *stream, const PaStreamParameters *inputParameters, const PaStreamParameters *outputParameters,
839
PaStreamCallback callback, void *userData, PaStreamFlags streamFlags,
840
PaOSSHostApiRepresentation *ossApi )
842
PaError result = paNoError;
844
PaUtilHostApiRepresentation *hostApi = &ossApi->inheritedHostApiRep;
845
const char *idevName = NULL, *odevName = NULL;
849
memset( stream, 0, sizeof (PaOssStream) );
850
stream->isStopped = 1;
852
PA_ENSURE( PaUtil_InitializeThreading( &stream->threading ) );
854
if( inputParameters && outputParameters )
856
if( inputParameters->device == outputParameters->device )
857
stream->sharedDevice = 1;
860
if( inputParameters )
861
idevName = hostApi->deviceInfos[inputParameters->device]->name;
862
if( outputParameters )
863
odevName = hostApi->deviceInfos[outputParameters->device]->name;
864
PA_ENSURE( OpenDevices( idevName, odevName, &idev, &odev ) );
865
if( inputParameters )
867
PA_UNLESS( stream->capture = PaUtil_AllocateMemory( sizeof (PaOssStreamComponent) ), paInsufficientMemory );
868
PA_ENSURE( PaOssStreamComponent_Initialize( stream->capture, inputParameters, callback != NULL, idev, idevName ) );
870
if( outputParameters )
872
PA_UNLESS( stream->playback = PaUtil_AllocateMemory( sizeof (PaOssStreamComponent) ), paInsufficientMemory );
873
PA_ENSURE( PaOssStreamComponent_Initialize( stream->playback, outputParameters, callback != NULL, odev, odevName ) );
876
if( callback != NULL )
878
PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
879
&ossApi->callbackStreamInterface, callback, userData );
880
stream->callbackMode = 1;
884
PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
885
&ossApi->blockingStreamInterface, callback, userData );
888
ENSURE_( sem_init( &stream->semaphore, 0, 0 ), paInternalError );
894
static void PaOssStream_Terminate( PaOssStream *stream )
898
PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
899
PaUtil_TerminateThreading( &stream->threading );
901
if( stream->capture )
902
PaOssStreamComponent_Terminate( stream->capture );
903
if( stream->playback )
904
PaOssStreamComponent_Terminate( stream->playback );
906
sem_destroy( &stream->semaphore );
908
PaUtil_FreeMemory( stream );
911
/** Translate from PA format to OSS native.
914
static PaError Pa2OssFormat( PaSampleFormat paFormat, int *ossFormat )
919
*ossFormat = AFMT_U8;
922
*ossFormat = AFMT_S8;
925
*ossFormat = AFMT_S16_NE;
928
return paInternalError; /* This shouldn't happen */
934
/** Return the PA-compatible formats that this device can support.
937
static PaError GetAvailableFormats( PaOssStreamComponent *component, PaSampleFormat *availableFormats )
939
PaError result = paNoError;
941
PaSampleFormat frmts = 0;
943
ENSURE_( ioctl( component->fd, SNDCTL_DSP_GETFMTS, &mask ), paUnanticipatedHostError );
948
if( mask & AFMT_S16_NE )
951
result = paSampleFormatNotSupported;
953
*availableFormats = frmts;
959
static unsigned int PaOssStreamComponent_FrameSize( PaOssStreamComponent *component )
961
return Pa_GetSampleSize( component->hostFormat ) * component->hostChannelCount;
964
/** Buffer size in bytes.
967
static unsigned long PaOssStreamComponent_BufferSize( PaOssStreamComponent *component )
969
return PaOssStreamComponent_FrameSize( component ) * component->hostFrames * component->numBufs;
972
static int CalcHigherLogTwo( int n )
975
while( (1<<log2) < n ) log2++;
979
static PaError PaOssStreamComponent_Configure( PaOssStreamComponent *component, double sampleRate, unsigned long framesPerBuffer,
980
StreamMode streamMode, PaOssStreamComponent *master )
982
PaError result = paNoError;
983
int temp, nativeFormat;
984
int sr = (int)sampleRate;
985
PaSampleFormat availableFormats = 0, hostFormat = 0;
986
int chans = component->userChannelCount;
991
unsigned long fragSz;
992
audio_buf_info bufInfo;
994
/* We may have a situation where only one component (the master) is configured, if both point to the same device.
995
* In that case, the second component will copy settings from the other */
998
/* Aspect BufferSettings: If framesPerBuffer is unspecified we have to infer a suitable fragment size.
999
* The hardware need not respect the requested fragment size, so we may have to adapt.
1001
if( framesPerBuffer == paFramesPerBufferUnspecified )
1003
bufSz = (unsigned long)(component->latency * sampleRate);
1008
fragSz = framesPerBuffer;
1009
bufSz = (unsigned long)(component->latency * sampleRate) + fragSz; /* Latency + 1 buffer */
1012
PA_ENSURE( GetAvailableFormats( component, &availableFormats ) );
1013
hostFormat = PaUtil_SelectClosestAvailableFormat( availableFormats, component->userFormat );
1015
/* OSS demands at least 2 buffers, and 16 bytes per buffer */
1016
numBufs = (int)PA_MAX( bufSz / fragSz, 2 );
1017
bytesPerBuf = PA_MAX( fragSz * Pa_GetSampleSize( hostFormat ) * chans, 16 );
1019
/* The fragment parameters are encoded like this:
1020
* Most significant byte: number of fragments
1021
* Least significant byte: exponent of fragment size (i.e., for 256, 8)
1023
frgmt = (numBufs << 16) + (CalcHigherLogTwo( bytesPerBuf ) & 0xffff);
1024
ENSURE_( ioctl( component->fd, SNDCTL_DSP_SETFRAGMENT, &frgmt ), paUnanticipatedHostError );
1026
/* A: according to the OSS programmer's guide parameters should be set in this order:
1027
* format, channels, rate */
1029
/* This format should be deemed good before we get this far */
1030
PA_ENSURE( Pa2OssFormat( hostFormat, &temp ) );
1031
nativeFormat = temp;
1032
ENSURE_( ioctl( component->fd, SNDCTL_DSP_SETFMT, &temp ), paUnanticipatedHostError );
1033
PA_UNLESS( temp == nativeFormat, paInternalError );
1035
/* try to set the number of channels */
1036
ENSURE_( ioctl( component->fd, SNDCTL_DSP_CHANNELS, &chans ), paSampleFormatNotSupported ); /* XXX: Should be paInvalidChannelCount? */
1037
/* It's possible that the minimum number of host channels is greater than what the user requested */
1038
PA_UNLESS( chans >= component->userChannelCount, paInvalidChannelCount );
1040
/* try to set the sample rate */
1041
ENSURE_( ioctl( component->fd, SNDCTL_DSP_SPEED, &sr ), paInvalidSampleRate );
1043
/* reject if there's no sample rate within 1% of the one requested */
1044
if( (fabs( sampleRate - sr ) / sampleRate) > 0.01 )
1046
PA_DEBUG(("%s: Wanted %f, closest sample rate was %d\n", __FUNCTION__, sampleRate, sr ));
1047
PA_ENSURE( paInvalidSampleRate );
1050
ENSURE_( ioctl( component->fd, streamMode == StreamMode_In ? SNDCTL_DSP_GETISPACE : SNDCTL_DSP_GETOSPACE, &bufInfo ),
1051
paUnanticipatedHostError );
1052
component->numBufs = bufInfo.fragstotal;
1054
/* This needs to be the last ioctl call before the first read/write, according to the OSS programmer's guide */
1055
ENSURE_( ioctl( component->fd, SNDCTL_DSP_GETBLKSIZE, &bytesPerBuf ), paUnanticipatedHostError );
1057
component->hostFrames = bytesPerBuf / Pa_GetSampleSize( hostFormat ) / chans;
1058
component->hostChannelCount = chans;
1059
component->hostFormat = hostFormat;
1063
component->hostFormat = master->hostFormat;
1064
component->hostFrames = master->hostFrames;
1065
component->hostChannelCount = master->hostChannelCount;
1066
component->numBufs = master->numBufs;
1069
PA_UNLESS( component->buffer = PaUtil_AllocateMemory( PaOssStreamComponent_BufferSize( component ) ),
1070
paInsufficientMemory );
1076
static PaError PaOssStreamComponent_Read( PaOssStreamComponent *component, unsigned long *frames )
1078
PaError result = paNoError;
1079
size_t len = *frames * PaOssStreamComponent_FrameSize( component );
1082
ENSURE_( bytesRead = read( component->fd, component->buffer, len ), paUnanticipatedHostError );
1083
*frames = bytesRead / PaOssStreamComponent_FrameSize( component );
1084
/* TODO: Handle condition where number of frames read doesn't equal number of frames requested */
1090
static PaError PaOssStreamComponent_Write( PaOssStreamComponent *component, unsigned long *frames )
1092
PaError result = paNoError;
1093
size_t len = *frames * PaOssStreamComponent_FrameSize( component );
1094
ssize_t bytesWritten;
1096
ENSURE_( bytesWritten = write( component->fd, component->buffer, len ), paUnanticipatedHostError );
1097
*frames = bytesWritten / PaOssStreamComponent_FrameSize( component );
1098
/* TODO: Handle condition where number of frames written doesn't equal number of frames requested */
1104
/** Configure the stream according to input/output parameters.
1106
* Aspect StreamChannels: The minimum number of channels supported by the device may exceed that requested by
1107
* the user, if so we'll record the actual number of host channels and adapt later.
1109
static PaError PaOssStream_Configure( PaOssStream *stream, double sampleRate, unsigned long framesPerBuffer,
1110
double *inputLatency, double *outputLatency )
1112
PaError result = paNoError;
1113
int duplex = stream->capture && stream->playback;
1114
unsigned long framesPerHostBuffer = 0;
1116
/* We should request full duplex first thing after opening the device */
1117
if( duplex && stream->sharedDevice )
1118
ENSURE_( ioctl( stream->capture->fd, SNDCTL_DSP_SETDUPLEX, 0 ), paUnanticipatedHostError );
1120
if( stream->capture )
1122
PaOssStreamComponent *component = stream->capture;
1123
PA_ENSURE( PaOssStreamComponent_Configure( component, sampleRate, framesPerBuffer, StreamMode_In,
1126
assert( component->hostChannelCount > 0 );
1127
assert( component->hostFrames > 0 );
1129
*inputLatency = component->hostFrames * (component->numBufs - 1) / sampleRate;
1131
if( stream->playback )
1133
PaOssStreamComponent *component = stream->playback, *master = stream->sharedDevice ? stream->capture : NULL;
1134
PA_ENSURE( PaOssStreamComponent_Configure( component, sampleRate, framesPerBuffer, StreamMode_Out,
1137
assert( component->hostChannelCount > 0 );
1138
assert( component->hostFrames > 0 );
1140
*outputLatency = component->hostFrames * (component->numBufs - 1) / sampleRate;
1144
framesPerHostBuffer = PA_MIN( stream->capture->hostFrames, stream->playback->hostFrames );
1145
else if( stream->capture )
1146
framesPerHostBuffer = stream->capture->hostFrames;
1147
else if( stream->playback )
1148
framesPerHostBuffer = stream->playback->hostFrames;
1150
stream->framesPerHostBuffer = framesPerHostBuffer;
1151
stream->pollTimeout = (int) ceil( 1e6 * framesPerHostBuffer / sampleRate ); /* Period in usecs, rounded up */
1153
stream->sampleRate = stream->streamRepresentation.streamInfo.sampleRate = sampleRate;
1159
/* see pa_hostapi.h for a list of validity guarantees made about OpenStream parameters */
1161
/** Open a PA OSS stream.
1163
* Aspect StreamChannels: The number of channels is specified per direction (in/out), and can differ between the
1164
* two. However, OSS doesn't support separate configuration spaces for capture and playback so if both
1165
* directions are the same device we will demand the same number of channels. The number of channels can range
1166
* from 1 to the maximum supported by the device.
1168
* Aspect BufferSettings: If framesPerBuffer != paFramesPerBufferUnspecified the number of frames per callback
1169
* must reflect this, in addition the host latency per device should approximate the corresponding
1170
* suggestedLatency. Based on these constraints we need to determine a number of frames per host buffer that
1171
* both capture and playback can agree on (they can be different devices), the buffer processor can adapt
1172
* between host and user buffer size, but the ratio should preferably be integral.
1174
static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
1176
const PaStreamParameters *inputParameters,
1177
const PaStreamParameters *outputParameters,
1179
unsigned long framesPerBuffer,
1180
PaStreamFlags streamFlags,
1181
PaStreamCallback *streamCallback,
1184
PaError result = paNoError;
1185
PaOSSHostApiRepresentation *ossHostApi = (PaOSSHostApiRepresentation*)hostApi;
1186
PaOssStream *stream = NULL;
1187
int inputChannelCount = 0, outputChannelCount = 0;
1188
PaSampleFormat inputSampleFormat = 0, outputSampleFormat = 0, inputHostFormat = 0, outputHostFormat = 0;
1189
const PaDeviceInfo *inputDeviceInfo = 0, *outputDeviceInfo = 0;
1190
int bpInitialized = 0;
1191
double inLatency = 0., outLatency = 0.;
1193
/* validate platform specific flags */
1194
if( (streamFlags & paPlatformSpecificFlags) != 0 )
1195
return paInvalidFlag; /* unexpected platform specific flag */
1197
if( inputParameters )
1199
/* unless alternate device specification is supported, reject the use of
1200
paUseHostApiSpecificDeviceSpecification */
1201
inputDeviceInfo = hostApi->deviceInfos[inputParameters->device];
1202
PA_ENSURE( ValidateParameters( inputParameters, inputDeviceInfo, StreamMode_In ) );
1204
inputChannelCount = inputParameters->channelCount;
1205
inputSampleFormat = inputParameters->sampleFormat;
1207
if( outputParameters )
1209
outputDeviceInfo = hostApi->deviceInfos[outputParameters->device];
1210
PA_ENSURE( ValidateParameters( outputParameters, outputDeviceInfo, StreamMode_Out ) );
1212
outputChannelCount = outputParameters->channelCount;
1213
outputSampleFormat = outputParameters->sampleFormat;
1216
/* Aspect StreamChannels: We currently demand that number of input and output channels are the same, if the same
1217
* device is opened for both directions
1219
if( inputChannelCount > 0 && outputChannelCount > 0 )
1221
if( inputParameters->device == outputParameters->device )
1223
if( inputParameters->channelCount != outputParameters->channelCount )
1224
return paInvalidChannelCount;
1228
/* allocate and do basic initialization of the stream structure */
1229
PA_UNLESS( stream = (PaOssStream*)PaUtil_AllocateMemory( sizeof(PaOssStream) ), paInsufficientMemory );
1230
PA_ENSURE( PaOssStream_Initialize( stream, inputParameters, outputParameters, streamCallback, userData, streamFlags, ossHostApi ) );
1232
PA_ENSURE( PaOssStream_Configure( stream, sampleRate, framesPerBuffer, &inLatency, &outLatency ) );
1234
PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate );
1236
if( inputParameters )
1238
inputHostFormat = stream->capture->hostFormat;
1239
stream->streamRepresentation.streamInfo.inputLatency = inLatency +
1240
PaUtil_GetBufferProcessorInputLatency( &stream->bufferProcessor ) / sampleRate;
1242
if( outputParameters )
1244
outputHostFormat = stream->playback->hostFormat;
1245
stream->streamRepresentation.streamInfo.outputLatency = outLatency +
1246
PaUtil_GetBufferProcessorOutputLatency( &stream->bufferProcessor ) / sampleRate;
1249
/* Initialize buffer processor with fixed host buffer size.
1250
* Aspect StreamSampleFormat: Here we commit the user and host sample formats, PA infrastructure will
1251
* convert between the two.
1253
PA_ENSURE( PaUtil_InitializeBufferProcessor( &stream->bufferProcessor,
1254
inputChannelCount, inputSampleFormat, inputHostFormat, outputChannelCount, outputSampleFormat,
1255
outputHostFormat, sampleRate, streamFlags, framesPerBuffer, stream->framesPerHostBuffer,
1256
paUtilFixedHostBufferSize, streamCallback, userData ) );
1259
*s = (PaStream*)stream;
1265
PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
1267
PaOssStream_Terminate( stream );
1272
/*! Poll on I/O filedescriptors.
1274
Poll till we've determined there's data for read or write. In the full-duplex case,
1275
we don't want to hang around forever waiting for either input or output frames, so
1276
whenever we have a timed out filedescriptor we check if we're nearing under/overrun
1277
for the other direction (critical limit set at one buffer). If so, we exit the waiting
1278
state, and go on with what we got. We align the number of frames on a host buffer
1279
boundary because it is possible that the buffer size differs for the two directions and
1280
the host buffer size is a compromise between the two.
1282
static PaError PaOssStream_WaitForFrames( PaOssStream *stream, unsigned long *frames )
1284
PaError result = paNoError;
1285
int pollPlayback = 0, pollCapture = 0;
1286
int captureAvail = INT_MAX, playbackAvail = INT_MAX, commonAvail;
1287
audio_buf_info bufInfo;
1288
/* int ofs = 0, nfds = stream->nfds; */
1289
fd_set readFds, writeFds;
1291
struct timeval selectTimeval = {0, 0};
1292
unsigned long timeout = stream->pollTimeout; /* In usecs */
1293
int captureFd = -1, playbackFd = -1;
1298
if( stream->capture )
1301
captureFd = stream->capture->fd;
1302
/* stream->capture->pfd->events = POLLIN; */
1304
if( stream->playback )
1307
playbackFd = stream->playback->fd;
1308
/* stream->playback->pfd->events = POLLOUT; */
1311
FD_ZERO( &readFds );
1312
FD_ZERO( &writeFds );
1314
while( pollPlayback || pollCapture )
1316
pthread_testcancel();
1318
/* select may modify the timeout parameter */
1319
selectTimeval.tv_usec = timeout;
1324
FD_SET( captureFd, &readFds );
1325
nfds = captureFd + 1;
1329
FD_SET( playbackFd, &writeFds );
1330
nfds = PA_MAX( nfds, playbackFd + 1 );
1332
ENSURE_( select( nfds, &readFds, &writeFds, NULL, &selectTimeval ), paUnanticipatedHostError );
1334
if( poll( stream->pfds + ofs, nfds, stream->pollTimeout ) < 0 )
1337
ENSURE_( -1, paUnanticipatedHostError );
1340
pthread_testcancel();
1344
if( FD_ISSET( captureFd, &readFds ) )
1346
FD_CLR( captureFd, &readFds );
1350
if( stream->capture->pfd->revents & POLLIN )
1357
else if( stream->playback ) /* Timed out, go on with playback? */
1359
/*PA_DEBUG(( "%s: Trying to poll again for capture frames, pollTimeout: %d\n",
1360
__FUNCTION__, stream->pollTimeout ));*/
1365
if( FD_ISSET( playbackFd, &writeFds ) )
1367
FD_CLR( playbackFd, &writeFds );
1371
if( stream->playback->pfd->revents & POLLOUT )
1377
else if( stream->capture ) /* Timed out, go on with capture? */
1379
/*PA_DEBUG(( "%s: Trying to poll again for playback frames, pollTimeout: %d\n\n",
1380
__FUNCTION__, stream->pollTimeout ));*/
1385
if( stream->capture )
1387
ENSURE_( ioctl( captureFd, SNDCTL_DSP_GETISPACE, &bufInfo ), paUnanticipatedHostError );
1388
captureAvail = bufInfo.fragments * stream->capture->hostFrames;
1390
PA_DEBUG(( "%s: captureAvail: 0\n", __FUNCTION__ ));
1392
captureAvail = captureAvail == 0 ? INT_MAX : captureAvail; /* Disregard if zero */
1394
if( stream->playback )
1396
ENSURE_( ioctl( playbackFd, SNDCTL_DSP_GETOSPACE, &bufInfo ), paUnanticipatedHostError );
1397
playbackAvail = bufInfo.fragments * stream->playback->hostFrames;
1398
if( !playbackAvail )
1400
PA_DEBUG(( "%s: playbackAvail: 0\n", __FUNCTION__ ));
1403
playbackAvail = playbackAvail == 0 ? INT_MAX : playbackAvail; /* Disregard if zero */
1406
commonAvail = PA_MIN( captureAvail, playbackAvail );
1407
if( commonAvail == INT_MAX )
1409
commonAvail -= commonAvail % stream->framesPerHostBuffer;
1411
assert( commonAvail != INT_MAX );
1412
assert( commonAvail >= 0 );
1413
*frames = commonAvail;
1419
/** Prepare stream for capture/playback.
1421
* In order to synchronize capture and playback properly we use the SETTRIGGER command.
1423
static PaError PaOssStream_Prepare( PaOssStream *stream )
1425
PaError result = paNoError;
1428
if( stream->triggered )
1431
if( stream->playback )
1433
size_t bufSz = PaOssStreamComponent_BufferSize( stream->playback );
1434
memset( stream->playback->buffer, 0, bufSz );
1436
/* Looks like we have to turn off blocking before we try this, but if we don't fill the buffer
1437
* OSS will complain. */
1438
PA_ENSURE( ModifyBlocking( stream->playback->fd, 0 ) );
1441
if( write( stream->playback->fd, stream->playback->buffer, bufSz ) < 0 )
1444
PA_ENSURE( ModifyBlocking( stream->playback->fd, 1 ) );
1447
if( stream->sharedDevice )
1449
enableBits = PCM_ENABLE_INPUT | PCM_ENABLE_OUTPUT;
1450
ENSURE_( ioctl( stream->capture->fd, SNDCTL_DSP_SETTRIGGER, &enableBits ), paUnanticipatedHostError );
1454
if( stream->capture )
1456
enableBits = PCM_ENABLE_INPUT;
1457
ENSURE_( ioctl( stream->capture->fd, SNDCTL_DSP_SETTRIGGER, &enableBits ), paUnanticipatedHostError );
1459
if( stream->playback )
1461
enableBits = PCM_ENABLE_OUTPUT;
1462
ENSURE_( ioctl( stream->playback->fd, SNDCTL_DSP_SETTRIGGER, &enableBits ), paUnanticipatedHostError );
1466
/* Ok, we have triggered the stream */
1467
stream->triggered = 1;
1473
/** Stop audio processing
1476
static PaError PaOssStream_Stop( PaOssStream *stream, int abort )
1478
PaError result = paNoError;
1480
/* Looks like the only safe way to stop audio without reopening the device is SNDCTL_DSP_POST.
1481
* Also disable capture/playback till the stream is started again */
1482
if( stream->capture )
1484
ENSURE_( ioctl( stream->capture->fd, SNDCTL_DSP_POST, 0 ), paUnanticipatedHostError );
1486
if( stream->playback && !stream->sharedDevice )
1488
ENSURE_( ioctl( stream->playback->fd, SNDCTL_DSP_POST, 0 ), paUnanticipatedHostError );
1495
/** Clean up after thread exit.
1497
* Aspect StreamState: If the user has registered a streamFinishedCallback it will be called here
1499
static void OnExit( void *data )
1501
PaOssStream *stream = (PaOssStream *) data;
1504
PaUtil_ResetCpuLoadMeasurer( &stream->cpuLoadMeasurer );
1506
PaOssStream_Stop( stream, stream->callbackAbort );
1508
PA_DEBUG(( "OnExit: Stoppage\n" ));
1510
/* Eventually notify user all buffers have played */
1511
if( stream->streamRepresentation.streamFinishedCallback )
1512
stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
1514
stream->callbackAbort = 0; /* Clear state */
1515
stream->isActive = 0;
1518
static PaError SetUpBuffers( PaOssStream *stream, unsigned long framesAvail )
1520
PaError result = paNoError;
1522
if( stream->capture )
1524
PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, 0, stream->capture->buffer,
1525
stream->capture->hostChannelCount );
1526
PaUtil_SetInputFrameCount( &stream->bufferProcessor, framesAvail );
1528
if( stream->playback )
1530
PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, 0, stream->playback->buffer,
1531
stream->playback->hostChannelCount );
1532
PaUtil_SetOutputFrameCount( &stream->bufferProcessor, framesAvail );
1538
/** Thread procedure for callback processing.
1540
* Aspect StreamState: StartStream will wait on this to initiate audio processing, useful in case the
1541
* callback should be used for buffer priming. When the stream is cancelled a separate function will
1542
* take care of the transition to the Callback Finished state (the stream isn't considered Stopped
1543
* before StopStream() or AbortStream() are called).
1545
static void *PaOSS_AudioThreadProc( void *userData )
1547
PaError result = paNoError;
1548
PaOssStream *stream = (PaOssStream*)userData;
1549
unsigned long framesAvail = 0, framesProcessed = 0;
1550
int callbackResult = paContinue;
1551
int triggered = stream->triggered; /* See if SNDCTL_DSP_TRIGGER has been issued already */
1552
int initiateProcessing = triggered; /* Already triggered? */
1553
PaStreamCallbackFlags cbFlags = 0; /* We might want to keep state across iterations */
1554
PaStreamCallbackTimeInfo timeInfo = {0,0,0}; /* TODO: IMPLEMENT ME */
1557
#if ( SOUND_VERSION > 0x030904 )
1558
audio_errinfo errinfo;
1564
pthread_cleanup_push( &OnExit, stream ); /* Execute OnExit when exiting */
1566
/* The first time the stream is started we use SNDCTL_DSP_TRIGGER to accurately start capture and
1567
* playback in sync, when the stream is restarted after being stopped we simply start by reading/
1570
PA_ENSURE( PaOssStream_Prepare( stream ) );
1572
/* If we are to initiate processing implicitly by reading/writing data, we start off in blocking mode */
1573
if( initiateProcessing )
1575
/* Make sure devices are in blocking mode */
1576
if( stream->capture )
1577
ModifyBlocking( stream->capture->fd, 1 );
1578
if( stream->playback )
1579
ModifyBlocking( stream->playback->fd, 1 );
1584
pthread_testcancel();
1586
if( stream->callbackStop && callbackResult == paContinue )
1588
PA_DEBUG(( "Setting callbackResult to paComplete\n" ));
1589
callbackResult = paComplete;
1592
/* Aspect StreamState: Because of the messy OSS scheme we can't explicitly trigger device start unless
1593
* the stream has been recently started, we will have to go right ahead and read/write in blocking
1594
* fashion to trigger operation. Therefore we begin with processing one host buffer before we switch
1595
* to non-blocking mode.
1597
if( !initiateProcessing )
1599
/* Wait on available frames */
1600
PA_ENSURE( PaOssStream_WaitForFrames( stream, &framesAvail ) );
1601
assert( framesAvail % stream->framesPerHostBuffer == 0 );
1605
framesAvail = stream->framesPerHostBuffer;
1608
while( framesAvail > 0 )
1610
unsigned long frames = framesAvail;
1612
pthread_testcancel();
1614
PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer );
1617
if ( stream->capture )
1619
PA_ENSURE( PaOssStreamComponent_Read( stream->capture, &frames ) );
1620
if( frames < framesAvail )
1622
PA_DEBUG(( "Read %lu less frames than requested\n", framesAvail - frames ));
1623
framesAvail = frames;
1627
#if ( SOUND_VERSION >= 0x030904 )
1629
Check with OSS to see if there have been any under/overruns
1630
since last time we checked.
1633
if( ioctl( stream->deviceHandle, SNDCTL_DSP_GETERROR, &errinfo ) >= 0 )
1635
if( errinfo.play_underruns )
1636
cbFlags |= paOutputUnderflow ;
1637
if( errinfo.record_underruns )
1638
cbFlags |= paInputUnderflow ;
1641
PA_DEBUG(( "SNDCTL_DSP_GETERROR command failed: %s\n", strerror( errno ) ));
1645
PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo,
1648
PA_ENSURE( SetUpBuffers( stream, framesAvail ) );
1650
framesProcessed = PaUtil_EndBufferProcessing( &stream->bufferProcessor,
1652
assert( framesProcessed == framesAvail );
1653
PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed );
1655
if ( stream->playback )
1657
frames = framesAvail;
1659
PA_ENSURE( PaOssStreamComponent_Write( stream->playback, &frames ) );
1660
if( frames < framesAvail )
1662
/* TODO: handle bytesWritten != bytesRequested (slippage?) */
1663
PA_DEBUG(( "Wrote %lu less frames than requested\n", framesAvail - frames ));
1667
framesAvail -= framesProcessed;
1668
stream->framesProcessed += framesProcessed;
1670
if( callbackResult != paContinue )
1674
if( initiateProcessing || !triggered )
1677
if( stream->capture )
1678
PA_ENSURE( ModifyBlocking( stream->capture->fd, 0 ) );
1679
if( stream->playback && !stream->sharedDevice )
1680
PA_ENSURE( ModifyBlocking( stream->playback->fd, 0 ) );
1682
initiateProcessing = 0;
1683
sem_post( &stream->semaphore );
1686
if( callbackResult != paContinue )
1688
stream->callbackAbort = callbackResult == paAbort;
1689
if( stream->callbackAbort || PaUtil_IsBufferProcessorOutputEmpty( &stream->bufferProcessor ) )
1694
pthread_cleanup_pop( 1 );
1697
pthread_exit( NULL );
1700
/** Close the stream.
1703
static PaError CloseStream( PaStream* s )
1705
PaError result = paNoError;
1706
PaOssStream *stream = (PaOssStream*)s;
1710
PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
1711
PaOssStream_Terminate( stream );
1716
/** Start the stream.
1718
* Aspect StreamState: After returning, the stream shall be in the Active state, implying that an eventual
1719
* callback will be repeatedly called in a separate thread. If a separate thread is started this function
1720
* will block untill it has started processing audio, otherwise audio processing is started directly.
1722
static PaError StartStream( PaStream *s )
1724
PaError result = paNoError;
1725
PaOssStream *stream = (PaOssStream*)s;
1727
stream->isActive = 1;
1728
stream->isStopped = 0;
1729
stream->lastPosPtr = 0;
1730
stream->lastStreamBytes = 0;
1731
stream->framesProcessed = 0;
1733
/* only use the thread for callback streams */
1734
if( stream->bufferProcessor.streamCallback )
1736
PA_ENSURE( PaUtil_StartThreading( &stream->threading, &PaOSS_AudioThreadProc, stream ) );
1737
sem_wait( &stream->semaphore );
1740
PA_ENSURE( PaOssStream_Prepare( stream ) );
1746
static PaError RealStop( PaOssStream *stream, int abort )
1748
PaError result = paNoError;
1750
if( stream->callbackMode )
1753
stream->callbackAbort = 1;
1755
stream->callbackStop = 1;
1757
PA_ENSURE( PaUtil_CancelThreading( &stream->threading, !abort, NULL ) );
1759
stream->callbackStop = stream->callbackAbort = 0;
1762
PA_ENSURE( PaOssStream_Stop( stream, abort ) );
1764
stream->isStopped = 1;
1770
/** Stop the stream.
1772
* Aspect StreamState: This will cause the stream to transition to the Stopped state, playing all enqueued
1775
static PaError StopStream( PaStream *s )
1777
return RealStop( (PaOssStream *)s, 0 );
1780
/** Abort the stream.
1782
* Aspect StreamState: This will cause the stream to transition to the Stopped state, discarding all enqueued
1783
* buffers. Note that the buffers are not currently correctly discarded, this is difficult without closing
1786
static PaError AbortStream( PaStream *s )
1788
return RealStop( (PaOssStream *)s, 1 );
1791
/** Is the stream in the Stopped state.
1794
static PaError IsStreamStopped( PaStream *s )
1796
PaOssStream *stream = (PaOssStream*)s;
1798
return (stream->isStopped);
1801
/** Is the stream in the Active state.
1804
static PaError IsStreamActive( PaStream *s )
1806
PaOssStream *stream = (PaOssStream*)s;
1808
return (stream->isActive);
1811
static PaTime GetStreamTime( PaStream *s )
1813
PaOssStream *stream = (PaOssStream*)s;
1817
if( stream->playback ) {
1818
if( ioctl( stream->playback->fd, SNDCTL_DSP_GETOPTR, &info) == 0 ) {
1819
delta = ( info.bytes - stream->lastPosPtr ) /* & 0x000FFFFF*/;
1820
return (float)(stream->lastStreamBytes + delta) / PaOssStreamComponent_FrameSize( stream->playback ) / stream->sampleRate;
1824
if (ioctl( stream->capture->fd, SNDCTL_DSP_GETIPTR, &info) == 0) {
1825
delta = (info.bytes - stream->lastPosPtr) /*& 0x000FFFFF*/;
1826
return (float)(stream->lastStreamBytes + delta) / PaOssStreamComponent_FrameSize( stream->capture ) / stream->sampleRate;
1830
/* the ioctl failed, but we can still give a coarse estimate */
1832
return stream->framesProcessed / stream->sampleRate;
1836
static double GetStreamCpuLoad( PaStream* s )
1838
PaOssStream *stream = (PaOssStream*)s;
1840
return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer );
1845
As separate stream interfaces are used for blocking and callback
1846
streams, the following functions can be guaranteed to only be called
1847
for blocking streams.
1851
static PaError ReadStream( PaStream* s,
1853
unsigned long frames )
1855
PaOssStream *stream = (PaOssStream*)s;
1856
int bytesRequested, bytesRead;
1857
unsigned long framesRequested;
1860
/* If user input is non-interleaved, PaUtil_CopyInput will manipulate the channel pointers,
1861
* so we copy the user provided pointers */
1862
if( stream->bufferProcessor.userInputIsInterleaved )
1863
userBuffer = buffer;
1864
else /* Copy channels into local array */
1866
userBuffer = stream->capture->userBuffers;
1867
memcpy( (void *)userBuffer, buffer, sizeof (void *) * stream->capture->userChannelCount );
1872
framesRequested = PA_MIN( frames, stream->capture->hostFrames );
1874
bytesRequested = framesRequested * PaOssStreamComponent_FrameSize( stream->capture );
1875
bytesRead = read( stream->capture->fd, stream->capture->buffer, bytesRequested );
1876
if ( bytesRequested != bytesRead )
1877
return paUnanticipatedHostError;
1879
PaUtil_SetInputFrameCount( &stream->bufferProcessor, stream->capture->hostFrames );
1880
PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, 0, stream->capture->buffer, stream->capture->hostChannelCount );
1881
PaUtil_CopyInput( &stream->bufferProcessor, &userBuffer, framesRequested );
1882
frames -= framesRequested;
1888
static PaError WriteStream( PaStream *s, const void *buffer, unsigned long frames )
1890
PaOssStream *stream = (PaOssStream*)s;
1891
int bytesRequested, bytesWritten;
1892
unsigned long framesConverted;
1893
const void *userBuffer;
1895
/* If user output is non-interleaved, PaUtil_CopyOutput will manipulate the channel pointers,
1896
* so we copy the user provided pointers */
1897
if( stream->bufferProcessor.userOutputIsInterleaved )
1898
userBuffer = buffer;
1901
/* Copy channels into local array */
1902
userBuffer = stream->playback->userBuffers;
1903
memcpy( (void *)userBuffer, buffer, sizeof (void *) * stream->playback->userChannelCount );
1908
PaUtil_SetOutputFrameCount( &stream->bufferProcessor, stream->playback->hostFrames );
1909
PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, 0, stream->playback->buffer, stream->playback->hostChannelCount );
1911
framesConverted = PaUtil_CopyOutput( &stream->bufferProcessor, &userBuffer, frames );
1912
frames -= framesConverted;
1914
bytesRequested = framesConverted * PaOssStreamComponent_FrameSize( stream->playback );
1915
bytesWritten = write( stream->playback->fd, stream->playback->buffer, bytesRequested );
1917
if ( bytesRequested != bytesWritten )
1918
return paUnanticipatedHostError;
1924
static signed long GetStreamReadAvailable( PaStream* s )
1926
PaOssStream *stream = (PaOssStream*)s;
1927
audio_buf_info info;
1929
if( ioctl( stream->capture->fd, SNDCTL_DSP_GETISPACE, &info ) < 0 )
1930
return paUnanticipatedHostError;
1931
return info.fragments * stream->capture->hostFrames;
1935
/* TODO: Compute number of allocated bytes somewhere else, can we use ODELAY with capture */
1936
static signed long GetStreamWriteAvailable( PaStream* s )
1938
PaOssStream *stream = (PaOssStream*)s;
1941
if( ioctl( stream->playback->fd, SNDCTL_DSP_GETODELAY, &delay ) < 0 )
1942
return paUnanticipatedHostError;
1944
return (PaOssStreamComponent_BufferSize( stream->playback ) - delay) / PaOssStreamComponent_FrameSize( stream->playback );