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