1 /*
2  * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 #define USE_ERROR
27 #define USE_TRACE
28 
29 #include "PLATFORM_API_SolarisOS_Utils.h"
30 #include "DirectAudio.h"
31 
32 #if USE_DAUDIO == TRUE
33 
34 
35 // The default buffer time
36 #define DEFAULT_PERIOD_TIME_MILLIS 50
37 
38 ///// implemented functions of DirectAudio.h
39 
DAUDIO_GetDirectAudioDeviceCount()40 INT32 DAUDIO_GetDirectAudioDeviceCount() {
41     return (INT32) getAudioDeviceCount();
42 }
43 
44 
DAUDIO_GetDirectAudioDeviceDescription(INT32 mixerIndex,DirectAudioDeviceDescription * description)45 INT32 DAUDIO_GetDirectAudioDeviceDescription(INT32 mixerIndex,
46                                              DirectAudioDeviceDescription* description) {
47     AudioDeviceDescription desc;
48 
49     if (getAudioDeviceDescriptionByIndex(mixerIndex, &desc, TRUE)) {
50         description->maxSimulLines = desc.maxSimulLines;
51         strncpy(description->name, desc.name, DAUDIO_STRING_LENGTH-1);
52         description->name[DAUDIO_STRING_LENGTH-1] = 0;
53         strncpy(description->vendor, desc.vendor, DAUDIO_STRING_LENGTH-1);
54         description->vendor[DAUDIO_STRING_LENGTH-1] = 0;
55         strncpy(description->version, desc.version, DAUDIO_STRING_LENGTH-1);
56         description->version[DAUDIO_STRING_LENGTH-1] = 0;
57         /*strncpy(description->description, desc.description, DAUDIO_STRING_LENGTH-1);*/
58         strncpy(description->description, "Solaris Mixer", DAUDIO_STRING_LENGTH-1);
59         description->description[DAUDIO_STRING_LENGTH-1] = 0;
60         return TRUE;
61     }
62     return FALSE;
63 
64 }
65 
66 #define MAX_SAMPLE_RATES   20
67 
DAUDIO_GetFormats(INT32 mixerIndex,INT32 deviceID,int isSource,void * creator)68 void DAUDIO_GetFormats(INT32 mixerIndex, INT32 deviceID, int isSource, void* creator) {
69     int fd = -1;
70     AudioDeviceDescription desc;
71     am_sample_rates_t      *sr;
72     /* hardcoded bits and channels */
73     int bits[] = {8, 16};
74     int bitsCount = 2;
75     int channels[] = {1, 2};
76     int channelsCount = 2;
77     /* for querying sample rates */
78     int err;
79     int ch, b, s;
80 
81     TRACE2("DAUDIO_GetFormats, mixer %d, isSource=%d\n", mixerIndex, isSource);
82     if (getAudioDeviceDescriptionByIndex(mixerIndex, &desc, FALSE)) {
83         fd = open(desc.pathctl, O_RDONLY);
84     }
85     if (fd < 0) {
86         ERROR1("Couldn't open audio device ctl for device %d!\n", mixerIndex);
87         return;
88     }
89 
90     /* get sample rates */
91     sr = (am_sample_rates_t*) malloc(AUDIO_MIXER_SAMP_RATES_STRUCT_SIZE(MAX_SAMPLE_RATES));
92     if (sr == NULL) {
93         ERROR1("DAUDIO_GetFormats: out of memory for mixer %d\n", (int) mixerIndex);
94         close(fd);
95         return;
96     }
97 
98     sr->num_samp_rates = MAX_SAMPLE_RATES;
99     sr->type = isSource?AUDIO_PLAY:AUDIO_RECORD;
100     sr->samp_rates[0] = -2;
101     err = ioctl(fd, AUDIO_MIXER_GET_SAMPLE_RATES, sr);
102     if (err < 0) {
103         ERROR1("  DAUDIO_GetFormats: AUDIO_MIXER_GET_SAMPLE_RATES failed for mixer %d!\n",
104                (int)mixerIndex);
105         ERROR2(" -> num_sample_rates=%d sample_rates[0] = %d\n",
106                (int) sr->num_samp_rates,
107                (int) sr->samp_rates[0]);
108         /* Some Solaris 8 drivers fail for get sample rates!
109          * Do as if we support all sample rates
110          */
111         sr->flags = MIXER_SR_LIMITS;
112     }
113     if ((sr->flags & MIXER_SR_LIMITS)
114         || (sr->num_samp_rates > MAX_SAMPLE_RATES)) {
115 #ifdef USE_TRACE
116         if ((sr->flags & MIXER_SR_LIMITS)) {
117             TRACE1("  DAUDIO_GetFormats: floating sample rate allowed by mixer %d\n",
118                    (int)mixerIndex);
119         }
120         if (sr->num_samp_rates > MAX_SAMPLE_RATES) {
121             TRACE2("  DAUDIO_GetFormats: more than %d formats. Use -1 for sample rates mixer %d\n",
122                    MAX_SAMPLE_RATES, (int)mixerIndex);
123         }
124 #endif
125         /*
126          * Fake it to have only one sample rate: -1
127          */
128         sr->num_samp_rates = 1;
129         sr->samp_rates[0] = -1;
130     }
131     close(fd);
132 
133     for (ch = 0; ch < channelsCount; ch++) {
134         for (b = 0; b < bitsCount; b++) {
135             for (s = 0; s < sr->num_samp_rates; s++) {
136                 DAUDIO_AddAudioFormat(creator,
137                                       bits[b], /* significant bits */
138                                       0, /* frameSize: let it be calculated */
139                                       channels[ch],
140                                       (float) ((int) sr->samp_rates[s]),
141                                       DAUDIO_PCM, /* encoding - let's only do PCM */
142                                       (bits[b] > 8)?TRUE:TRUE, /* isSigned */
143 #ifdef _LITTLE_ENDIAN
144                                       FALSE /* little endian */
145 #else
146                                       (bits[b] > 8)?TRUE:FALSE  /* big endian */
147 #endif
148                                       );
149             }
150         }
151     }
152     free(sr);
153 }
154 
155 
156 typedef struct {
157     int fd;
158     audio_info_t info;
159     int bufferSizeInBytes;
160     int frameSize; /* storage size in Bytes */
161     /* how many bytes were written or read */
162     INT32 transferedBytes;
163     /* if transferedBytes exceed 32-bit boundary,
164      * it will be reset and positionOffset will receive
165      * the offset
166      */
167     INT64 positionOffset;
168 } SolPcmInfo;
169 
170 
DAUDIO_Open(INT32 mixerIndex,INT32 deviceID,int isSource,int encoding,float sampleRate,int sampleSizeInBits,int frameSize,int channels,int isSigned,int isBigEndian,int bufferSizeInBytes)171 void* DAUDIO_Open(INT32 mixerIndex, INT32 deviceID, int isSource,
172                   int encoding, float sampleRate, int sampleSizeInBits,
173                   int frameSize, int channels,
174                   int isSigned, int isBigEndian, int bufferSizeInBytes) {
175     int err = 0;
176     int openMode;
177     AudioDeviceDescription desc;
178     SolPcmInfo* info;
179 
180     TRACE0("> DAUDIO_Open\n");
181     if (encoding != DAUDIO_PCM) {
182         ERROR1(" DAUDIO_Open: invalid encoding %d\n", (int) encoding);
183         return NULL;
184     }
185     if (channels <= 0) {
186         ERROR1(" DAUDIO_Open: Invalid number of channels=%d!\n", channels);
187         return NULL;
188     }
189 
190     info = (SolPcmInfo*) malloc(sizeof(SolPcmInfo));
191     if (!info) {
192         ERROR0("Out of memory\n");
193         return NULL;
194     }
195     memset(info, 0, sizeof(SolPcmInfo));
196     info->frameSize = frameSize;
197     info->fd = -1;
198 
199     if (isSource) {
200         openMode = O_WRONLY;
201     } else {
202         openMode = O_RDONLY;
203     }
204 
205 #ifndef __linux__
206     /* blackdown does not use NONBLOCK */
207     openMode |= O_NONBLOCK;
208 #endif
209 
210     if (getAudioDeviceDescriptionByIndex(mixerIndex, &desc, FALSE)) {
211         info->fd = open(desc.path, openMode);
212     }
213     if (info->fd < 0) {
214         ERROR1("Couldn't open audio device for mixer %d!\n", mixerIndex);
215         free(info);
216         return NULL;
217     }
218     /* set to multiple open */
219     if (ioctl(info->fd, AUDIO_MIXER_MULTIPLE_OPEN, NULL) >= 0) {
220         TRACE1("DAUDIO_Open: %s set to multiple open\n", desc.path);
221     } else {
222         ERROR1("DAUDIO_Open: ioctl AUDIO_MIXER_MULTIPLE_OPEN failed on %s!\n", desc.path);
223     }
224 
225     AUDIO_INITINFO(&(info->info));
226     /* need AUDIO_GETINFO ioctl to get this to work on solaris x86  */
227     err = ioctl(info->fd, AUDIO_GETINFO, &(info->info));
228 
229     /* not valid to call AUDIO_SETINFO ioctl with all the fields from AUDIO_GETINFO. */
230     AUDIO_INITINFO(&(info->info));
231 
232     if (isSource) {
233         info->info.play.sample_rate = sampleRate;
234         info->info.play.precision = sampleSizeInBits;
235         info->info.play.channels = channels;
236         info->info.play.encoding = AUDIO_ENCODING_LINEAR;
237         info->info.play.buffer_size = bufferSizeInBytes;
238         info->info.play.pause = 1;
239     } else {
240         info->info.record.sample_rate = sampleRate;
241         info->info.record.precision = sampleSizeInBits;
242         info->info.record.channels = channels;
243         info->info.record.encoding = AUDIO_ENCODING_LINEAR;
244         info->info.record.buffer_size = bufferSizeInBytes;
245         info->info.record.pause = 1;
246     }
247     err = ioctl(info->fd, AUDIO_SETINFO,  &(info->info));
248     if (err < 0) {
249         ERROR0("DAUDIO_Open: could not set info!\n");
250         DAUDIO_Close((void*) info, isSource);
251         return NULL;
252     }
253     DAUDIO_Flush((void*) info, isSource);
254 
255     err = ioctl(info->fd, AUDIO_GETINFO, &(info->info));
256     if (err >= 0) {
257         if (isSource) {
258             info->bufferSizeInBytes = info->info.play.buffer_size;
259         } else {
260             info->bufferSizeInBytes = info->info.record.buffer_size;
261         }
262         TRACE2("DAUDIO: buffersize in bytes: requested=%d, got %d\n",
263                (int) bufferSizeInBytes,
264                (int) info->bufferSizeInBytes);
265     } else {
266         ERROR0("DAUDIO_Open: cannot get info!\n");
267         DAUDIO_Close((void*) info, isSource);
268         return NULL;
269     }
270     TRACE0("< DAUDIO_Open: Opened device successfully.\n");
271     return (void*) info;
272 }
273 
274 
DAUDIO_Start(void * id,int isSource)275 int DAUDIO_Start(void* id, int isSource) {
276     SolPcmInfo* info = (SolPcmInfo*) id;
277     int err, modified;
278     audio_info_t audioInfo;
279 
280     TRACE0("> DAUDIO_Start\n");
281 
282     AUDIO_INITINFO(&audioInfo);
283     err = ioctl(info->fd, AUDIO_GETINFO, &audioInfo);
284     if (err >= 0) {
285         // unpause
286         modified = FALSE;
287         if (isSource && audioInfo.play.pause) {
288             audioInfo.play.pause = 0;
289             modified = TRUE;
290         }
291         if (!isSource && audioInfo.record.pause) {
292             audioInfo.record.pause = 0;
293             modified = TRUE;
294         }
295         if (modified) {
296             err = ioctl(info->fd, AUDIO_SETINFO, &audioInfo);
297         }
298     }
299 
300     TRACE1("< DAUDIO_Start %s\n", (err>=0)?"success":"error");
301     return (err >= 0)?TRUE:FALSE;
302 }
303 
DAUDIO_Stop(void * id,int isSource)304 int DAUDIO_Stop(void* id, int isSource) {
305     SolPcmInfo* info = (SolPcmInfo*) id;
306     int err, modified;
307     audio_info_t audioInfo;
308 
309     TRACE0("> DAUDIO_Stop\n");
310 
311     AUDIO_INITINFO(&audioInfo);
312     err = ioctl(info->fd, AUDIO_GETINFO, &audioInfo);
313     if (err >= 0) {
314         // pause
315         modified = FALSE;
316         if (isSource && !audioInfo.play.pause) {
317             audioInfo.play.pause = 1;
318             modified = TRUE;
319         }
320         if (!isSource && !audioInfo.record.pause) {
321             audioInfo.record.pause = 1;
322             modified = TRUE;
323         }
324         if (modified) {
325             err = ioctl(info->fd, AUDIO_SETINFO, &audioInfo);
326         }
327     }
328 
329     TRACE1("< DAUDIO_Stop %s\n", (err>=0)?"success":"error");
330     return (err >= 0)?TRUE:FALSE;
331 }
332 
DAUDIO_Close(void * id,int isSource)333 void DAUDIO_Close(void* id, int isSource) {
334     SolPcmInfo* info = (SolPcmInfo*) id;
335 
336     TRACE0("DAUDIO_Close\n");
337     if (info != NULL) {
338         if (info->fd >= 0) {
339             DAUDIO_Flush(id, isSource);
340             close(info->fd);
341         }
342         free(info);
343     }
344 }
345 
346 #ifndef USE_TRACE
347 /* close to 2^31 */
348 #define POSITION_MAX 2000000000
349 #else
350 /* for testing */
351 #define POSITION_MAX 1000000
352 #endif
353 
resetErrorFlagAndAdjustPosition(SolPcmInfo * info,int isSource,int count)354 void resetErrorFlagAndAdjustPosition(SolPcmInfo* info, int isSource, int count) {
355     audio_info_t audioInfo;
356     audio_prinfo_t* prinfo;
357     int err;
358     int offset = -1;
359     int underrun = FALSE;
360     int devBytes = 0;
361 
362     if (count > 0) {
363         info->transferedBytes += count;
364 
365         if (isSource) {
366             prinfo = &(audioInfo.play);
367         } else {
368             prinfo = &(audioInfo.record);
369         }
370         AUDIO_INITINFO(&audioInfo);
371         err = ioctl(info->fd, AUDIO_GETINFO, &audioInfo);
372         if (err >= 0) {
373             underrun = prinfo->error;
374             devBytes = prinfo->samples * info->frameSize;
375         }
376         AUDIO_INITINFO(&audioInfo);
377         if (underrun) {
378             /* if an underrun occurred, reset */
379             ERROR1("DAUDIO_Write/Read: Underrun/overflow: adjusting positionOffset by %d:\n",
380                    (devBytes - info->transferedBytes));
381             ERROR1("    devBytes from %d to 0, ", devBytes);
382             ERROR2(" positionOffset from %d to %d ",
383                    (int) info->positionOffset,
384                    (int) (info->positionOffset + info->transferedBytes));
385             ERROR1(" transferedBytes from %d to 0\n",
386                    (int) info->transferedBytes);
387             prinfo->samples = 0;
388             info->positionOffset += info->transferedBytes;
389             info->transferedBytes = 0;
390         }
391         else if (info->transferedBytes > POSITION_MAX) {
392             /* we will reset transferedBytes and
393              * the samples field in prinfo
394              */
395             offset = devBytes;
396             prinfo->samples = 0;
397         }
398         /* reset error flag */
399         prinfo->error = 0;
400 
401         err = ioctl(info->fd, AUDIO_SETINFO, &audioInfo);
402         if (err >= 0) {
403             if (offset > 0) {
404                 /* upon exit of AUDIO_SETINFO, the samples parameter
405                  * was set to the previous value. This is our
406                  * offset.
407                  */
408                 TRACE1("Adjust samplePos: offset=%d, ", (int) offset);
409                 TRACE2("transferedBytes=%d -> %d, ",
410                        (int) info->transferedBytes,
411                        (int) (info->transferedBytes - offset));
412                 TRACE2("positionOffset=%d -> %d\n",
413                        (int) (info->positionOffset),
414                        (int) (((int) info->positionOffset) + offset));
415                 info->transferedBytes -= offset;
416                 info->positionOffset += offset;
417             }
418         } else {
419             ERROR0("DAUDIO: resetErrorFlagAndAdjustPosition ioctl failed!\n");
420         }
421     }
422 }
423 
424 // returns -1 on error
DAUDIO_Write(void * id,char * data,int byteSize)425 int DAUDIO_Write(void* id, char* data, int byteSize) {
426     SolPcmInfo* info = (SolPcmInfo*) id;
427     int ret = -1;
428 
429     TRACE1("> DAUDIO_Write %d bytes\n", byteSize);
430     if (info!=NULL) {
431         ret = write(info->fd, data, byteSize);
432         resetErrorFlagAndAdjustPosition(info, TRUE, ret);
433         /* sets ret to -1 if buffer full, no error! */
434         if (ret < 0) {
435             ret = 0;
436         }
437     }
438     TRACE1("< DAUDIO_Write: returning %d bytes.\n", ret);
439     return ret;
440 }
441 
442 // returns -1 on error
DAUDIO_Read(void * id,char * data,int byteSize)443 int DAUDIO_Read(void* id, char* data, int byteSize) {
444     SolPcmInfo* info = (SolPcmInfo*) id;
445     int ret = -1;
446 
447     TRACE1("> DAUDIO_Read %d bytes\n", byteSize);
448     if (info != NULL) {
449         ret = read(info->fd, data, byteSize);
450         resetErrorFlagAndAdjustPosition(info, TRUE, ret);
451         /* sets ret to -1 if buffer full, no error! */
452         if (ret < 0) {
453             ret = 0;
454         }
455     }
456     TRACE1("< DAUDIO_Read: returning %d bytes.\n", ret);
457     return ret;
458 }
459 
460 
DAUDIO_GetBufferSize(void * id,int isSource)461 int DAUDIO_GetBufferSize(void* id, int isSource) {
462     SolPcmInfo* info = (SolPcmInfo*) id;
463     if (info) {
464         return info->bufferSizeInBytes;
465     }
466     return 0;
467 }
468 
DAUDIO_StillDraining(void * id,int isSource)469 int DAUDIO_StillDraining(void* id, int isSource) {
470     SolPcmInfo* info = (SolPcmInfo*) id;
471     audio_info_t audioInfo;
472     audio_prinfo_t* prinfo;
473     int ret = FALSE;
474 
475     if (info!=NULL) {
476         if (isSource) {
477             prinfo = &(audioInfo.play);
478         } else {
479             prinfo = &(audioInfo.record);
480         }
481         /* check error flag */
482         AUDIO_INITINFO(&audioInfo);
483         ioctl(info->fd, AUDIO_GETINFO, &audioInfo);
484         ret = (prinfo->error != 0)?FALSE:TRUE;
485     }
486     return ret;
487 }
488 
489 
getDevicePosition(SolPcmInfo * info,int isSource)490 int getDevicePosition(SolPcmInfo* info, int isSource) {
491     audio_info_t audioInfo;
492     audio_prinfo_t* prinfo;
493     int err;
494 
495     if (isSource) {
496         prinfo = &(audioInfo.play);
497     } else {
498         prinfo = &(audioInfo.record);
499     }
500     AUDIO_INITINFO(&audioInfo);
501     err = ioctl(info->fd, AUDIO_GETINFO, &audioInfo);
502     if (err >= 0) {
503         /*TRACE2("---> device paused: %d  eof=%d\n",
504                prinfo->pause, prinfo->eof);
505         */
506         return (int) (prinfo->samples * info->frameSize);
507     }
508     ERROR0("DAUDIO: getDevicePosition: ioctl failed!\n");
509     return -1;
510 }
511 
DAUDIO_Flush(void * id,int isSource)512 int DAUDIO_Flush(void* id, int isSource) {
513     SolPcmInfo* info = (SolPcmInfo*) id;
514     int err = -1;
515     int pos;
516 
517     TRACE0("DAUDIO_Flush\n");
518     if (info) {
519         if (isSource) {
520             err = ioctl(info->fd, I_FLUSH, FLUSHW);
521         } else {
522             err = ioctl(info->fd, I_FLUSH, FLUSHR);
523         }
524         if (err >= 0) {
525             /* resets the transferedBytes parameter to
526              * the current samples count of the device
527              */
528             pos = getDevicePosition(info, isSource);
529             if (pos >= 0) {
530                 info->transferedBytes = pos;
531             }
532         }
533     }
534     if (err < 0) {
535         ERROR0("ERROR in DAUDIO_Flush\n");
536     }
537     return (err < 0)?FALSE:TRUE;
538 }
539 
DAUDIO_GetAvailable(void * id,int isSource)540 int DAUDIO_GetAvailable(void* id, int isSource) {
541     SolPcmInfo* info = (SolPcmInfo*) id;
542     int ret = 0;
543     int pos;
544 
545     if (info) {
546         /* unfortunately, the STREAMS architecture
547          * seems to not have a method for querying
548          * the available bytes to read/write!
549          * estimate it...
550          */
551         pos = getDevicePosition(info, isSource);
552         if (pos >= 0) {
553             if (isSource) {
554                 /* we usually have written more bytes
555                  * to the queue than the device position should be
556                  */
557                 ret = (info->bufferSizeInBytes) - (info->transferedBytes - pos);
558             } else {
559                 /* for record, the device stream should
560                  * be usually ahead of our read actions
561                  */
562                 ret = pos - info->transferedBytes;
563             }
564             if (ret > info->bufferSizeInBytes) {
565                 ERROR2("DAUDIO_GetAvailable: available=%d, too big at bufferSize=%d!\n",
566                        (int) ret, (int) info->bufferSizeInBytes);
567                 ERROR2("                     devicePos=%d, transferedBytes=%d\n",
568                        (int) pos, (int) info->transferedBytes);
569                 ret = info->bufferSizeInBytes;
570             }
571             else if (ret < 0) {
572                 ERROR1("DAUDIO_GetAvailable: available=%d, in theory not possible!\n",
573                        (int) ret);
574                 ERROR2("                     devicePos=%d, transferedBytes=%d\n",
575                        (int) pos, (int) info->transferedBytes);
576                 ret = 0;
577             }
578         }
579     }
580 
581     TRACE1("DAUDIO_GetAvailable returns %d bytes\n", ret);
582     return ret;
583 }
584 
DAUDIO_GetBytePosition(void * id,int isSource,INT64 javaBytePos)585 INT64 DAUDIO_GetBytePosition(void* id, int isSource, INT64 javaBytePos) {
586     SolPcmInfo* info = (SolPcmInfo*) id;
587     int ret;
588     int pos;
589     INT64 result = javaBytePos;
590 
591     if (info) {
592         pos = getDevicePosition(info, isSource);
593         if (pos >= 0) {
594             result = info->positionOffset + pos;
595         }
596     }
597 
598     //printf("getbyteposition: javaBytePos=%d , return=%d\n", (int) javaBytePos, (int) result);
599     return result;
600 }
601 
602 
DAUDIO_SetBytePosition(void * id,int isSource,INT64 javaBytePos)603 void DAUDIO_SetBytePosition(void* id, int isSource, INT64 javaBytePos) {
604     SolPcmInfo* info = (SolPcmInfo*) id;
605     int ret;
606     int pos;
607 
608     if (info) {
609         pos = getDevicePosition(info, isSource);
610         if (pos >= 0) {
611             info->positionOffset = javaBytePos - pos;
612         }
613     }
614 }
615 
DAUDIO_RequiresServicing(void * id,int isSource)616 int DAUDIO_RequiresServicing(void* id, int isSource) {
617     // never need servicing on Solaris
618     return FALSE;
619 }
620 
DAUDIO_Service(void * id,int isSource)621 void DAUDIO_Service(void* id, int isSource) {
622     // never need servicing on Solaris
623 }
624 
625 
626 #endif // USE_DAUDIO
627