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