1 /*
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
5  *
6  * Copyright (c) 1999-2000 Phil Burk
7  *
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:
15  *
16  * The above copyright notice and this permission notice shall be
17  * included in all copies or substantial portions of the Software.
18  *
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.
22  *
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.
30  *
31  */
32 /*
33 Modfication History
34   1/2001 - Phil Burk - initial hack for Linux
35   2/2001 - Douglas Repetto - many improvements, initial query support
36   4/2/2001 - Phil - stop/abort thread control, separate in/out native buffers
37   5/28/2001 - Phil - use pthread_create() instead of clone(). Thanks Stephen Brandon!
38 		     use pthread_join() after thread shutdown.
39   5/29/2001 - Phil - query for multiple devices, multiple formats,
40                      input mode and input+output mode working,
41 		     Pa_GetCPULoad() implemented.
42   PLB20010817 - Phil & Janos Haber - don't halt if test of sample rate fails.
43   SB20010904 - Stephen Brandon - mods needed for GNUSTEP and SndKit
44   JH20010905 - Janos Haber - FreeBSD mods
45   2001-09-22 - Heiko - (i.e. Heiko Purnhagen <purnhage@tnt.uni-hannover.de> ;-)
46                        added 24k and 16k to ratesToTry[]
47 		       fixed Pa_GetInternalDevice()
48 		       changed DEVICE_NAME_BASE from /dev/audio to /dev/dsp
49 		       handled SNDCTL_DSP_SPEED in Pq_QueryDevice() more graceful
50 		       fixed Pa_StreamTime() for paqa_errs.c
51 		       fixed numCannel=2 oddity and error handling in Pa_SetupDeviceFormat()
52 		       grep also for HP20010922 ...
53   PLB20010924 - Phil - merged Heiko's changes
54                        removed sNumDevices and potential related bugs,
55 		       use getenv("PA_MIN_LATENCY_MSEC") to set desired latency,
56 		       simplify CPU Load calculation by comparing real-time to framesPerBuffer,
57 		       always close device when querying even if error occurs,
58   PLB20010927 - Phil - Improved negotiation for numChannels.
59 
60 
61 TODO
62 O- change Pa_StreamTime() to query device (patest_sync.c)
63 O- put semaphore lock around shared data?
64 O- handle native formats better
65 O- handle stereo-only device better ???
66 O- what if input and output of a device capabilities differ (e.g. es1371) ???
67 */
68 /*
69 	PROPOSED - should we add this to "portaudio.h". Problem with
70 	Pa_QueryDevice() not having same driver name os Pa_OpenStream().
71 
72 	A PaDriverInfo structure can be passed to the underlying device
73 	on the Pa_OpenStream() call. The contents and interpretation of
74 	the structure is determined by the PA implementation.
75 */
76 typedef struct PaDriverInfo /* PROPOSED */
77 {
78 /* Size of structure. Allows driver to extend the structure without breaking existing applications. */
79 	int           size;
80 /* Can be used to request a specific device name. */
81 	const char   *name;
82 	unsigned long data;
83 } PaDriverInfo;
84 
85 
86 
87 #include <stdio.h>
88 #include <stdlib.h>
89 #include <malloc.h>
90 #include <memory.h>
91 #include <math.h>
92 #if 0
93 #include "portaudio.h"
94 #include "pa_host.h"
95 #include "pa_trace.h"
96 #endif
97 #include <sys/ioctl.h>
98 #include <sys/time.h>
99 #include <fcntl.h>
100 #include <unistd.h>
101 #include <signal.h>
102 #include <stdio.h>
103 #include <stdlib.h>
104 
105 #ifdef __linux__
106 #include <linux/soundcard.h>
107 #else
108 #include <machine/soundcard.h> /* JH20010905 */
109 #endif
110 
111 #include <sched.h>
112 #include <pthread.h>
113 
114 /* Some versions of OSS do not define AFMT_S16_NE. Assume little endian. FIXME - check CPU*/
115 #ifndef AFMT_S16_NE
116 	#define AFMT_S16_NE AFMT_S16_LE
117 #endif
118 
119 #define PRINT(x)   { printf x; fflush(stdout); }
120 #define ERR_RPT(x) PRINT(x)
121 #define DBUG(x)    /* PRINT(x) */
122 #define DBUGX(x)   /* PRINT(x) */
123 
124 #define BAD_DEVICE_ID (-1)
125 
126 #define MIN_LATENCY_MSEC   (2)
127 #define MIN_TIMEOUT_MSEC   (100)
128 #define MAX_TIMEOUT_MSEC   (1000)
129 
130 /************************************************* Definitions ********/
131 #ifdef __linux__
132 	#define DEVICE_NAME_BASE            "/dev/dsp"
133 #else
134 	#define DEVICE_NAME_BASE            "/dev/audio"
135 #endif
136 
137 #define MAX_CHARS_DEVNAME           (32)
138 #define MAX_SAMPLE_RATES            (10)
139 typedef struct internalPortAudioDevice
140 {
141 	struct internalPortAudioDevice *pad_Next; /* Singly linked list. */
142 	double          pad_SampleRates[MAX_SAMPLE_RATES]; /* for pointing to from pad_Info */
143 	char            pad_DeviceName[MAX_CHARS_DEVNAME];
144 	PaDeviceInfo    pad_Info;
145 } internalPortAudioDevice;
146 
147 /* Define structure to contain all OSS and Linux specific data. */
148 typedef struct PaHostSoundControl
149 {
150 	int              pahsc_OutputHandle;
151 	int              pahsc_InputHandle;
152 	pthread_t        pahsc_ThreadPID;
153 	short           *pahsc_NativeInputBuffer;
154 	short           *pahsc_NativeOutputBuffer;
155 	unsigned int     pahsc_BytesPerInputBuffer;    /* native buffer size in bytes */
156 	unsigned int     pahsc_BytesPerOutputBuffer;   /* native buffer size in bytes */
157 /* For measuring CPU utilization. */
158 	struct timeval   pahsc_EntryTime;
159 	double           pahsc_InverseMicrosPerBuffer; /* 1/Microseconds of real-time audio per user buffer. */
160 } PaHostSoundControl;
161 
162 /************************************************* Shared Data ********/
163 /* FIXME - put Mutex around this shared data. */
164 static int sDeviceIndex = 0;
165 static internalPortAudioDevice *sDeviceList = NULL;
166 static int sDefaultInputDeviceID = paNoDevice;
167 static int sDefaultOutputDeviceID = paNoDevice;
168 static int sEnumerationError;
169 static int sPaHostError = 0;
170 
171 /************************************************* Prototypes **********/
172 
173 static internalPortAudioDevice *Pa_GetInternalDevice( PaDeviceID id );
174 static Pa_QueryDevices( void );
175 static PaError Pa_QueryDevice( const char *deviceName, internalPortAudioDevice *pad );
176 static PaError Pa_SetupDeviceFormat( int devHandle, int numChannels, int sampleRate );
177 
178 /********************************* BEGIN CPU UTILIZATION MEASUREMENT ****/
Pa_StartUsageCalculation(internalPortAudioStream * past)179 static void Pa_StartUsageCalculation( internalPortAudioStream   *past )
180 {
181 	struct itimerval itimer;
182 	PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
183 	if( pahsc == NULL ) return;
184 /* Query system timer for usage analysis and to prevent overuse of CPU. */
185 	gettimeofday( &pahsc->pahsc_EntryTime, NULL );
186 }
187 
SubtractTime_AminusB(struct timeval * timeA,struct timeval * timeB)188 static long SubtractTime_AminusB( struct timeval *timeA, struct timeval *timeB )
189 {
190 	long secs = timeA->tv_sec - timeB->tv_sec;
191 	long usecs = secs * 1000000;
192 	usecs += (timeA->tv_usec - timeB->tv_usec);
193 	return usecs;
194 }
195 
196 /******************************************************************************
197 ** Measure fractional CPU load based on real-time it took to calculate
198 ** buffers worth of output.
199 */
Pa_EndUsageCalculation(internalPortAudioStream * past)200 static void Pa_EndUsageCalculation( internalPortAudioStream   *past )
201 {
202 	struct timeval currentTime;
203 	long  usecsElapsed;
204 	double newUsage;
205 
206 #define LOWPASS_COEFFICIENT_0   (0.95)
207 #define LOWPASS_COEFFICIENT_1   (0.99999 - LOWPASS_COEFFICIENT_0)
208 
209 	PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
210 	if( pahsc == NULL ) return;
211 
212 	if( gettimeofday( &currentTime, NULL ) == 0 )
213 	{
214 		usecsElapsed = SubtractTime_AminusB( &currentTime, &pahsc->pahsc_EntryTime );
215 	/* Use inverse because it is faster than the divide. */
216 		newUsage =  usecsElapsed * pahsc->pahsc_InverseMicrosPerBuffer;
217 
218 		past->past_Usage = (LOWPASS_COEFFICIENT_0 * past->past_Usage) +
219 			(LOWPASS_COEFFICIENT_1 * newUsage);
220 	}
221 }
222 /****************************************** END CPU UTILIZATION *******/
223 
224 /*********************************************************************
225  * Try to open the named device.
226  * If it opens, try to set various rates and formats and fill in
227  * the device info structure.
228  */
Pa_QueryDevice(const char * deviceName,internalPortAudioDevice * pad)229 static PaError Pa_QueryDevice( const char *deviceName, internalPortAudioDevice *pad )
230 {
231 	int result = paHostError;
232 	int numBytes;
233 	int tempDevHandle;
234 	int numChannels, maxNumChannels;
235 	int format;
236 	int numSampleRates;
237 	int sampleRate;
238 	int numRatesToTry;
239 	int ratesToTry[9] = {96000, 48000, 44100, 32000, 24000, 22050, 16000, 11025, 8000};
240 	int i;
241 
242 /* douglas:
243 	we have to do this querying in a slightly different order. apparently
244 	some sound cards will give you different info based on their settins.
245 	e.g. a card might give you stereo at 22kHz but only mono at 44kHz.
246 	the correct order for OSS is: format, channels, sample rate
247 
248 */
249 	if ( (tempDevHandle = open(deviceName,O_WRONLY))  == -1 )
250 	{
251 		DBUG(("Pa_QueryDevice: could not open %s\n", deviceName ));
252 		return paHostError;
253 	}
254 
255 /*  Ask OSS what formats are supported by the hardware. */
256 	pad->pad_Info.nativeSampleFormats = 0;
257 	if (ioctl(tempDevHandle, SNDCTL_DSP_GETFMTS, &format) == -1)
258 	{
259 		ERR_RPT(("Pa_QueryDevice: could not get format info\n" ));
260 		goto error;
261 	}
262 	if( format & AFMT_U8 )     pad->pad_Info.nativeSampleFormats |= paUInt8;
263 	if( format & AFMT_S16_NE ) pad->pad_Info.nativeSampleFormats |= paInt16;
264 
265 /* Negotiate for the maximum number of channels for this device. PLB20010927
266  * Start with 16 as a hard coded upper number of channels.
267  * Variable numChannels should contain the actual upper limit after the call.
268  * Thanks to John Lazzaro and Heiko Purnhagen for suggestions.
269  */
270 	maxNumChannels = 0;
271 	for( numChannels = 1; numChannels <= 16; numChannels++ )
272 	{
273 		int temp = numChannels;
274 		DBUG(("Pa_QueryDevice: use SNDCTL_DSP_CHANNELS, numChannels = %d\n", numChannels ))
275 		if(ioctl(tempDevHandle, SNDCTL_DSP_CHANNELS, &temp) < 0 )
276 		{
277 		/* ioctl() failed so bail out if we already have stereo */
278 			if( numChannels > 2 ) break;
279 		}
280 		else
281 		{
282 		/* ioctl() worked but bail out if it does not support numChannels.
283 		 * We don't want to leave gaps in the numChannels supported.
284 		 */
285 			if( (numChannels > 2) && (temp != numChannels) ) break;
286 			DBUG(("Pa_QueryDevice: temp = %d\n", temp ))
287 			if( temp > maxNumChannels ) maxNumChannels = temp; /* Save maximum. */
288 		}
289 	}
290 /* The above negotiation may fail for an old driver so try this older technique. */
291 	if( maxNumChannels < 1 )
292 	{
293 		int stereo = 1;
294 		if(ioctl(tempDevHandle, SNDCTL_DSP_STEREO, &stereo) < 0)
295 		{
296 			maxNumChannels = 1;
297 		}
298 		else
299 		{
300 			maxNumChannels = (stereo) ? 2 : 1;
301 		}
302 		DBUG(("Pa_QueryDevice: use SNDCTL_DSP_STEREO, maxNumChannels = %d\n", maxNumChannels ))
303 	}
304 
305 	pad->pad_Info.maxOutputChannels = maxNumChannels;
306 	DBUG(("Pa_QueryDevice: maxNumChannels = %d\n", maxNumChannels))
307 
308 /* FIXME - for now, assume maxInputChannels = maxOutputChannels.
309  *    Eventually do separate queries for O_WRONLY and O_RDONLY
310 */
311 	pad->pad_Info.maxInputChannels = pad->pad_Info.maxOutputChannels;
312 
313 	DBUG(("Pa_QueryDevice: maxInputChannels = %d\n",
314 		pad->pad_Info.maxInputChannels))
315 
316 
317 /* Determine available sample rates by trying each one and seeing result.
318  */
319 	numSampleRates = 0;
320 	numRatesToTry = sizeof(ratesToTry)/sizeof(int);
321 	for (i = 0; i < numRatesToTry; i++)
322 	{
323 		sampleRate = ratesToTry[i];
324 
325 		if (ioctl(tempDevHandle, SNDCTL_DSP_SPEED, &sampleRate) >= 0 ) // PLB20010817
326 		{
327 			if (sampleRate == ratesToTry[i])
328 			{
329 				DBUG(("Pa_QueryDevice: got sample rate: %d\n", sampleRate))
330 				pad->pad_SampleRates[numSampleRates] = (float)ratesToTry[i];
331 				numSampleRates++;
332 			}
333 		}
334 	}
335 
336 	DBUG(("Pa_QueryDevice: final numSampleRates = %d\n", numSampleRates))
337 	if (numSampleRates==0)   /* HP20010922 */
338 	{
339 		ERR_RPT(("Pa_QueryDevice: no supported sample rate (or SNDCTL_DSP_SPEED ioctl call failed).\n" ));
340 		goto error;
341 	}
342 
343 	pad->pad_Info.numSampleRates = numSampleRates;
344 	pad->pad_Info.sampleRates = pad->pad_SampleRates;
345 
346 	pad->pad_Info.name = deviceName;
347 
348 	result = paNoError;
349 
350 error:
351 /* We MUST close the handle here or we won't be able to reopen it later!!!  */
352 	close(tempDevHandle);
353 
354 	return result;
355 }
356 
357 /*********************************************************************
358  * Determines the number of available devices by trying to open
359  * each "/dev/dsp#" or "/dsp/audio#" in order until it fails.
360  * Add each working device to a singly linked list of devices.
361  */
Pa_QueryDevices(void)362 static PaError Pa_QueryDevices( void )
363 {
364 	internalPortAudioDevice *pad, *lastPad;
365 	int      numBytes;
366 	int      go = 1;
367 	int      numDevices = 0;
368 	PaError  testResult;
369 	PaError  result = paNoError;
370 
371 	sDefaultInputDeviceID = paNoDevice;
372 	sDefaultOutputDeviceID = paNoDevice;
373 
374 	lastPad = NULL;
375 
376 	while( go )
377 	{
378 /* Allocate structure to hold device info. */
379 		pad = PaHost_AllocateFastMemory( sizeof(internalPortAudioDevice) );
380 		if( pad == NULL ) return paInsufficientMemory;
381 		memset( pad, 0, sizeof(internalPortAudioDevice) );
382 
383 /* Build name for device. */
384 		if( numDevices == 0 )
385 		{
386 			sprintf( pad->pad_DeviceName, DEVICE_NAME_BASE);
387 		}
388 		else
389 		{
390 			sprintf( pad->pad_DeviceName, DEVICE_NAME_BASE "%d", numDevices );
391 		}
392 
393 		DBUG(("Try device %s\n", pad->pad_DeviceName ));
394 		testResult = Pa_QueryDevice( pad->pad_DeviceName, pad );
395 		DBUG(("Pa_QueryDevice returned %d\n", testResult ));
396 		if( testResult != paNoError )
397 		{
398 			if( lastPad == NULL )
399 			{
400 				result = testResult; /* No good devices! */
401 			}
402 			go = 0;
403 			PaHost_FreeFastMemory( pad, sizeof(internalPortAudioDevice) );
404 		}
405 		else
406 		{
407 			numDevices += 1;
408 		/* Add to linked list of devices. */
409 			if( lastPad )
410 			{
411 				lastPad->pad_Next = pad;
412 			}
413 			else
414 			{
415 				sDeviceList = pad; /* First element in linked list. */
416 			}
417 			lastPad = pad;
418 		}
419 	}
420 
421 	return result;
422 
423 }
424 
425 /*************************************************************************/
Pa_CountDevices()426 int Pa_CountDevices()
427 {
428 	int numDevices = 0;
429 	internalPortAudioDevice *pad;
430 
431 	if( sDeviceList == NULL ) Pa_Initialize();
432 /* Count devices in list. */
433 	pad = sDeviceList;
434 	while( pad != NULL )
435 	{
436 		pad = pad->pad_Next;
437 		numDevices++;
438 	}
439 
440 	return numDevices;
441 }
442 
Pa_GetInternalDevice(PaDeviceID id)443 static internalPortAudioDevice *Pa_GetInternalDevice( PaDeviceID id )
444 {
445 	internalPortAudioDevice *pad;
446 	if( (id < 0) || ( id >= Pa_CountDevices()) ) return NULL;
447 	pad = sDeviceList;
448 	while( id > 0 )
449 	{
450 		pad = pad->pad_Next;
451 		id--;
452 	}
453 	return pad;
454 }
455 
456 /*************************************************************************/
Pa_GetDeviceInfo(PaDeviceID id)457 const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceID id )
458 {
459 	internalPortAudioDevice *pad;
460 	if( (id < 0) || ( id >= Pa_CountDevices()) ) return NULL;
461 	pad = Pa_GetInternalDevice( id );
462 	return  &pad->pad_Info ;
463 }
464 
Pa_MaybeQueryDevices(void)465 static PaError Pa_MaybeQueryDevices( void )
466 {
467 	if( sDeviceList == NULL )
468 	{
469 		return Pa_QueryDevices();
470 	}
471 	return 0;
472 }
473 
Pa_GetDefaultInputDeviceID(void)474 PaDeviceID Pa_GetDefaultInputDeviceID( void )
475 {
476 	/* return paNoDevice; */
477 	return 0;
478 }
479 
Pa_GetDefaultOutputDeviceID(void)480 PaDeviceID Pa_GetDefaultOutputDeviceID( void )
481 {
482 	return 0;
483 }
484 
485 /**********************************************************************
486 ** Make sure that we have queried the device capabilities.
487 */
488 
PaHost_Init(void)489 PaError PaHost_Init( void )
490 {
491 	return Pa_MaybeQueryDevices();
492 }
493 
494 /*******************************************************************************************/
Pa_AudioThreadProc(internalPortAudioStream * past)495 static PaError Pa_AudioThreadProc( internalPortAudioStream   *past )
496 {
497 	PaError				result = 0;
498 	PaHostSoundControl             *pahsc;
499  	short				bytes_read = 0;
500 
501 #ifdef GNUSTEP
502 	GSRegisterCurrentThread(); /* SB20010904 */
503 #endif
504 
505 	pahsc = (PaHostSoundControl *) past->past_DeviceData;
506 	if( pahsc == NULL ) return paInternalError;
507 
508 	past->past_IsActive = 1;
509 	DBUG(("entering thread.\n"));
510 
511 	while( (past->past_StopNow == 0) && (past->past_StopSoon == 0) )
512 	{
513 
514 		DBUG(("go!\n"));
515 	/* Read data from device */
516 		if(pahsc->pahsc_NativeInputBuffer)
517 		{
518 	           	bytes_read = read(pahsc->pahsc_InputHandle,
519 				(void *)pahsc->pahsc_NativeInputBuffer,
520 				pahsc->pahsc_BytesPerInputBuffer);
521 
522 			DBUG(("bytes_read: %d\n", bytes_read));
523 		}
524 
525 	/* Convert 16 bit native data to user data and call user routine. */
526 		DBUG(("converting...\n"));
527 		Pa_StartUsageCalculation( past );
528 		result = Pa_CallConvertInt16( past,
529 				pahsc->pahsc_NativeInputBuffer,
530 				pahsc->pahsc_NativeOutputBuffer );
531 		Pa_EndUsageCalculation( past );
532 		if( result != 0)
533 		{
534 			DBUG(("hmm, Pa_CallConvertInt16() says: %d. i'm bailing.\n",
535 				result));
536 			break;
537 		}
538 
539 	/* Write data to device. */
540 		if( pahsc->pahsc_NativeOutputBuffer )
541 		{
542 
543             		write(pahsc->pahsc_OutputHandle,
544 				(void *)pahsc->pahsc_NativeOutputBuffer,
545             			pahsc->pahsc_BytesPerOutputBuffer);
546 		}
547 	}
548 
549 	past->past_IsActive = 0;
550 	DBUG(("leaving thread.\n"));
551 
552 #ifdef GNUSTEP
553 	GSUnregisterCurrentThread();  /* SB20010904 */
554 #endif
555 	return 0;
556 }
557 
558 /*******************************************************************************************/
Pa_SetupDeviceFormat(int devHandle,int numChannels,int sampleRate)559 static PaError Pa_SetupDeviceFormat( int devHandle, int numChannels, int sampleRate )
560 {
561 	PaError result = paNoError;
562 	int     tmp;
563 
564 /* Set format, channels, and rate in this order to keep OSS happy. */
565 /* Set data format. FIXME - handle more native formats. */
566 	tmp = AFMT_S16_NE;
567 	if( ioctl(devHandle,SNDCTL_DSP_SETFMT,&tmp) == -1)
568 	{
569 		ERR_RPT(("Pa_SetupDeviceFormat: could not SNDCTL_DSP_SETFMT\n" ));
570 		return paHostError;
571 	}
572 	if( tmp != AFMT_S16_NE)
573 	{
574 		ERR_RPT(("Pa_SetupDeviceFormat: HW does not support AFMT_S16_NE\n" ));
575 		return paHostError;
576 	}
577 
578 
579 /* Set number of channels. */
580 	tmp = numChannels;
581 	if (ioctl(devHandle, SNDCTL_DSP_CHANNELS, &numChannels) == -1)
582 	{
583 		ERR_RPT(("Pa_SetupDeviceFormat: could not SNDCTL_DSP_CHANNELS\n" ));
584 		return paHostError;
585 	}
586 	if( tmp != numChannels)
587 	{
588 		ERR_RPT(("Pa_SetupDeviceFormat: HW does not support %d channels\n", numChannels ));
589 		return paHostError;
590 	}
591 
592 /* Set playing frequency. 44100, 22050 and 11025 are safe bets. */
593 	tmp = sampleRate;
594  	if( ioctl(devHandle,SNDCTL_DSP_SPEED,&tmp) == -1)
595  	{
596 		ERR_RPT(("Pa_SetupDeviceFormat: could not SNDCTL_DSP_SPEED\n" ));
597 		return paHostError;
598 	}
599 	if( tmp != sampleRate)
600 	{
601 		ERR_RPT(("Pa_SetupDeviceFormat: HW does not support %d Hz sample rate\n",sampleRate ));
602 		return paHostError;
603 	}
604 
605 	return result;
606 }
607 
CalcHigherLogTwo(int n)608 static int CalcHigherLogTwo( int n )
609 {
610 	int log2 = 0;
611 	while( (1<<log2) < n ) log2++;
612 	return log2;
613 }
614 
615 /*******************************************************************************************
616 ** Set number of fragments and size of fragments to achieve desired latency.
617 */
Pa_SetLatency(int devHandle,int numBuffers,int framesPerBuffer,int channelsPerFrame)618 static void Pa_SetLatency( int devHandle, int numBuffers, int framesPerBuffer, int channelsPerFrame  )
619 {
620 	int     tmp;
621 	int     numFrames , bufferSize, powerOfTwo;
622 
623 /* Increase size of buffers and reduce number of buffers to reduce latency inside driver. */
624 	while( numBuffers > 8 )
625 	{
626 		numBuffers = (numBuffers + 1) >> 1;
627 		framesPerBuffer = framesPerBuffer << 1;
628 	}
629 
630 /* calculate size of buffers in bytes */
631 	bufferSize = framesPerBuffer * channelsPerFrame * sizeof(short); /* FIXME */
632 
633 /* Calculate next largest power of two */
634 	powerOfTwo = CalcHigherLogTwo( bufferSize );
635 	DBUG(("Pa_SetLatency: numBuffers = %d, framesPerBuffer = %d, powerOfTwo = %d\n",
636 		numBuffers, framesPerBuffer, powerOfTwo ));
637 
638 /* Encode info into a single int */
639 	tmp=(numBuffers<<16) + powerOfTwo;
640 
641 	if(ioctl(devHandle,SNDCTL_DSP_SETFRAGMENT,&tmp) == -1)
642 	{
643 		ERR_RPT(("Pa_SetLatency: could not SNDCTL_DSP_SETFRAGMENT\n" ));
644 		/* Don't return an error. Best to just continue and hope for the best. */
645 		ERR_RPT(("Pa_SetLatency: numBuffers = %d, framesPerBuffer = %d, powerOfTwo = %d\n",
646 			numBuffers, framesPerBuffer, powerOfTwo ));
647 	}
648 }
649 
650 /*************************************************************************
651 ** Determine minimum number of buffers required for this host based
652 ** on minimum latency. Latency can be optionally set by user by setting
653 ** an environment variable. For example, to set latency to 200 msec, put:
654 **
655 **    set PA_MIN_LATENCY_MSEC=200
656 **
657 ** in the cshrc file.
658 */
659 #define PA_LATENCY_ENV_NAME  ("PA_MIN_LATENCY_MSEC")
660 
Pa_GetMinNumBuffers(int framesPerBuffer,double framesPerSecond)661 int Pa_GetMinNumBuffers( int framesPerBuffer, double framesPerSecond )
662 {
663 	int minBuffers;
664 	int minLatencyMsec = MIN_LATENCY_MSEC;
665 	char *minLatencyText = getenv(PA_LATENCY_ENV_NAME);
666 	if( minLatencyText != NULL )
667 	{
668 		PRINT(("PA_MIN_LATENCY_MSEC = %s\n", minLatencyText ));
669 		minLatencyMsec = atoi( minLatencyText );
670 		if( minLatencyMsec < 1 ) minLatencyMsec = 1;
671 		else if( minLatencyMsec > 5000 ) minLatencyMsec = 5000;
672 	}
673 
674 	minBuffers = (int) ((minLatencyMsec * framesPerSecond) / ( 1000.0 * framesPerBuffer ));
675 	if( minBuffers < 2 ) minBuffers = 2;
676 	return minBuffers;
677 }
678 
679 /*******************************************************************/
PaHost_OpenStream(internalPortAudioStream * past)680 PaError PaHost_OpenStream( internalPortAudioStream   *past )
681 {
682 	PaError          result = paNoError;
683 	PaHostSoundControl *pahsc;
684 	int tmp;
685 	int flags;
686 	int              numBytes, maxChannels;
687 	unsigned int     minNumBuffers;
688 	internalPortAudioDevice *pad;
689 	DBUG(("PaHost_OpenStream() called.\n" ));
690 
691 /* Allocate and initialize host data. */
692 	pahsc = (PaHostSoundControl *) malloc(sizeof(PaHostSoundControl));
693 	if( pahsc == NULL )
694 	{
695 		result = paInsufficientMemory;
696 		goto error;
697 	}
698 	memset( pahsc, 0, sizeof(PaHostSoundControl) );
699 	past->past_DeviceData = (void *) pahsc;
700 
701 	pahsc->pahsc_OutputHandle = BAD_DEVICE_ID; /* No device currently opened. */
702 	pahsc->pahsc_InputHandle = BAD_DEVICE_ID;
703 
704 /* Allocate native buffers. */
705 	pahsc->pahsc_BytesPerInputBuffer = past->past_FramesPerUserBuffer *
706 		past->past_NumInputChannels * sizeof(short);
707 	if( past->past_NumInputChannels > 0)
708 	{
709 		pahsc->pahsc_NativeInputBuffer = (short *) malloc(pahsc->pahsc_BytesPerInputBuffer);
710 		if( pahsc->pahsc_NativeInputBuffer == NULL )
711 		{
712 			result = paInsufficientMemory;
713 			goto error;
714 		}
715 	}
716 	pahsc->pahsc_BytesPerOutputBuffer = past->past_FramesPerUserBuffer *
717 		past->past_NumOutputChannels * sizeof(short);
718 	if( past->past_NumOutputChannels > 0)
719 	{
720 		pahsc->pahsc_NativeOutputBuffer = (short *) malloc(pahsc->pahsc_BytesPerOutputBuffer);
721 		if( pahsc->pahsc_NativeOutputBuffer == NULL )
722 		{
723 			result = paInsufficientMemory;
724 			goto error;
725 		}
726 	}
727 
728 	/* DBUG(("PaHost_OpenStream: pahsc_MinFramesPerHostBuffer = %d\n", pahsc->pahsc_MinFramesPerHostBuffer )); */
729 	minNumBuffers = Pa_GetMinNumBuffers( past->past_FramesPerUserBuffer, past->past_SampleRate );
730 	past->past_NumUserBuffers = ( minNumBuffers > past->past_NumUserBuffers ) ? minNumBuffers : past->past_NumUserBuffers;
731 
732 	pahsc->pahsc_InverseMicrosPerBuffer = past->past_SampleRate / (1000000.0 * past->past_FramesPerUserBuffer);
733 	DBUG(("pahsc_InverseMicrosPerBuffer = %g\n", pahsc->pahsc_InverseMicrosPerBuffer ));
734 
735 /* ------------------------- OPEN DEVICE -----------------------*/
736 
737 	/* just output */
738 	if (past->past_OutputDeviceID == past->past_InputDeviceID)
739 	{
740 
741 		if ((past->past_NumOutputChannels > 0) && (past->past_NumInputChannels > 0) )
742 		{
743 			pad = Pa_GetInternalDevice( past->past_OutputDeviceID );
744 			DBUG(("PaHost_OpenStream: attempt to open %s for O_RDWR\n", pad->pad_DeviceName ));
745 			pahsc->pahsc_OutputHandle = pahsc->pahsc_InputHandle =
746 				open(pad->pad_DeviceName,O_RDWR);
747 			if(pahsc->pahsc_InputHandle==-1)
748 			{
749 				ERR_RPT(("PaHost_OpenStream: could not open %s for O_RDWR\n", pad->pad_DeviceName ));
750 				result = paHostError;
751 				goto error;
752 			}
753 			Pa_SetLatency( pahsc->pahsc_OutputHandle,
754 				past->past_NumUserBuffers, past->past_FramesPerUserBuffer,
755 				past->past_NumOutputChannels );
756 			result = Pa_SetupDeviceFormat( pahsc->pahsc_OutputHandle,
757 				past->past_NumOutputChannels, (int)past->past_SampleRate );
758 		}
759 	}
760 	else
761 	{
762 		if (past->past_NumOutputChannels > 0)
763 		{
764 			pad = Pa_GetInternalDevice( past->past_OutputDeviceID );
765 			DBUG(("PaHost_OpenStream: attempt to open %s for O_WRONLY\n", pad->pad_DeviceName ));
766 			pahsc->pahsc_OutputHandle = open(pad->pad_DeviceName,O_WRONLY);
767 			if(pahsc->pahsc_OutputHandle==-1)
768 			{
769 				ERR_RPT(("PaHost_OpenStream: could not open %s for O_WRONLY\n", pad->pad_DeviceName ));
770 				result = paHostError;
771 				goto error;
772 			}
773 			Pa_SetLatency( pahsc->pahsc_OutputHandle,
774 				past->past_NumUserBuffers, past->past_FramesPerUserBuffer,
775 				past->past_NumOutputChannels );
776 			result = Pa_SetupDeviceFormat( pahsc->pahsc_OutputHandle,
777 				past->past_NumOutputChannels, (int)past->past_SampleRate );
778 		}
779 
780 		if (past->past_NumInputChannels > 0)
781 		{
782 			pad = Pa_GetInternalDevice( past->past_InputDeviceID );
783 			DBUG(("PaHost_OpenStream: attempt to open %s for O_RDONLY\n", pad->pad_DeviceName ));
784 			pahsc->pahsc_InputHandle = open(pad->pad_DeviceName,O_RDONLY);
785 			if(pahsc->pahsc_InputHandle==-1)
786 			{
787 				ERR_RPT(("PaHost_OpenStream: could not open %s for O_RDONLY\n", pad->pad_DeviceName ));
788 				result = paHostError;
789 				goto error;
790 			}
791 			Pa_SetLatency( pahsc->pahsc_OutputHandle,
792 				past->past_NumUserBuffers, past->past_FramesPerUserBuffer,
793 				past->past_NumInputChannels );
794 			result = Pa_SetupDeviceFormat( pahsc->pahsc_InputHandle,
795 				past->past_NumInputChannels, (int)past->past_SampleRate );
796 		}
797 	}
798 
799 
800 	DBUG(("PaHost_OpenStream: SUCCESS - result = %d\n", result ));
801 	return result;
802 
803 error:
804 	ERR_RPT(("PaHost_OpenStream: ERROR - result = %d\n", result ));
805 	PaHost_CloseStream( past );
806 	return result;
807 }
808 
809 /*************************************************************************/
PaHost_StartOutput(internalPortAudioStream * past)810 PaError PaHost_StartOutput( internalPortAudioStream *past )
811 {
812 	return paNoError;
813 }
814 
815 /*************************************************************************/
PaHost_StartInput(internalPortAudioStream * past)816 PaError PaHost_StartInput( internalPortAudioStream *past )
817 {
818 	return paNoError;
819 }
820 
821 /*************************************************************************/
PaHost_StartEngine(internalPortAudioStream * past)822 PaError PaHost_StartEngine( internalPortAudioStream *past )
823 {
824 	PaHostSoundControl *pahsc;
825 	PaError             result = paNoError;
826 	int                 hres;
827 
828 	pahsc = (PaHostSoundControl *) past->past_DeviceData;
829 
830 	past->past_StopSoon = 0;
831 	past->past_StopNow = 0;
832         past->past_IsActive = 1;
833 
834 /* Use pthread_create() instead of __clone() because:
835  *   - pthread_create also works for other UNIX systems like Solaris,
836  *   - the Java HotSpot VM crashes in pthread_setcanceltype() when using __clone()
837  */
838 	hres = pthread_create(&(pahsc->pahsc_ThreadPID),
839 		NULL /*pthread_attr_t * attr*/,
840 		(void*)Pa_AudioThreadProc, past);
841 	if( hres != 0 )
842 	{
843 		result = paHostError;
844 		sPaHostError = hres;
845 		goto error;
846 	}
847 
848 error:
849 	return result;
850 }
851 
852 /*************************************************************************/
PaHost_StopEngine(internalPortAudioStream * past,int abort)853 PaError PaHost_StopEngine( internalPortAudioStream *past, int abort )
854 {
855 	int                 hres;
856 	long                timeOut;
857 	PaError             result = paNoError;
858 	PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
859 
860 	if( pahsc == NULL ) return paNoError;
861 
862 /* Tell background thread to stop generating more data and to let current data play out. */
863         past->past_StopSoon = 1;
864 /* If aborting, tell background thread to stop NOW! */
865         if( abort ) past->past_StopNow = 1;
866 
867 /* Join thread to recover memory resources. */
868 	if( pahsc->pahsc_ThreadPID != -1 )
869 	{
870 	/* This check is needed for GNUSTEP - SB20010904 */
871 		if ( !pthread_equal( pahsc->pahsc_ThreadPID, pthread_self() ) )
872 		{
873 			hres = pthread_join( pahsc->pahsc_ThreadPID, NULL );
874 		}
875 		else {
876 			DBUG(("Play thread was stopped from itself - can't do pthread_join()\n"));
877 			hres = 0;
878 		}
879 
880 		if( hres != 0 )
881 		{
882 			result = paHostError;
883 			sPaHostError = hres;
884 		}
885 		pahsc->pahsc_ThreadPID = -1;
886 	}
887 
888         past->past_IsActive = 0;
889 
890 	return result;
891 }
892 
893 /*************************************************************************/
PaHost_StopInput(internalPortAudioStream * past,int abort)894 PaError PaHost_StopInput( internalPortAudioStream *past, int abort )
895 {
896 	return paNoError;
897 }
898 
899 /*************************************************************************/
PaHost_StopOutput(internalPortAudioStream * past,int abort)900 PaError PaHost_StopOutput( internalPortAudioStream *past, int abort )
901 {
902 	return paNoError;
903 }
904 
905 /*******************************************************************/
PaHost_CloseStream(internalPortAudioStream * past)906 PaError PaHost_CloseStream( internalPortAudioStream   *past )
907 {
908 	PaHostSoundControl *pahsc;
909 	if( past == NULL ) return paBadStreamPtr;
910 	pahsc = (PaHostSoundControl *) past->past_DeviceData;
911 	if( pahsc == NULL ) return paNoError;
912 
913 	if( pahsc->pahsc_OutputHandle != BAD_DEVICE_ID )
914 	{
915 		int err;
916 		DBUG(("PaHost_CloseStream: attempt to close output device handle = %d\n",
917 			pahsc->pahsc_OutputHandle ));
918 		err = close(pahsc->pahsc_OutputHandle);
919 		if( err < 0 )
920 		{
921 			ERR_RPT(("PaHost_CloseStream: warning, closing output device failed.\n"));
922 		}
923 	}
924 
925 	if( (pahsc->pahsc_InputHandle != BAD_DEVICE_ID) &&
926 	    (pahsc->pahsc_InputHandle != pahsc->pahsc_OutputHandle) )
927 	{
928 		int err;
929 		DBUG(("PaHost_CloseStream: attempt to close input device handle = %d\n",
930 			pahsc->pahsc_InputHandle ));
931 		err = close(pahsc->pahsc_InputHandle);
932 		if( err < 0 )
933 		{
934 			ERR_RPT(("PaHost_CloseStream: warning, closing input device failed.\n"));
935 		}
936 	}
937 	pahsc->pahsc_OutputHandle = BAD_DEVICE_ID;
938 	pahsc->pahsc_InputHandle = BAD_DEVICE_ID;
939 
940 	if( pahsc->pahsc_NativeInputBuffer )
941 	{
942 		free( pahsc->pahsc_NativeInputBuffer );
943 		pahsc->pahsc_NativeInputBuffer = NULL;
944 	}
945 	if( pahsc->pahsc_NativeOutputBuffer )
946 	{
947 		free( pahsc->pahsc_NativeOutputBuffer );
948 		pahsc->pahsc_NativeOutputBuffer = NULL;
949 	}
950 
951 	free( pahsc );
952 	past->past_DeviceData = NULL;
953 	return paNoError;
954 }
955 
956 /*************************************************************************/
PaHost_Term(void)957 PaError PaHost_Term( void )
958 {
959 /* Free all of the linked devices. */
960 	internalPortAudioDevice *pad, *nextPad;
961 	pad = sDeviceList;
962 	while( pad != NULL )
963 	{
964 		nextPad = pad->pad_Next;
965 		DBUG(("PaHost_Term: freeing %s\n", pad->pad_DeviceName ));
966 		PaHost_FreeFastMemory( pad, sizeof(internalPortAudioDevice) );
967 		pad = nextPad;
968 	}
969 	sDeviceList = NULL;
970 	return 0;
971 }
972 
973 /*************************************************************************
974  * Sleep for the requested number of milliseconds.
975  */
Pa_Sleep(long msec)976 void Pa_Sleep( long msec )
977 {
978 #if 0
979 	struct timeval timeout;
980 	timeout.tv_sec = msec / 1000;
981 	timeout.tv_usec = (msec % 1000) * 1000;
982 	select( 0, NULL, NULL, NULL, &timeout );
983 #else
984 	long usecs = msec * 1000;
985 	usleep( usecs );
986 #endif
987 }
988 
989 /*************************************************************************
990  * Allocate memory that can be accessed in real-time.
991  * This may need to be held in physical memory so that it is not
992  * paged to virtual memory.
993  * This call MUST be balanced with a call to PaHost_FreeFastMemory().
994  */
PaHost_AllocateFastMemory(long numBytes)995 void *PaHost_AllocateFastMemory( long numBytes )
996 {
997 	void *addr = malloc( numBytes ); /* FIXME - do we need physical memory? */
998 	if( addr != NULL ) memset( addr, 0, numBytes );
999 	return addr;
1000 }
1001 
1002 /*************************************************************************
1003  * Free memory that could be accessed in real-time.
1004  * This call MUST be balanced with a call to PaHost_AllocateFastMemory().
1005  */
PaHost_FreeFastMemory(void * addr,long numBytes)1006 void PaHost_FreeFastMemory( void *addr, long numBytes )
1007 {
1008 	if( addr != NULL ) free( addr );
1009 }
1010 
1011 
1012 /***********************************************************************/
PaHost_StreamActive(internalPortAudioStream * past)1013 PaError PaHost_StreamActive( internalPortAudioStream   *past )
1014 {
1015 	PaHostSoundControl *pahsc;
1016 	if( past == NULL ) return paBadStreamPtr;
1017 	pahsc = (PaHostSoundControl *) past->past_DeviceData;
1018 	if( pahsc == NULL ) return paInternalError;
1019 	return (PaError) (past->past_IsActive != 0);
1020 }
1021 
1022 /***********************************************************************/
Pa_StreamTime(PortAudioStream * stream)1023 PaTimestamp Pa_StreamTime( PortAudioStream *stream )
1024 {
1025 	internalPortAudioStream *past = (internalPortAudioStream *) stream;
1026 /* FIXME - return actual frames played, not frames generated.
1027 ** Need to query the output device somehow.
1028 */
1029 	if( past == NULL ) return paBadStreamPtr;
1030 	return past->past_FrameCount;
1031 }
1032 
1033 /***********************************************************************/
Pa_GetHostError(void)1034 long Pa_GetHostError( void )
1035 {
1036 	return (long) sPaHostError;
1037 }
1038