1 /* 2 * Copyright (c) 2002, 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_LinuxOS_ALSA_PCMUtils.h" 30 #include "PLATFORM_API_LinuxOS_ALSA_CommonUtils.h" 31 #include "DirectAudio.h" 32 33 #if USE_DAUDIO == TRUE 34 35 // GetPosition method 1: based on how many bytes are passed to the kernel driver 36 // + does not need much processor resources 37 // - not very exact, "jumps" 38 // GetPosition method 2: ask kernel about actual position of playback. 39 // - very exact 40 // - switch to kernel layer for each call 41 // GetPosition method 3: use snd_pcm_avail() call - not yet in official ALSA 42 // quick tests on a Pentium 200MMX showed max. 1.5% processor usage 43 // for playing back a CD-quality file and printing 20x per second a line 44 // on the console with the current time. So I guess performance is not such a 45 // factor here. 46 //#define GET_POSITION_METHOD1 47 #define GET_POSITION_METHOD2 48 49 50 // The default time for a period in microseconds. 51 // For very small buffers, only 2 periods are used. 52 #define DEFAULT_PERIOD_TIME 20000 /* 20ms */ 53 54 ///// implemented functions of DirectAudio.h 55 56 INT32 DAUDIO_GetDirectAudioDeviceCount() { 57 return (INT32) getAudioDeviceCount(); 58 } 59 60 61 INT32 DAUDIO_GetDirectAudioDeviceDescription(INT32 mixerIndex, DirectAudioDeviceDescription* description) { 62 ALSA_AudioDeviceDescription adesc; 63 64 adesc.index = (int) mixerIndex; 65 adesc.strLen = DAUDIO_STRING_LENGTH; 66 67 adesc.maxSimultaneousLines = (int*) (&(description->maxSimulLines)); 68 adesc.deviceID = &(description->deviceID); 69 adesc.name = description->name; 70 adesc.vendor = description->vendor; 71 adesc.description = description->description; 72 adesc.version = description->version; 73 74 return getAudioDeviceDescriptionByIndex(&adesc); 75 } 76 77 #define MAX_BIT_INDEX 6 78 // returns 79 // 6: for anything above 24-bit 80 // 5: for 4 bytes sample size, 24-bit 81 // 4: for 3 bytes sample size, 24-bit 82 // 3: for 3 bytes sample size, 20-bit 83 // 2: for 2 bytes sample size, 16-bit 84 // 1: for 1 byte sample size, 8-bit 85 // 0: for anything else 86 int getBitIndex(int sampleSizeInBytes, int significantBits) { 87 if (significantBits > 24) return 6; 88 if (sampleSizeInBytes == 4 && significantBits == 24) return 5; 89 if (sampleSizeInBytes == 3) { 90 if (significantBits == 24) return 4; 91 if (significantBits == 20) return 3; 92 } 93 if (sampleSizeInBytes == 2 && significantBits == 16) return 2; 94 if (sampleSizeInBytes == 1 && significantBits == 8) return 1; 95 return 0; 96 } 97 98 int getSampleSizeInBytes(int bitIndex, int sampleSizeInBytes) { 99 switch(bitIndex) { 100 case 1: return 1; 101 case 2: return 2; 102 case 3: /* fall through */ 103 case 4: return 3; 104 case 5: return 4; 105 } 106 return sampleSizeInBytes; 107 } 108 109 int getSignificantBits(int bitIndex, int significantBits) { 110 switch(bitIndex) { 111 case 1: return 8; 112 case 2: return 16; 113 case 3: return 20; 114 case 4: /* fall through */ 115 case 5: return 24; 116 } 117 return significantBits; 118 } 119 120 void DAUDIO_GetFormats(INT32 mixerIndex, INT32 deviceID, int isSource, void* creator) { 121 snd_pcm_t* handle; 122 snd_pcm_format_mask_t* formatMask; 123 snd_pcm_format_t format; 124 snd_pcm_hw_params_t* hwParams; 125 int handledBits[MAX_BIT_INDEX+1]; 126 127 int ret; 128 int sampleSizeInBytes, significantBits, isSigned, isBigEndian, enc; 129 int origSampleSizeInBytes, origSignificantBits; 130 int channels, minChannels, maxChannels; 131 int rate, bitIndex; 132 133 for (bitIndex = 0; bitIndex <= MAX_BIT_INDEX; bitIndex++) handledBits[bitIndex] = FALSE; 134 if (openPCMfromDeviceID(deviceID, &handle, isSource, TRUE /*query hardware*/) < 0) { 135 return; 136 } 137 ret = snd_pcm_format_mask_malloc(&formatMask); 138 if (ret != 0) { 139 ERROR1("snd_pcm_format_mask_malloc returned error %d\n", ret); 140 } else { 141 ret = snd_pcm_hw_params_malloc(&hwParams); 142 if (ret != 0) { 143 ERROR1("snd_pcm_hw_params_malloc returned error %d\n", ret); 144 } else { 145 ret = snd_pcm_hw_params_any(handle, hwParams); 146 /* snd_pcm_hw_params_any can return a positive value on success too */ 147 if (ret < 0) { 148 ERROR1("snd_pcm_hw_params_any returned error %d\n", ret); 149 } else { 150 /* for the logic following this code, set ret to 0 to indicate success */ 151 ret = 0; 152 } 153 } 154 snd_pcm_hw_params_get_format_mask(hwParams, formatMask); 155 #ifdef ALSA_PCM_NEW_HW_PARAMS_API 156 if (ret == 0) { 157 ret = snd_pcm_hw_params_get_channels_min(hwParams, &minChannels); 158 if (ret != 0) { 159 ERROR1("snd_pcm_hw_params_get_channels_min returned error %d\n", ret); 160 } 161 } 162 if (ret == 0) { 163 ret = snd_pcm_hw_params_get_channels_max(hwParams, &maxChannels); 164 if (ret != 0) { 165 ERROR1("snd_pcm_hw_params_get_channels_max returned error %d\n", ret); 166 } 167 } 168 #else 169 minChannels = snd_pcm_hw_params_get_channels_min(hwParams); 170 maxChannels = snd_pcm_hw_params_get_channels_max(hwParams); 171 if (minChannels > maxChannels) { 172 ERROR2("MinChannels=%d, maxChannels=%d\n", minChannels, maxChannels); 173 } 174 #endif 175 176 // since we queried the hw: device, for many soundcards, it will only 177 // report the maximum number of channels (which is the only way to talk 178 // to the hw: device). Since we will, however, open the plughw: device 179 // when opening the Source/TargetDataLine, we can safely assume that 180 // also the channels 1..maxChannels are available. 181 #ifdef ALSA_PCM_USE_PLUGHW 182 minChannels = 1; 183 #endif 184 if (ret == 0) { 185 // plughw: supports any sample rate 186 rate = -1; 187 for (format = 0; format <= SND_PCM_FORMAT_LAST; format++) { 188 if (snd_pcm_format_mask_test(formatMask, format)) { 189 // format exists 190 if (getFormatFromAlsaFormat(format, &origSampleSizeInBytes, 191 &origSignificantBits, 192 &isSigned, &isBigEndian, &enc)) { 193 // now if we use plughw:, we can use any bit size below the 194 // natively supported ones. Some ALSA drivers only support the maximum 195 // bit size, so we add any sample rates below the reported one. 196 // E.g. this iteration reports support for 16-bit. 197 // getBitIndex will return 2, so it will add entries for 198 // 16-bit (bitIndex=2) and in the next do-while loop iteration, 199 // it will decrease bitIndex and will therefore add 8-bit support. 200 bitIndex = getBitIndex(origSampleSizeInBytes, origSignificantBits); 201 do { 202 if (bitIndex == 0 203 || bitIndex == MAX_BIT_INDEX 204 || !handledBits[bitIndex]) { 205 handledBits[bitIndex] = TRUE; 206 sampleSizeInBytes = getSampleSizeInBytes(bitIndex, origSampleSizeInBytes); 207 significantBits = getSignificantBits(bitIndex, origSignificantBits); 208 if (maxChannels - minChannels > MAXIMUM_LISTED_CHANNELS) { 209 // avoid too many channels explicitly listed 210 // just add -1, min, and max 211 DAUDIO_AddAudioFormat(creator, significantBits, 212 -1, -1, rate, 213 enc, isSigned, isBigEndian); 214 DAUDIO_AddAudioFormat(creator, significantBits, 215 sampleSizeInBytes * minChannels, 216 minChannels, rate, 217 enc, isSigned, isBigEndian); 218 DAUDIO_AddAudioFormat(creator, significantBits, 219 sampleSizeInBytes * maxChannels, 220 maxChannels, rate, 221 enc, isSigned, isBigEndian); 222 } else { 223 for (channels = minChannels; channels <= maxChannels; channels++) { 224 DAUDIO_AddAudioFormat(creator, significantBits, 225 (channels < 0)?-1:(sampleSizeInBytes * channels), 226 channels, rate, 227 enc, isSigned, isBigEndian); 228 } 229 } 230 } 231 #ifndef ALSA_PCM_USE_PLUGHW 232 // without plugin, do not add fake formats 233 break; 234 #endif 235 } while (--bitIndex > 0); 236 } else { 237 TRACE1("could not get format from alsa for format %d\n", format); 238 } 239 } else { 240 //TRACE1("Format %d not supported\n", format); 241 } 242 } // for loop 243 snd_pcm_hw_params_free(hwParams); 244 } 245 snd_pcm_format_mask_free(formatMask); 246 } 247 snd_pcm_close(handle); 248 } 249 250 /* ******* ALSA PCM INFO ******************** */ 251 typedef struct tag_AlsaPcmInfo { 252 snd_pcm_t* handle; 253 snd_pcm_hw_params_t* hwParams; 254 snd_pcm_sw_params_t* swParams; 255 int bufferSizeInBytes; 256 int frameSize; // storage size in Bytes 257 int periods; 258 snd_pcm_uframes_t periodSize; 259 #ifdef GET_POSITION_METHOD2 260 // to be used exclusively by getBytePosition! 261 snd_pcm_status_t* positionStatus; 262 #endif 263 } AlsaPcmInfo; 264 265 266 int setStartThresholdNoCommit(AlsaPcmInfo* info, int useThreshold) { 267 int ret; 268 int threshold; 269 270 if (useThreshold) { 271 // start device whenever anything is written to the buffer 272 threshold = 1; 273 } else { 274 // never start the device automatically 275 threshold = 2000000000; /* near UINT_MAX */ 276 } 277 ret = snd_pcm_sw_params_set_start_threshold(info->handle, info->swParams, threshold); 278 if (ret < 0) { 279 ERROR1("Unable to set start threshold mode: %s\n", snd_strerror(ret)); 280 return FALSE; 281 } 282 return TRUE; 283 } 284 285 int setStartThreshold(AlsaPcmInfo* info, int useThreshold) { 286 int ret = 0; 287 288 if (!setStartThresholdNoCommit(info, useThreshold)) { 289 ret = -1; 290 } 291 if (ret == 0) { 292 // commit it 293 ret = snd_pcm_sw_params(info->handle, info->swParams); 294 if (ret < 0) { 295 ERROR1("Unable to set sw params: %s\n", snd_strerror(ret)); 296 } 297 } 298 return (ret == 0)?TRUE:FALSE; 299 } 300 301 302 // returns TRUE if successful 303 int setHWParams(AlsaPcmInfo* info, 304 float sampleRate, 305 int channels, 306 int bufferSizeInFrames, 307 snd_pcm_format_t format) { 308 unsigned int rrate; 309 int ret, dir, periods, periodTime; 310 snd_pcm_uframes_t alsaBufferSizeInFrames = (snd_pcm_uframes_t) bufferSizeInFrames; 311 312 /* choose all parameters */ 313 ret = snd_pcm_hw_params_any(info->handle, info->hwParams); 314 if (ret < 0) { 315 ERROR1("Broken configuration: no configurations available: %s\n", snd_strerror(ret)); 316 return FALSE; 317 } 318 /* set the interleaved read/write format */ 319 ret = snd_pcm_hw_params_set_access(info->handle, info->hwParams, SND_PCM_ACCESS_RW_INTERLEAVED); 320 if (ret < 0) { 321 ERROR1("SND_PCM_ACCESS_RW_INTERLEAVED access type not available: %s\n", snd_strerror(ret)); 322 return FALSE; 323 } 324 /* set the sample format */ 325 ret = snd_pcm_hw_params_set_format(info->handle, info->hwParams, format); 326 if (ret < 0) { 327 ERROR1("Sample format not available: %s\n", snd_strerror(ret)); 328 return FALSE; 329 } 330 /* set the count of channels */ 331 ret = snd_pcm_hw_params_set_channels(info->handle, info->hwParams, channels); 332 if (ret < 0) { 333 ERROR2("Channels count (%d) not available: %s\n", channels, snd_strerror(ret)); 334 return FALSE; 335 } 336 /* set the stream rate */ 337 rrate = (int) (sampleRate + 0.5f); 338 #ifdef ALSA_PCM_NEW_HW_PARAMS_API 339 dir = 0; 340 ret = snd_pcm_hw_params_set_rate_near(info->handle, info->hwParams, &rrate, &dir); 341 #else 342 ret = snd_pcm_hw_params_set_rate_near(info->handle, info->hwParams, rrate, 0); 343 #endif 344 if (ret < 0) { 345 ERROR2("Rate %dHz not available for playback: %s\n", (int) (sampleRate+0.5f), snd_strerror(ret)); 346 return FALSE; 347 } 348 if ((rrate-sampleRate > 2) || (rrate-sampleRate < - 2)) { 349 ERROR2("Rate doesn't match (requested %2.2fHz, got %dHz)\n", sampleRate, rrate); 350 return FALSE; 351 } 352 /* set the buffer time */ 353 #ifdef ALSA_PCM_NEW_HW_PARAMS_API 354 355 ret = snd_pcm_hw_params_set_buffer_size_near(info->handle, info->hwParams, &alsaBufferSizeInFrames); 356 #else 357 ret = snd_pcm_hw_params_set_buffer_size_near(info->handle, info->hwParams, alsaBufferSizeInFrames); 358 #endif 359 if (ret < 0) { 360 ERROR2("Unable to set buffer size to %d frames: %s\n", 361 (int) alsaBufferSizeInFrames, snd_strerror(ret)); 362 return FALSE; 363 } 364 bufferSizeInFrames = (int) alsaBufferSizeInFrames; 365 /* set the period time */ 366 if (bufferSizeInFrames > 1024) { 367 dir = 0; 368 periodTime = DEFAULT_PERIOD_TIME; 369 #ifdef ALSA_PCM_NEW_HW_PARAMS_API 370 ret = snd_pcm_hw_params_set_period_time_near(info->handle, info->hwParams, &periodTime, &dir); 371 #else 372 periodTime = snd_pcm_hw_params_set_period_time_near(info->handle, info->hwParams, periodTime, &dir); 373 ret = periodTime; 374 #endif 375 if (ret < 0) { 376 ERROR2("Unable to set period time to %d: %s\n", DEFAULT_PERIOD_TIME, snd_strerror(ret)); 377 return FALSE; 378 } 379 } else { 380 /* set the period count for very small buffer sizes to 2 */ 381 dir = 0; 382 periods = 2; 383 #ifdef ALSA_PCM_NEW_HW_PARAMS_API 384 ret = snd_pcm_hw_params_set_periods_near(info->handle, info->hwParams, &periods, &dir); 385 #else 386 periods = snd_pcm_hw_params_set_periods_near(info->handle, info->hwParams, periods, &dir); 387 ret = periods; 388 #endif 389 if (ret < 0) { 390 ERROR2("Unable to set period count to %d: %s\n", /*periods*/ 2, snd_strerror(ret)); 391 return FALSE; 392 } 393 } 394 /* write the parameters to device */ 395 ret = snd_pcm_hw_params(info->handle, info->hwParams); 396 if (ret < 0) { 397 ERROR1("Unable to set hw params: %s\n", snd_strerror(ret)); 398 return FALSE; 399 } 400 return TRUE; 401 } 402 403 // returns 1 if successful 404 int setSWParams(AlsaPcmInfo* info) { 405 int ret; 406 407 /* get the current swparams */ 408 ret = snd_pcm_sw_params_current(info->handle, info->swParams); 409 if (ret < 0) { 410 ERROR1("Unable to determine current swparams: %s\n", snd_strerror(ret)); 411 return FALSE; 412 } 413 /* never start the transfer automatically */ 414 if (!setStartThresholdNoCommit(info, FALSE /* don't use threshold */)) { 415 return FALSE; 416 } 417 418 /* allow the transfer when at least period_size samples can be processed */ 419 ret = snd_pcm_sw_params_set_avail_min(info->handle, info->swParams, info->periodSize); 420 if (ret < 0) { 421 ERROR1("Unable to set avail min for playback: %s\n", snd_strerror(ret)); 422 return FALSE; 423 } 424 /* align all transfers to 1 sample */ 425 ret = snd_pcm_sw_params_set_xfer_align(info->handle, info->swParams, 1); 426 if (ret < 0) { 427 ERROR1("Unable to set transfer align: %s\n", snd_strerror(ret)); 428 return FALSE; 429 } 430 /* write the parameters to the playback device */ 431 ret = snd_pcm_sw_params(info->handle, info->swParams); 432 if (ret < 0) { 433 ERROR1("Unable to set sw params: %s\n", snd_strerror(ret)); 434 return FALSE; 435 } 436 return TRUE; 437 } 438 439 static snd_output_t* ALSA_OUTPUT = NULL; 440 441 void* DAUDIO_Open(INT32 mixerIndex, INT32 deviceID, int isSource, 442 int encoding, float sampleRate, int sampleSizeInBits, 443 int frameSize, int channels, 444 int isSigned, int isBigEndian, int bufferSizeInBytes) { 445 snd_pcm_format_mask_t* formatMask; 446 snd_pcm_format_t format; 447 int dir; 448 int ret = 0; 449 AlsaPcmInfo* info = NULL; 450 /* snd_pcm_uframes_t is 64 bit on 64-bit systems */ 451 snd_pcm_uframes_t alsaPeriodSize = 0; 452 snd_pcm_uframes_t alsaBufferSizeInFrames = 0; 453 454 455 TRACE0("> DAUDIO_Open\n"); 456 #ifdef USE_TRACE 457 // for using ALSA debug dump methods 458 if (ALSA_OUTPUT == NULL) { 459 snd_output_stdio_attach(&ALSA_OUTPUT, stdout, 0); 460 } 461 #endif 462 if (channels <= 0) { 463 ERROR1("ERROR: Invalid number of channels=%d!\n", channels); 464 return NULL; 465 } 466 info = (AlsaPcmInfo*) malloc(sizeof(AlsaPcmInfo)); 467 if (!info) { 468 ERROR0("Out of memory\n"); 469 return NULL; 470 } 471 memset(info, 0, sizeof(AlsaPcmInfo)); 472 473 ret = openPCMfromDeviceID(deviceID, &(info->handle), isSource, FALSE /* do open device*/); 474 if (ret == 0) { 475 // set to blocking mode 476 snd_pcm_nonblock(info->handle, 0); 477 ret = snd_pcm_hw_params_malloc(&(info->hwParams)); 478 if (ret != 0) { 479 ERROR1(" snd_pcm_hw_params_malloc returned error %d\n", ret); 480 } else { 481 ret = -1; 482 if (getAlsaFormatFromFormat(&format, frameSize / channels, sampleSizeInBits, 483 isSigned, isBigEndian, encoding)) { 484 if (setHWParams(info, 485 sampleRate, 486 channels, 487 bufferSizeInBytes / frameSize, 488 format)) { 489 info->frameSize = frameSize; 490 #ifdef ALSA_PCM_NEW_HW_PARAMS_API 491 ret = snd_pcm_hw_params_get_period_size(info->hwParams, &alsaPeriodSize, &dir); 492 info->periodSize = (int) alsaPeriodSize; 493 if (ret < 0) { 494 ERROR1("ERROR: snd_pcm_hw_params_get_period: %s\n", snd_strerror(ret)); 495 } 496 snd_pcm_hw_params_get_periods(info->hwParams, &(info->periods), &dir); 497 snd_pcm_hw_params_get_buffer_size(info->hwParams, &alsaBufferSizeInFrames); 498 info->bufferSizeInBytes = (int) alsaBufferSizeInFrames * frameSize; 499 #else 500 info->periodSize = snd_pcm_hw_params_get_period_size(info->hwParams, &dir); 501 info->periods = snd_pcm_hw_params_get_periods(info->hwParams, &dir); 502 info->bufferSizeInBytes = snd_pcm_hw_params_get_buffer_size(info->hwParams) * frameSize; 503 ret = 0; 504 #endif 505 TRACE3(" DAUDIO_Open: period size = %d frames, periods = %d. Buffer size: %d bytes.\n", 506 (int) info->periodSize, info->periods, info->bufferSizeInBytes); 507 } 508 } 509 } 510 if (ret == 0) { 511 // set software parameters 512 ret = snd_pcm_sw_params_malloc(&(info->swParams)); 513 if (ret != 0) { 514 ERROR1("snd_pcm_hw_params_malloc returned error %d\n", ret); 515 } else { 516 if (!setSWParams(info)) { 517 ret = -1; 518 } 519 } 520 } 521 if (ret == 0) { 522 // prepare device 523 ret = snd_pcm_prepare(info->handle); 524 if (ret < 0) { 525 ERROR1("ERROR: snd_pcm_prepare: %s\n", snd_strerror(ret)); 526 } 527 } 528 529 #ifdef GET_POSITION_METHOD2 530 if (ret == 0) { 531 ret = snd_pcm_status_malloc(&(info->positionStatus)); 532 if (ret != 0) { 533 ERROR1("ERROR in snd_pcm_status_malloc: %s\n", snd_strerror(ret)); 534 } 535 } 536 #endif 537 } 538 if (ret != 0) { 539 DAUDIO_Close((void*) info, isSource); 540 info = NULL; 541 } else { 542 // set to non-blocking mode 543 snd_pcm_nonblock(info->handle, 1); 544 TRACE1("< DAUDIO_Open: Opened device successfully. Handle=%p\n", 545 (void*) info->handle); 546 } 547 return (void*) info; 548 } 549 550 #ifdef USE_TRACE 551 void printState(snd_pcm_state_t state) { 552 if (state == SND_PCM_STATE_OPEN) { 553 TRACE0("State: SND_PCM_STATE_OPEN\n"); 554 } 555 else if (state == SND_PCM_STATE_SETUP) { 556 TRACE0("State: SND_PCM_STATE_SETUP\n"); 557 } 558 else if (state == SND_PCM_STATE_PREPARED) { 559 TRACE0("State: SND_PCM_STATE_PREPARED\n"); 560 } 561 else if (state == SND_PCM_STATE_RUNNING) { 562 TRACE0("State: SND_PCM_STATE_RUNNING\n"); 563 } 564 else if (state == SND_PCM_STATE_XRUN) { 565 TRACE0("State: SND_PCM_STATE_XRUN\n"); 566 } 567 else if (state == SND_PCM_STATE_DRAINING) { 568 TRACE0("State: SND_PCM_STATE_DRAINING\n"); 569 } 570 else if (state == SND_PCM_STATE_PAUSED) { 571 TRACE0("State: SND_PCM_STATE_PAUSED\n"); 572 } 573 else if (state == SND_PCM_STATE_SUSPENDED) { 574 TRACE0("State: SND_PCM_STATE_SUSPENDED\n"); 575 } 576 } 577 #endif 578 579 int DAUDIO_Start(void* id, int isSource) { 580 AlsaPcmInfo* info = (AlsaPcmInfo*) id; 581 int ret; 582 snd_pcm_state_t state; 583 584 TRACE0("> DAUDIO_Start\n"); 585 // set to blocking mode 586 snd_pcm_nonblock(info->handle, 0); 587 // set start mode so that it always starts as soon as data is there 588 setStartThreshold(info, TRUE /* use threshold */); 589 state = snd_pcm_state(info->handle); 590 if (state == SND_PCM_STATE_PAUSED) { 591 // in case it was stopped previously 592 TRACE0(" Un-pausing...\n"); 593 ret = snd_pcm_pause(info->handle, FALSE); 594 if (ret != 0) { 595 ERROR2(" NOTE: error in snd_pcm_pause:%d: %s\n", ret, snd_strerror(ret)); 596 } 597 } 598 if (state == SND_PCM_STATE_SUSPENDED) { 599 TRACE0(" Resuming...\n"); 600 ret = snd_pcm_resume(info->handle); 601 if (ret < 0) { 602 if ((ret != -EAGAIN) && (ret != -ENOSYS)) { 603 ERROR2(" ERROR: error in snd_pcm_resume:%d: %s\n", ret, snd_strerror(ret)); 604 } 605 } 606 } 607 if (state == SND_PCM_STATE_SETUP) { 608 TRACE0("need to call prepare again...\n"); 609 // prepare device 610 ret = snd_pcm_prepare(info->handle); 611 if (ret < 0) { 612 ERROR1("ERROR: snd_pcm_prepare: %s\n", snd_strerror(ret)); 613 } 614 } 615 // in case there is still data in the buffers 616 ret = snd_pcm_start(info->handle); 617 if (ret != 0) { 618 if (ret != -EPIPE) { 619 ERROR2(" NOTE: error in snd_pcm_start: %d: %s\n", ret, snd_strerror(ret)); 620 } 621 } 622 // set to non-blocking mode 623 ret = snd_pcm_nonblock(info->handle, 1); 624 if (ret != 0) { 625 ERROR1(" ERROR in snd_pcm_nonblock: %s\n", snd_strerror(ret)); 626 } 627 state = snd_pcm_state(info->handle); 628 #ifdef USE_TRACE 629 printState(state); 630 #endif 631 ret = (state == SND_PCM_STATE_PREPARED) 632 || (state == SND_PCM_STATE_RUNNING) 633 || (state == SND_PCM_STATE_XRUN) 634 || (state == SND_PCM_STATE_SUSPENDED); 635 TRACE1("< DAUDIO_Start %s\n", ret?"success":"error"); 636 return ret?TRUE:FALSE; 637 } 638 639 int DAUDIO_Stop(void* id, int isSource) { 640 AlsaPcmInfo* info = (AlsaPcmInfo*) id; 641 int ret; 642 643 TRACE0("> DAUDIO_Stop\n"); 644 // set to blocking mode 645 snd_pcm_nonblock(info->handle, 0); 646 setStartThreshold(info, FALSE /* don't use threshold */); // device will not start after buffer xrun 647 ret = snd_pcm_pause(info->handle, 1); 648 // set to non-blocking mode 649 snd_pcm_nonblock(info->handle, 1); 650 if (ret != 0) { 651 ERROR1("ERROR in snd_pcm_pause: %s\n", snd_strerror(ret)); 652 return FALSE; 653 } 654 TRACE0("< DAUDIO_Stop success\n"); 655 return TRUE; 656 } 657 658 void DAUDIO_Close(void* id, int isSource) { 659 AlsaPcmInfo* info = (AlsaPcmInfo*) id; 660 661 TRACE0("DAUDIO_Close\n"); 662 if (info != NULL) { 663 if (info->handle != NULL) { 664 snd_pcm_close(info->handle); 665 } 666 if (info->hwParams) { 667 snd_pcm_hw_params_free(info->hwParams); 668 } 669 if (info->swParams) { 670 snd_pcm_sw_params_free(info->swParams); 671 } 672 #ifdef GET_POSITION_METHOD2 673 if (info->positionStatus) { 674 snd_pcm_status_free(info->positionStatus); 675 } 676 #endif 677 free(info); 678 } 679 } 680 681 /* 682 * Underrun and suspend recovery 683 * returns 684 * 0: exit native and return 0 685 * 1: try again to write/read 686 * -1: error - exit native with return value -1 687 */ 688 int xrun_recovery(AlsaPcmInfo* info, int err) { 689 int ret; 690 691 if (err == -EPIPE) { /* underrun / overflow */ 692 TRACE0("xrun_recovery: underrun/overflow.\n"); 693 ret = snd_pcm_prepare(info->handle); 694 if (ret < 0) { 695 ERROR1("Can't recover from underrun/overflow, prepare failed: %s\n", snd_strerror(ret)); 696 return -1; 697 } 698 return 1; 699 } 700 else if (err == -ESTRPIPE) { 701 TRACE0("xrun_recovery: suspended.\n"); 702 ret = snd_pcm_resume(info->handle); 703 if (ret < 0) { 704 if (ret == -EAGAIN) { 705 return 0; /* wait until the suspend flag is released */ 706 } 707 return -1; 708 } 709 ret = snd_pcm_prepare(info->handle); 710 if (ret < 0) { 711 ERROR1("Can't recover from underrun/overflow, prepare failed: %s\n", snd_strerror(ret)); 712 return -1; 713 } 714 return 1; 715 } 716 else if (err == -EAGAIN) { 717 TRACE0("xrun_recovery: EAGAIN try again flag.\n"); 718 return 0; 719 } 720 TRACE2("xrun_recovery: unexpected error %d: %s\n", err, snd_strerror(err)); 721 return -1; 722 } 723 724 // returns -1 on error 725 int DAUDIO_Write(void* id, char* data, int byteSize) { 726 AlsaPcmInfo* info = (AlsaPcmInfo*) id; 727 int ret, count; 728 snd_pcm_sframes_t frameSize, writtenFrames; 729 730 TRACE1("> DAUDIO_Write %d bytes\n", byteSize); 731 732 /* sanity */ 733 if (byteSize <= 0 || info->frameSize <= 0) { 734 ERROR2(" DAUDIO_Write: byteSize=%d, frameSize=%d!\n", 735 (int) byteSize, (int) info->frameSize); 736 TRACE0("< DAUDIO_Write returning -1\n"); 737 return -1; 738 } 739 count = 2; // maximum number of trials to recover from underrun 740 //frameSize = snd_pcm_bytes_to_frames(info->handle, byteSize); 741 frameSize = (snd_pcm_sframes_t) (byteSize / info->frameSize); 742 do { 743 writtenFrames = snd_pcm_writei(info->handle, (const void*) data, (snd_pcm_uframes_t) frameSize); 744 745 if (writtenFrames < 0) { 746 ret = xrun_recovery(info, (int) writtenFrames); 747 if (ret <= 0) { 748 TRACE1("DAUDIO_Write: xrun recovery returned %d -> return.\n", ret); 749 return ret; 750 } 751 if (count-- <= 0) { 752 ERROR0("DAUDIO_Write: too many attempts to recover from xrun/suspend\n"); 753 return -1; 754 } 755 } else { 756 break; 757 } 758 } while (TRUE); 759 //ret = snd_pcm_frames_to_bytes(info->handle, writtenFrames); 760 ret = (int) (writtenFrames * info->frameSize); 761 TRACE1("< DAUDIO_Write: returning %d bytes.\n", ret); 762 return ret; 763 } 764 765 // returns -1 on error 766 int DAUDIO_Read(void* id, char* data, int byteSize) { 767 AlsaPcmInfo* info = (AlsaPcmInfo*) id; 768 int ret, count; 769 snd_pcm_sframes_t frameSize, readFrames; 770 771 TRACE1("> DAUDIO_Read %d bytes\n", byteSize); 772 /*TRACE3(" info=%p, data=%p, byteSize=%d\n", 773 (void*) info, (void*) data, (int) byteSize); 774 TRACE2(" info->frameSize=%d, info->handle=%p\n", 775 (int) info->frameSize, (void*) info->handle); 776 */ 777 /* sanity */ 778 if (byteSize <= 0 || info->frameSize <= 0) { 779 ERROR2(" DAUDIO_Read: byteSize=%d, frameSize=%d!\n", 780 (int) byteSize, (int) info->frameSize); 781 TRACE0("< DAUDIO_Read returning -1\n"); 782 return -1; 783 } 784 count = 2; // maximum number of trials to recover from error 785 //frameSize = snd_pcm_bytes_to_frames(info->handle, byteSize); 786 frameSize = (snd_pcm_sframes_t) (byteSize / info->frameSize); 787 do { 788 readFrames = snd_pcm_readi(info->handle, (void*) data, (snd_pcm_uframes_t) frameSize); 789 if (readFrames < 0) { 790 ret = xrun_recovery(info, (int) readFrames); 791 if (ret <= 0) { 792 TRACE1("DAUDIO_Read: xrun recovery returned %d -> return.\n", ret); 793 return ret; 794 } 795 if (count-- <= 0) { 796 ERROR0("DAUDIO_Read: too many attempts to recover from xrun/suspend\n"); 797 return -1; 798 } 799 } else { 800 break; 801 } 802 } while (TRUE); 803 //ret = snd_pcm_frames_to_bytes(info->handle, readFrames); 804 ret = (int) (readFrames * info->frameSize); 805 TRACE1("< DAUDIO_Read: returning %d bytes.\n", ret); 806 return ret; 807 } 808 809 810 int DAUDIO_GetBufferSize(void* id, int isSource) { 811 AlsaPcmInfo* info = (AlsaPcmInfo*) id; 812 813 return info->bufferSizeInBytes; 814 } 815 816 int DAUDIO_StillDraining(void* id, int isSource) { 817 AlsaPcmInfo* info = (AlsaPcmInfo*) id; 818 snd_pcm_state_t state; 819 820 state = snd_pcm_state(info->handle); 821 //printState(state); 822 //TRACE1("Still draining: %s\n", (state != SND_PCM_STATE_XRUN)?"TRUE":"FALSE"); 823 return (state == SND_PCM_STATE_RUNNING)?TRUE:FALSE; 824 } 825 826 827 int DAUDIO_Flush(void* id, int isSource) { 828 AlsaPcmInfo* info = (AlsaPcmInfo*) id; 829 int ret; 830 831 TRACE0("DAUDIO_Flush\n"); 832 ret = snd_pcm_drop(info->handle); 833 if (ret != 0) { 834 ERROR1("ERROR in snd_pcm_drop: %s\n", snd_strerror(ret)); 835 return FALSE; 836 } 837 ret = DAUDIO_Start(id, isSource); 838 return ret; 839 } 840 841 int DAUDIO_GetAvailable(void* id, int isSource) { 842 AlsaPcmInfo* info = (AlsaPcmInfo*) id; 843 snd_pcm_sframes_t availableInFrames; 844 snd_pcm_state_t state; 845 int ret; 846 847 state = snd_pcm_state(info->handle); 848 if (state == SND_PCM_STATE_XRUN) { 849 // if in xrun state then we have the entire buffer available, 850 // not 0 as alsa reports 851 ret = info->bufferSizeInBytes; 852 } else { 853 availableInFrames = snd_pcm_avail_update(info->handle); 854 if (availableInFrames < 0) { 855 ret = 0; 856 } else { 857 //ret = snd_pcm_frames_to_bytes(info->handle, availableInFrames); 858 ret = (int) (availableInFrames * info->frameSize); 859 } 860 } 861 TRACE1("DAUDIO_GetAvailable returns %d bytes\n", ret); 862 return ret; 863 } 864 865 INT64 estimatePositionFromAvail(AlsaPcmInfo* info, int isSource, INT64 javaBytePos, int availInBytes) { 866 // estimate the current position with the buffer size and 867 // the available bytes to read or write in the buffer. 868 // not an elegant solution - bytePos will stop on xruns, 869 // and in race conditions it may jump backwards 870 // Advantage is that it is indeed based on the samples that go through 871 // the system (rather than time-based methods) 872 if (isSource) { 873 // javaBytePos is the position that is reached when the current 874 // buffer is played completely 875 return (INT64) (javaBytePos - info->bufferSizeInBytes + availInBytes); 876 } else { 877 // javaBytePos is the position that was when the current buffer was empty 878 return (INT64) (javaBytePos + availInBytes); 879 } 880 } 881 882 INT64 DAUDIO_GetBytePosition(void* id, int isSource, INT64 javaBytePos) { 883 AlsaPcmInfo* info = (AlsaPcmInfo*) id; 884 int ret; 885 INT64 result = javaBytePos; 886 snd_pcm_state_t state; 887 state = snd_pcm_state(info->handle); 888 889 if (state != SND_PCM_STATE_XRUN) { 890 #ifdef GET_POSITION_METHOD2 891 snd_timestamp_t* ts; 892 snd_pcm_uframes_t framesAvail; 893 894 // note: slight race condition if this is called simultaneously from 2 threads 895 ret = snd_pcm_status(info->handle, info->positionStatus); 896 if (ret != 0) { 897 ERROR1("ERROR in snd_pcm_status: %s\n", snd_strerror(ret)); 898 result = javaBytePos; 899 } else { 900 // calculate from time value, or from available bytes 901 framesAvail = snd_pcm_status_get_avail(info->positionStatus); 902 result = estimatePositionFromAvail(info, isSource, javaBytePos, framesAvail * info->frameSize); 903 } 904 #endif 905 #ifdef GET_POSITION_METHOD3 906 snd_pcm_uframes_t framesAvail; 907 ret = snd_pcm_avail(info->handle, &framesAvail); 908 if (ret != 0) { 909 ERROR1("ERROR in snd_pcm_avail: %s\n", snd_strerror(ret)); 910 result = javaBytePos; 911 } else { 912 result = estimatePositionFromAvail(info, isSource, javaBytePos, framesAvail * info->frameSize); 913 } 914 #endif 915 #ifdef GET_POSITION_METHOD1 916 result = estimatePositionFromAvail(info, isSource, javaBytePos, DAUDIO_GetAvailable(id, isSource)); 917 #endif 918 } 919 //printf("getbyteposition: javaBytePos=%d , return=%d\n", (int) javaBytePos, (int) result); 920 return result; 921 } 922 923 924 925 void DAUDIO_SetBytePosition(void* id, int isSource, INT64 javaBytePos) { 926 /* save to ignore, since GetBytePosition 927 * takes the javaBytePos param into account 928 */ 929 } 930 931 int DAUDIO_RequiresServicing(void* id, int isSource) { 932 // never need servicing on Linux 933 return FALSE; 934 } 935 936 void DAUDIO_Service(void* id, int isSource) { 937 // never need servicing on Linux 938 } 939 940 941 #endif // USE_DAUDIO