~ubuntu-branches/debian/jessie/linpsk/jessie

« back to all changes in this revision

Viewing changes to src/portaudio/pa_unix.c

  • Committer: Bazaar Package Importer
  • Author(s): Hamish Moffatt
  • Date: 2005-04-10 18:17:27 UTC
  • mfrom: (1.1.1 upstream) (2.1.1 warty)
  • Revision ID: james.westby@ubuntu.com-20050410181727-3l9dnfg0sp7bhk13
Tags: 0.8.1-1
* New upstream release 0.8.1
  * Modified upstream configure.in to support FHS-compliant Qt
    installation! (ie /usr/include/qt3, not /usr/lib/qt3/include) :-(
  * Re-autotools with autoconf2.59 and automake-1.9
* linpsk is no longer a Debian-native package (dsc/tar.gz)
* Now maintained by the debian-hams group
* Switch to debhelper 4

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
/*
 
34
Modification History
 
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.
 
62
 
 
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.
 
75
 
 
76
  20030206 - Martin Rohrbach - various mods for Solaris
 
77
  
 
78
  20030410 - Bjorn Dittmer-Roche - fixed numerous problems associated with pthread_t
 
79
  
 
80
  20030630 - Thomas Richter - eliminated unused variable warnings.
 
81
 
 
82
TODO
 
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) ???
 
87
*/
 
88
 
 
89
 
 
90
#include "pa_unix.h"
 
91
 
 
92
typedef void *(*pthread_function_t)(void *);
 
93
 
 
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;
 
100
 
 
101
/********************************* BEGIN CPU UTILIZATION MEASUREMENT ****/
 
102
static void Pa_StartUsageCalculation( internalPortAudioStream   *past )
 
103
{
 
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 );
 
108
}
 
109
 
 
110
static long SubtractTime_AminusB( struct timeval *timeA, struct timeval *timeB )
 
111
{
 
112
    long secs = timeA->tv_sec - timeB->tv_sec;
 
113
    long usecs = secs * 1000000;
 
114
    usecs += (timeA->tv_usec - timeB->tv_usec);
 
115
    return usecs;
 
116
}
 
117
 
 
118
/******************************************************************************
 
119
** Measure fractional CPU load based on real-time it took to calculate
 
120
** buffers worth of output.
 
121
*/
 
122
static void Pa_EndUsageCalculation( internalPortAudioStream   *past )
 
123
{
 
124
    struct timeval currentTime;
 
125
    long  usecsElapsed;
 
126
    double newUsage;
 
127
 
 
128
#define LOWPASS_COEFFICIENT_0   (0.95)
 
129
#define LOWPASS_COEFFICIENT_1   (0.99999 - LOWPASS_COEFFICIENT_0)
 
130
 
 
131
    PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
 
132
    if( pahsc == NULL ) return;
 
133
 
 
134
    if( gettimeofday( &currentTime, NULL ) == 0 )
 
135
    {
 
136
        usecsElapsed = SubtractTime_AminusB( &currentTime, &pahsc->pahsc_EntryTime );
 
137
        /* Use inverse because it is faster than the divide. */
 
138
        newUsage =  usecsElapsed * pahsc->pahsc_InverseMicrosPerBuffer;
 
139
 
 
140
        past->past_Usage = (LOWPASS_COEFFICIENT_0 * past->past_Usage) +
 
141
                           (LOWPASS_COEFFICIENT_1 * newUsage);
 
142
 
 
143
    }
 
144
}
 
145
/****************************************** END CPU UTILIZATION *******/
 
146
 
 
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.
 
151
 */
 
152
PaError Pa_QueryDevices( void )
 
153
{
 
154
    internalPortAudioDevice *pad, *lastPad;
 
155
    int      go = 1;
 
156
    int      numDevices = 0;
 
157
    PaError  testResult;
 
158
    PaError  result = paNoError;
 
159
    char     *envdev;
 
160
 
 
161
    sDefaultInputDeviceID = paNoDevice;
 
162
    sDefaultOutputDeviceID = paNoDevice;
 
163
 
 
164
    lastPad = NULL;
 
165
 
 
166
    while( go )
 
167
    {
 
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) );
 
173
 
 
174
        /* Build name for device. */
 
175
        if( numDevices == 0 )
 
176
        {
 
177
            sprintf( pad->pad_DeviceName, DEVICE_NAME_BASE);
 
178
        }
 
179
        else
 
180
        {
 
181
            sprintf( pad->pad_DeviceName, DEVICE_NAME_BASE "%d", numDevices );
 
182
        }
 
183
 
 
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 )
 
