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( ¤tTime, NULL ) == 0 )
213 {
214 usecsElapsed = SubtractTime_AminusB( ¤tTime, &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