1 /* 2 * Copyright (c) 2003, 2007, 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 186 info = (SolPcmInfo*) malloc(sizeof(SolPcmInfo)); 187 if (!info) { 188 ERROR0("Out of memory\n"); 189 return NULL; 190 } 191 memset(info, 0, sizeof(SolPcmInfo)); 192 info->frameSize = frameSize; 193 info->fd = -1; 194 195 if (isSource) { 196 openMode = O_WRONLY; 197 } else { 198 openMode = O_RDONLY; 199 } 200 201 #ifndef __linux__ 202 /* blackdown does not use NONBLOCK */ 203 openMode |= O_NONBLOCK; 204 #endif 205 206 if (getAudioDeviceDescriptionByIndex(mixerIndex, &desc, FALSE)) { 207 info->fd = open(desc.path, openMode); 208 } 209 if (info->fd < 0) { 210 ERROR1("Couldn't open audio device for mixer %d!\n", mixerIndex); 211 free(info); 212 return NULL; 213 } 214 /* set to multiple open */ 215 if (ioctl(info->fd, AUDIO_MIXER_MULTIPLE_OPEN, NULL) >= 0) { 216 TRACE1("DAUDIO_Open: %s set to multiple open\n", desc.path); 217 } else { 218 ERROR1("DAUDIO_Open: ioctl AUDIO_MIXER_MULTIPLE_OPEN failed on %s!\n", desc.path); 219 } 220 221 AUDIO_INITINFO(&(info->info)); 222 /* need AUDIO_GETINFO ioctl to get this to work on solaris x86 */ 223 err = ioctl(info->fd, AUDIO_GETINFO, &(info->info)); 224 225 /* not valid to call AUDIO_SETINFO ioctl with all the fields from AUDIO_GETINFO. */ 226 AUDIO_INITINFO(&(info->info)); 227 228 if (isSource) { 229 info->info.play.sample_rate = sampleRate; 230 info->info.play.precision = sampleSizeInBits; 231 info->info.play.channels = channels; 232 info->info.play.encoding = AUDIO_ENCODING_LINEAR; 233 info->info.play.buffer_size = bufferSizeInBytes; 234 info->info.play.pause = 1; 235 } else { 236 info->info.record.sample_rate = sampleRate; 237 info->info.record.precision = sampleSizeInBits; 238 info->info.record.channels = channels; 239 info->info.record.encoding = AUDIO_ENCODING_LINEAR; 240 info->info.record.buffer_size = bufferSizeInBytes; 241 info->info.record.pause = 1; 242 } 243 err = ioctl(info->fd, AUDIO_SETINFO, &(info->info)); 244 if (err < 0) { 245 ERROR0("DAUDIO_Open: could not set info!\n"); 246 DAUDIO_Close((void*) info, isSource); 247 return NULL; 248 } 249 DAUDIO_Flush((void*) info, isSource); 250 251 err = ioctl(info->fd, AUDIO_GETINFO, &(info->info)); 252 if (err >= 0) { 253 if (isSource) { 254 info->bufferSizeInBytes = info->info.play.buffer_size; 255 } else { 256 info->bufferSizeInBytes = info->info.record.buffer_size; 257 } 258 TRACE2("DAUDIO: buffersize in bytes: requested=%d, got %d\n", 259 (int) bufferSizeInBytes, 260 (int) info->bufferSizeInBytes); 261 } else { 262 ERROR0("DAUDIO_Open: cannot get info!\n"); 263 DAUDIO_Close((void*) info, isSource); 264 return NULL; 265 } 266 TRACE0("< DAUDIO_Open: Opened device successfully.\n"); 267 return (void*) info; 268 } 269 270 271 int DAUDIO_Start(void* id, int isSource) { 272 SolPcmInfo* info = (SolPcmInfo*) id; 273 int err, modified; 274 audio_info_t audioInfo; 275 276 TRACE0("> DAUDIO_Start\n"); 277 278 AUDIO_INITINFO(&audioInfo); 279 err = ioctl(info->fd, AUDIO_GETINFO, &audioInfo); 280 if (err >= 0) { 281 // unpause 282 modified = FALSE; 283 if (isSource && audioInfo.play.pause) { 284 audioInfo.play.pause = 0; 285 modified = TRUE; 286 } 287 if (!isSource && audioInfo.record.pause) { 288 audioInfo.record.pause = 0; 289 modified = TRUE; 290 } 291 if (modified) { 292 err = ioctl(info->fd, AUDIO_SETINFO, &audioInfo); 293 } 294 } 295 296 TRACE1("< DAUDIO_Start %s\n", (err>=0)?"success":"error"); 297 return (err >= 0)?TRUE:FALSE; 298 } 299 300 int DAUDIO_Stop(void* id, int isSource) { 301 SolPcmInfo* info = (SolPcmInfo*) id; 302 int err, modified; 303 audio_info_t audioInfo; 304 305 TRACE0("> DAUDIO_Stop\n"); 306 307 AUDIO_INITINFO(&audioInfo); 308 err = ioctl(info->fd, AUDIO_GETINFO, &audioInfo); 309 if (err >= 0) { 310 // pause 311 modified = FALSE; 312 if (isSource && !audioInfo.play.pause) { 313 audioInfo.play.pause = 1; 314 modified = TRUE; 315 } 316 if (!isSource && !audioInfo.record.pause) { 317 audioInfo.record.pause = 1; 318 modified = TRUE; 319 } 320 if (modified) { 321 err = ioctl(info->fd, AUDIO_SETINFO, &audioInfo); 322 } 323 } 324 325 TRACE1("< DAUDIO_Stop %s\n", (err>=0)?"success":"error"); 326 return (err >= 0)?TRUE:FALSE; 327 } 328 329 void DAUDIO_Close(void* id, int isSource) { 330 SolPcmInfo* info = (SolPcmInfo*) id; 331 332 TRACE0("DAUDIO_Close\n"); 333 if (info != NULL) { 334 if (info->fd >= 0) { 335 DAUDIO_Flush(id, isSource); 336 close(info->fd); 337 } 338 free(info); 339 } 340 } 341 342 #ifndef USE_TRACE 343 /* close to 2^31 */ 344 #define POSITION_MAX 2000000000 345 #else 346 /* for testing */ 347 #define POSITION_MAX 1000000 348 #endif 349 350 void resetErrorFlagAndAdjustPosition(SolPcmInfo* info, int isSource, int count) { 351 audio_info_t audioInfo; 352 audio_prinfo_t* prinfo; 353 int err; 354 int offset = -1; 355 int underrun = FALSE; 356 int devBytes = 0; 357 358 if (count > 0) { 359 info->transferedBytes += count; 360 361 if (isSource) { 362 prinfo = &(audioInfo.play); 363 } else { 364 prinfo = &(audioInfo.record); 365 } 366 AUDIO_INITINFO(&audioInfo); 367 err = ioctl(info->fd, AUDIO_GETINFO, &audioInfo); 368 if (err >= 0) { 369 underrun = prinfo->error; 370 devBytes = prinfo->samples * info->frameSize; 371 } 372 AUDIO_INITINFO(&audioInfo); 373 if (underrun) { 374 /* if an underrun occured, reset */ 375 ERROR1("DAUDIO_Write/Read: Underrun/overflow: adjusting positionOffset by %d:\n", 376 (devBytes - info->transferedBytes)); 377 ERROR1(" devBytes from %d to 0, ", devBytes); 378 ERROR2(" positionOffset from %d to %d ", 379 (int) info->positionOffset, 380 (int) (info->positionOffset + info->transferedBytes)); 381 ERROR1(" transferedBytes from %d to 0\n", 382 (int) info->transferedBytes); 383 prinfo->samples = 0; 384 info->positionOffset += info->transferedBytes; 385 info->transferedBytes = 0; 386 } 387 else if (info->transferedBytes > POSITION_MAX) { 388 /* we will reset transferedBytes and 389 * the samples field in prinfo 390 */ 391 offset = devBytes; 392 prinfo->samples = 0; 393 } 394 /* reset error flag */ 395 prinfo->error = 0; 396 397 err = ioctl(info->fd, AUDIO_SETINFO, &audioInfo); 398 if (err >= 0) { 399 if (offset > 0) { 400 /* upon exit of AUDIO_SETINFO, the samples parameter 401 * was set to the previous value. This is our 402 * offset. 403 */ 404 TRACE1("Adjust samplePos: offset=%d, ", (int) offset); 405 TRACE2("transferedBytes=%d -> %d, ", 406 (int) info->transferedBytes, 407 (int) (info->transferedBytes - offset)); 408 TRACE2("positionOffset=%d -> %d\n", 409 (int) (info->positionOffset), 410 (int) (((int) info->positionOffset) + offset)); 411 info->transferedBytes -= offset; 412 info->positionOffset += offset; 413 } 414 } else { 415 ERROR0("DAUDIO: resetErrorFlagAndAdjustPosition ioctl failed!\n"); 416 } 417 } 418 } 419 420 // returns -1 on error 421 int DAUDIO_Write(void* id, char* data, int byteSize) { 422 SolPcmInfo* info = (SolPcmInfo*) id; 423 int ret = -1; 424 425 TRACE1("> DAUDIO_Write %d bytes\n", byteSize); 426 if (info!=NULL) { 427 ret = write(info->fd, data, byteSize); 428 resetErrorFlagAndAdjustPosition(info, TRUE, ret); 429 /* sets ret to -1 if buffer full, no error! */ 430 if (ret < 0) { 431 ret = 0; 432 } 433 } 434 TRACE1("< DAUDIO_Write: returning %d bytes.\n", ret); 435 return ret; 436 } 437 438 // returns -1 on error 439 int DAUDIO_Read(void* id, char* data, int byteSize) { 440 SolPcmInfo* info = (SolPcmInfo*) id; 441 int ret = -1; 442 443 TRACE1("> DAUDIO_Read %d bytes\n", byteSize); 444 if (info != NULL) { 445 ret = read(info->fd, data, byteSize); 446 resetErrorFlagAndAdjustPosition(info, TRUE, ret); 447 /* sets ret to -1 if buffer full, no error! */ 448 if (ret < 0) { 449 ret = 0; 450 } 451 } 452 TRACE1("< DAUDIO_Read: returning %d bytes.\n", ret); 453 return ret; 454 } 455 456 457 int DAUDIO_GetBufferSize(void* id, int isSource) { 458 SolPcmInfo* info = (SolPcmInfo*) id; 459 if (info) { 460 return info->bufferSizeInBytes; 461 } 462 return 0; 463 } 464 465 int DAUDIO_StillDraining(void* id, int isSource) { 466 SolPcmInfo* info = (SolPcmInfo*) id; 467 audio_info_t audioInfo; 468 audio_prinfo_t* prinfo; 469 int ret = FALSE; 470 471 if (info!=NULL) { 472 if (isSource) { 473 prinfo = &(audioInfo.play); 474 } else { 475 prinfo = &(audioInfo.record); 476 } 477 /* check error flag */ 478 AUDIO_INITINFO(&audioInfo); 479 ioctl(info->fd, AUDIO_GETINFO, &audioInfo); 480 ret = (prinfo->error != 0)?FALSE:TRUE; 481 } 482 return ret; 483 } 484 485 486 int getDevicePosition(SolPcmInfo* info, int isSource) { 487 audio_info_t audioInfo; 488 audio_prinfo_t* prinfo; 489 int err; 490 491 if (isSource) { 492 prinfo = &(audioInfo.play); 493 } else { 494 prinfo = &(audioInfo.record); 495 } 496 AUDIO_INITINFO(&audioInfo); 497 err = ioctl(info->fd, AUDIO_GETINFO, &audioInfo); 498 if (err >= 0) { 499 /*TRACE2("---> device paused: %d eof=%d\n", 500 prinfo->pause, prinfo->eof); 501 */ 502 return (int) (prinfo->samples * info->frameSize); 503 } 504 ERROR0("DAUDIO: getDevicePosition: ioctl failed!\n"); 505 return -1; 506 } 507 508 int DAUDIO_Flush(void* id, int isSource) { 509 SolPcmInfo* info = (SolPcmInfo*) id; 510 int err = -1; 511 int pos; 512 513 TRACE0("DAUDIO_Flush\n"); 514 if (info) { 515 if (isSource) { 516 err = ioctl(info->fd, I_FLUSH, FLUSHW); 517 } else { 518 err = ioctl(info->fd, I_FLUSH, FLUSHR); 519 } 520 if (err >= 0) { 521 /* resets the transferedBytes parameter to 522 * the current samples count of the device 523 */ 524 pos = getDevicePosition(info, isSource); 525 if (pos >= 0) { 526 info->transferedBytes = pos; 527 } 528 } 529 } 530 if (err < 0) { 531 ERROR0("ERROR in DAUDIO_Flush\n"); 532 } 533 return (err < 0)?FALSE:TRUE; 534 } 535 536 int DAUDIO_GetAvailable(void* id, int isSource) { 537 SolPcmInfo* info = (SolPcmInfo*) id; 538 int ret = 0; 539 int pos; 540 541 if (info) { 542 /* unfortunately, the STREAMS architecture 543 * seems to not have a method for querying 544 * the available bytes to read/write! 545 * estimate it... 546 */ 547 pos = getDevicePosition(info, isSource); 548 if (pos >= 0) { 549 if (isSource) { 550 /* we usually have written more bytes 551 * to the queue than the device position should be 552 */ 553 ret = (info->bufferSizeInBytes) - (info->transferedBytes - pos); 554 } else { 555 /* for record, the device stream should 556 * be usually ahead of our read actions 557 */ 558 ret = pos - info->transferedBytes; 559 } 560 if (ret > info->bufferSizeInBytes) { 561 ERROR2("DAUDIO_GetAvailable: available=%d, too big at bufferSize=%d!\n", 562 (int) ret, (int) info->bufferSizeInBytes); 563 ERROR2(" devicePos=%d, transferedBytes=%d\n", 564 (int) pos, (int) info->transferedBytes); 565 ret = info->bufferSizeInBytes; 566 } 567 else if (ret < 0) { 568 ERROR1("DAUDIO_GetAvailable: available=%d, in theory not possible!\n", 569 (int) ret); 570 ERROR2(" devicePos=%d, transferedBytes=%d\n", 571 (int) pos, (int) info->transferedBytes); 572 ret = 0; 573 } 574 } 575 } 576 577 TRACE1("DAUDIO_GetAvailable returns %d bytes\n", ret); 578 return ret; 579 } 580 581 INT64 DAUDIO_GetBytePosition(void* id, int isSource, INT64 javaBytePos) { 582 SolPcmInfo* info = (SolPcmInfo*) id; 583 int ret; 584 int pos; 585 INT64 result = javaBytePos; 586 587 if (info) { 588 pos = getDevicePosition(info, isSource); 589 if (pos >= 0) { 590 result = info->positionOffset + pos; 591 } 592 } 593 594 //printf("getbyteposition: javaBytePos=%d , return=%d\n", (int) javaBytePos, (int) result); 595 return result; 596 } 597 598 599 void DAUDIO_SetBytePosition(void* id, int isSource, INT64 javaBytePos) { 600 SolPcmInfo* info = (SolPcmInfo*) id; 601 int ret; 602 int pos; 603 604 if (info) { 605 pos = getDevicePosition(info, isSource); 606 if (pos >= 0) { 607 info->positionOffset = javaBytePos - pos; 608 } 609 } 610 } 611 612 int DAUDIO_RequiresServicing(void* id, int isSource) { 613 // never need servicing on Solaris 614 return FALSE; 615 } 616 617 void DAUDIO_Service(void* id, int isSource) { 618 // never need servicing on Solaris 619 } 620 621 622 #endif // USE_DAUDIO