188
        {
 
189
            if( lastPad == NULL )
 
190
            {
 
191
                result = testResult; /* No good devices! */
 
192
            }
 
193
            go = 0;
 
194
            PaHost_FreeFastMemory( pad, sizeof(internalPortAudioDevice) );
 
195
        }
 
196
        else
 
197
        {
 
198
            numDevices += 1;
 
199
            /* Add to linked list of devices. */
 
200
            if( lastPad )
 
201
            {
 
202
                lastPad->pad_Next = pad;
 
203
            }
 
204
            else
 
205
            {
 
206
                sDeviceList = pad; /* First element in linked list. */
 
207
            }
 
208
            lastPad = pad;
 
209
        }
 
210
    }
 
211
 
 
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 */
 
216
 
 
217
    DBUG(("Checking for AUDIODEV and UTAUDIODEV\n"));
 
218
    envdev = getenv("AUDIODEV");
 
219
    if (envdev != NULL && !strstr(envdev, DEVICE_NAME_BASE)) {
 
220
        result = paNoError;
 
221
 
 
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) );
 
227
 
 
228
        /* Build name for device. */
 
229
        strcpy(pad->pad_DeviceName, envdev);
 
230
 
 
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 )
 
235
        {
 
236
            if( lastPad == NULL )
 
237
            {
 
238
                result = testResult; /* No good devices! */
 
239
            }
 
240
            PaHost_FreeFastMemory( pad, sizeof(internalPortAudioDevice) );
 
241
        }
 
242
        else
 
243
        {
 
244
            numDevices += 1;
 
245
            /* Add to linked list of devices. */
 
246
            if( lastPad )
 
247
            {
 
248
                lastPad->pad_Next = pad;
 
249
            }
 
250
            else
 
251
            {
 
252
                sDeviceList = pad; /* First element in linked list. */
 
253
            }
 
254
            lastPad = pad;
 
255
        }
 
256
    }
 
257
 
 
258
    envdev = getenv("UTAUDIODEV");
 
259
    if (envdev != NULL && !strstr(envdev, DEVICE_NAME_BASE) && getenv("AUDIODEV") != NULL && strcmp(envdev, getenv("AUDIODEV"))) {
 
260
        result = paNoError;
 
261
 
 
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) );
 
267
 
 
268
        /* Build name for device. */
 
269
        strcpy(pad->pad_DeviceName, envdev);
 
270
 
 
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 )
 
275
        {
 
276
            if( lastPad == NULL )
 
277
            {
 
278
                result = testResult; /* No good devices! */
 
279
            }
 
280
            PaHost_FreeFastMemory( pad, sizeof(internalPortAudioDevice) );
 
281
        }
 
282
        else
 
283
        {
 
284
            numDevices += 1;
 
285
            /* Add to linked list of devices. */
 
286
            if( lastPad )
 
287
            {
 
288
                lastPad->pad_Next = pad;
 
289
            }
 
290
            else
 
291
            {
 
292
                sDeviceList = pad; /* First element in linked list. */
 
293
            }
 
294
            lastPad = pad;
 
295
        }
 
296
    }
 
297
 
 
298
    return result;
 
299
}
 
300
 
 
301
/*************************************************************************/
 
302
int Pa_CountDevices()
 
303
{
 
304
    int numDevices = 0;
 
305
    internalPortAudioDevice *pad;
 
306
 
 
307
    if( sDeviceList == NULL ) Pa_Initialize();
 
308
    /* Count devices in list. */
 
309
    pad = sDeviceList;
 
310
    while( pad != NULL )
 
311
    {
 
312
        pad = pad->pad_Next;
 
313
        numDevices++;
 
314
    }
 
315
 
 
316
    return numDevices;
 
317
}
 
318
 
 
319
/*************************************************************************/
 
320
internalPortAudioDevice *Pa_GetInternalDevice( PaDeviceID id )
 
321
{
 
322
    internalPortAudioDevice *pad;
 
323
    if( (id < 0) || ( id >= Pa_CountDevices()) ) return NULL;
 
324
    pad = sDeviceList;
 
325
    while( id > 0 )
 
326
    {
 
327
        pad = pad->pad_Next;
 
328
        id--;
 
329
    }
 
330
    return pad;
 
331
}
 
332
 
 
333
/*************************************************************************/
 
334
const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceID id )
 
335
{
 
336
    internalPortAudioDevice *pad;
 
337
    if( (id < 0) || ( id >= Pa_CountDevices()) ) return NULL;
 
338
    pad = Pa_GetInternalDevice( id );
 
339
    return  &pad->pad_Info ;
 
340
}
 
