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
40INT32 DAUDIO_GetDirectAudioDeviceCount() {
41    return (INT32) getAudioDeviceCount();
42}
43
44
45INT32 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
68void 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
156typedef 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
171void* 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
275int 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
304int 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
333void 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
354void 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
425int 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
443int 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
461int DAUDIO_GetBufferSize(void* id, int isSource) {
462    SolPcmInfo* info = (SolPcmInfo*) id;
463    if (info) {
464        return info->bufferSizeInBytes;
465    }
466    return 0;
467}
468
469int 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
490int 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
512int 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
540int 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
585INT64 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
603void 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
616int DAUDIO_RequiresServicing(void* id, int isSource) {
617    // never need servicing on Solaris
618    return FALSE;
619}
620
621void DAUDIO_Service(void* id, int isSource) {
622    // never need servicing on Solaris
623}
624
625
626#endif // USE_DAUDIO
627