2
* PortAudio Portable Real-Time Audio Library
3
* Latest Version at: http://www.portaudio.com
4
* Linux OSS Implementation by douglas repetto and Phil Burk
6
* Copyright (c) 1999-2000 Phil Burk
8
* Permission is hereby granted, free of charge, to any person obtaining
9
* a copy of this software and associated documentation files
10
* (the "Software"), to deal in the Software without restriction,
11
* including without limitation the rights to use, copy, modify, merge,
12
* publish, distribute, sublicense, and/or sell copies of the Software,
13
* and to permit persons to whom the Software is furnished to do so,
14
* subject to the following conditions:
16
* The above copyright notice and this permission notice shall be
17
* included in all copies or substantial portions of the Software.
19
* Any person wishing to distribute modifications to the Software is
20
* requested to send the modifications to the original developer so that
21
* they can be incorporated into the canonical version.
23
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
26
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
27
* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
28
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
35
1/2001 - Phil Burk - initial hack for Linux
36
2/2001 - Douglas Repetto - many improvements, initial query support
37
4/2/2001 - Phil - stop/abort thread control, separate in/out native buffers
38
5/28/2001 - Phil - use pthread_create() instead of clone(). Thanks Stephen Brandon!
39
use pthread_join() after thread shutdown.
40
5/29/2001 - Phil - query for multiple devices, multiple formats,
41
input mode and input+output mode working,
42
Pa_GetCPULoad() implemented.
43
PLB20010817 - Phil & Janos Haber - don't halt if test of sample rate fails.
44
SB20010904 - Stephen Brandon - mods needed for GNUSTEP and SndKit
45
JH20010905 - Janos Haber - FreeBSD mods
46
2001-09-22 - Heiko - (i.e. Heiko Purnhagen <purnhage@tnt.uni-hannover.de> ;-)
47
added 24k and 16k to ratesToTry[]
48
fixed Pa_GetInternalDevice()
49
changed DEVICE_NAME_BASE from /dev/audio to /dev/dsp
50
handled SNDCTL_DSP_SPEED in Pq_QueryDevice() more graceful
51
fixed Pa_StreamTime() for paqa_errs.c
52
fixed numCannel=2 oddity and error handling in Pa_SetupDeviceFormat()
53
grep also for HP20010922 ...
54
PLB20010924 - Phil - merged Heiko's changes
55
removed sNumDevices and potential related bugs,
56
use getenv("PA_MIN_LATENCY_MSEC") to set desired latency,
57
simplify CPU Load calculation by comparing real-time to framesPerBuffer,
58
always close device when querying even if error occurs,
59
PLB20010927 - Phil - Improved negotiation for numChannels.
60
SG20011005 - Stewart Greenhill - set numChannels back to reasonable value after query.
61
DH20010115 - David Herring - fixed uninitialized handle.
63
DM20020218 - Dominic Mazzoni - Try to open in nonblocking mode first, in case
64
the device is already open. New implementation of
65
Pa_StreamTime that uses SNDCTL_DSP_GETOPTR but
66
uses our own counter to avoid wraparound.
67
PLB20020222 - Phil Burk - Added WatchDog proc if audio running at high priority.
68
Check error return from read() and write().
69
Check CPU endianness instead of assuming Little Endian.
70
20020621 - pa_unix_oss.c split into pa_unix.c, pa_unix.h, pa_unix_oss.c by
71
Augustus Saunders. Return values from usleep() ignored by Sam Bayer
72
because not cross-platform compatible (at least until we get configure
73
going). Pa_SetupDeviceFormat split into input and output sides to
74
reflect capabilities of Solaris.
76
20030206 - Martin Rohrbach - various mods for Solaris
78
20030410 - Bjorn Dittmer-Roche - fixed numerous problems associated with pthread_t
80
20030630 - Thomas Richter - eliminated unused variable warnings.
83
O- put semaphore lock around shared data?
84
O- handle native formats better
85
O- handle stereo-only device better ???
86
O- what if input and output of a device capabilities differ (e.g. es1371) ???
92
typedef void *(*pthread_function_t)(void *);
94
/************************************************* Shared Data ********/
95
/* FIXME - put Mutex around this shared data. */
96
static internalPortAudioDevice *sDeviceList = NULL;
97
static int sDefaultInputDeviceID = paNoDevice;
98
static int sDefaultOutputDeviceID = paNoDevice;
99
static int sPaHostError = 0;
101
/********************************* BEGIN CPU UTILIZATION MEASUREMENT ****/
102
static void Pa_StartUsageCalculation( internalPortAudioStream *past )
104
PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
105
if( pahsc == NULL ) return;
106
/* Query system timer for usage analysis and to prevent overuse of CPU. */
107
gettimeofday( &pahsc->pahsc_EntryTime, NULL );
110
static long SubtractTime_AminusB( struct timeval *timeA, struct timeval *timeB )
112
long secs = timeA->tv_sec - timeB->tv_sec;
113
long usecs = secs * 1000000;
114
usecs += (timeA->tv_usec - timeB->tv_usec);
118
/******************************************************************************
119
** Measure fractional CPU load based on real-time it took to calculate
120
** buffers worth of output.
122
static void Pa_EndUsageCalculation( internalPortAudioStream *past )
124
struct timeval currentTime;
128
#define LOWPASS_COEFFICIENT_0 (0.95)
129
#define LOWPASS_COEFFICIENT_1 (0.99999 - LOWPASS_COEFFICIENT_0)
131
PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
132
if( pahsc == NULL ) return;
134
if( gettimeofday( ¤tTime, NULL ) == 0 )
136
usecsElapsed = SubtractTime_AminusB( ¤tTime, &pahsc->pahsc_EntryTime );
137
/* Use inverse because it is faster than the divide. */
138
newUsage = usecsElapsed * pahsc->pahsc_InverseMicrosPerBuffer;
140
past->past_Usage = (LOWPASS_COEFFICIENT_0 * past->past_Usage) +
141
(LOWPASS_COEFFICIENT_1 * newUsage);
145
/****************************************** END CPU UTILIZATION *******/
147
/*********************************************************************
148
* Determines the number of available devices by trying to open
149
* each "/dev/dsp#" or "/dsp/audio#" in order until it fails.
150
* Add each working device to a singly linked list of devices.
152
PaError Pa_QueryDevices( void )
154
internalPortAudioDevice *pad, *lastPad;
158
PaError result = paNoError;
161
sDefaultInputDeviceID = paNoDevice;
162
sDefaultOutputDeviceID = paNoDevice;
168
/* Allocate structure to hold device info. */
169
pad = (internalPortAudioDevice *)
170
PaHost_AllocateFastMemory( sizeof(internalPortAudioDevice) );
171
if( pad == NULL ) return paInsufficientMemory;
172
memset( pad, 0, sizeof(internalPortAudioDevice) );
174
/* Build name for device. */
175
if( numDevices == 0 )
177
sprintf( pad->pad_DeviceName, DEVICE_NAME_BASE);
181
sprintf( pad->pad_DeviceName, DEVICE_NAME_BASE "%d", numDevices );
184
DBUG(("Try device %s\n", pad->pad_DeviceName ));
185
testResult = Pa_QueryDevice( pad->pad_DeviceName, pad );
186
DBUG(("Pa_QueryDevice returned %d\n", testResult ));
187
if( testResult != paNoError )
189
if( lastPad == NULL )
191
result = testResult; /* No good devices! */
194
PaHost_FreeFastMemory( pad, sizeof(internalPortAudioDevice) );
199
/* Add to linked list of devices. */
202
lastPad->pad_Next = pad;
206
sDeviceList = pad; /* First element in linked list. */
212
/* I'm sitting at a SunRay1 and I neither have /dev/audio# nor /dev/dsp#.
213
Instead, the correct audio device is stored in the environment variable
214
AUDIODEV and/or UTAUDIODEV, so check these devices as well if we haven't
215
checked them yet above - MR */
217
DBUG(("Checking for AUDIODEV and UTAUDIODEV\n"));
218
envdev = getenv("AUDIODEV");
219
if (envdev != NULL && !strstr(envdev, DEVICE_NAME_BASE)) {
222
/* Allocate structure to hold device info. */
223
pad = (internalPortAudioDevice *)
224
PaHost_AllocateFastMemory( sizeof(internalPortAudioDevice) );
225
if( pad == NULL ) return paInsufficientMemory;
226
memset( pad, 0, sizeof(internalPortAudioDevice) );
228
/* Build name for device. */
229
strcpy(pad->pad_DeviceName, envdev);
231
DBUG(("Try device %s\n", pad->pad_DeviceName ));
232
testResult = Pa_QueryDevice( pad->pad_DeviceName, pad );
233
DBUG(("Pa_QueryDevice returned %d\n", testResult ));
234
if( testResult != paNoError )
236
if( lastPad == NULL )
238
result = testResult; /* No good devices! */
240
PaHost_FreeFastMemory( pad, sizeof(internalPortAudioDevice) );
245
/* Add to linked list of devices. */
248
lastPad->pad_Next = pad;
252
sDeviceList = pad; /* First element in linked list. */
258
envdev = getenv("UTAUDIODEV");
259
if (envdev != NULL && !strstr(envdev, DEVICE_NAME_BASE) && getenv("AUDIODEV") != NULL && strcmp(envdev, getenv("AUDIODEV"))) {
262
/* Allocate structure to hold device info. */
263
pad = (internalPortAudioDevice *)
264
PaHost_AllocateFastMemory( sizeof(internalPortAudioDevice) );
265
if( pad == NULL ) return paInsufficientMemory;
266
memset( pad, 0, sizeof(internalPortAudioDevice) );
268
/* Build name for device. */
269
strcpy(pad->pad_DeviceName, envdev);
271
DBUG(("Try device %s\n", pad->pad_DeviceName ));
272
testResult = Pa_QueryDevice( pad->pad_DeviceName, pad );
273
DBUG(("Pa_QueryDevice returned %d\n", testResult ));
274
if( testResult != paNoError )
276
if( lastPad == NULL )
278
result = testResult; /* No good devices! */
280
PaHost_FreeFastMemory( pad, sizeof(internalPortAudioDevice) );
285
/* Add to linked list of devices. */
288
lastPad->pad_Next = pad;
292
sDeviceList = pad; /* First element in linked list. */
301
/*************************************************************************/
302
int Pa_CountDevices()
305
internalPortAudioDevice *pad;
307
if( sDeviceList == NULL ) Pa_Initialize();
308
/* Count devices in list. */
319
/*************************************************************************/
320
internalPortAudioDevice *Pa_GetInternalDevice( PaDeviceID id )
322
internalPortAudioDevice *pad;
323
if( (id < 0) || ( id >= Pa_CountDevices()) ) return NULL;
333
/*************************************************************************/
334
const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceID id )
336
internalPortAudioDevice *pad;
337
if( (id < 0) || ( id >= Pa_CountDevices()) ) return NULL;
338
pad = Pa_GetInternalDevice( id );
339
return &pad->pad_Info ;
342
static PaError Pa_MaybeQueryDevices( void )
344
if( sDeviceList == NULL )
346
return Pa_QueryDevices();
351
PaDeviceID Pa_GetDefaultInputDeviceID( void )
353
/* return paNoDevice; */
357
PaDeviceID Pa_GetDefaultOutputDeviceID( void )
362
/**********************************************************************
363
** Make sure that we have queried the device capabilities.
366
PaError PaHost_Init( void )
368
return Pa_MaybeQueryDevices();
371
/*******************************************************************************************
372
* The ol' Canary in a Coal Mine trick.
373
* Just update the time periodically.
374
* Runs at low priority so if audio thread runs wild, this thread will get starved
375
* and the watchdog will detect it.
378
#define SCHEDULER_POLICY SCHED_RR
379
#define WATCHDOG_MAX_SECONDS (3)
380
#define WATCHDOG_INTERVAL_USEC (1000000)
382
static int PaHost_CanaryProc( PaHostSoundControl *pahsc )
387
GSRegisterCurrentThread(); /* SB20010904 */
390
while( pahsc->pahsc_CanaryRun) {
391
usleep( WATCHDOG_INTERVAL_USEC );
392
gettimeofday( &pahsc->pahsc_CanaryTime, NULL );
395
DBUG(("PaHost_CanaryProc: exiting.\n"));
398
GSUnregisterCurrentThread(); /* SB20010904 */
404
/*******************************************************************************************
405
* Monitor audio thread and lower its it if it hogs the CPU.
406
* To prevent getting killed, the audio thread must update a
407
* variable with a timer value.
408
* If the value is not recent enough, then the
409
* thread will get killed.
412
static PaError PaHost_WatchDogProc( PaHostSoundControl *pahsc )
414
struct sched_param schp = { 0 };
418
GSRegisterCurrentThread(); /* SB20010904 */
421
/* Run at a priority level above audio thread so we can still run if it hangs. */
422
/* Rise more than 1 because of rumored off-by-one scheduler bugs. */
423
schp.sched_priority = pahsc->pahsc_AudioPriority + 4;
424
maxPri = sched_get_priority_max(SCHEDULER_POLICY);
425
if( schp.sched_priority > maxPri ) schp.sched_priority = maxPri;
427
if (sched_setscheduler(0, SCHEDULER_POLICY, &schp) != 0)
429
ERR_RPT(("PaHost_WatchDogProc: cannot set watch dog priority!\n"));
433
/* Compare watchdog time with audio and canary thread times. */
434
/* Sleep for a while or until thread cancelled. */
435
while( pahsc->pahsc_WatchDogRun )
439
struct timeval currentTime;
441
usleep( WATCHDOG_INTERVAL_USEC );
442
gettimeofday( ¤tTime, NULL );
444
/* If audio thread is not advancing, then it must be hung so kill it. */
445
delta = currentTime.tv_sec - pahsc->pahsc_EntryTime.tv_sec;
446
DBUG(("PaHost_WatchDogProc: audio delta = %d\n", delta ));
447
if( delta > WATCHDOG_MAX_SECONDS )
452
/* If canary died, then lower audio priority and halt canary. */
453
delta = currentTime.tv_sec - pahsc->pahsc_CanaryTime.tv_sec;
454
if( delta > WATCHDOG_MAX_SECONDS )
456
ERR_RPT(("PaHost_WatchDogProc: canary died!\n"));
461
DBUG(("PaHost_WatchDogProc: exiting.\n"));
463
GSUnregisterCurrentThread(); /* SB20010904 */
469
struct sched_param schat = { 0 };
470
if( sched_setscheduler(pahsc->pahsc_AudioThreadPID, SCHED_OTHER, &schat) != 0)
472
ERR_RPT(("PaHost_WatchDogProc: failed to lower audio priority. errno = %d\n", errno ));
473
/* Fall through into killing audio thread. */
477
ERR_RPT(("PaHost_WatchDogProc: lowered audio priority to prevent hogging of CPU.\n"));
483
ERR_RPT(("PaHost_WatchDogProc: killing hung audio thread!\n"));
484
pthread_kill( pahsc->pahsc_AudioThread, SIGKILL );
487
pahsc->pahsc_CanaryRun = 0;
488
DBUG(("PaHost_WatchDogProc: cancel Canary\n"));
489
pthread_cancel( pahsc->pahsc_CanaryThread );
490
DBUG(("PaHost_WatchDogProc: join Canary\n"));
491
pthread_join( pahsc->pahsc_CanaryThread, NULL );
492
DBUG(("PaHost_WatchDogProc: forget Canary\n"));
493
pahsc->pahsc_IsCanaryThreadValid = 0;
496
GSUnregisterCurrentThread(); /* SB20010904 */
501
/*******************************************************************************************/
502
static void PaHost_StopWatchDog( PaHostSoundControl *pahsc )
504
/* Cancel WatchDog thread if there is one. */
505
if( pahsc->pahsc_IsWatchDogThreadValid )
507
pahsc->pahsc_WatchDogRun = 0;
508
DBUG(("PaHost_StopWatchDog: cancel WatchDog\n"));
509
pthread_cancel( pahsc->pahsc_WatchDogThread );
510
pthread_join( pahsc->pahsc_WatchDogThread, NULL );
511
pahsc->pahsc_IsWatchDogThreadValid = 0;
513
/* Cancel Canary thread if there is one. */
514
if( pahsc->pahsc_IsCanaryThreadValid )
516
pahsc->pahsc_CanaryRun = 0;
517
DBUG(("PaHost_StopWatchDog: cancel Canary\n"));
518
pthread_cancel( pahsc->pahsc_CanaryThread );
519
DBUG(("PaHost_StopWatchDog: join Canary\n"));
520
pthread_join( pahsc->pahsc_CanaryThread, NULL );
521
pahsc->pahsc_IsCanaryThreadValid = 0;
525
/*******************************************************************************************/
526
static PaError PaHost_StartWatchDog( PaHostSoundControl *pahsc )
531
/* The watch dog watches for these timer updates */
532
gettimeofday( &pahsc->pahsc_EntryTime, NULL );
533
gettimeofday( &pahsc->pahsc_CanaryTime, NULL );
535
/* Launch a canary thread to detect priority abuse. */
536
pahsc->pahsc_CanaryRun = 1;
537
hres = pthread_create(&(pahsc->pahsc_CanaryThread),
538
NULL /*pthread_attr_t * attr*/,
539
(pthread_function_t)PaHost_CanaryProc, pahsc);
542
pahsc->pahsc_IsCanaryThreadValid = 0;
543
result = paHostError;
547
pahsc->pahsc_IsCanaryThreadValid = 1;
549
/* Launch a watchdog thread to prevent runaway audio thread. */
550
pahsc->pahsc_WatchDogRun = 1;
551
hres = pthread_create(&(pahsc->pahsc_WatchDogThread),
552
NULL /*pthread_attr_t * attr*/,
553
(pthread_function_t)PaHost_WatchDogProc, pahsc);
556
pahsc->pahsc_IsWatchDogThreadValid = 0;
557
result = paHostError;
561
pahsc->pahsc_IsWatchDogThreadValid = 1;
565
PaHost_StopWatchDog( pahsc );
569
/*******************************************************************************************
570
* Bump priority of audio thread if running with superuser priveledges.
571
* if priority bumped then launch a watchdog.
573
static PaError PaHost_BoostPriority( internalPortAudioStream *past )
575
PaHostSoundControl *pahsc;
576
PaError result = paNoError;
577
struct sched_param schp = { 0 };
579
pahsc = (PaHostSoundControl *) past->past_DeviceData;
580
if( pahsc == NULL ) return paInternalError;
582
pahsc->pahsc_AudioThreadPID = getpid();
583
DBUG(("PaHost_BoostPriority: audio PID = %d\n", pahsc->pahsc_AudioThreadPID ));
585
/* Choose a priority in the middle of the range. */
586
pahsc->pahsc_AudioPriority = (sched_get_priority_max(SCHEDULER_POLICY) -
587
sched_get_priority_min(SCHEDULER_POLICY)) / 2;
588
schp.sched_priority = pahsc->pahsc_AudioPriority;
590
if (sched_setscheduler(0, SCHEDULER_POLICY, &schp) != 0)
592
DBUG(("PortAudio: only superuser can use real-time priority.\n"));
596
DBUG(("PortAudio: audio callback priority set to level %d!\n", schp.sched_priority));
597
/* We are running at high priority so we should have a watchdog in case audio goes wild. */
598
result = PaHost_StartWatchDog( pahsc );
604
/*******************************************************************************************/
605
static PaError Pa_AudioThreadProc( internalPortAudioStream *past )
608
PaHostSoundControl *pahsc;
609
ssize_t bytes_read, bytes_written;
611
pahsc = (PaHostSoundControl *) past->past_DeviceData;
612
if( pahsc == NULL ) return paInternalError;
615
GSRegisterCurrentThread(); /* SB20010904 */
618
result = PaHost_BoostPriority( past );
619
if( result < 0 ) goto error;
621
past->past_IsActive = 1;
622
DBUG(("entering thread.\n"));
624
while( (past->past_StopNow == 0) && (past->past_StopSoon == 0) )
626
/* Read data from device */
627
if(pahsc->pahsc_NativeInputBuffer)
629
unsigned int totalread = 0;
630
DBUG(("Pa_AudioThreadProc: attempt to read %d bytes\n", pahsc->pahsc_BytesPerInputBuffer));
633
bytes_read = read(pahsc->pahsc_InputHandle,
634
(char *)pahsc->pahsc_NativeInputBuffer + totalread,
635
pahsc->pahsc_BytesPerInputBuffer - totalread);
639
ERR_RPT(("PortAudio: read interrupted!\n"));
643
totalread += bytes_read;
644
} while( totalread < pahsc->pahsc_BytesPerInputBuffer);
647
/* Convert 16 bit native data to user data and call user routine. */
648
DBUG(("converting...\n"));
649
Pa_StartUsageCalculation( past );
650
result = Pa_CallConvertInt16( past,
651
pahsc->pahsc_NativeInputBuffer,
652
pahsc->pahsc_NativeOutputBuffer );
653
Pa_EndUsageCalculation( past );
656
DBUG(("hmm, Pa_CallConvertInt16() says: %d. i'm bailing.\n",
661
/* Write data to device. */
662
if( pahsc->pahsc_NativeOutputBuffer )
664
unsigned int totalwritten = 0;
667
bytes_written = write(pahsc->pahsc_OutputHandle,
668
(void *)pahsc->pahsc_NativeOutputBuffer,
669
pahsc->pahsc_BytesPerOutputBuffer);
670
if( bytes_written < 0 )
672
ERR_RPT(("PortAudio: write interrupted!"));
676
totalwritten += bytes_written;
677
} while( totalwritten < pahsc->pahsc_BytesPerOutputBuffer);
680
Pa_UpdateStreamTime(pahsc);
682
DBUG(("Pa_AudioThreadProc: left audio loop.\n"));
684
past->past_IsActive = 0;
685
PaHost_StopWatchDog( pahsc );
688
DBUG(("leaving audio thread.\n"));
690
GSUnregisterCurrentThread(); /* SB20010904 */
695
/*************************************************************************
696
** Determine minimum number of buffers required for this host based
697
** on minimum latency. Latency can be optionally set by user by setting
698
** an environment variable. For example, to set latency to 200 msec, put:
700
** set PA_MIN_LATENCY_MSEC=200
702
** in the cshrc file.
704
#define PA_LATENCY_ENV_NAME ("PA_MIN_LATENCY_MSEC")
706
int Pa_GetMinNumBuffers( int framesPerBuffer, double framesPerSecond )
709
int minLatencyMsec = MIN_LATENCY_MSEC;
710
char *minLatencyText = getenv(PA_LATENCY_ENV_NAME);
711
if( minLatencyText != NULL )
713
PRINT(("PA_MIN_LATENCY_MSEC = %s\n", minLatencyText ));
714
minLatencyMsec = atoi( minLatencyText );
715
if( minLatencyMsec < 1 ) minLatencyMsec = 1;
716
else if( minLatencyMsec > 5000 ) minLatencyMsec = 5000;
719
minBuffers = (int) ((minLatencyMsec * framesPerSecond) / ( 1000.0 * framesPerBuffer ));
720
if( minBuffers < 2 ) minBuffers = 2;
724
/*******************************************************************/
725
PaError PaHost_OpenStream( internalPortAudioStream *past )
727
PaError result = paNoError;
728
PaHostSoundControl *pahsc;
729
unsigned int minNumBuffers;
730
internalPortAudioDevice *pad;
731
DBUG(("PaHost_OpenStream() called.\n" ));
733
/* Allocate and initialize host data. */
734
pahsc = (PaHostSoundControl *) malloc(sizeof(PaHostSoundControl));
737
result = paInsufficientMemory;
740
memset( pahsc, 0, sizeof(PaHostSoundControl) );
741
past->past_DeviceData = (void *) pahsc;
743
pahsc->pahsc_OutputHandle = BAD_DEVICE_ID; /* No device currently opened. */
744
pahsc->pahsc_InputHandle = BAD_DEVICE_ID;
745
pahsc->pahsc_IsAudioThreadValid = 0;
746
pahsc->pahsc_IsWatchDogThreadValid = 0;
748
/* Allocate native buffers. */
749
pahsc->pahsc_BytesPerInputBuffer = past->past_FramesPerUserBuffer *
750
past->past_NumInputChannels * sizeof(short);
751
if( past->past_NumInputChannels > 0)
753
pahsc->pahsc_NativeInputBuffer = (short *) malloc(pahsc->pahsc_BytesPerInputBuffer);
754
if( pahsc->pahsc_NativeInputBuffer == NULL )
756
result = paInsufficientMemory;
760
pahsc->pahsc_BytesPerOutputBuffer = past->past_FramesPerUserBuffer *
761
past->past_NumOutputChannels * sizeof(short);
762
if( past->past_NumOutputChannels > 0)
764
pahsc->pahsc_NativeOutputBuffer = (short *) malloc(pahsc->pahsc_BytesPerOutputBuffer);
765
if( pahsc->pahsc_NativeOutputBuffer == NULL )
767
result = paInsufficientMemory;
772
/* DBUG(("PaHost_OpenStream: pahsc_MinFramesPerHostBuffer = %d\n", pahsc->pahsc_MinFramesPerHostBuffer )); */
773
minNumBuffers = Pa_GetMinNumBuffers( past->past_FramesPerUserBuffer, past->past_SampleRate );
774
past->past_NumUserBuffers = ( minNumBuffers > past->past_NumUserBuffers ) ? minNumBuffers : past->past_NumUserBuffers;
776
pahsc->pahsc_InverseMicrosPerBuffer = past->past_SampleRate / (1000000.0 * past->past_FramesPerUserBuffer);
777
DBUG(("past_SampleRate = %g\n", past->past_SampleRate ));
778
DBUG(("past_FramesPerUserBuffer = %d\n", past->past_FramesPerUserBuffer ));
779
DBUG(("pahsc_InverseMicrosPerBuffer = %g\n", pahsc->pahsc_InverseMicrosPerBuffer ));
781
/* ------------------------- OPEN DEVICE -----------------------*/
784
if (past->past_OutputDeviceID == past->past_InputDeviceID)
787
if ((past->past_NumOutputChannels > 0) && (past->past_NumInputChannels > 0) )
789
pad = Pa_GetInternalDevice( past->past_OutputDeviceID );
790
DBUG(("PaHost_OpenStream: attempt to open %s for O_RDWR\n", pad->pad_DeviceName ));
792
/* dmazzoni: test it first in nonblocking mode to
793
make sure the device is not busy */
794
pahsc->pahsc_InputHandle = open(pad->pad_DeviceName,O_RDWR|O_NONBLOCK);
795
if(pahsc->pahsc_InputHandle==-1)
797
ERR_RPT(("PaHost_OpenStream: could not open %s for O_RDWR\n", pad->pad_DeviceName ));
798
result = paHostError;
801
close(pahsc->pahsc_InputHandle);
803
pahsc->pahsc_OutputHandle = pahsc->pahsc_InputHandle =
804
open(pad->pad_DeviceName,O_RDWR);
805
if(pahsc->pahsc_InputHandle==-1)
807
ERR_RPT(("PaHost_OpenStream: could not open %s for O_RDWR\n", pad->pad_DeviceName ));
808
result = paHostError;
811
Pa_SetLatency( pahsc->pahsc_OutputHandle,
812
past->past_NumUserBuffers, past->past_FramesPerUserBuffer,
813
past->past_NumOutputChannels );
814
result = Pa_SetupDeviceFormat( pahsc->pahsc_OutputHandle,
815
past->past_NumOutputChannels, (int)past->past_SampleRate );
820
if (past->past_NumOutputChannels > 0)
822
pad = Pa_GetInternalDevice( past->past_OutputDeviceID );
823
DBUG(("PaHost_OpenStream: attempt to open %s for O_WRONLY\n", pad->pad_DeviceName ));
824
/* dmazzoni: test it first in nonblocking mode to
825
make sure the device is not busy */
826
pahsc->pahsc_OutputHandle = open(pad->pad_DeviceName,O_WRONLY|O_NONBLOCK);
827
if(pahsc->pahsc_OutputHandle==-1)
829
ERR_RPT(("PaHost_OpenStream: could not open %s for O_WRONLY\n", pad->pad_DeviceName ));
830
result = paHostError;
833
close(pahsc->pahsc_OutputHandle);
835
pahsc->pahsc_OutputHandle = open(pad->pad_DeviceName,O_WRONLY);
836
if(pahsc->pahsc_OutputHandle==-1)
838
ERR_RPT(("PaHost_OpenStream: could not open %s for O_WRONLY\n", pad->pad_DeviceName ));
839
result = paHostError;
842
Pa_SetLatency( pahsc->pahsc_OutputHandle,
843
past->past_NumUserBuffers, past->past_FramesPerUserBuffer,
844
past->past_NumOutputChannels );
845
result = Pa_SetupOutputDeviceFormat( pahsc->pahsc_OutputHandle,
846
past->past_NumOutputChannels, (int)past->past_SampleRate );
849
if (past->past_NumInputChannels > 0)
851
pad = Pa_GetInternalDevice( past->past_InputDeviceID );
852
DBUG(("PaHost_OpenStream: attempt to open %s for O_RDONLY\n", pad->pad_DeviceName ));
853
/* dmazzoni: test it first in nonblocking mode to
854
make sure the device is not busy */
855
pahsc->pahsc_InputHandle = open(pad->pad_DeviceName,O_RDONLY|O_NONBLOCK);
856
if(pahsc->pahsc_InputHandle==-1)
858
ERR_RPT(("PaHost_OpenStream: could not open %s for O_RDONLY\n", pad->pad_DeviceName ));
859
result = paHostError;
862
close(pahsc->pahsc_InputHandle);
864
pahsc->pahsc_InputHandle = open(pad->pad_DeviceName,O_RDONLY);
865
if(pahsc->pahsc_InputHandle==-1)
867
ERR_RPT(("PaHost_OpenStream: could not open %s for O_RDONLY\n", pad->pad_DeviceName ));
868
result = paHostError;
871
Pa_SetLatency( pahsc->pahsc_InputHandle, /* DH20010115 - was OutputHandle! */
872
past->past_NumUserBuffers, past->past_FramesPerUserBuffer,
873
past->past_NumInputChannels );
874
result = Pa_SetupInputDeviceFormat( pahsc->pahsc_InputHandle,
875
past->past_NumInputChannels, (int)past->past_SampleRate );
880
DBUG(("PaHost_OpenStream: SUCCESS - result = %d\n", result ));
884
ERR_RPT(("PaHost_OpenStream: ERROR - result = %d\n", result ));
885
PaHost_CloseStream( past );
889
/*************************************************************************/
890
PaError PaHost_StartOutput( internalPortAudioStream *past )
892
past = past; /* unused */
896
/*************************************************************************/
897
PaError PaHost_StartInput( internalPortAudioStream *past )
899
past = past; /* unused */
903
/*************************************************************************/
904
PaError PaHost_StartEngine( internalPortAudioStream *past )
906
PaHostSoundControl *pahsc;
907
PaError result = paNoError;
910
pahsc = (PaHostSoundControl *) past->past_DeviceData;
912
past->past_StopSoon = 0;
913
past->past_StopNow = 0;
914
past->past_IsActive = 1;
916
/* Use pthread_create() instead of __clone() because:
917
* - pthread_create also works for other UNIX systems like Solaris,
918
* - the Java HotSpot VM crashes in pthread_setcanceltype() when using __clone()
920
hres = pthread_create(&(pahsc->pahsc_AudioThread),
921
NULL /*pthread_attr_t * attr*/,
922
(pthread_function_t)Pa_AudioThreadProc, past);
925
result = paHostError;
927
pahsc->pahsc_IsAudioThreadValid = 0;
930
pahsc->pahsc_IsAudioThreadValid = 1;
936
/*************************************************************************/
937
PaError PaHost_StopEngine( internalPortAudioStream *past, int abort )
940
PaError result = paNoError;
941
PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
943
if( pahsc == NULL ) return paNoError;
945
/* Tell background thread to stop generating more data and to let current data play out. */
946
past->past_StopSoon = 1;
947
/* If aborting, tell background thread to stop NOW! */
948
if( abort ) past->past_StopNow = 1;
950
/* Join thread to recover memory resources. */
951
if( pahsc->pahsc_IsAudioThreadValid )
953
/* This check is needed for GNUSTEP - SB20010904 */
954
if ( !pthread_equal( pahsc->pahsc_AudioThread, pthread_self() ) )
956
hres = pthread_join( pahsc->pahsc_AudioThread, NULL );
960
DBUG(("Play thread was stopped from itself - can't do pthread_join()\n"));
966
result = paHostError;
969
pahsc->pahsc_IsAudioThreadValid = 0;
972
past->past_IsActive = 0;
977
/*************************************************************************/
978
PaError PaHost_StopInput( internalPortAudioStream *past, int abort )
980
past = past; /* unused */
981
abort = abort; /* unused */
985
/*************************************************************************/
986
PaError PaHost_StopOutput( internalPortAudioStream *past, int abort )
988
past = past; /* unused */
989
abort = abort; /* unused */
993
/*******************************************************************/
994
PaError PaHost_CloseStream( internalPortAudioStream *past )
996
PaHostSoundControl *pahsc;
998
if( past == NULL ) return paBadStreamPtr;
999
pahsc = (PaHostSoundControl *) past->past_DeviceData;
1000
if( pahsc == NULL ) return paNoError;
1002
if( pahsc->pahsc_OutputHandle != BAD_DEVICE_ID )
1005
DBUG(("PaHost_CloseStream: attempt to close output device handle = %d\n",
1006
pahsc->pahsc_OutputHandle ));
1008
Pa_FlushStream(pahsc->pahsc_OutputHandle);
1010
err = close(pahsc->pahsc_OutputHandle);
1013
ERR_RPT(("PaHost_CloseStream: warning, closing output device failed.\n"));
1017
if( (pahsc->pahsc_InputHandle != BAD_DEVICE_ID) &&
1018
(pahsc->pahsc_InputHandle != pahsc->pahsc_OutputHandle) )
1021
DBUG(("PaHost_CloseStream: attempt to close input device handle = %d\n",
1022
pahsc->pahsc_InputHandle ));
1024
Pa_FlushStream(pahsc->pahsc_InputHandle);
1026
err = close(pahsc->pahsc_InputHandle);
1029
ERR_RPT(("PaHost_CloseStream: warning, closing input device failed.\n"));
1032
pahsc->pahsc_OutputHandle = BAD_DEVICE_ID;
1033
pahsc->pahsc_InputHandle = BAD_DEVICE_ID;
1035
if( pahsc->pahsc_NativeInputBuffer )
1037
free( pahsc->pahsc_NativeInputBuffer );
1038
pahsc->pahsc_NativeInputBuffer = NULL;
1040
if( pahsc->pahsc_NativeOutputBuffer )
1042
free( pahsc->pahsc_NativeOutputBuffer );
1043
pahsc->pahsc_NativeOutputBuffer = NULL;
1047
past->past_DeviceData = NULL;
1051
/*************************************************************************/
1052
PaError PaHost_Term( void )
1054
/* Free all of the linked devices. */
1055
internalPortAudioDevice *pad, *nextPad;
1057
while( pad != NULL )
1059
nextPad = pad->pad_Next;
1060
DBUG(("PaHost_Term: freeing %s\n", pad->pad_DeviceName ));
1061
PaHost_FreeFastMemory( pad, sizeof(internalPortAudioDevice) );
1068
/*************************************************************************
1069
* Sleep for the requested number of milliseconds.
1071
void Pa_Sleep( long msec )
1074
struct timeval timeout;
1075
timeout.tv_sec = msec / 1000;
1076
timeout.tv_usec = (msec % 1000) * 1000;
1077
select( 0, NULL, NULL, NULL, &timeout );
1079
long usecs = msec * 1000;
1084
/*************************************************************************
1085
* Allocate memory that can be accessed in real-time.
1086
* This may need to be held in physical memory so that it is not
1087
* paged to virtual memory.
1088
* This call MUST be balanced with a call to PaHost_FreeFastMemory().
1090
void *PaHost_AllocateFastMemory( long numBytes )
1092
void *addr = malloc( numBytes ); /* FIXME - do we need physical, wired, non-virtual memory? */
1093
if( addr != NULL ) memset( addr, 0, numBytes );
1097
/*************************************************************************
1098
* Free memory that could be accessed in real-time.
1099
* This call MUST be balanced with a call to PaHost_AllocateFastMemory().
1101
void PaHost_FreeFastMemory( void *addr, long numBytes )
1103
numBytes = numBytes; /* unused */
1104
if( addr != NULL ) free( addr );
1108
/***********************************************************************/
1109
PaError PaHost_StreamActive( internalPortAudioStream *past )
1111
PaHostSoundControl *pahsc;
1112
if( past == NULL ) return paBadStreamPtr;
1113
pahsc = (PaHostSoundControl *) past->past_DeviceData;
1114
if( pahsc == NULL ) return paInternalError;
1115
return (PaError) (past->past_IsActive != 0);
1118
/***********************************************************************/
1119
long Pa_GetHostError( void )
1121
return (long) sPaHostError;