341
 
 
342
static PaError Pa_MaybeQueryDevices( void )
 
343
{
 
344
    if( sDeviceList == NULL )
 
345
    {
 
346
        return Pa_QueryDevices();
 
347
    }
 
348
    return 0;
 
349
}
 
350
 
 
351
PaDeviceID Pa_GetDefaultInputDeviceID( void )
 
352
{
 
353
    /* return paNoDevice; */
 
354
    return 0;
 
355
}
 
356
 
 
357
PaDeviceID Pa_GetDefaultOutputDeviceID( void )
 
358
{
 
359
    return 0;
 
360
}
 
361
 
 
362
/**********************************************************************
 
363
** Make sure that we have queried the device capabilities.
 
364
*/
 
365
 
 
366
PaError PaHost_Init( void )
 
367
{
 
368
    return Pa_MaybeQueryDevices();
 
369
}
 
370
 
 
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.
 
376
 */
 
377
 
 
378
#define SCHEDULER_POLICY         SCHED_RR
 
379
#define WATCHDOG_MAX_SECONDS    (3)
 
380
#define WATCHDOG_INTERVAL_USEC  (1000000)
 
381
 
 
382
static int PaHost_CanaryProc( PaHostSoundControl   *pahsc )
 
383
{
 
384
    int   result = 0;
 
385
 
 
386
#ifdef GNUSTEP
 
387
    GSRegisterCurrentThread(); /* SB20010904 */
 
388
#endif
 
389
 
 
390
    while( pahsc->pahsc_CanaryRun) {
 
391
      usleep( WATCHDOG_INTERVAL_USEC );
 
392
      gettimeofday( &pahsc->pahsc_CanaryTime, NULL );
 
393
    }
 
394
 
 
395
    DBUG(("PaHost_CanaryProc: exiting.\n"));
 
396
 
 
397
#ifdef GNUSTEP
 
398
    GSUnregisterCurrentThread();  /* SB20010904 */
 
399
#endif
 
400
 
 
401
    return result;
 
402
}
 
403
 
 
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.
 
410
 */
 
411
 
 
412
static PaError PaHost_WatchDogProc( PaHostSoundControl   *pahsc )
 
413
{
 
414
    struct sched_param    schp = { 0 };
 
415
    int                   maxPri;
 
416
 
 
417
#ifdef GNUSTEP
 
418
    GSRegisterCurrentThread(); /* SB20010904 */
 
419
#endif
 
420
 
 
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;
 
426
 
 
427
    if (sched_setscheduler(0, SCHEDULER_POLICY, &schp) != 0)
 
428
    {
 
429
        ERR_RPT(("PaHost_WatchDogProc: cannot set watch dog priority!\n"));
 
430
        goto killAudio;
 
431
    }
 
432
 
 
433
    /* Compare watchdog time with audio and canary thread times. */
 
434
    /* Sleep for a while or until thread cancelled. */
 
435
    while( pahsc->pahsc_WatchDogRun )
 
436
    {
 
437
 
 
438
        int              delta;
 
439
        struct timeval   currentTime;
 
440
 
 
441
        usleep( WATCHDOG_INTERVAL_USEC );
 
442
        gettimeofday( &currentTime, NULL );
 
443
 
 
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 )
 
448
        {
 
449
            goto killAudio;
 
450
        }
 
451
 
 
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 )
 
455
        {
 
456
            ERR_RPT(("PaHost_WatchDogProc: canary died!\n"));
 
457
            goto lowerAudio;
 
458
        }
 
459
    }
 
460
 
 
461
    DBUG(("PaHost_WatchDogProc: exiting.\n"));
 
462
#ifdef GNUSTEP
 
463
    GSUnregisterCurrentThread();  /* SB20010904 */
 
464
#endif
 
465
    return 0;
 
466
 
 
467
lowerAudio:
 
468
    {
 
469
        struct sched_param    schat = { 0 };
 
470
        if( sched_setscheduler(pahsc->pahsc_AudioThreadPID, SCHED_OTHER, &schat) != 0)
 
471
        {
 
472
            ERR_RPT(("PaHost_WatchDogProc: failed to lower audio priority. errno = %d\n", errno ));
 
473
            /* Fall through into killing audio thread. */
 
474
        }
 
475
        else
 
476
        {
 
477
            ERR_RPT(("PaHost_WatchDogProc: lowered audio priority to prevent hogging of CPU.\n"));
 
478
            goto cleanup;
 
479
        }
 
480
    }
 
481
 
 
482
killAudio:
 
