1 /*
   2  * Copyright (c) 2002, 2012, 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_BsdOS_ALSA_PCMUtils.h"
  30 #include "PLATFORM_API_BsdOS_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     unsigned 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         if (ret == 0) {
 156             ret = snd_pcm_hw_params_get_channels_min(hwParams, &minChannels);
 157             if (ret != 0) {
 158                 ERROR1("snd_pcm_hw_params_get_channels_min returned error %d\n", ret);
 159             }
 160         }
 161         if (ret == 0) {
 162             ret = snd_pcm_hw_params_get_channels_max(hwParams, &maxChannels);
 163             if (ret != 0) {
 164                 ERROR1("snd_pcm_hw_params_get_channels_max returned error %d\n", ret);
 165             }
 166         }
 167 
 168         // since we queried the hw: device, for many soundcards, it will only
 169         // report the maximum number of channels (which is the only way to talk
 170         // to the hw: device). Since we will, however, open the plughw: device
 171         // when opening the Source/TargetDataLine, we can safely assume that
 172         // also the channels 1..maxChannels are available.
 173 #ifdef ALSA_PCM_USE_PLUGHW
 174         minChannels = 1;
 175 #endif
 176         if (ret == 0) {
 177             // plughw: supports any sample rate
 178             rate = -1;
 179             for (format = 0; format <= SND_PCM_FORMAT_LAST; format++) {
 180                 if (snd_pcm_format_mask_test(formatMask, format)) {
 181                     // format exists
 182                     if (getFormatFromAlsaFormat(format, &origSampleSizeInBytes,
 183                                                 &origSignificantBits,
 184                                                 &isSigned, &isBigEndian, &enc)) {
 185                         // now if we use plughw:, we can use any bit size below the
 186                         // natively supported ones. Some ALSA drivers only support the maximum
 187                         // bit size, so we add any sample rates below the reported one.
 188                         // E.g. this iteration reports support for 16-bit.
 189                         // getBitIndex will return 2, so it will add entries for
 190                         // 16-bit (bitIndex=2) and in the next do-while loop iteration,
 191                         // it will decrease bitIndex and will therefore add 8-bit support.
 192                         bitIndex = getBitIndex(origSampleSizeInBytes, origSignificantBits);
 193                         do {
 194                             if (bitIndex == 0
 195                                 || bitIndex == MAX_BIT_INDEX
 196                                 || !handledBits[bitIndex]) {
 197                                 handledBits[bitIndex] = TRUE;
 198                                 sampleSizeInBytes = getSampleSizeInBytes(bitIndex, origSampleSizeInBytes);
 199                                 significantBits = getSignificantBits(bitIndex, origSignificantBits);
 200                                 if (maxChannels - minChannels > MAXIMUM_LISTED_CHANNELS) {
 201                                     // avoid too many channels explicitly listed
 202                                     // just add -1, min, and max
 203                                     DAUDIO_AddAudioFormat(creator, significantBits,
 204                                                           -1, -1, rate,
 205                                                           enc, isSigned, isBigEndian);
 206                                     DAUDIO_AddAudioFormat(creator, significantBits,
 207                                                           sampleSizeInBytes * minChannels,
 208                                                           minChannels, rate,
 209                                                           enc, isSigned, isBigEndian);
 210                                     DAUDIO_AddAudioFormat(creator, significantBits,
 211                                                           sampleSizeInBytes * maxChannels,
 212                                                           maxChannels, rate,
 213                                                           enc, isSigned, isBigEndian);
 214                                 } else {
 215                                     for (channels = minChannels; channels <= maxChannels; channels++) {
 216                                         DAUDIO_AddAudioFormat(creator, significantBits,
 217                                                               sampleSizeInBytes * channels,
 218                                                               channels, rate,
 219                                                               enc, isSigned, isBigEndian);
 220                                     }
 221                                 }
 222                             }
 223 #ifndef ALSA_PCM_USE_PLUGHW
 224                             // without plugin, do not add fake formats
 225                             break;
 226 #endif
 227                         } while (--bitIndex > 0);
 228                     } else {
 229                         TRACE1("could not get format from alsa for format %d\n", format);
 230                     }
 231                 } else {
 232                     //TRACE1("Format %d not supported\n", format);
 233                 }
 234             } // for loop
 235             snd_pcm_hw_params_free(hwParams);
 236         }
 237         snd_pcm_format_mask_free(formatMask);
 238     }
 239     snd_pcm_close(handle);
 240 }
 241 
 242 /** Workaround for cr 7033899, 7030629:
 243  * dmix plugin doesn't like flush (snd_pcm_drop) when the buffer is empty
 244  * (just opened, underruned or already flushed).
 245  * Sometimes it causes PCM falls to -EBADFD error,
 246  * sometimes causes bufferSize change.
 247  * To prevent unnecessary flushes AlsaPcmInfo::isRunning & isFlushed are used.
 248  */
 249 /* ******* ALSA PCM INFO ******************** */
 250 typedef struct tag_AlsaPcmInfo {
 251     snd_pcm_t* handle;
 252     snd_pcm_hw_params_t* hwParams;
 253     snd_pcm_sw_params_t* swParams;
 254     int bufferSizeInBytes;
 255     int frameSize; // storage size in Bytes
 256     unsigned int periods;
 257     snd_pcm_uframes_t periodSize;
 258     short int isRunning;    // see comment above
 259     short int isFlushed;    // see comment above
 260 #ifdef GET_POSITION_METHOD2
 261     // to be used exclusively by getBytePosition!
 262     snd_pcm_status_t* positionStatus;
 263 #endif
 264 } AlsaPcmInfo;
 265 
 266 
 267 int setStartThresholdNoCommit(AlsaPcmInfo* info, int useThreshold) {
 268     int ret;
 269     int threshold;
 270 
 271     if (useThreshold) {
 272         // start device whenever anything is written to the buffer
 273         threshold = 1;
 274     } else {
 275         // never start the device automatically
 276         threshold = 2000000000; /* near UINT_MAX */
 277     }
 278     ret = snd_pcm_sw_params_set_start_threshold(info->handle, info->swParams, threshold);
 279     if (ret < 0) {
 280         ERROR1("Unable to set start threshold mode: %s\n", snd_strerror(ret));
 281         return FALSE;
 282     }
 283     return TRUE;
 284 }
 285 
 286 int setStartThreshold(AlsaPcmInfo* info, int useThreshold) {
 287     int ret = 0;
 288 
 289     if (!setStartThresholdNoCommit(info, useThreshold)) {
 290         ret = -1;
 291     }
 292     if (ret == 0) {
 293         // commit it
 294         ret = snd_pcm_sw_params(info->handle, info->swParams);
 295         if (ret < 0) {
 296             ERROR1("Unable to set sw params: %s\n", snd_strerror(ret));
 297         }
 298     }
 299     return (ret == 0)?TRUE:FALSE;
 300 }
 301 
 302 
 303 // returns TRUE if successful
 304 int setHWParams(AlsaPcmInfo* info,
 305                 float sampleRate,
 306                 int channels,
 307                 int bufferSizeInFrames,
 308                 snd_pcm_format_t format) {
 309     unsigned int rrate, periodTime, periods;
 310     int ret, dir;
 311     snd_pcm_uframes_t alsaBufferSizeInFrames = (snd_pcm_uframes_t) bufferSizeInFrames;
 312 
 313     /* choose all parameters */
 314     ret = snd_pcm_hw_params_any(info->handle, info->hwParams);
 315     if (ret < 0) {
 316         ERROR1("Broken configuration: no configurations available: %s\n", snd_strerror(ret));
 317         return FALSE;
 318     }
 319     /* set the interleaved read/write format */
 320     ret = snd_pcm_hw_params_set_access(info->handle, info->hwParams, SND_PCM_ACCESS_RW_INTERLEAVED);
 321     if (ret < 0) {
 322         ERROR1("SND_PCM_ACCESS_RW_INTERLEAVED access type not available: %s\n", snd_strerror(ret));
 323         return FALSE;
 324     }
 325     /* set the sample format */
 326     ret = snd_pcm_hw_params_set_format(info->handle, info->hwParams, format);
 327     if (ret < 0) {
 328         ERROR1("Sample format not available: %s\n", snd_strerror(ret));
 329         return FALSE;
 330     }
 331     /* set the count of channels */
 332     ret = snd_pcm_hw_params_set_channels(info->handle, info->hwParams, channels);
 333     if (ret < 0) {
 334         ERROR2("Channels count (%d) not available: %s\n", channels, snd_strerror(ret));
 335         return FALSE;
 336     }
 337     /* set the stream rate */
 338     rrate = (int) (sampleRate + 0.5f);
 339     dir = 0;
 340     ret = snd_pcm_hw_params_set_rate_near(info->handle, info->hwParams, &rrate, &dir);
 341     if (ret < 0) {
 342         ERROR2("Rate %dHz not available for playback: %s\n", (int) (sampleRate+0.5f), snd_strerror(ret));
 343         return FALSE;
 344     }
 345     if ((rrate-sampleRate > 2) || (rrate-sampleRate < - 2)) {
 346         ERROR2("Rate doesn't match (requested %2.2fHz, got %dHz)\n", sampleRate, rrate);
 347         return FALSE;
 348     }
 349     /* set the buffer time */
 350     ret = snd_pcm_hw_params_set_buffer_size_near(info->handle, info->hwParams, &alsaBufferSizeInFrames);
 351     if (ret < 0) {
 352         ERROR2("Unable to set buffer size to %d frames: %s\n",
 353                (int) alsaBufferSizeInFrames, snd_strerror(ret));
 354         return FALSE;
 355     }
 356     bufferSizeInFrames = (int) alsaBufferSizeInFrames;
 357     /* set the period time */
 358     if (bufferSizeInFrames > 1024) {
 359         dir = 0;
 360         periodTime = DEFAULT_PERIOD_TIME;
 361         ret = snd_pcm_hw_params_set_period_time_near(info->handle, info->hwParams, &periodTime, &dir);
 362         if (ret < 0) {
 363             ERROR2("Unable to set period time to %d: %s\n", DEFAULT_PERIOD_TIME, snd_strerror(ret));
 364             return FALSE;
 365         }
 366     } else {
 367         /* set the period count for very small buffer sizes to 2 */
 368         dir = 0;
 369         periods = 2;
 370         ret = snd_pcm_hw_params_set_periods_near(info->handle, info->hwParams, &periods, &dir);
 371         if (ret < 0) {
 372             ERROR2("Unable to set period count to %d: %s\n", /*periods*/ 2, snd_strerror(ret));
 373             return FALSE;
 374         }
 375     }
 376     /* write the parameters to device */
 377     ret = snd_pcm_hw_params(info->handle, info->hwParams);
 378     if (ret < 0) {
 379         ERROR1("Unable to set hw params: %s\n", snd_strerror(ret));
 380         return FALSE;
 381     }
 382     return TRUE;
 383 }
 384 
 385 // returns 1 if successful
 386 int setSWParams(AlsaPcmInfo* info) {
 387     int ret;
 388 
 389     /* get the current swparams */
 390     ret = snd_pcm_sw_params_current(info->handle, info->swParams);
 391     if (ret < 0) {
 392         ERROR1("Unable to determine current swparams: %s\n", snd_strerror(ret));
 393         return FALSE;
 394     }
 395     /* never start the transfer automatically */
 396     if (!setStartThresholdNoCommit(info, FALSE /* don't use threshold */)) {
 397         return FALSE;
 398     }
 399 
 400     /* allow the transfer when at least period_size samples can be processed */
 401     ret = snd_pcm_sw_params_set_avail_min(info->handle, info->swParams, info->periodSize);
 402     if (ret < 0) {
 403         ERROR1("Unable to set avail min for playback: %s\n", snd_strerror(ret));
 404         return FALSE;
 405     }
 406     /* write the parameters to the playback device */
 407     ret = snd_pcm_sw_params(info->handle, info->swParams);
 408     if (ret < 0) {
 409         ERROR1("Unable to set sw params: %s\n", snd_strerror(ret));
 410         return FALSE;
 411     }
 412     return TRUE;
 413 }
 414 
 415 static snd_output_t* ALSA_OUTPUT = NULL;
 416 
 417 void* DAUDIO_Open(INT32 mixerIndex, INT32 deviceID, int isSource,
 418                   int encoding, float sampleRate, int sampleSizeInBits,
 419                   int frameSize, int channels,
 420                   int isSigned, int isBigEndian, int bufferSizeInBytes) {
 421     snd_pcm_format_mask_t* formatMask;
 422     snd_pcm_format_t format;
 423     int dir;
 424     int ret = 0;
 425     AlsaPcmInfo* info = NULL;
 426     /* snd_pcm_uframes_t is 64 bit on 64-bit systems */
 427     snd_pcm_uframes_t alsaBufferSizeInFrames = 0;
 428 
 429 
 430     TRACE0("> DAUDIO_Open\n");
 431 #ifdef USE_TRACE
 432     // for using ALSA debug dump methods
 433     if (ALSA_OUTPUT == NULL) {
 434         snd_output_stdio_attach(&ALSA_OUTPUT, stdout, 0);
 435     }
 436 #endif
 437     if (channels <= 0) {
 438         ERROR1("ERROR: Invalid number of channels=%d!\n", channels);
 439         return NULL;
 440     }
 441     info = (AlsaPcmInfo*) malloc(sizeof(AlsaPcmInfo));
 442     if (!info) {
 443         ERROR0("Out of memory\n");
 444         return NULL;
 445     }
 446     memset(info, 0, sizeof(AlsaPcmInfo));
 447     // initial values are: stopped, flushed
 448     info->isRunning = 0;
 449     info->isFlushed = 1;
 450 
 451     ret = openPCMfromDeviceID(deviceID, &(info->handle), isSource, FALSE /* do open device*/);
 452     if (ret == 0) {
 453         // set to blocking mode
 454         snd_pcm_nonblock(info->handle, 0);
 455         ret = snd_pcm_hw_params_malloc(&(info->hwParams));
 456         if (ret != 0) {
 457             ERROR1("  snd_pcm_hw_params_malloc returned error %d\n", ret);
 458         } else {
 459             ret = -1;
 460             if (getAlsaFormatFromFormat(&format, frameSize / channels, sampleSizeInBits,
 461                                         isSigned, isBigEndian, encoding)) {
 462                 if (setHWParams(info,
 463                                 sampleRate,
 464                                 channels,
 465                                 bufferSizeInBytes / frameSize,
 466                                 format)) {
 467                     info->frameSize = frameSize;
 468                     ret = snd_pcm_hw_params_get_period_size(info->hwParams, &info->periodSize, &dir);
 469                     if (ret < 0) {
 470                         ERROR1("ERROR: snd_pcm_hw_params_get_period: %s\n", snd_strerror(ret));
 471                     }
 472                     snd_pcm_hw_params_get_periods(info->hwParams, &(info->periods), &dir);
 473                     snd_pcm_hw_params_get_buffer_size(info->hwParams, &alsaBufferSizeInFrames);
 474                     info->bufferSizeInBytes = (int) alsaBufferSizeInFrames * frameSize;
 475                     TRACE3("  DAUDIO_Open: period size = %d frames, periods = %d. Buffer size: %d bytes.\n",
 476                            (int) info->periodSize, info->periods, info->bufferSizeInBytes);
 477                 }
 478             }
 479         }
 480         if (ret == 0) {
 481             // set software parameters
 482             ret = snd_pcm_sw_params_malloc(&(info->swParams));
 483             if (ret != 0) {
 484                 ERROR1("snd_pcm_hw_params_malloc returned error %d\n", ret);
 485             } else {
 486                 if (!setSWParams(info)) {
 487                     ret = -1;
 488                 }
 489             }
 490         }
 491         if (ret == 0) {
 492             // prepare device
 493             ret = snd_pcm_prepare(info->handle);
 494             if (ret < 0) {
 495                 ERROR1("ERROR: snd_pcm_prepare: %s\n", snd_strerror(ret));
 496             }
 497         }
 498 
 499 #ifdef GET_POSITION_METHOD2
 500         if (ret == 0) {
 501             ret = snd_pcm_status_malloc(&(info->positionStatus));
 502             if (ret != 0) {
 503                 ERROR1("ERROR in snd_pcm_status_malloc: %s\n", snd_strerror(ret));
 504             }
 505         }
 506 #endif
 507     }
 508     if (ret != 0) {
 509         DAUDIO_Close((void*) info, isSource);
 510         info = NULL;
 511     } else {
 512         // set to non-blocking mode
 513         snd_pcm_nonblock(info->handle, 1);
 514         TRACE1("< DAUDIO_Open: Opened device successfully. Handle=%p\n",
 515                (void*) info->handle);
 516     }
 517     return (void*) info;
 518 }
 519 
 520 #ifdef USE_TRACE
 521 void printState(snd_pcm_state_t state) {
 522     if (state == SND_PCM_STATE_OPEN) {
 523         TRACE0("State: SND_PCM_STATE_OPEN\n");
 524     }
 525     else if (state == SND_PCM_STATE_SETUP) {
 526         TRACE0("State: SND_PCM_STATE_SETUP\n");
 527     }
 528     else if (state == SND_PCM_STATE_PREPARED) {
 529         TRACE0("State: SND_PCM_STATE_PREPARED\n");
 530     }
 531     else if (state == SND_PCM_STATE_RUNNING) {
 532         TRACE0("State: SND_PCM_STATE_RUNNING\n");
 533     }
 534     else if (state == SND_PCM_STATE_XRUN) {
 535         TRACE0("State: SND_PCM_STATE_XRUN\n");
 536     }
 537     else if (state == SND_PCM_STATE_DRAINING) {
 538         TRACE0("State: SND_PCM_STATE_DRAINING\n");
 539     }
 540     else if (state == SND_PCM_STATE_PAUSED) {
 541         TRACE0("State: SND_PCM_STATE_PAUSED\n");
 542     }
 543     else if (state == SND_PCM_STATE_SUSPENDED) {
 544         TRACE0("State: SND_PCM_STATE_SUSPENDED\n");
 545     }
 546 }
 547 #endif
 548 
 549 int DAUDIO_Start(void* id, int isSource) {
 550     AlsaPcmInfo* info = (AlsaPcmInfo*) id;
 551     int ret;
 552     snd_pcm_state_t state;
 553 
 554     TRACE0("> DAUDIO_Start\n");
 555     // set to blocking mode
 556     snd_pcm_nonblock(info->handle, 0);
 557     // set start mode so that it always starts as soon as data is there
 558     setStartThreshold(info, TRUE /* use threshold */);
 559     state = snd_pcm_state(info->handle);
 560     if (state == SND_PCM_STATE_PAUSED) {
 561         // in case it was stopped previously
 562         TRACE0("  Un-pausing...\n");
 563         ret = snd_pcm_pause(info->handle, FALSE);
 564         if (ret != 0) {
 565             ERROR2("  NOTE: error in snd_pcm_pause:%d: %s\n", ret, snd_strerror(ret));
 566         }
 567     }
 568     if (state == SND_PCM_STATE_SUSPENDED) {
 569         TRACE0("  Resuming...\n");
 570         ret = snd_pcm_resume(info->handle);
 571         if (ret < 0) {
 572             if ((ret != -EAGAIN) && (ret != -ENOSYS)) {
 573                 ERROR2("  ERROR: error in snd_pcm_resume:%d: %s\n", ret, snd_strerror(ret));
 574             }
 575         }
 576     }
 577     if (state == SND_PCM_STATE_SETUP) {
 578         TRACE0("need to call prepare again...\n");
 579         // prepare device
 580         ret = snd_pcm_prepare(info->handle);
 581         if (ret < 0) {
 582             ERROR1("ERROR: snd_pcm_prepare: %s\n", snd_strerror(ret));
 583         }
 584     }
 585     // in case there is still data in the buffers
 586     ret = snd_pcm_start(info->handle);
 587     if (ret != 0) {
 588         if (ret != -EPIPE) {
 589             ERROR2("  NOTE: error in snd_pcm_start: %d: %s\n", ret, snd_strerror(ret));
 590         }
 591     }
 592     // set to non-blocking mode
 593     ret = snd_pcm_nonblock(info->handle, 1);
 594     if (ret != 0) {
 595         ERROR1("  ERROR in snd_pcm_nonblock: %s\n", snd_strerror(ret));
 596     }
 597     state = snd_pcm_state(info->handle);
 598 #ifdef USE_TRACE
 599     printState(state);
 600 #endif
 601     ret = (state == SND_PCM_STATE_PREPARED)
 602         || (state == SND_PCM_STATE_RUNNING)
 603         || (state == SND_PCM_STATE_XRUN)
 604         || (state == SND_PCM_STATE_SUSPENDED);
 605     if (ret) {
 606         info->isRunning = 1;
 607         // source line should keep isFlushed value until Write() is called;
 608         // for target data line reset it right now.
 609         if (!isSource) {
 610             info->isFlushed = 0;
 611         }
 612     }
 613     TRACE1("< DAUDIO_Start %s\n", ret?"success":"error");
 614     return ret?TRUE:FALSE;
 615 }
 616 
 617 int DAUDIO_Stop(void* id, int isSource) {
 618     AlsaPcmInfo* info = (AlsaPcmInfo*) id;
 619     int ret;
 620 
 621     TRACE0("> DAUDIO_Stop\n");
 622     // set to blocking mode
 623     snd_pcm_nonblock(info->handle, 0);
 624     setStartThreshold(info, FALSE /* don't use threshold */); // device will not start after buffer xrun
 625     ret = snd_pcm_pause(info->handle, 1);
 626     // set to non-blocking mode
 627     snd_pcm_nonblock(info->handle, 1);
 628     if (ret != 0) {
 629         ERROR1("ERROR in snd_pcm_pause: %s\n", snd_strerror(ret));
 630         return FALSE;
 631     }
 632     info->isRunning = 0;
 633     TRACE0("< DAUDIO_Stop success\n");
 634     return TRUE;
 635 }
 636 
 637 void DAUDIO_Close(void* id, int isSource) {
 638     AlsaPcmInfo* info = (AlsaPcmInfo*) id;
 639 
 640     TRACE0("DAUDIO_Close\n");
 641     if (info != NULL) {
 642         if (info->handle != NULL) {
 643             snd_pcm_close(info->handle);
 644         }
 645         if (info->hwParams) {
 646             snd_pcm_hw_params_free(info->hwParams);
 647         }
 648         if (info->swParams) {
 649             snd_pcm_sw_params_free(info->swParams);
 650         }
 651 #ifdef GET_POSITION_METHOD2
 652         if (info->positionStatus) {
 653             snd_pcm_status_free(info->positionStatus);
 654         }
 655 #endif
 656         free(info);
 657     }
 658 }
 659 
 660 /*
 661  * Underrun and suspend recovery
 662  * returns
 663  * 0:  exit native and return 0
 664  * 1:  try again to write/read
 665  * -1: error - exit native with return value -1
 666  */
 667 int xrun_recovery(AlsaPcmInfo* info, int err) {
 668     int ret;
 669 
 670     if (err == -EPIPE) {    /* underrun / overflow */
 671         TRACE0("xrun_recovery: underrun/overflow.\n");
 672         ret = snd_pcm_prepare(info->handle);
 673         if (ret < 0) {
 674             ERROR1("Can't recover from underrun/overflow, prepare failed: %s\n", snd_strerror(ret));
 675             return -1;
 676         }
 677         return 1;
 678     } else if (err == -ESTRPIPE) {
 679         TRACE0("xrun_recovery: suspended.\n");
 680         ret = snd_pcm_resume(info->handle);
 681         if (ret < 0) {
 682             if (ret == -EAGAIN) {
 683                 return 0; /* wait until the suspend flag is released */
 684             }
 685             return -1;
 686         }
 687         ret = snd_pcm_prepare(info->handle);
 688         if (ret < 0) {
 689             ERROR1("Can't recover from underrun/overflow, prepare failed: %s\n", snd_strerror(ret));
 690             return -1;
 691         }
 692         return 1;
 693     } else if (err == -EAGAIN) {
 694         TRACE0("xrun_recovery: EAGAIN try again flag.\n");
 695         return 0;
 696     }
 697 
 698     TRACE2("xrun_recovery: unexpected error %d: %s\n", err, snd_strerror(err));
 699     return -1;
 700 }
 701 
 702 // returns -1 on error
 703 int DAUDIO_Write(void* id, char* data, int byteSize) {
 704     AlsaPcmInfo* info = (AlsaPcmInfo*) id;
 705     int ret, count;
 706     snd_pcm_sframes_t frameSize, writtenFrames;
 707 
 708     TRACE1("> DAUDIO_Write %d bytes\n", byteSize);
 709 
 710     /* sanity */
 711     if (byteSize <= 0 || info->frameSize <= 0) {
 712         ERROR2(" DAUDIO_Write: byteSize=%d, frameSize=%d!\n",
 713                (int) byteSize, (int) info->frameSize);
 714         TRACE0("< DAUDIO_Write returning -1\n");
 715         return -1;
 716     }
 717 
 718     count = 2; // maximum number of trials to recover from underrun
 719     //frameSize = snd_pcm_bytes_to_frames(info->handle, byteSize);
 720     frameSize = (snd_pcm_sframes_t) (byteSize / info->frameSize);
 721     do {
 722         writtenFrames = snd_pcm_writei(info->handle, (const void*) data, (snd_pcm_uframes_t) frameSize);
 723 
 724         if (writtenFrames < 0) {
 725             ret = xrun_recovery(info, (int) writtenFrames);
 726             if (ret <= 0) {
 727                 TRACE1("DAUDIO_Write: xrun recovery returned %d -> return.\n", ret);
 728                 return ret;
 729             }
 730             if (count-- <= 0) {
 731                 ERROR0("DAUDIO_Write: too many attempts to recover from xrun/suspend\n");
 732                 return -1;
 733             }
 734         } else {
 735             break;
 736         }
 737     } while (TRUE);
 738     //ret =  snd_pcm_frames_to_bytes(info->handle, writtenFrames);
 739 
 740     if (writtenFrames > 0) {
 741         // reset "flushed" flag
 742         info->isFlushed = 0;
 743     }
 744 
 745     ret =  (int) (writtenFrames * info->frameSize);
 746     TRACE1("< DAUDIO_Write: returning %d bytes.\n", ret);
 747     return ret;
 748 }
 749 
 750 // returns -1 on error
 751 int DAUDIO_Read(void* id, char* data, int byteSize) {
 752     AlsaPcmInfo* info = (AlsaPcmInfo*) id;
 753     int ret, count;
 754     snd_pcm_sframes_t frameSize, readFrames;
 755 
 756     TRACE1("> DAUDIO_Read %d bytes\n", byteSize);
 757     /*TRACE3("  info=%p, data=%p, byteSize=%d\n",
 758       (void*) info, (void*) data, (int) byteSize);
 759       TRACE2("  info->frameSize=%d, info->handle=%p\n",
 760       (int) info->frameSize, (void*) info->handle);
 761     */
 762     /* sanity */
 763     if (byteSize <= 0 || info->frameSize <= 0) {
 764         ERROR2(" DAUDIO_Read: byteSize=%d, frameSize=%d!\n",
 765                (int) byteSize, (int) info->frameSize);
 766         TRACE0("< DAUDIO_Read returning -1\n");
 767         return -1;
 768     }
 769     if (!info->isRunning && info->isFlushed) {
 770         // PCM has nothing to read
 771         return 0;
 772     }
 773 
 774     count = 2; // maximum number of trials to recover from error
 775     //frameSize = snd_pcm_bytes_to_frames(info->handle, byteSize);
 776     frameSize = (snd_pcm_sframes_t) (byteSize / info->frameSize);
 777     do {
 778         readFrames = snd_pcm_readi(info->handle, (void*) data, (snd_pcm_uframes_t) frameSize);
 779         if (readFrames < 0) {
 780             ret = xrun_recovery(info, (int) readFrames);
 781             if (ret <= 0) {
 782                 TRACE1("DAUDIO_Read: xrun recovery returned %d -> return.\n", ret);
 783                 return ret;
 784             }
 785             if (count-- <= 0) {
 786                 ERROR0("DAUDIO_Read: too many attempts to recover from xrun/suspend\n");
 787                 return -1;
 788             }
 789         } else {
 790             break;
 791         }
 792     } while (TRUE);
 793     //ret =  snd_pcm_frames_to_bytes(info->handle, readFrames);
 794     ret =  (int) (readFrames * info->frameSize);
 795     TRACE1("< DAUDIO_Read: returning %d bytes.\n", ret);
 796     return ret;
 797 }
 798 
 799 
 800 int DAUDIO_GetBufferSize(void* id, int isSource) {
 801     AlsaPcmInfo* info = (AlsaPcmInfo*) id;
 802 
 803     return info->bufferSizeInBytes;
 804 }
 805 
 806 int DAUDIO_StillDraining(void* id, int isSource) {
 807     AlsaPcmInfo* info = (AlsaPcmInfo*) id;
 808     snd_pcm_state_t state;
 809 
 810     state = snd_pcm_state(info->handle);
 811     //printState(state);
 812     //TRACE1("Still draining: %s\n", (state != SND_PCM_STATE_XRUN)?"TRUE":"FALSE");
 813     return (state == SND_PCM_STATE_RUNNING)?TRUE:FALSE;
 814 }
 815 
 816 
 817 int DAUDIO_Flush(void* id, int isSource) {
 818     AlsaPcmInfo* info = (AlsaPcmInfo*) id;
 819     int ret;
 820 
 821     TRACE0("DAUDIO_Flush\n");
 822 
 823     if (info->isFlushed) {
 824         // nothing to drop
 825         return 1;
 826     }
 827 
 828     ret = snd_pcm_drop(info->handle);
 829     if (ret != 0) {
 830         ERROR1("ERROR in snd_pcm_drop: %s\n", snd_strerror(ret));
 831         return FALSE;
 832     }
 833 
 834     info->isFlushed = 1;
 835     if (info->isRunning) {
 836         ret = DAUDIO_Start(id, isSource);
 837     }
 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 (info->isFlushed || 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 (!info->isFlushed && 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 Bsd
 933     return FALSE;
 934 }
 935 
 936 void DAUDIO_Service(void* id, int isSource) {
 937     // never need servicing on Bsd
 938 }
 939 
 940 
 941 #endif // USE_DAUDIO