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 40 INT32 DAUDIO_GetDirectAudioDeviceCount() { 41 return (INT32) getAudioDeviceCount(); 42 } 43 44 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 616 int DAUDIO_RequiresServicing(void* id, int isSource) { 617 // never need servicing on Solaris 618 return FALSE; 619 } 620 621 void DAUDIO_Service(void* id, int isSource) { 622 // never need servicing on Solaris 623 } 624 625 626 #endif // USE_DAUDIO