483
    ERR_RPT(("PaHost_WatchDogProc: killing hung audio thread!\n"));
 
484
    pthread_kill( pahsc->pahsc_AudioThread, SIGKILL );
 
485
 
 
486
cleanup:
 
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;
 
494
 
 
495
#ifdef GNUSTEP
 
496
    GSUnregisterCurrentThread();  /* SB20010904 */
 
497
#endif
 
498
    return 0;
 
499
}
 
500
 
 
501
/*******************************************************************************************/
 
502
static void PaHost_StopWatchDog( PaHostSoundControl   *pahsc )
 
503
{
 
504
/* Cancel WatchDog thread if there is one. */
 
505
    if( pahsc->pahsc_IsWatchDogThreadValid )
 
506
    {
 
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;
 
512
    }
 
513
/* Cancel Canary thread if there is one. */
 
514
    if( pahsc->pahsc_IsCanaryThreadValid )
 
515
    {
 
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;
 
522
    }
 
523
}
 
524
 
 
525
/*******************************************************************************************/
 
526
static PaError PaHost_StartWatchDog( PaHostSoundControl   *pahsc )
 
527
{
 
528
    int      hres;
 
529
    PaError  result = 0;
 
530
 
 
531
    /* The watch dog watches for these timer updates */
 
532
    gettimeofday( &pahsc->pahsc_EntryTime, NULL );
 
533
    gettimeofday( &pahsc->pahsc_CanaryTime, NULL );
 
534
 
 
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);
 
540
    if( hres != 0 )
 
541
    {
 
542
        pahsc->pahsc_IsCanaryThreadValid = 0;
 
543
        result = paHostError;
 
544
        sPaHostError = hres;
 
545
        goto error;
 
546
    }
 
547
    pahsc->pahsc_IsCanaryThreadValid = 1;
 
548
 
 
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);
 
554
    if( hres != 0 )
 
555
    {
 
556
        pahsc->pahsc_IsWatchDogThreadValid = 0;
 
557
        result = paHostError;
 
558
        sPaHostError = hres;
 
559
        goto error;
 
560
    }
 
561
    pahsc->pahsc_IsWatchDogThreadValid = 1;
 
562
    return result;
 
563
 
 
564
error:
 
565
    PaHost_StopWatchDog( pahsc );
 
566
    return result;
 
567
}
 
568
 
 
569
/*******************************************************************************************
 
570
 * Bump priority of audio thread if running with superuser priveledges.
 
571
 * if priority bumped then launch a watchdog.
 
572
 */
 
573
static PaError PaHost_BoostPriority( internalPortAudioStream *past )
 
574
{
 
575
    PaHostSoundControl  *pahsc;
 
576
    PaError              result = paNoError;
 
577
    struct sched_param   schp = { 0 };
 
578
 
 
579
    pahsc = (PaHostSoundControl *) past->past_DeviceData;
 
580
    if( pahsc == NULL ) return paInternalError;
 
581
 
 
582
    pahsc->pahsc_AudioThreadPID = getpid();
 
583
    DBUG(("PaHost_BoostPriority: audio PID = %d\n", pahsc->pahsc_AudioThreadPID ));
 
584
 
 
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;
 
589
 
 
590
    if (sched_setscheduler(0, SCHEDULER_POLICY, &schp) != 0)
 
591
    {
 
592
        DBUG(("PortAudio: only superuser can use real-time priority.\n"));
 
593
    }
 
594
    else
 
595
    {
 
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 );
 
599
    }
 
600
 
 
601
    return result;
 
602
}
 
603
 
 
604
/*******************************************************************************************/
 
605
static PaError Pa_AudioThreadProc( internalPortAudioStream   *past )
 
