2
* $Id: pa_unix_oss.c 1385 2008-06-05 21:13:54Z aknudsen $
3
* PortAudio Portable Real-Time Audio Library
4
* Latest Version at: http://www.portaudio.com
5
* OSS implementation by:
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>
69
# define DEVICE_NAME_BASE "/dev/audio"
71
# define DEVICE_NAME_BASE "/dev/dsp"
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"
80
# error No sound card header file
83
#include "portaudio.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"
94
static pthread_t mainThread_;
96
/* Check return value of system call, and map it to PaError */
97
#define ENSURE_(expr, code) \
99
if( UNLIKELY( (sysErr_ = (expr)) < 0 ) ) \
101
/* PaUtil_SetLastHostErrorInfo should only be used in the main thread */ \
102
if( (code) == paUnanticipatedHostError && pthread_self() == mainThread_ ) \
104
PaUtil_SetLastHostErrorInfo( paOSS, sysErr_, strerror( errno ) ); \
107
PaUtil_DebugPrint(( "Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" )); \
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.
119
static int Get_AFMT_S16_NE( void )
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;
128
/* PaOSSHostApiRepresentation - host api datastructure specific to this implementation */
132
PaUtilHostApiRepresentation inheritedHostApiRep;
133
PaUtilStreamInterface callbackStreamInterface;
134
PaUtilStreamInterface blockingStreamInterface;
136
PaUtilAllocationGroup *allocations;
138
PaHostApiIndex hostApiIndex;
140
PaOSSHostApiRepresentation;
142
/** Per-direction structure for PaOssStream.
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
153
int userChannelCount, hostChannelCount;
156
PaSampleFormat userFormat, hostFormat;
158
unsigned long hostFrames, numBufs;
159
void **userBuffers; /* For non-interleaved blocking */
160
} PaOssStreamComponent;
162
/** Implementation specific representation of a PaStream.
165
typedef struct PaOssStream
167
PaUtilStreamRepresentation streamRepresentation;
168
PaUtilCpuLoadMeasurer cpuLoadMeasurer;
169
PaUtilBufferProcessor bufferProcessor;
171
PaUtilThreading threading;
174
unsigned long framesPerHostBuffer;
175
int triggered; /* Have the devices been triggered yet (first start) */
181
double lastStreamBytes;
188
int callbackStop, callbackAbort;
190
PaOssStreamComponent *capture, *playback;
191
unsigned long pollTimeout;
201
/* prototypes for functions declared in this file */
203
static void Terminate( struct PaUtilHostApiRepresentation *hostApi );
204
static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
205
const PaStreamParameters *inputParameters,
206
const PaStreamParameters *outputParameters,
208
static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
210
const PaStreamParameters *inputParameters,
211
const PaStreamParameters *outputParameters,
213
unsigned long framesPerBuffer,
214
PaStreamFlags streamFlags,
215
PaStreamCallback *streamCallback,
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 );
232
/** Initialize the OSS API implementation.
234
* This function will initialize host API datastructures and query host devices for information.
236
* Aspect DeviceCapabilities: Enumeration of host API devices is initiated from here
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.
241
PaError PaOSS_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
243
PaError result = paNoError;
244
PaOSSHostApiRepresentation *ossHostApi = NULL;
246
PA_UNLESS( ossHostApi = (PaOSSHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaOSSHostApiRepresentation) ),
247
paInsufficientMemory );
248
PA_UNLESS( ossHostApi->allocations = PaUtil_CreateAllocationGroup(), paInsufficientMemory );
249
ossHostApi->hostApiIndex = hostApiIndex;
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;
260
PA_ENSURE( BuildDeviceList( ossHostApi ) );
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 );
269
PaUtil_InitializeStreamInterface( &ossHostApi->blockingStreamInterface, CloseStream, StartStream,
270
StopStream, AbortStream, IsStreamStopped, IsStreamActive,
271
GetStreamTime, PaUtil_DummyGetCpuLoad,
272
ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable );
274
mainThread_ = pthread_self();
281
if( ossHostApi->allocations )
283
PaUtil_FreeAllAllocations( ossHostApi->allocations );
284
PaUtil_DestroyAllocationGroup( ossHostApi->allocations );
287
PaUtil_FreeMemory( ossHostApi );
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 )
296
PaError result = paNoError;
298
deviceInfo->structVersion = 2;
301
size_t len = strlen( name ) + 1;
302
PA_UNLESS( deviceInfo->name = PaUtil_GroupAllocateMemory( allocations, len ), paInsufficientMemory );
303
strncpy( (char *)deviceInfo->name, name, len );
306
deviceInfo->name = name;
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;
321
static PaError QueryDirection( const char *deviceName, StreamMode mode, double *defaultSampleRate, int *maxChannelCount,
322
double *defaultLowLatency, double *defaultHighLatency )
324
PaError result = paNoError;
325
int numChannels, maxNumChannels;
329
*maxChannelCount = 0; /* Default value in case this fails */
331
if ( (devHandle = open( deviceName, (mode == StreamMode_In ? O_RDONLY : O_WRONLY) | O_NONBLOCK )) < 0 )
333
if( errno == EBUSY || errno == EAGAIN )
335
PA_DEBUG(( "%s: Device %s busy\n", __FUNCTION__, deviceName ));
339
/* Ignore ENOENT, which means we've tried a non-existent device */
340
if( errno != ENOENT )
342
PA_DEBUG(( "%s: Can't access device %s: %s\n", __FUNCTION__, deviceName, strerror( errno ) ));
346
return paDeviceUnavailable;
349
/* Negotiate for the maximum number of channels for this device. PLB20010927
350
* Consider up to 16 as the upper number of channels.
351
* Variable maxNumChannels should contain the actual upper limit after the call.
352
* Thanks to John Lazzaro and Heiko Purnhagen for suggestions.
355
for( numChannels = 1; numChannels <= 16; numChannels++ )
357
int temp = numChannels;
358
if( ioctl( devHandle, SNDCTL_DSP_CHANNELS, &temp ) < 0 )
360
busy = EAGAIN == errno || EBUSY == errno;
361
/* ioctl() failed so bail out if we already have stereo */
362
if( maxNumChannels >= 2 )
367
/* ioctl() worked but bail out if it does not support numChannels.
368
* We don't want to leave gaps in the numChannels supported.
370
if( (numChannels > 2) && (temp != numChannels) )
372
if( temp > maxNumChannels )
373
maxNumChannels = temp; /* Save maximum. */
376
/* A: We're able to open a device for capture if it's busy playing back and vice versa,
377
* but we can't configure anything */
378
if( 0 == maxNumChannels && busy )
380
result = paDeviceUnavailable;
384
/* The above negotiation may fail for an old driver so try this older technique. */
385
if( maxNumChannels < 1 )
388
if( ioctl( devHandle, SNDCTL_DSP_STEREO, &stereo ) < 0 )
394
maxNumChannels = (stereo) ? 2 : 1;
396
PA_DEBUG(( "%s: use SNDCTL_DSP_STEREO, maxNumChannels = %d\n", __FUNCTION__, maxNumChannels ));
399
/* During channel negotiation, the last ioctl() may have failed. This can
400
* also cause sample rate negotiation to fail. Hence the following, to return
401
* to a supported number of channels. SG20011005 */
403
/* use most reasonable default value */
404
int temp = PA_MIN( maxNumChannels, 2 );
405
ENSURE_( ioctl( devHandle, SNDCTL_DSP_CHANNELS, &temp ), paUnanticipatedHostError );
408
/* Get supported sample rate closest to 44100 Hz */
409
if( *defaultSampleRate < 0 )
412
ENSURE_( ioctl( devHandle, SNDCTL_DSP_SPEED, &sr ), paUnanticipatedHostError );
414
*defaultSampleRate = sr;
417
*maxChannelCount = maxNumChannels;
419
*defaultLowLatency = 512. / *defaultSampleRate;
420
*defaultHighLatency = 2048. / *defaultSampleRate;
429
/** Query OSS device.
431
* This is where PaDeviceInfo objects are constructed and filled in with relevant information.
433
* Aspect DeviceCapabilities: The inferred device capabilities are recorded in a PaDeviceInfo object that is constructed
436
static PaError QueryDevice( char *deviceName, PaOSSHostApiRepresentation *ossApi, PaDeviceInfo **deviceInfo )
438
PaError result = paNoError;
439
double sampleRate = -1.;
440
int maxInputChannels, maxOutputChannels;
441
PaTime defaultLowInputLatency, defaultLowOutputLatency, defaultHighInputLatency, defaultHighOutputLatency;
442
PaError tmpRes = paNoError;
447
we have to do this querying in a slightly different order. apparently
448
some sound cards will give you different info based on their settins.
449
e.g. a card might give you stereo at 22kHz but only mono at 44kHz.
450
the correct order for OSS is: format, channels, sample rate
453
/* Aspect StreamChannels: The number of channels supported for a device may depend on the mode it is
454
* opened in, it may have more channels available for capture than playback and vice versa. Therefore
455
* we will open the device in both read- and write-only mode to determine the supported number.
457
if( (tmpRes = QueryDirection( deviceName, StreamMode_In, &sampleRate, &maxInputChannels, &defaultLowInputLatency,
458
&defaultHighInputLatency )) != paNoError )
460
if( tmpRes != paDeviceUnavailable )
462
PA_DEBUG(( "%s: Querying device %s for capture failed!\n", __FUNCTION__, deviceName ));
463
/* PA_ENSURE( tmpRes ); */
467
if( (tmpRes = QueryDirection( deviceName, StreamMode_Out, &sampleRate, &maxOutputChannels, &defaultLowOutputLatency,
468
&defaultHighOutputLatency )) != paNoError )
470
if( tmpRes != paDeviceUnavailable )
472
PA_DEBUG(( "%s: Querying device %s for playback failed!\n", __FUNCTION__, deviceName ));
473
/* PA_ENSURE( tmpRes ); */
477
assert( 0 <= busy && busy <= 2 );
478
if( 2 == busy ) /* Both directions are unavailable to us */
480
result = paDeviceUnavailable;
484
PA_UNLESS( *deviceInfo = PaUtil_GroupAllocateMemory( ossApi->allocations, sizeof (PaDeviceInfo) ), paInsufficientMemory );
485
PA_ENSURE( PaUtil_InitializeDeviceInfo( *deviceInfo, deviceName, ossApi->hostApiIndex, maxInputChannels, maxOutputChannels,
486
defaultLowInputLatency, defaultLowOutputLatency, defaultHighInputLatency, defaultHighOutputLatency, sampleRate,
487
ossApi->allocations ) );
493
/** Query host devices.
495
* Loop over host devices and query their capabilitiesu
497
* Aspect DeviceCapabilities: This function calls QueryDevice on each device entry and receives a filled in PaDeviceInfo object
498
* per device, these are placed in the host api representation's deviceInfos array.
500
static PaError BuildDeviceList( PaOSSHostApiRepresentation *ossApi )
502
PaError result = paNoError;
503
PaUtilHostApiRepresentation *commonApi = &ossApi->inheritedHostApiRep;
505
int numDevices = 0, maxDeviceInfos = 1;
506
PaDeviceInfo **deviceInfos = NULL;
508
/* These two will be set to the first working input and output device, respectively */
509
commonApi->info.defaultInputDevice = paNoDevice;
510
commonApi->info.defaultOutputDevice = paNoDevice;
512
/* Find devices by calling QueryDevice on each
513
* potential device names. When we find a valid one,
514
* add it to a linked list.
515
* A: Set an arbitrary of 100 devices, should probably be a smarter way. */
517
for( i = 0; i < 100; i++ )
520
PaDeviceInfo *deviceInfo;
524
snprintf(deviceName, sizeof (deviceName), "%s", DEVICE_NAME_BASE);
526
snprintf(deviceName, sizeof (deviceName), "%s%d", DEVICE_NAME_BASE, i);
528
/* PA_DEBUG(("%s: trying device %s\n", __FUNCTION__, deviceName )); */
529
if( (testResult = QueryDevice( deviceName, ossApi, &deviceInfo )) != paNoError )
531
if( testResult != paDeviceUnavailable )
532
PA_ENSURE( testResult );
538
if( !deviceInfos || numDevices > maxDeviceInfos )
541
PA_UNLESS( deviceInfos = (PaDeviceInfo **) realloc( deviceInfos, maxDeviceInfos * sizeof (PaDeviceInfo *) ),
542
paInsufficientMemory );
545
int devIdx = numDevices - 1;
546
deviceInfos[devIdx] = deviceInfo;
548
if( commonApi->info.defaultInputDevice == paNoDevice && deviceInfo->maxInputChannels > 0 )
549
commonApi->info.defaultInputDevice = devIdx;
550
if( commonApi->info.defaultOutputDevice == paNoDevice && deviceInfo->maxOutputChannels > 0 )
551
commonApi->info.defaultOutputDevice = devIdx;
555
/* Make an array of PaDeviceInfo pointers out of the linked list */
557
PA_DEBUG(("PaOSS %s: Total number of devices found: %d\n", __FUNCTION__, numDevices));
559
commonApi->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(
560
ossApi->allocations, sizeof(PaDeviceInfo*) * numDevices );
561
memcpy( commonApi->deviceInfos, deviceInfos, numDevices * sizeof (PaDeviceInfo *) );
563
commonApi->info.deviceCount = numDevices;
571
static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
573
PaOSSHostApiRepresentation *ossHostApi = (PaOSSHostApiRepresentation*)hostApi;
575
if( ossHostApi->allocations )
577
PaUtil_FreeAllAllocations( ossHostApi->allocations );
578
PaUtil_DestroyAllocationGroup( ossHostApi->allocations );
581
PaUtil_FreeMemory( ossHostApi );
584
static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
585
const PaStreamParameters *inputParameters,
586
const PaStreamParameters *outputParameters,
589
PaError result = paNoError;
590
PaDeviceIndex device;
591
PaDeviceInfo *deviceInfo;
593
int inputChannelCount, outputChannelCount;
594
int tempDevHandle = -1;
596
PaSampleFormat inputSampleFormat, outputSampleFormat;
598
if( inputParameters )
600
inputChannelCount = inputParameters->channelCount;
601
inputSampleFormat = inputParameters->sampleFormat;
603
/* unless alternate device specification is supported, reject the use of
604
paUseHostApiSpecificDeviceSpecification */
606
if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
607
return paInvalidDevice;
609
/* check that input device can support inputChannelCount */
610
if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels )
611
return paInvalidChannelCount;
613
/* validate inputStreamInfo */
614
if( inputParameters->hostApiSpecificStreamInfo )
615
return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
619
inputChannelCount = 0;
622
if( outputParameters )
624
outputChannelCount = outputParameters->channelCount;
625
outputSampleFormat = outputParameters->sampleFormat;
627
/* unless alternate device specification is supported, reject the use of
628
paUseHostApiSpecificDeviceSpecification */
630
if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
631
return paInvalidDevice;
633
/* check that output device can support inputChannelCount */
634
if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels )
635
return paInvalidChannelCount;
637
/* validate outputStreamInfo */
638
if( outputParameters->hostApiSpecificStreamInfo )
639
return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
643
outputChannelCount = 0;
646
if (inputChannelCount == 0 && outputChannelCount == 0)
647
return paInvalidChannelCount;
649
/* if full duplex, make sure that they're the same device */
651
if (inputChannelCount > 0 && outputChannelCount > 0 &&
652
inputParameters->device != outputParameters->device)
653
return paInvalidDevice;
655
/* if full duplex, also make sure that they're the same number of channels */
657
if (inputChannelCount > 0 && outputChannelCount > 0 &&
658
inputChannelCount != outputChannelCount)
659
return paInvalidChannelCount;
661
/* open the device so we can do more tests */
663
if( inputChannelCount > 0 )
665
result = PaUtil_DeviceIndexToHostApiDeviceIndex(&device, inputParameters->device, hostApi);
666
if (result != paNoError)
671
result = PaUtil_DeviceIndexToHostApiDeviceIndex(&device, outputParameters->device, hostApi);
672
if (result != paNoError)
676
deviceInfo = hostApi->deviceInfos[device];
677
deviceName = (char *)deviceInfo->name;
680
if (inputChannelCount > 0 && outputChannelCount > 0)
682
else if (inputChannelCount > 0)
687
ENSURE_( tempDevHandle = open( deviceInfo->name, flags ), paDeviceUnavailable );
689
/* PaOssStream_Configure will do the rest of the checking for us */
690
/* PA_ENSURE( PaOssStream_Configure( tempDevHandle, deviceName, outputChannelCount, &sampleRate ) ); */
692
/* everything succeeded! */
695
if( tempDevHandle >= 0 )
696
close( tempDevHandle );
701
/** Validate stream parameters.
703
* Aspect StreamChannels: We verify that the number of channels is within the allowed range for the device
705
static PaError ValidateParameters( const PaStreamParameters *parameters, const PaDeviceInfo *deviceInfo, StreamMode mode )
709
assert( parameters );
711
if( parameters->device == paUseHostApiSpecificDeviceSpecification )
713
return paInvalidDevice;
716
maxChans = (mode == StreamMode_In ? deviceInfo->maxInputChannels :
717
deviceInfo->maxOutputChannels);
718
if( parameters->channelCount > maxChans )
720
return paInvalidChannelCount;
726
static PaError PaOssStreamComponent_Initialize( PaOssStreamComponent *component, const PaStreamParameters *parameters,
727
int callbackMode, int fd, const char *deviceName )
729
PaError result = paNoError;
732
memset( component, 0, sizeof (PaOssStreamComponent) );
735
component->devName = deviceName;
736
component->userChannelCount = parameters->channelCount;
737
component->userFormat = parameters->sampleFormat;
738
component->latency = parameters->suggestedLatency;
739
component->userInterleaved = !(parameters->sampleFormat & paNonInterleaved);
741
if( !callbackMode && !component->userInterleaved )
743
/* Pre-allocate non-interleaved user provided buffers */
744
PA_UNLESS( component->userBuffers = PaUtil_AllocateMemory( sizeof (void *) * component->userChannelCount ),
745
paInsufficientMemory );
752
static void PaOssStreamComponent_Terminate( PaOssStreamComponent *component )
756
if( component->fd >= 0 )
757
close( component->fd );
758
if( component->buffer )
759
PaUtil_FreeMemory( component->buffer );
761
if( component->userBuffers )
762
PaUtil_FreeMemory( component->userBuffers );
764
PaUtil_FreeMemory( component );
767
static PaError ModifyBlocking( int fd, int blocking )
769
PaError result = paNoError;
772
ENSURE_( fflags = fcntl( fd, F_GETFL ), paUnanticipatedHostError );
775
fflags &= ~O_NONBLOCK;
777
fflags |= O_NONBLOCK;
779
ENSURE_( fcntl( fd, F_SETFL, fflags ), paUnanticipatedHostError );
785
/** Open input and output devices.
787
* @param idev: Returned input device file descriptor.
788
* @param odev: Returned output device file descriptor.
790
static PaError OpenDevices( const char *idevName, const char *odevName, int *idev, int *odev )
792
PaError result = paNoError;
793
int flags = O_NONBLOCK, duplex = 0;
796
if( idevName && odevName )
806
/* open first in nonblocking mode, in case it's busy...
807
* A: then unset the non-blocking attribute */
808
assert( flags & O_NONBLOCK );
811
ENSURE_( *idev = open( idevName, flags ), paDeviceUnavailable );
812
PA_ENSURE( ModifyBlocking( *idev, 1 ) ); /* Blocking */
818
ENSURE_( *odev = open( odevName, flags ), paDeviceUnavailable );
819
PA_ENSURE( ModifyBlocking( *odev, 1 ) ); /* Blocking */
823
ENSURE_( *odev = dup( *idev ), paUnanticipatedHostError );
831
static PaError PaOssStream_Initialize( PaOssStream *stream, const PaStreamParameters *inputParameters, const PaStreamParameters *outputParameters,
832
PaStreamCallback callback, void *userData, PaStreamFlags streamFlags,
833
PaOSSHostApiRepresentation *ossApi )
835
PaError result = paNoError;
837
PaUtilHostApiRepresentation *hostApi = &ossApi->inheritedHostApiRep;
838
const char *idevName = NULL, *odevName = NULL;
842
memset( stream, 0, sizeof (PaOssStream) );
843
stream->isStopped = 1;
845
PA_ENSURE( PaUtil_InitializeThreading( &stream->threading ) );
847
if( inputParameters && outputParameters )
849
if( inputParameters->device == outputParameters->device )
850
stream->sharedDevice = 1;
853
if( inputParameters )
854
idevName = hostApi->deviceInfos[inputParameters->device]->name;
855
if( outputParameters )
856
odevName = hostApi->deviceInfos[outputParameters->device]->name;
857
PA_ENSURE( OpenDevices( idevName, odevName, &idev, &odev ) );
858
if( inputParameters )
860
PA_UNLESS( stream->capture = PaUtil_AllocateMemory( sizeof (PaOssStreamComponent) ), paInsufficientMemory );
861
PA_ENSURE( PaOssStreamComponent_Initialize( stream->capture, inputParameters, callback != NULL, idev, idevName ) );
863
if( outputParameters )
865
PA_UNLESS( stream->playback = PaUtil_AllocateMemory( sizeof (PaOssStreamComponent) ), paInsufficientMemory );
866
PA_ENSURE( PaOssStreamComponent_Initialize( stream->playback, outputParameters, callback != NULL, odev, odevName ) );
869
if( callback != NULL )
871
PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
872
&ossApi->callbackStreamInterface, callback, userData );
873
stream->callbackMode = 1;
877
PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
878
&ossApi->blockingStreamInterface, callback, userData );
881
ENSURE_( sem_init( &stream->semaphore, 0, 0 ), paInternalError );
887
static void PaOssStream_Terminate( PaOssStream *stream )
891
PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
892
PaUtil_TerminateThreading( &stream->threading );
894
if( stream->capture )
895
PaOssStreamComponent_Terminate( stream->capture );
896
if( stream->playback )
897
PaOssStreamComponent_Terminate( stream->playback );
899
sem_destroy( &stream->semaphore );
901
PaUtil_FreeMemory( stream );
904
/** Translate from PA format to OSS native.
907
static PaError Pa2OssFormat( PaSampleFormat paFormat, int *ossFormat )
912
*ossFormat = AFMT_U8;
915
*ossFormat = AFMT_S8;
918
*ossFormat = AFMT_S16_NE;
921
return paInternalError; /* This shouldn't happen */
927
/** Return the PA-compatible formats that this device can support.
930
static PaError GetAvailableFormats( PaOssStreamComponent *component, PaSampleFormat *availableFormats )
932
PaError result = paNoError;
934
PaSampleFormat frmts = 0;
936
ENSURE_( ioctl( component->fd, SNDCTL_DSP_GETFMTS, &mask ), paUnanticipatedHostError );
941
if( mask & AFMT_S16_NE )
944
result = paSampleFormatNotSupported;
946
*availableFormats = frmts;
952
static unsigned int PaOssStreamComponent_FrameSize( PaOssStreamComponent *component )
954
return Pa_GetSampleSize( component->hostFormat ) * component->hostChannelCount;
957
/** Buffer size in bytes.
960
static unsigned long PaOssStreamComponent_BufferSize( PaOssStreamComponent *component )
962
return PaOssStreamComponent_FrameSize( component ) * component->hostFrames * component->numBufs;
965
static int CalcHigherLogTwo( int n )
968
while( (1<<log2) < n ) log2++;
972
/** Configure stream component device parameters.
974
static PaError PaOssStreamComponent_Configure( PaOssStreamComponent *component, double sampleRate, unsigned long
975
framesPerBuffer, StreamMode streamMode, PaOssStreamComponent *master )
977
PaError result = paNoError;
978
int temp, nativeFormat;
979
int sr = (int)sampleRate;
980
PaSampleFormat availableFormats = 0, hostFormat = 0;
981
int chans = component->userChannelCount;
986
unsigned long fragSz;
987
audio_buf_info bufInfo;
989
/* We may have a situation where only one component (the master) is configured, if both point to the same device.
990
* In that case, the second component will copy settings from the other */
993
/* Aspect BufferSettings: If framesPerBuffer is unspecified we have to infer a suitable fragment size.
994
* The hardware need not respect the requested fragment size, so we may have to adapt.
996
if( framesPerBuffer == paFramesPerBufferUnspecified )
998
bufSz = (unsigned long)(component->latency * sampleRate);
1003
fragSz = framesPerBuffer;
1004
bufSz = (unsigned long)(component->latency * sampleRate) + fragSz; /* Latency + 1 buffer */
1007
PA_ENSURE( GetAvailableFormats( component, &availableFormats ) );
1008
hostFormat = PaUtil_SelectClosestAvailableFormat( availableFormats, component->userFormat );
1010
/* OSS demands at least 2 buffers, and 16 bytes per buffer */
1011
numBufs = (int)PA_MAX( bufSz / fragSz, 2 );
1012
bytesPerBuf = PA_MAX( fragSz * Pa_GetSampleSize( hostFormat ) * chans, 16 );
1014
/* The fragment parameters are encoded like this:
1015
* Most significant byte: number of fragments
1016
* Least significant byte: exponent of fragment size (i.e., for 256, 8)
1018
frgmt = (numBufs << 16) + (CalcHigherLogTwo( bytesPerBuf ) & 0xffff);
1019
ENSURE_( ioctl( component->fd, SNDCTL_DSP_SETFRAGMENT, &frgmt ), paUnanticipatedHostError );
1021
/* A: according to the OSS programmer's guide parameters should be set in this order:
1022
* format, channels, rate */
1024
/* This format should be deemed good before we get this far */
1025
PA_ENSURE( Pa2OssFormat( hostFormat, &temp ) );
1026
nativeFormat = temp;
1027
ENSURE_( ioctl( component->fd, SNDCTL_DSP_SETFMT, &temp ), paUnanticipatedHostError );
1028
PA_UNLESS( temp == nativeFormat, paInternalError );
1030
/* try to set the number of channels */
1031
ENSURE_( ioctl( component->fd, SNDCTL_DSP_CHANNELS, &chans ), paSampleFormatNotSupported ); /* XXX: Should be paInvalidChannelCount? */
1032
/* It's possible that the minimum number of host channels is greater than what the user requested */
1033
PA_UNLESS( chans >= component->userChannelCount, paInvalidChannelCount );
1035
/* try to set the sample rate */
1036
ENSURE_( ioctl( component->fd, SNDCTL_DSP_SPEED, &sr ), paInvalidSampleRate );
1038
/* reject if there's no sample rate within 1% of the one requested */
1039
if( (fabs( sampleRate - sr ) / sampleRate) > 0.01 )
1041
PA_DEBUG(("%s: Wanted %f, closest sample rate was %d\n", __FUNCTION__, sampleRate, sr ));
1042
PA_ENSURE( paInvalidSampleRate );
1045
ENSURE_( ioctl( component->fd, streamMode == StreamMode_In ? SNDCTL_DSP_GETISPACE : SNDCTL_DSP_GETOSPACE, &bufInfo ),
1046
paUnanticipatedHostError );
1047
component->numBufs = bufInfo.fragstotal;
1049
/* This needs to be the last ioctl call before the first read/write, according to the OSS programmer's guide */
1050
ENSURE_( ioctl( component->fd, SNDCTL_DSP_GETBLKSIZE, &bytesPerBuf ), paUnanticipatedHostError );
1052
component->hostFrames = bytesPerBuf / Pa_GetSampleSize( hostFormat ) / chans;
1053
component->hostChannelCount = chans;
1054
component->hostFormat = hostFormat;
1058
component->hostFormat = master->hostFormat;
1059
component->hostFrames = master->hostFrames;
1060
component->hostChannelCount = master->hostChannelCount;
1061
component->numBufs = master->numBufs;
1064
PA_UNLESS( component->buffer = PaUtil_AllocateMemory( PaOssStreamComponent_BufferSize( component ) ),
1065
paInsufficientMemory );
1071
static PaError PaOssStreamComponent_Read( PaOssStreamComponent *component, unsigned long *frames )
1073
PaError result = paNoError;
1074
size_t len = *frames * PaOssStreamComponent_FrameSize( component );
1077
ENSURE_( bytesRead = read( component->fd, component->buffer, len ), paUnanticipatedHostError );
1078
*frames = bytesRead / PaOssStreamComponent_FrameSize( component );
1079
/* TODO: Handle condition where number of frames read doesn't equal number of frames requested */
1085
static PaError PaOssStreamComponent_Write( PaOssStreamComponent *component, unsigned long *frames )
1087
PaError result = paNoError;
1088
size_t len = *frames * PaOssStreamComponent_FrameSize( component );
1089
ssize_t bytesWritten;
1091
ENSURE_( bytesWritten = write( component->fd, component->buffer, len ), paUnanticipatedHostError );
1092
*frames = bytesWritten / PaOssStreamComponent_FrameSize( component );
1093
/* TODO: Handle condition where number of frames written doesn't equal number of frames requested */
1099
/** Configure the stream according to input/output parameters.
1101
* Aspect StreamChannels: The minimum number of channels supported by the device may exceed that requested by
1102
* the user, if so we'll record the actual number of host channels and adapt later.
1104
static PaError PaOssStream_Configure( PaOssStream *stream, double sampleRate, unsigned long framesPerBuffer,
1105
double *inputLatency, double *outputLatency )
1107
PaError result = paNoError;
1108
int duplex = stream->capture && stream->playback;
1109
unsigned long framesPerHostBuffer = 0;
1111
/* We should request full duplex first thing after opening the device */
1112
if( duplex && stream->sharedDevice )
1113
ENSURE_( ioctl( stream->capture->fd, SNDCTL_DSP_SETDUPLEX, 0 ), paUnanticipatedHostError );
1115
if( stream->capture )
1117
PaOssStreamComponent *component = stream->capture;
1118
PA_ENSURE( PaOssStreamComponent_Configure( component, sampleRate, framesPerBuffer, StreamMode_In,
1121
assert( component->hostChannelCount > 0 );
1122
assert( component->hostFrames > 0 );
1124
*inputLatency = component->hostFrames * (component->numBufs - 1) / sampleRate;
1126
if( stream->playback )
1128
PaOssStreamComponent *component = stream->playback, *master = stream->sharedDevice ? stream->capture : NULL;
1129
PA_ENSURE( PaOssStreamComponent_Configure( component, sampleRate, framesPerBuffer, StreamMode_Out,
1132
assert( component->hostChannelCount > 0 );
1133
assert( component->hostFrames > 0 );
1135
*outputLatency = component->hostFrames * (component->numBufs - 1) / sampleRate;
1139
framesPerHostBuffer = PA_MIN( stream->capture->hostFrames, stream->playback->hostFrames );
1140
else if( stream->capture )
1141
framesPerHostBuffer = stream->capture->hostFrames;
1142
else if( stream->playback )
1143
framesPerHostBuffer = stream->playback->hostFrames;
1145
stream->framesPerHostBuffer = framesPerHostBuffer;
1146
stream->pollTimeout = (int) ceil( 1e6 * framesPerHostBuffer / sampleRate ); /* Period in usecs, rounded up */
1148
stream->sampleRate = stream->streamRepresentation.streamInfo.sampleRate = sampleRate;
1154
/* see pa_hostapi.h for a list of validity guarantees made about OpenStream parameters */
1156
/** Open a PA OSS stream.
1158
* Aspect StreamChannels: The number of channels is specified per direction (in/out), and can differ between the
1159
* two. However, OSS doesn't support separate configuration spaces for capture and playback so if both
1160
* directions are the same device we will demand the same number of channels. The number of channels can range
1161
* from 1 to the maximum supported by the device.
1163
* Aspect BufferSettings: If framesPerBuffer != paFramesPerBufferUnspecified the number of frames per callback
1164
* must reflect this, in addition the host latency per device should approximate the corresponding
1165
* suggestedLatency. Based on these constraints we need to determine a number of frames per host buffer that
1166
* both capture and playback can agree on (they can be different devices), the buffer processor can adapt
1167
* between host and user buffer size, but the ratio should preferably be integral.
1169
static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
1171
const PaStreamParameters *inputParameters,
1172
const PaStreamParameters *outputParameters,
1174
unsigned long framesPerBuffer,
1175
PaStreamFlags streamFlags,
1176
PaStreamCallback *streamCallback,
1179
PaError result = paNoError;
1180
PaOSSHostApiRepresentation *ossHostApi = (PaOSSHostApiRepresentation*)hostApi;
1181
PaOssStream *stream = NULL;
1182
int inputChannelCount = 0, outputChannelCount = 0;
1183
PaSampleFormat inputSampleFormat = 0, outputSampleFormat = 0, inputHostFormat = 0, outputHostFormat = 0;
1184
const PaDeviceInfo *inputDeviceInfo = 0, *outputDeviceInfo = 0;
1185
int bpInitialized = 0;
1186
double inLatency = 0., outLatency = 0.;
1189
/* validate platform specific flags */
1190
if( (streamFlags & paPlatformSpecificFlags) != 0 )
1191
return paInvalidFlag; /* unexpected platform specific flag */
1193
if( inputParameters )
1195
/* unless alternate device specification is supported, reject the use of
1196
paUseHostApiSpecificDeviceSpecification */
1197
inputDeviceInfo = hostApi->deviceInfos[inputParameters->device];
1198
PA_ENSURE( ValidateParameters( inputParameters, inputDeviceInfo, StreamMode_In ) );
1200
inputChannelCount = inputParameters->channelCount;
1201
inputSampleFormat = inputParameters->sampleFormat;
1203
if( outputParameters )
1205
outputDeviceInfo = hostApi->deviceInfos[outputParameters->device];
1206
PA_ENSURE( ValidateParameters( outputParameters, outputDeviceInfo, StreamMode_Out ) );
1208
outputChannelCount = outputParameters->channelCount;
1209
outputSampleFormat = outputParameters->sampleFormat;
1212
/* Aspect StreamChannels: We currently demand that number of input and output channels are the same, if the same
1213
* device is opened for both directions
1215
if( inputChannelCount > 0 && outputChannelCount > 0 )
1217
if( inputParameters->device == outputParameters->device )
1219
if( inputParameters->channelCount != outputParameters->channelCount )
1220
return paInvalidChannelCount;
1224
/* Round framesPerBuffer to the next power-of-two to make OSS happy. */
1225
if( framesPerBuffer != paFramesPerBufferUnspecified )
1227
framesPerBuffer &= INT_MAX;
1228
for (i = 1; framesPerBuffer > i; i <<= 1) ;
1229
framesPerBuffer = i;
1232
/* allocate and do basic initialization of the stream structure */
1233
PA_UNLESS( stream = (PaOssStream*)PaUtil_AllocateMemory( sizeof(PaOssStream) ), paInsufficientMemory );
1234
PA_ENSURE( PaOssStream_Initialize( stream, inputParameters, outputParameters, streamCallback, userData, streamFlags, ossHostApi ) );
1236
PA_ENSURE( PaOssStream_Configure( stream, sampleRate, framesPerBuffer, &inLatency, &outLatency ) );
1238
PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate );
1240
if( inputParameters )
1242
inputHostFormat = stream->capture->hostFormat;
1243
stream->streamRepresentation.streamInfo.inputLatency = inLatency +
1244
PaUtil_GetBufferProcessorInputLatency( &stream->bufferProcessor ) / sampleRate;
1246
if( outputParameters )
1248
outputHostFormat = stream->playback->hostFormat;
1249
stream->streamRepresentation.streamInfo.outputLatency = outLatency +
1250
PaUtil_GetBufferProcessorOutputLatency( &stream->bufferProcessor ) / sampleRate;
1253
/* Initialize buffer processor with fixed host buffer size.
1254
* Aspect StreamSampleFormat: Here we commit the user and host sample formats, PA infrastructure will
1255
* convert between the two.
1257
PA_ENSURE( PaUtil_InitializeBufferProcessor( &stream->bufferProcessor,
1258
inputChannelCount, inputSampleFormat, inputHostFormat, outputChannelCount, outputSampleFormat,
1259
outputHostFormat, sampleRate, streamFlags, framesPerBuffer, stream->framesPerHostBuffer,
1260
paUtilFixedHostBufferSize, streamCallback, userData ) );
1263
*s = (PaStream*)stream;
1269
PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
1271
PaOssStream_Terminate( stream );
1276
/*! Poll on I/O filedescriptors.
1278
Poll till we've determined there's data for read or write. In the full-duplex case,
1279
we don't want to hang around forever waiting for either input or output frames, so
1280
whenever we have a timed out filedescriptor we check if we're nearing under/overrun
1281
for the other direction (critical limit set at one buffer). If so, we exit the waiting
1282
state, and go on with what we got. We align the number of frames on a host buffer
1283
boundary because it is possible that the buffer size differs for the two directions and
1284
the host buffer size is a compromise between the two.
1286
static PaError PaOssStream_WaitForFrames( PaOssStream *stream, unsigned long *frames )
1288
PaError result = paNoError;
1289
int pollPlayback = 0, pollCapture = 0;
1290
int captureAvail = INT_MAX, playbackAvail = INT_MAX, commonAvail;
1291
audio_buf_info bufInfo;
1292
/* int ofs = 0, nfds = stream->nfds; */
1293
fd_set readFds, writeFds;
1295
struct timeval selectTimeval = {0, 0};
1296
unsigned long timeout = stream->pollTimeout; /* In usecs */
1297
int captureFd = -1, playbackFd = -1;
1302
if( stream->capture )
1305
captureFd = stream->capture->fd;
1306
/* stream->capture->pfd->events = POLLIN; */
1308
if( stream->playback )
1311
playbackFd = stream->playback->fd;
1312
/* stream->playback->pfd->events = POLLOUT; */
1315
FD_ZERO( &readFds );
1316
FD_ZERO( &writeFds );
1318
while( pollPlayback || pollCapture )
1320
pthread_testcancel();
1322
/* select may modify the timeout parameter */
1323
selectTimeval.tv_usec = timeout;
1328
FD_SET( captureFd, &readFds );
1329
nfds = captureFd + 1;
1333
FD_SET( playbackFd, &writeFds );
1334
nfds = PA_MAX( nfds, playbackFd + 1 );
1336
ENSURE_( select( nfds, &readFds, &writeFds, NULL, &selectTimeval ), paUnanticipatedHostError );
1338
if( poll( stream->pfds + ofs, nfds, stream->pollTimeout ) < 0 )
1341
ENSURE_( -1, paUnanticipatedHostError );
1344
pthread_testcancel();
1348
if( FD_ISSET( captureFd, &readFds ) )
1350
FD_CLR( captureFd, &readFds );
1354
if( stream->capture->pfd->revents & POLLIN )
1361
else if( stream->playback ) /* Timed out, go on with playback? */
1363
/*PA_DEBUG(( "%s: Trying to poll again for capture frames, pollTimeout: %d\n",
1364
__FUNCTION__, stream->pollTimeout ));*/
1369
if( FD_ISSET( playbackFd, &writeFds ) )
1371
FD_CLR( playbackFd, &writeFds );
1375
if( stream->playback->pfd->revents & POLLOUT )
1381
else if( stream->capture ) /* Timed out, go on with capture? */
1383
/*PA_DEBUG(( "%s: Trying to poll again for playback frames, pollTimeout: %d\n\n",
1384
__FUNCTION__, stream->pollTimeout ));*/
1389
if( stream->capture )
1391
ENSURE_( ioctl( captureFd, SNDCTL_DSP_GETISPACE, &bufInfo ), paUnanticipatedHostError );
1392
captureAvail = bufInfo.fragments * stream->capture->hostFrames;
1394
PA_DEBUG(( "%s: captureAvail: 0\n", __FUNCTION__ ));
1396
captureAvail = captureAvail == 0 ? INT_MAX : captureAvail; /* Disregard if zero */
1398
if( stream->playback )
1400
ENSURE_( ioctl( playbackFd, SNDCTL_DSP_GETOSPACE, &bufInfo ), paUnanticipatedHostError );
1401
playbackAvail = bufInfo.fragments * stream->playback->hostFrames;
1402
if( !playbackAvail )
1404
PA_DEBUG(( "%s: playbackAvail: 0\n", __FUNCTION__ ));
1407
playbackAvail = playbackAvail == 0 ? INT_MAX : playbackAvail; /* Disregard if zero */
1410
commonAvail = PA_MIN( captureAvail, playbackAvail );
1411
if( commonAvail == INT_MAX )
1413
commonAvail -= commonAvail % stream->framesPerHostBuffer;
1415
assert( commonAvail != INT_MAX );
1416
assert( commonAvail >= 0 );
1417
*frames = commonAvail;
1423
/** Prepare stream for capture/playback.
1425
* In order to synchronize capture and playback properly we use the SETTRIGGER command.
1427
static PaError PaOssStream_Prepare( PaOssStream *stream )
1429
PaError result = paNoError;
1432
if( stream->triggered )
1435
/* The OSS reference instructs us to clear direction bits before setting them.*/
1436
if( stream->playback )
1437
ENSURE_( ioctl( stream->playback->fd, SNDCTL_DSP_SETTRIGGER, &enableBits ), paUnanticipatedHostError );
1438
if( stream->capture )
1439
ENSURE_( ioctl( stream->capture->fd, SNDCTL_DSP_SETTRIGGER, &enableBits ), paUnanticipatedHostError );
1441
if( stream->playback )
1443
size_t bufSz = PaOssStreamComponent_BufferSize( stream->playback );
1444
memset( stream->playback->buffer, 0, bufSz );
1446
/* Looks like we have to turn off blocking before we try this, but if we don't fill the buffer
1447
* OSS will complain. */
1448
PA_ENSURE( ModifyBlocking( stream->playback->fd, 0 ) );
1451
if( write( stream->playback->fd, stream->playback->buffer, bufSz ) < 0 )
1454
PA_ENSURE( ModifyBlocking( stream->playback->fd, 1 ) );
1457
if( stream->sharedDevice )
1459
enableBits = PCM_ENABLE_INPUT | PCM_ENABLE_OUTPUT;
1460
ENSURE_( ioctl( stream->capture->fd, SNDCTL_DSP_SETTRIGGER, &enableBits ), paUnanticipatedHostError );
1464
if( stream->capture )
1466
enableBits = PCM_ENABLE_INPUT;
1467
ENSURE_( ioctl( stream->capture->fd, SNDCTL_DSP_SETTRIGGER, &enableBits ), paUnanticipatedHostError );
1469
if( stream->playback )
1471
enableBits = PCM_ENABLE_OUTPUT;
1472
ENSURE_( ioctl( stream->playback->fd, SNDCTL_DSP_SETTRIGGER, &enableBits ), paUnanticipatedHostError );
1476
/* Ok, we have triggered the stream */
1477
stream->triggered = 1;
1483
/** Stop audio processing
1486
static PaError PaOssStream_Stop( PaOssStream *stream, int abort )
1488
PaError result = paNoError;
1490
/* Looks like the only safe way to stop audio without reopening the device is SNDCTL_DSP_POST.
1491
* Also disable capture/playback till the stream is started again.
1493
int captureErr = 0, playbackErr = 0;
1494
if( stream->capture )
1496
if( (captureErr = ioctl( stream->capture->fd, SNDCTL_DSP_POST, 0 )) < 0 )
1498
PA_DEBUG(( "%s: Failed to stop capture device, error: %d\n", __FUNCTION__, captureErr ));
1501
if( stream->playback && !stream->sharedDevice )
1503
if( (playbackErr = ioctl( stream->playback->fd, SNDCTL_DSP_POST, 0 )) < 0 )
1505
PA_DEBUG(( "%s: Failed to stop playback device, error: %d\n", __FUNCTION__, playbackErr ));
1509
if( captureErr || playbackErr )
1511
result = paUnanticipatedHostError;
1517
/** Clean up after thread exit.
1519
* Aspect StreamState: If the user has registered a streamFinishedCallback it will be called here
1521
static void OnExit( void *data )
1523
PaOssStream *stream = (PaOssStream *) data;
1526
PaUtil_ResetCpuLoadMeasurer( &stream->cpuLoadMeasurer );
1528
PaOssStream_Stop( stream, stream->callbackAbort );
1530
PA_DEBUG(( "OnExit: Stoppage\n" ));
1532
/* Eventually notify user all buffers have played */
1533
if( stream->streamRepresentation.streamFinishedCallback )
1534
stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
1536
stream->callbackAbort = 0; /* Clear state */
1537
stream->isActive = 0;
1540
static PaError SetUpBuffers( PaOssStream *stream, unsigned long framesAvail )
1542
PaError result = paNoError;
1544
if( stream->capture )
1546
PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, 0, stream->capture->buffer,
1547
stream->capture->hostChannelCount );
1548
PaUtil_SetInputFrameCount( &stream->bufferProcessor, framesAvail );
1550
if( stream->playback )
1552
PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, 0, stream->playback->buffer,
1553
stream->playback->hostChannelCount );
1554
PaUtil_SetOutputFrameCount( &stream->bufferProcessor, framesAvail );
1560
/** Thread procedure for callback processing.
1562
* Aspect StreamState: StartStream will wait on this to initiate audio processing, useful in case the
1563
* callback should be used for buffer priming. When the stream is cancelled a separate function will
1564
* take care of the transition to the Callback Finished state (the stream isn't considered Stopped
1565
* before StopStream() or AbortStream() are called).
1567
static void *PaOSS_AudioThreadProc( void *userData )
1569
PaError result = paNoError;
1570
PaOssStream *stream = (PaOssStream*)userData;
1571
unsigned long framesAvail = 0, framesProcessed = 0;
1572
int callbackResult = paContinue;
1573
int triggered = stream->triggered; /* See if SNDCTL_DSP_TRIGGER has been issued already */
1574
int initiateProcessing = triggered; /* Already triggered? */
1575
PaStreamCallbackFlags cbFlags = 0; /* We might want to keep state across iterations */
1576
PaStreamCallbackTimeInfo timeInfo = {0,0,0}; /* TODO: IMPLEMENT ME */
1579
#if ( SOUND_VERSION > 0x030904 )
1580
audio_errinfo errinfo;
1586
pthread_cleanup_push( &OnExit, stream ); /* Execute OnExit when exiting */
1588
/* The first time the stream is started we use SNDCTL_DSP_TRIGGER to accurately start capture and
1589
* playback in sync, when the stream is restarted after being stopped we simply start by reading/
1592
PA_ENSURE( PaOssStream_Prepare( stream ) );
1594
/* If we are to initiate processing implicitly by reading/writing data, we start off in blocking mode */
1595
if( initiateProcessing )
1597
/* Make sure devices are in blocking mode */
1598
if( stream->capture )
1599
ModifyBlocking( stream->capture->fd, 1 );
1600
if( stream->playback )
1601
ModifyBlocking( stream->playback->fd, 1 );
1606
pthread_testcancel();
1608
if( stream->callbackStop && callbackResult == paContinue )
1610
PA_DEBUG(( "Setting callbackResult to paComplete\n" ));
1611
callbackResult = paComplete;
1614
/* Aspect StreamState: Because of the messy OSS scheme we can't explicitly trigger device start unless
1615
* the stream has been recently started, we will have to go right ahead and read/write in blocking
1616
* fashion to trigger operation. Therefore we begin with processing one host buffer before we switch
1617
* to non-blocking mode.
1619
if( !initiateProcessing )
1621
/* Wait on available frames */
1622
PA_ENSURE( PaOssStream_WaitForFrames( stream, &framesAvail ) );
1623
assert( framesAvail % stream->framesPerHostBuffer == 0 );
1627
framesAvail = stream->framesPerHostBuffer;
1630
while( framesAvail > 0 )
1632
unsigned long frames = framesAvail;
1634
pthread_testcancel();
1636
PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer );
1639
if ( stream->capture )
1641
PA_ENSURE( PaOssStreamComponent_Read( stream->capture, &frames ) );
1642
if( frames < framesAvail )
1644
PA_DEBUG(( "Read %lu less frames than requested\n", framesAvail - frames ));
1645
framesAvail = frames;
1649
#if ( SOUND_VERSION >= 0x030904 )
1651
Check with OSS to see if there have been any under/overruns
1652
since last time we checked.
1655
if( ioctl( stream->deviceHandle, SNDCTL_DSP_GETERROR, &errinfo ) >= 0 )
1657
if( errinfo.play_underruns )
1658
cbFlags |= paOutputUnderflow ;
1659
if( errinfo.record_underruns )
1660
cbFlags |= paInputUnderflow ;
1663
PA_DEBUG(( "SNDCTL_DSP_GETERROR command failed: %s\n", strerror( errno ) ));
1667
PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo,
1670
PA_ENSURE( SetUpBuffers( stream, framesAvail ) );
1672
framesProcessed = PaUtil_EndBufferProcessing( &stream->bufferProcessor,
1674
assert( framesProcessed == framesAvail );
1675
PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed );
1677
if ( stream->playback )
1679
frames = framesAvail;
1681
PA_ENSURE( PaOssStreamComponent_Write( stream->playback, &frames ) );
1682
if( frames < framesAvail )
1684
/* TODO: handle bytesWritten != bytesRequested (slippage?) */
1685
PA_DEBUG(( "Wrote %lu less frames than requested\n", framesAvail - frames ));
1689
framesAvail -= framesProcessed;
1690
stream->framesProcessed += framesProcessed;
1692
if( callbackResult != paContinue )
1696
if( initiateProcessing || !triggered )
1699
if( stream->capture )
1700
PA_ENSURE( ModifyBlocking( stream->capture->fd, 0 ) );
1701
if( stream->playback && !stream->sharedDevice )
1702
PA_ENSURE( ModifyBlocking( stream->playback->fd, 0 ) );
1704
initiateProcessing = 0;
1705
sem_post( &stream->semaphore );
1708
if( callbackResult != paContinue )
1710
stream->callbackAbort = callbackResult == paAbort;
1711
if( stream->callbackAbort || PaUtil_IsBufferProcessorOutputEmpty( &stream->bufferProcessor ) )
1716
pthread_cleanup_pop( 1 );
1719
pthread_exit( NULL );
1722
/** Close the stream.
1725
static PaError CloseStream( PaStream* s )
1727
PaError result = paNoError;
1728
PaOssStream *stream = (PaOssStream*)s;
1732
PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
1733
PaOssStream_Terminate( stream );
1738
/** Start the stream.
1740
* Aspect StreamState: After returning, the stream shall be in the Active state, implying that an eventual
1741
* callback will be repeatedly called in a separate thread. If a separate thread is started this function
1742
* will block untill it has started processing audio, otherwise audio processing is started directly.
1744
static PaError StartStream( PaStream *s )
1746
PaError result = paNoError;
1747
PaOssStream *stream = (PaOssStream*)s;
1749
stream->isActive = 1;
1750
stream->isStopped = 0;
1751
stream->lastPosPtr = 0;
1752
stream->lastStreamBytes = 0;
1753
stream->framesProcessed = 0;
1755
/* only use the thread for callback streams */
1756
if( stream->bufferProcessor.streamCallback )
1758
PA_ENSURE( PaUtil_StartThreading( &stream->threading, &PaOSS_AudioThreadProc, stream ) );
1759
sem_wait( &stream->semaphore );
1762
PA_ENSURE( PaOssStream_Prepare( stream ) );
1768
static PaError RealStop( PaOssStream *stream, int abort )
1770
PaError result = paNoError;
1772
if( stream->callbackMode )
1775
stream->callbackAbort = 1;
1777
stream->callbackStop = 1;
1779
PA_ENSURE( PaUtil_CancelThreading( &stream->threading, !abort, NULL ) );
1781
stream->callbackStop = stream->callbackAbort = 0;
1784
PA_ENSURE( PaOssStream_Stop( stream, abort ) );
1786
stream->isStopped = 1;
1792
/** Stop the stream.
1794
* Aspect StreamState: This will cause the stream to transition to the Stopped state, playing all enqueued
1797
static PaError StopStream( PaStream *s )
1799
return RealStop( (PaOssStream *)s, 0 );
1802
/** Abort the stream.
1804
* Aspect StreamState: This will cause the stream to transition to the Stopped state, discarding all enqueued
1805
* buffers. Note that the buffers are not currently correctly discarded, this is difficult without closing
1808
static PaError AbortStream( PaStream *s )
1810
return RealStop( (PaOssStream *)s, 1 );
1813
/** Is the stream in the Stopped state.
1816
static PaError IsStreamStopped( PaStream *s )
1818
PaOssStream *stream = (PaOssStream*)s;
1820
return (stream->isStopped);
1823
/** Is the stream in the Active state.
1826
static PaError IsStreamActive( PaStream *s )
1828
PaOssStream *stream = (PaOssStream*)s;
1830
return (stream->isActive);
1833
static PaTime GetStreamTime( PaStream *s )
1835
PaOssStream *stream = (PaOssStream*)s;
1839
if( stream->playback ) {
1840
if( ioctl( stream->playback->fd, SNDCTL_DSP_GETOPTR, &info) == 0 ) {
1841
delta = ( info.bytes - stream->lastPosPtr ) /* & 0x000FFFFF*/;
1842
return (float)(stream->lastStreamBytes + delta) / PaOssStreamComponent_FrameSize( stream->playback ) / stream->sampleRate;
1846
if (ioctl( stream->capture->fd, SNDCTL_DSP_GETIPTR, &info) == 0) {
1847
delta = (info.bytes - stream->lastPosPtr) /*& 0x000FFFFF*/;
1848
return (float)(stream->lastStreamBytes + delta) / PaOssStreamComponent_FrameSize( stream->capture ) / stream->sampleRate;
1852
/* the ioctl failed, but we can still give a coarse estimate */
1854
return stream->framesProcessed / stream->sampleRate;
1858
static double GetStreamCpuLoad( PaStream* s )
1860
PaOssStream *stream = (PaOssStream*)s;
1862
return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer );
1867
As separate stream interfaces are used for blocking and callback
1868
streams, the following functions can be guaranteed to only be called
1869
for blocking streams.
1873
static PaError ReadStream( PaStream* s,
1875
unsigned long frames )
1877
PaError result = paNoError;
1878
PaOssStream *stream = (PaOssStream*)s;
1879
int bytesRequested, bytesRead;
1880
unsigned long framesRequested;
1883
/* If user input is non-interleaved, PaUtil_CopyInput will manipulate the channel pointers,
1884
* so we copy the user provided pointers */
1885
if( stream->bufferProcessor.userInputIsInterleaved )
1886
userBuffer = buffer;
1887
else /* Copy channels into local array */
1889
userBuffer = stream->capture->userBuffers;
1890
memcpy( (void *)userBuffer, buffer, sizeof (void *) * stream->capture->userChannelCount );
1895
framesRequested = PA_MIN( frames, stream->capture->hostFrames );
1897
bytesRequested = framesRequested * PaOssStreamComponent_FrameSize( stream->capture );
1898
ENSURE_( (bytesRead = read( stream->capture->fd, stream->capture->buffer, bytesRequested )),
1899
paUnanticipatedHostError );
1900
if ( bytesRequested != bytesRead )
1902
PA_DEBUG(( "Requested %d bytes, read %d\n", bytesRequested, bytesRead ));
1903
return paUnanticipatedHostError;
1906
PaUtil_SetInputFrameCount( &stream->bufferProcessor, stream->capture->hostFrames );
1907
PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, 0, stream->capture->buffer, stream->capture->hostChannelCount );
1908
PaUtil_CopyInput( &stream->bufferProcessor, &userBuffer, framesRequested );
1909
frames -= framesRequested;
1917
static PaError WriteStream( PaStream *s, const void *buffer, unsigned long frames )
1919
PaError result = paNoError;
1920
PaOssStream *stream = (PaOssStream*)s;
1921
int bytesRequested, bytesWritten;
1922
unsigned long framesConverted;
1923
const void *userBuffer;
1925
/* If user output is non-interleaved, PaUtil_CopyOutput will manipulate the channel pointers,
1926
* so we copy the user provided pointers */
1927
if( stream->bufferProcessor.userOutputIsInterleaved )
1928
userBuffer = buffer;
1931
/* Copy channels into local array */
1932
userBuffer = stream->playback->userBuffers;
1933
memcpy( (void *)userBuffer, buffer, sizeof (void *) * stream->playback->userChannelCount );
1938
PaUtil_SetOutputFrameCount( &stream->bufferProcessor, stream->playback->hostFrames );
1939
PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, 0, stream->playback->buffer, stream->playback->hostChannelCount );
1941
framesConverted = PaUtil_CopyOutput( &stream->bufferProcessor, &userBuffer, frames );
1942
frames -= framesConverted;
1944
bytesRequested = framesConverted * PaOssStreamComponent_FrameSize( stream->playback );
1945
ENSURE_( (bytesWritten = write( stream->playback->fd, stream->playback->buffer, bytesRequested )),
1946
paUnanticipatedHostError );
1948
if ( bytesRequested != bytesWritten )
1950
PA_DEBUG(( "Requested %d bytes, wrote %d\n", bytesRequested, bytesWritten ));
1951
return paUnanticipatedHostError;
1960
static signed long GetStreamReadAvailable( PaStream* s )
1962
PaError result = paNoError;
1963
PaOssStream *stream = (PaOssStream*)s;
1964
audio_buf_info info;
1966
ENSURE_( ioctl( stream->capture->fd, SNDCTL_DSP_GETISPACE, &info ), paUnanticipatedHostError );
1967
return info.fragments * stream->capture->hostFrames;
1974
/* TODO: Compute number of allocated bytes somewhere else, can we use ODELAY with capture */
1975
static signed long GetStreamWriteAvailable( PaStream* s )
1977
PaError result = paNoError;
1978
PaOssStream *stream = (PaOssStream*)s;
1980
#ifdef SNDCTL_DSP_GETODELAY
1981
ENSURE_( ioctl( stream->playback->fd, SNDCTL_DSP_GETODELAY, &delay ), paUnanticipatedHostError );
1983
return (PaOssStreamComponent_BufferSize( stream->playback ) - delay) / PaOssStreamComponent_FrameSize( stream->playback );
1985
/* Conditionally compile this to avoid warning about unused label */
1986
#ifdef SNDCTL_DSP_GETODELAY