/[pcsx2_0.9.7]/branch/r3113_0.9.7_beta/3rdparty/portaudio/src/hostapi/oss/pa_unix_oss.c
ViewVC logotype

Contents of /branch/r3113_0.9.7_beta/3rdparty/portaudio/src/hostapi/oss/pa_unix_oss.c

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC 1.1.22