606
{
 
607
    PaError      result;
 
608
    PaHostSoundControl             *pahsc;
 
609
    ssize_t      bytes_read, bytes_written;
 
610
 
 
611
    pahsc = (PaHostSoundControl *) past->past_DeviceData;
 
612
    if( pahsc == NULL ) return paInternalError;
 
613
 
 
614
#ifdef GNUSTEP
 
615
    GSRegisterCurrentThread(); /* SB20010904 */
 
616
#endif
 
617
 
 
618
    result = PaHost_BoostPriority( past );
 
619
    if( result < 0 ) goto error;
 
620
 
 
621
    past->past_IsActive = 1;
 
622
    DBUG(("entering thread.\n"));
 
623
 
 
624
    while( (past->past_StopNow == 0) && (past->past_StopSoon == 0) )
 
625
    {
 
626
        /* Read data from device */
 
627
        if(pahsc->pahsc_NativeInputBuffer)
 
628
        {
 
629
            unsigned int totalread = 0;
 
630
            DBUG(("Pa_AudioThreadProc: attempt to read %d bytes\n", pahsc->pahsc_BytesPerInputBuffer));
 
631
            do
 
632
            {
 
633
                bytes_read = read(pahsc->pahsc_InputHandle,
 
634
                    (char *)pahsc->pahsc_NativeInputBuffer + totalread,
 
635
                    pahsc->pahsc_BytesPerInputBuffer - totalread);
 
636
 
 
637
                if (bytes_read < 0)
 
638
                {
 
639
                    ERR_RPT(("PortAudio: read interrupted!\n"));
 
640
                    break;
 
641
                }
 
642
 
 
643
                totalread += bytes_read;
 
644
            } while( totalread < pahsc->pahsc_BytesPerInputBuffer);
 
645
        }
 
646
 
 
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 );
 
654
        if( result != 0)
 
655
        {
 
656
            DBUG(("hmm, Pa_CallConvertInt16() says: %d. i'm bailing.\n",
 
657
                  result));
 
658
            break;
 
659
        }
 
660
 
 
661
        /* Write data to device. */
 
662
        if( pahsc->pahsc_NativeOutputBuffer )
 
663
        {
 
664
            unsigned int totalwritten = 0;
 
665
            do
 
666
            {
 
667
                bytes_written = write(pahsc->pahsc_OutputHandle,
 
668
                    (void *)pahsc->pahsc_NativeOutputBuffer,
 
669
                    pahsc->pahsc_BytesPerOutputBuffer);
 
670
                if( bytes_written < 0 )
 
671
                {
 
672
                    ERR_RPT(("PortAudio: write interrupted!"));
 
673
                    break;
 
674
                }
 
675
 
 
676
                totalwritten += bytes_written;
 
677
            } while( totalwritten < pahsc->pahsc_BytesPerOutputBuffer);
 
678
        }
 
679
 
 
680
        Pa_UpdateStreamTime(pahsc);
 
681
    }
 
682
    DBUG(("Pa_AudioThreadProc: left audio loop.\n"));
 
683
 
 
684
    past->past_IsActive = 0;
 
685
    PaHost_StopWatchDog( pahsc );
 
686
 
 
687
error:
 
688
    DBUG(("leaving audio thread.\n"));
 
689
#ifdef GNUSTEP
 
690
    GSUnregisterCurrentThread();  /* SB20010904 */
 
691
#endif
 
692
    return result;
 
693
}
 
694
 
 
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:
 
699
**
 
700
**    set PA_MIN_LATENCY_MSEC=200
 
701
**
 
702
** in the cshrc file.
 
703
*/
 
704
#define PA_LATENCY_ENV_NAME  ("PA_MIN_LATENCY_MSEC")
 
705
 
 
706
int Pa_GetMinNumBuffers( int framesPerBuffer, double framesPerSecond )
 
707
{
 
708
    int minBuffers;
 
709
    int minLatencyMsec = MIN_LATENCY_MSEC;
 
710
    char *minLatencyText = getenv(PA_LATENCY_ENV_NAME);
 
711
    if( minLatencyText != NULL )
 
712
    {
 
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;
 
717
    }
 
718
 
 
719
    minBuffers = (int) ((minLatencyMsec * framesPerSecond) / ( 1000.0 * framesPerBuffer ));
 
720
    if( minBuffers < 2 ) minBuffers = 2;
 
721
    return minBuffers;
 
722
}
 
723
 
 
724
/*******************************************************************/
 
725
PaError PaHost_OpenStream( internalPortAudioStream   *past )
 
726
{
 
727
    PaError          result = paNoError;
 
728
    PaHostSoundControl *pahsc;
 
729
    unsigned int     minNumBuffers;
 
730
    internalPortAudioDevice *pad;
 
731
    DBUG(("PaHost_OpenStream() called.\n" ));
 
732
 
 
733
    /* Allocate and initialize host data. */
 
734
    pahsc = (PaHostSoundControl *) malloc(sizeof(PaHostSoundControl));
 
735
    if( pahsc == NULL )
 
736
    {
 
737
        result = paInsufficientMemory;
 
738
        goto error;
 
739
    }
 
740
    memset( pahsc, 0, sizeof(PaHostSoundControl) );
 
741
    past->past_DeviceData = (void *) pahsc;
 
742
 
 
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;
 
747
 
 
748
    /* Allocate native buffers. */
 
749
    pahsc->pahsc_BytesPerInputBuffer = past->past_FramesPerUserBuffer *
 
750
                                       past->past_NumInputChannels * sizeof(short);
 
751
    if( past->past_NumInputChannels > 0)
 
752
    {
 
753
        pahsc->pahsc_NativeInputBuffer = (short *) malloc(pahsc->pahsc_BytesPerInputBuffer);
 
754
        if( pahsc->pahsc_NativeInputBuffer == NULL )
 
755
        {
 
756
            result = paInsufficientMemory;
 
757
            goto error;
 
758
        }
 
759
    }
 
760
    pahsc->pahsc_BytesPerOutputBuffer = past->past_FramesPerUserBuffer *
 
761
                                        past->past_NumOutputChannels * sizeof(short);
 
762
    if( past->past_NumOutputChannels > 0)
 
763
    {
 
764
        pahsc->pahsc_NativeOutputBuffer = (short *) malloc(pahsc->pahsc_BytesPerOutputBuffer);
 
765
        if( pahsc->pahsc_NativeOutputBuffer == NULL )
 
766
        {
 
767
            result = paInsufficientMemory;
 
768
            goto error;
 
769
        }
 
770
    }
 
771
 
 
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;
 
775
 
 
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 ));
 
780
 
 
781
    /* ------------------------- OPEN DEVICE -----------------------*/
 
782
 
 
783
    /* just output */
 
784
    if (past->past_OutputDeviceID == past->past_InputDeviceID)
 
785
    {
 
786
 
 
787
        if ((past->past_NumOutputChannels > 0) && (past->past_NumInputChannels > 0) )
 
788
        {
 
789
            pad = Pa_GetInternalDevice( past->past_OutputDeviceID );
 
790
            DBUG(("PaHost_OpenStream: attempt to open %s for O_RDWR\n", pad->pad_DeviceName ));
 
791
 
 
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)
 
796
            {
 
797
                ERR_RPT(("PaHost_OpenStream: could not open %s for O_RDWR\n", pad->pad_DeviceName ));
 
798
                result = paHostError;
 
799
                goto error;
 
800
            }
 
801
            close(pahsc->pahsc_InputHandle);
 
802
 
 
803
            pahsc->pahsc_OutputHandle = pahsc->pahsc_InputHandle =
 
804
                                            open(pad->pad_DeviceName,O_RDWR);
 
805
            if(pahsc->pahsc_InputHandle==-1)
 
806
            {
 
807
                ERR_RPT(("PaHost_OpenStream: could not open %s for O_RDWR\n", pad->pad_DeviceName ));
 
808
                result = paHostError;
 
809
                goto error;
 
810
            }
 
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 );
 
816
        }
 
817
    }
 
818
    else
 
819
    {
 
820
        if (past->past_NumOutputChannels > 0)
 
821
        {
 
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)
 
828
            {
 
829
                ERR_RPT(("PaHost_OpenStream: could not open %s for O_WRONLY\n", pad->pad_DeviceName ));
 
830
                result = paHostError;
 
831
                goto error;
 
832
            }
 
833
            close(pahsc->pahsc_OutputHandle);
 
834
 
 
835
            pahsc->pahsc_OutputHandle = open(pad->pad_DeviceName,O_WRONLY);
 
836
            if(pahsc->pahsc_OutputHandle==-1)
 
837
            {
 
838
                ERR_RPT(("PaHost_OpenStream: could not open %s for O_WRONLY\n", pad->pad_DeviceName ));
 
839
                result = paHostError;
 
840
                goto error;
 
841
            }
 
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 );
 
847
        }
 
848
 
 
849
        if (past->past_NumInputChannels > 0)
 
850
        {
 
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)
 
857
            {
 
858
                ERR_RPT(("PaHost_OpenStream: could not open %s for O_RDONLY\n", pad->pad_DeviceName ));
 
859
                result = paHostError;
 
860
                goto error;
 
861
            }
 
862
            close(pahsc->pahsc_InputHandle);
 
863
 
 
864
            pahsc->pahsc_InputHandle = open(pad->pad_DeviceName,O_RDONLY);
 
865
            if(pahsc->pahsc_InputHandle==-1)
 
866
            {
 
867
                ERR_RPT(("PaHost_OpenStream: could not open %s for O_RDONLY\n", pad->pad_DeviceName ));
 
868
                result = paHostError;
 
869
                goto error;
 
870
            }
 
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 );
 
876
        }
 
877
    }
 
878
 
 
879
 
 
880
    DBUG(("PaHost_OpenStream: SUCCESS - result = %d\n", result ));
 
881
    return result;
 
882
 
 
883
error:
 
884
    ERR_RPT(("PaHost_OpenStream: ERROR - result = %d\n", result ));
 
885
    PaHost_CloseStream( past );
 
886
    return result;
 
887
}
 
888
 
 
889
/*************************************************************************/
 
890
PaError PaHost_StartOutput( internalPortAudioStream *past )
 
891
{
 
892
    past = past; /* unused */
 
893
    return paNoError;
 
894
}
 
895
 
 
896
/*************************************************************************/
 
897
PaError PaHost_StartInput( internalPortAudioStream *past )
 
898
{
 
899
    past = past; /* unused */
 
900
    return paNoError;
 
901
}
 
902
 
 
903
/*************************************************************************/
 
904
PaError PaHost_StartEngine( internalPortAudioStream *past )
 
905
{
 
906
    PaHostSoundControl *pahsc;
 
907
    PaError             result = paNoError;
 
908
    int                 hres;
 
909
 
 
910
    pahsc = (PaHostSoundControl *) past->past_DeviceData;
 
911
 
 
912
    past->past_StopSoon = 0;
 
913
    past->past_StopNow = 0;
 
914
    past->past_IsActive = 1;
 
915
 
 
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()
 
919
     */
 
920
    hres = pthread_create(&(pahsc->pahsc_AudioThread),
 
921
                          NULL /*pthread_attr_t * attr*/,
 
922
                          (pthread_function_t)Pa_AudioThreadProc, past);
 
923
    if( hres != 0 )
 
924
    {
 
925
        result = paHostError;
 
926
        sPaHostError = hres;
 
927
        pahsc->pahsc_IsAudioThreadValid = 0;
 
928
        goto error;
 
929
    }
 
930
    pahsc->pahsc_IsAudioThreadValid = 1;
 
931
 
 
932
error:
 
933
    return result;
 
934
}
 
935
 
 
936
/*************************************************************************/
 
937
PaError PaHost_StopEngine( internalPortAudioStream *past, int abort )
 
938
{
 
939
    int                 hres;
 
940
    PaError             result = paNoError;
 
941
    PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
 
942
 
 
943
    if( pahsc == NULL ) return paNoError;
 
944
 
 
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;
 
949
 
 
950
    /* Join thread to recover memory resources. */
 
951
    if( pahsc->pahsc_IsAudioThreadValid )
 
952
    {
 
953
        /* This check is needed for GNUSTEP - SB20010904 */
 
954
        if ( !pthread_equal( pahsc->pahsc_AudioThread, pthread_self() ) )
 
955
        {
 
956
            hres = pthread_join( pahsc->pahsc_AudioThread, NULL );
 
957
        }
 
958
        else
 
959
        {
 
960
            DBUG(("Play thread was stopped from itself - can't do pthread_join()\n"));
 
961
            hres = 0;
 
962
        }
 
963
 
 
964
        if( hres != 0 )
 
965
        {
 
966
            result = paHostError;
 
967
            sPaHostError = hres;
 
968
        }
 
969
        pahsc->pahsc_IsAudioThreadValid = 0;
 
970
    }
 
971
 
 
972
    past->past_IsActive = 0;
 
973
 
 
974
    return result;
 
975
}
 
976
 
 
977
/*************************************************************************/
 
978
PaError PaHost_StopInput( internalPortAudioStream *past, int abort )
 
979
{
 
980
    past = past; /* unused */
 
981
    abort = abort; /* unused */
 
982
    return paNoError;
 
983
}
 
984
 
 
985
/*************************************************************************/
 
986
PaError PaHost_StopOutput( internalPortAudioStream *past, int abort )
 
987
{
 
988
    past = past; /* unused */
 
989
    abort = abort; /* unused */
 
990
    return paNoError;
 
991
}
 
992
 
 
993
/*******************************************************************/
 
994
PaError PaHost_CloseStream( internalPortAudioStream   *past )
 
995
{
 
996
    PaHostSoundControl *pahsc;
 
997
 
 
998
    if( past == NULL ) return paBadStreamPtr;
 
999
    pahsc = (PaHostSoundControl *) past->past_DeviceData;
 
1000
    if( pahsc == NULL ) return paNoError;
 
1001
 
 
1002
    if( pahsc->pahsc_OutputHandle != BAD_DEVICE_ID )
 
1003
    {
 
1004
        int err = 0;
 
1005
        DBUG(("PaHost_CloseStream: attempt to close output device handle = %d\n",
 
1006
              pahsc->pahsc_OutputHandle ));
 
1007
 
 
1008
        Pa_FlushStream(pahsc->pahsc_OutputHandle);
 
1009
 
 
1010
        err = close(pahsc->pahsc_OutputHandle);
 
1011
        if( err < 0 )
 
1012
        {
 
1013
            ERR_RPT(("PaHost_CloseStream: warning, closing output device failed.\n"));
 
1014
        }
 
1015
    }
 
1016
 
 
1017
    if( (pahsc->pahsc_InputHandle != BAD_DEVICE_ID) &&
 
1018
            (pahsc->pahsc_InputHandle != pahsc->pahsc_OutputHandle) )
 
1019
    {
 
1020
        int err = 0;
 
1021
        DBUG(("PaHost_CloseStream: attempt to close input device handle = %d\n",
 
1022
              pahsc->pahsc_InputHandle ));
 
1023
 
 
1024
        Pa_FlushStream(pahsc->pahsc_InputHandle);
 
1025
 
 
1026
        err = close(pahsc->pahsc_InputHandle);
 
1027
        if( err < 0 )
 
1028
        {
 
1029
            ERR_RPT(("PaHost_CloseStream: warning, closing input device failed.\n"));
 
1030
        }
 
1031
    }
 
1032
    pahsc->pahsc_OutputHandle = BAD_DEVICE_ID;
 
1033
    pahsc->pahsc_InputHandle = BAD_DEVICE_ID;
 
1034
 
 
1035
    if( pahsc->pahsc_NativeInputBuffer )
 
1036
    {
 
1037
        free( pahsc->pahsc_NativeInputBuffer );
 
1038
        pahsc->pahsc_NativeInputBuffer = NULL;
 
1039
    }
 
1040
    if( pahsc->pahsc_NativeOutputBuffer )
 
1041
    {
 
1042
        free( pahsc->pahsc_NativeOutputBuffer );
 
1043
        pahsc->pahsc_NativeOutputBuffer = NULL;
 
1044
    }
 
1045
 
 
1046
    free( pahsc );
 
1047
    past->past_DeviceData = NULL;
 
1048
    return paNoError;
 
1049
}
 
1050
 
 
1051
/*************************************************************************/
 
1052
PaError PaHost_Term( void )
 
1053
{
 
1054
    /* Free all of the linked devices. */
 
1055
    internalPortAudioDevice *pad, *nextPad;
 
1056
    pad = sDeviceList;
 
1057
    while( pad != NULL )
 
1058
    {
 
1059
        nextPad = pad->pad_Next;
 
1060
        DBUG(("PaHost_Term: freeing %s\n", pad->pad_DeviceName ));
 
1061
        PaHost_FreeFastMemory( pad, sizeof(internalPortAudioDevice) );
 
1062
        pad = nextPad;
 
1063
    }
 
1064
    sDeviceList = NULL;
 
1065
    return 0;
 
1066
}
 
1067
 
 
1068
/*************************************************************************
 
1069
 * Sleep for the requested number of milliseconds.
 
1070
 */
 
1071
void Pa_Sleep( long msec )
 
1072
{
 
1073
#if 0
 
1074
    struct timeval timeout;
 
1075
    timeout.tv_sec = msec / 1000;
 
1076
    timeout.tv_usec = (msec % 1000) * 1000;
 
1077
    select( 0, NULL, NULL, NULL, &timeout );
 
1078
#else
 
1079
    long usecs = msec * 1000;
 
1080
    usleep( usecs );
 
1081
#endif
 
1082
}
 
1083
 
 
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().
 
1089
 */
 
1090
void *PaHost_AllocateFastMemory( long numBytes )
 
1091
{
 
1092
    void *addr = malloc( numBytes ); /* FIXME - do we need physical, wired, non-virtual memory? */
 
1093
    if( addr != NULL ) memset( addr, 0, numBytes );
 
1094
    return addr;
 
1095
}
 
1096
 
 
1097
/*************************************************************************
 
1098
 * Free memory that could be accessed in real-time.
 
1099
 * This call MUST be balanced with a call to PaHost_AllocateFastMemory().
 
1100
 */
 
1101
void PaHost_FreeFastMemory( void *addr, long numBytes )
 
1102
{
 
1103
    numBytes = numBytes; /* unused */
 
1104
    if( addr != NULL ) free( addr );
 
1105
}
 
1106
 
 
1107
 
 
1108
/***********************************************************************/
 
1109
PaError PaHost_StreamActive( internalPortAudioStream   *past )
 
1110
{
 
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);
 
1116
}
 
1117
 
 
1118
/***********************************************************************/
 
1119
long Pa_GetHostError( void )
 
1120
{
 
1121
    return (long) sPaHostError;
 
1122
}