src/java.desktop/unix/native/libjsound/PLATFORM_API_BsdOS_ALSA_PCM.c
changeset 49311 67b897123581
parent 49310 edbc57573a1c
parent 49290 07779973cbe2
child 49312 f4188d890101
--- a/src/java.desktop/unix/native/libjsound/PLATFORM_API_BsdOS_ALSA_PCM.c	Thu Mar 22 14:10:30 2018 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,941 +0,0 @@
-/*
- * Copyright (c) 2002, 2012, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-#define USE_ERROR
-#define USE_TRACE
-
-#include "PLATFORM_API_BsdOS_ALSA_PCMUtils.h"
-#include "PLATFORM_API_BsdOS_ALSA_CommonUtils.h"
-#include "DirectAudio.h"
-
-#if USE_DAUDIO == TRUE
-
-// GetPosition method 1: based on how many bytes are passed to the kernel driver
-//                       + does not need much processor resources
-//                       - not very exact, "jumps"
-// GetPosition method 2: ask kernel about actual position of playback.
-//                       - very exact
-//                       - switch to kernel layer for each call
-// GetPosition method 3: use snd_pcm_avail() call - not yet in official ALSA
-// quick tests on a Pentium 200MMX showed max. 1.5% processor usage
-// for playing back a CD-quality file and printing 20x per second a line
-// on the console with the current time. So I guess performance is not such a
-// factor here.
-//#define GET_POSITION_METHOD1
-#define GET_POSITION_METHOD2
-
-
-// The default time for a period in microseconds.
-// For very small buffers, only 2 periods are used.
-#define DEFAULT_PERIOD_TIME 20000 /* 20ms */
-
-///// implemented functions of DirectAudio.h
-
-INT32 DAUDIO_GetDirectAudioDeviceCount() {
-    return (INT32) getAudioDeviceCount();
-}
-
-
-INT32 DAUDIO_GetDirectAudioDeviceDescription(INT32 mixerIndex, DirectAudioDeviceDescription* description) {
-    ALSA_AudioDeviceDescription adesc;
-
-    adesc.index = (int) mixerIndex;
-    adesc.strLen = DAUDIO_STRING_LENGTH;
-
-    adesc.maxSimultaneousLines = (int*) (&(description->maxSimulLines));
-    adesc.deviceID = &(description->deviceID);
-    adesc.name = description->name;
-    adesc.vendor = description->vendor;
-    adesc.description = description->description;
-    adesc.version = description->version;
-
-    return getAudioDeviceDescriptionByIndex(&adesc);
-}
-
-#define MAX_BIT_INDEX 6
-// returns
-// 6: for anything above 24-bit
-// 5: for 4 bytes sample size, 24-bit
-// 4: for 3 bytes sample size, 24-bit
-// 3: for 3 bytes sample size, 20-bit
-// 2: for 2 bytes sample size, 16-bit
-// 1: for 1 byte sample size, 8-bit
-// 0: for anything else
-int getBitIndex(int sampleSizeInBytes, int significantBits) {
-    if (significantBits > 24) return 6;
-    if (sampleSizeInBytes == 4 && significantBits == 24) return 5;
-    if (sampleSizeInBytes == 3) {
-        if (significantBits == 24) return 4;
-        if (significantBits == 20) return 3;
-    }
-    if (sampleSizeInBytes == 2 && significantBits == 16) return 2;
-    if (sampleSizeInBytes == 1 && significantBits == 8) return 1;
-    return 0;
-}
-
-int getSampleSizeInBytes(int bitIndex, int sampleSizeInBytes) {
-    switch(bitIndex) {
-    case 1: return 1;
-    case 2: return 2;
-    case 3: /* fall through */
-    case 4: return 3;
-    case 5: return 4;
-    }
-    return sampleSizeInBytes;
-}
-
-int getSignificantBits(int bitIndex, int significantBits) {
-    switch(bitIndex) {
-    case 1: return 8;
-    case 2: return 16;
-    case 3: return 20;
-    case 4: /* fall through */
-    case 5: return 24;
-    }
-    return significantBits;
-}
-
-void DAUDIO_GetFormats(INT32 mixerIndex, INT32 deviceID, int isSource, void* creator) {
-    snd_pcm_t* handle;
-    snd_pcm_format_mask_t* formatMask;
-    snd_pcm_format_t format;
-    snd_pcm_hw_params_t* hwParams;
-    int handledBits[MAX_BIT_INDEX+1];
-
-    int ret;
-    int sampleSizeInBytes, significantBits, isSigned, isBigEndian, enc;
-    int origSampleSizeInBytes, origSignificantBits;
-    unsigned int channels, minChannels, maxChannels;
-    int rate, bitIndex;
-
-    for (bitIndex = 0; bitIndex <= MAX_BIT_INDEX; bitIndex++) handledBits[bitIndex] = FALSE;
-    if (openPCMfromDeviceID(deviceID, &handle, isSource, TRUE /*query hardware*/) < 0) {
-        return;
-    }
-    ret = snd_pcm_format_mask_malloc(&formatMask);
-    if (ret != 0) {
-        ERROR1("snd_pcm_format_mask_malloc returned error %d\n", ret);
-    } else {
-        ret = snd_pcm_hw_params_malloc(&hwParams);
-        if (ret != 0) {
-            ERROR1("snd_pcm_hw_params_malloc returned error %d\n", ret);
-        } else {
-            ret = snd_pcm_hw_params_any(handle, hwParams);
-            /* snd_pcm_hw_params_any can return a positive value on success too */
-            if (ret < 0) {
-                 ERROR1("snd_pcm_hw_params_any returned error %d\n", ret);
-            } else {
-                /* for the logic following this code, set ret to 0 to indicate success */
-                ret = 0;
-            }
-        }
-        snd_pcm_hw_params_get_format_mask(hwParams, formatMask);
-        if (ret == 0) {
-            ret = snd_pcm_hw_params_get_channels_min(hwParams, &minChannels);
-            if (ret != 0) {
-                ERROR1("snd_pcm_hw_params_get_channels_min returned error %d\n", ret);
-            }
-        }
-        if (ret == 0) {
-            ret = snd_pcm_hw_params_get_channels_max(hwParams, &maxChannels);
-            if (ret != 0) {
-                ERROR1("snd_pcm_hw_params_get_channels_max returned error %d\n", ret);
-            }
-        }
-
-        // since we queried the hw: device, for many soundcards, it will only
-        // report the maximum number of channels (which is the only way to talk
-        // to the hw: device). Since we will, however, open the plughw: device
-        // when opening the Source/TargetDataLine, we can safely assume that
-        // also the channels 1..maxChannels are available.
-#ifdef ALSA_PCM_USE_PLUGHW
-        minChannels = 1;
-#endif
-        if (ret == 0) {
-            // plughw: supports any sample rate
-            rate = -1;
-            for (format = 0; format <= SND_PCM_FORMAT_LAST; format++) {
-                if (snd_pcm_format_mask_test(formatMask, format)) {
-                    // format exists
-                    if (getFormatFromAlsaFormat(format, &origSampleSizeInBytes,
-                                                &origSignificantBits,
-                                                &isSigned, &isBigEndian, &enc)) {
-                        // now if we use plughw:, we can use any bit size below the
-                        // natively supported ones. Some ALSA drivers only support the maximum
-                        // bit size, so we add any sample rates below the reported one.
-                        // E.g. this iteration reports support for 16-bit.
-                        // getBitIndex will return 2, so it will add entries for
-                        // 16-bit (bitIndex=2) and in the next do-while loop iteration,
-                        // it will decrease bitIndex and will therefore add 8-bit support.
-                        bitIndex = getBitIndex(origSampleSizeInBytes, origSignificantBits);
-                        do {
-                            if (bitIndex == 0
-                                || bitIndex == MAX_BIT_INDEX
-                                || !handledBits[bitIndex]) {
-                                handledBits[bitIndex] = TRUE;
-                                sampleSizeInBytes = getSampleSizeInBytes(bitIndex, origSampleSizeInBytes);
-                                significantBits = getSignificantBits(bitIndex, origSignificantBits);
-                                if (maxChannels - minChannels > MAXIMUM_LISTED_CHANNELS) {
-                                    // avoid too many channels explicitly listed
-                                    // just add -1, min, and max
-                                    DAUDIO_AddAudioFormat(creator, significantBits,
-                                                          -1, -1, rate,
-                                                          enc, isSigned, isBigEndian);
-                                    DAUDIO_AddAudioFormat(creator, significantBits,
-                                                          sampleSizeInBytes * minChannels,
-                                                          minChannels, rate,
-                                                          enc, isSigned, isBigEndian);
-                                    DAUDIO_AddAudioFormat(creator, significantBits,
-                                                          sampleSizeInBytes * maxChannels,
-                                                          maxChannels, rate,
-                                                          enc, isSigned, isBigEndian);
-                                } else {
-                                    for (channels = minChannels; channels <= maxChannels; channels++) {
-                                        DAUDIO_AddAudioFormat(creator, significantBits,
-                                                              sampleSizeInBytes * channels,
-                                                              channels, rate,
-                                                              enc, isSigned, isBigEndian);
-                                    }
-                                }
-                            }
-#ifndef ALSA_PCM_USE_PLUGHW
-                            // without plugin, do not add fake formats
-                            break;
-#endif
-                        } while (--bitIndex > 0);
-                    } else {
-                        TRACE1("could not get format from alsa for format %d\n", format);
-                    }
-                } else {
-                    //TRACE1("Format %d not supported\n", format);
-                }
-            } // for loop
-            snd_pcm_hw_params_free(hwParams);
-        }
-        snd_pcm_format_mask_free(formatMask);
-    }
-    snd_pcm_close(handle);
-}
-
-/** Workaround for cr 7033899, 7030629:
- * dmix plugin doesn't like flush (snd_pcm_drop) when the buffer is empty
- * (just opened, underruned or already flushed).
- * Sometimes it causes PCM falls to -EBADFD error,
- * sometimes causes bufferSize change.
- * To prevent unnecessary flushes AlsaPcmInfo::isRunning & isFlushed are used.
- */
-/* ******* ALSA PCM INFO ******************** */
-typedef struct tag_AlsaPcmInfo {
-    snd_pcm_t* handle;
-    snd_pcm_hw_params_t* hwParams;
-    snd_pcm_sw_params_t* swParams;
-    int bufferSizeInBytes;
-    int frameSize; // storage size in Bytes
-    unsigned int periods;
-    snd_pcm_uframes_t periodSize;
-    short int isRunning;    // see comment above
-    short int isFlushed;    // see comment above
-#ifdef GET_POSITION_METHOD2
-    // to be used exclusively by getBytePosition!
-    snd_pcm_status_t* positionStatus;
-#endif
-} AlsaPcmInfo;
-
-
-int setStartThresholdNoCommit(AlsaPcmInfo* info, int useThreshold) {
-    int ret;
-    int threshold;
-
-    if (useThreshold) {
-        // start device whenever anything is written to the buffer
-        threshold = 1;
-    } else {
-        // never start the device automatically
-        threshold = 2000000000; /* near UINT_MAX */
-    }
-    ret = snd_pcm_sw_params_set_start_threshold(info->handle, info->swParams, threshold);
-    if (ret < 0) {
-        ERROR1("Unable to set start threshold mode: %s\n", snd_strerror(ret));
-        return FALSE;
-    }
-    return TRUE;
-}
-
-int setStartThreshold(AlsaPcmInfo* info, int useThreshold) {
-    int ret = 0;
-
-    if (!setStartThresholdNoCommit(info, useThreshold)) {
-        ret = -1;
-    }
-    if (ret == 0) {
-        // commit it
-        ret = snd_pcm_sw_params(info->handle, info->swParams);
-        if (ret < 0) {
-            ERROR1("Unable to set sw params: %s\n", snd_strerror(ret));
-        }
-    }
-    return (ret == 0)?TRUE:FALSE;
-}
-
-
-// returns TRUE if successful
-int setHWParams(AlsaPcmInfo* info,
-                float sampleRate,
-                int channels,
-                int bufferSizeInFrames,
-                snd_pcm_format_t format) {
-    unsigned int rrate, periodTime, periods;
-    int ret, dir;
-    snd_pcm_uframes_t alsaBufferSizeInFrames = (snd_pcm_uframes_t) bufferSizeInFrames;
-
-    /* choose all parameters */
-    ret = snd_pcm_hw_params_any(info->handle, info->hwParams);
-    if (ret < 0) {
-        ERROR1("Broken configuration: no configurations available: %s\n", snd_strerror(ret));
-        return FALSE;
-    }
-    /* set the interleaved read/write format */
-    ret = snd_pcm_hw_params_set_access(info->handle, info->hwParams, SND_PCM_ACCESS_RW_INTERLEAVED);
-    if (ret < 0) {
-        ERROR1("SND_PCM_ACCESS_RW_INTERLEAVED access type not available: %s\n", snd_strerror(ret));
-        return FALSE;
-    }
-    /* set the sample format */
-    ret = snd_pcm_hw_params_set_format(info->handle, info->hwParams, format);
-    if (ret < 0) {
-        ERROR1("Sample format not available: %s\n", snd_strerror(ret));
-        return FALSE;
-    }
-    /* set the count of channels */
-    ret = snd_pcm_hw_params_set_channels(info->handle, info->hwParams, channels);
-    if (ret < 0) {
-        ERROR2("Channels count (%d) not available: %s\n", channels, snd_strerror(ret));
-        return FALSE;
-    }
-    /* set the stream rate */
-    rrate = (int) (sampleRate + 0.5f);
-    dir = 0;
-    ret = snd_pcm_hw_params_set_rate_near(info->handle, info->hwParams, &rrate, &dir);
-    if (ret < 0) {
-        ERROR2("Rate %dHz not available for playback: %s\n", (int) (sampleRate+0.5f), snd_strerror(ret));
-        return FALSE;
-    }
-    if ((rrate-sampleRate > 2) || (rrate-sampleRate < - 2)) {
-        ERROR2("Rate doesn't match (requested %2.2fHz, got %dHz)\n", sampleRate, rrate);
-        return FALSE;
-    }
-    /* set the buffer time */
-    ret = snd_pcm_hw_params_set_buffer_size_near(info->handle, info->hwParams, &alsaBufferSizeInFrames);
-    if (ret < 0) {
-        ERROR2("Unable to set buffer size to %d frames: %s\n",
-               (int) alsaBufferSizeInFrames, snd_strerror(ret));
-        return FALSE;
-    }
-    bufferSizeInFrames = (int) alsaBufferSizeInFrames;
-    /* set the period time */
-    if (bufferSizeInFrames > 1024) {
-        dir = 0;
-        periodTime = DEFAULT_PERIOD_TIME;
-        ret = snd_pcm_hw_params_set_period_time_near(info->handle, info->hwParams, &periodTime, &dir);
-        if (ret < 0) {
-            ERROR2("Unable to set period time to %d: %s\n", DEFAULT_PERIOD_TIME, snd_strerror(ret));
-            return FALSE;
-        }
-    } else {
-        /* set the period count for very small buffer sizes to 2 */
-        dir = 0;
-        periods = 2;
-        ret = snd_pcm_hw_params_set_periods_near(info->handle, info->hwParams, &periods, &dir);
-        if (ret < 0) {
-            ERROR2("Unable to set period count to %d: %s\n", /*periods*/ 2, snd_strerror(ret));
-            return FALSE;
-        }
-    }
-    /* write the parameters to device */
-    ret = snd_pcm_hw_params(info->handle, info->hwParams);
-    if (ret < 0) {
-        ERROR1("Unable to set hw params: %s\n", snd_strerror(ret));
-        return FALSE;
-    }
-    return TRUE;
-}
-
-// returns 1 if successful
-int setSWParams(AlsaPcmInfo* info) {
-    int ret;
-
-    /* get the current swparams */
-    ret = snd_pcm_sw_params_current(info->handle, info->swParams);
-    if (ret < 0) {
-        ERROR1("Unable to determine current swparams: %s\n", snd_strerror(ret));
-        return FALSE;
-    }
-    /* never start the transfer automatically */
-    if (!setStartThresholdNoCommit(info, FALSE /* don't use threshold */)) {
-        return FALSE;
-    }
-
-    /* allow the transfer when at least period_size samples can be processed */
-    ret = snd_pcm_sw_params_set_avail_min(info->handle, info->swParams, info->periodSize);
-    if (ret < 0) {
-        ERROR1("Unable to set avail min for playback: %s\n", snd_strerror(ret));
-        return FALSE;
-    }
-    /* write the parameters to the playback device */
-    ret = snd_pcm_sw_params(info->handle, info->swParams);
-    if (ret < 0) {
-        ERROR1("Unable to set sw params: %s\n", snd_strerror(ret));
-        return FALSE;
-    }
-    return TRUE;
-}
-
-static snd_output_t* ALSA_OUTPUT = NULL;
-
-void* DAUDIO_Open(INT32 mixerIndex, INT32 deviceID, int isSource,
-                  int encoding, float sampleRate, int sampleSizeInBits,
-                  int frameSize, int channels,
-                  int isSigned, int isBigEndian, int bufferSizeInBytes) {
-    snd_pcm_format_mask_t* formatMask;
-    snd_pcm_format_t format;
-    int dir;
-    int ret = 0;
-    AlsaPcmInfo* info = NULL;
-    /* snd_pcm_uframes_t is 64 bit on 64-bit systems */
-    snd_pcm_uframes_t alsaBufferSizeInFrames = 0;
-
-
-    TRACE0("> DAUDIO_Open\n");
-#ifdef USE_TRACE
-    // for using ALSA debug dump methods
-    if (ALSA_OUTPUT == NULL) {
-        snd_output_stdio_attach(&ALSA_OUTPUT, stdout, 0);
-    }
-#endif
-    if (channels <= 0) {
-        ERROR1("ERROR: Invalid number of channels=%d!\n", channels);
-        return NULL;
-    }
-    info = (AlsaPcmInfo*) malloc(sizeof(AlsaPcmInfo));
-    if (!info) {
-        ERROR0("Out of memory\n");
-        return NULL;
-    }
-    memset(info, 0, sizeof(AlsaPcmInfo));
-    // initial values are: stopped, flushed
-    info->isRunning = 0;
-    info->isFlushed = 1;
-
-    ret = openPCMfromDeviceID(deviceID, &(info->handle), isSource, FALSE /* do open device*/);
-    if (ret == 0) {
-        // set to blocking mode
-        snd_pcm_nonblock(info->handle, 0);
-        ret = snd_pcm_hw_params_malloc(&(info->hwParams));
-        if (ret != 0) {
-            ERROR1("  snd_pcm_hw_params_malloc returned error %d\n", ret);
-        } else {
-            ret = -1;
-            if (getAlsaFormatFromFormat(&format, frameSize / channels, sampleSizeInBits,
-                                        isSigned, isBigEndian, encoding)) {
-                if (setHWParams(info,
-                                sampleRate,
-                                channels,
-                                bufferSizeInBytes / frameSize,
-                                format)) {
-                    info->frameSize = frameSize;
-                    ret = snd_pcm_hw_params_get_period_size(info->hwParams, &info->periodSize, &dir);
-                    if (ret < 0) {
-                        ERROR1("ERROR: snd_pcm_hw_params_get_period: %s\n", snd_strerror(ret));
-                    }
-                    snd_pcm_hw_params_get_periods(info->hwParams, &(info->periods), &dir);
-                    snd_pcm_hw_params_get_buffer_size(info->hwParams, &alsaBufferSizeInFrames);
-                    info->bufferSizeInBytes = (int) alsaBufferSizeInFrames * frameSize;
-                    TRACE3("  DAUDIO_Open: period size = %d frames, periods = %d. Buffer size: %d bytes.\n",
-                           (int) info->periodSize, info->periods, info->bufferSizeInBytes);
-                }
-            }
-        }
-        if (ret == 0) {
-            // set software parameters
-            ret = snd_pcm_sw_params_malloc(&(info->swParams));
-            if (ret != 0) {
-                ERROR1("snd_pcm_hw_params_malloc returned error %d\n", ret);
-            } else {
-                if (!setSWParams(info)) {
-                    ret = -1;
-                }
-            }
-        }
-        if (ret == 0) {
-            // prepare device
-            ret = snd_pcm_prepare(info->handle);
-            if (ret < 0) {
-                ERROR1("ERROR: snd_pcm_prepare: %s\n", snd_strerror(ret));
-            }
-        }
-
-#ifdef GET_POSITION_METHOD2
-        if (ret == 0) {
-            ret = snd_pcm_status_malloc(&(info->positionStatus));
-            if (ret != 0) {
-                ERROR1("ERROR in snd_pcm_status_malloc: %s\n", snd_strerror(ret));
-            }
-        }
-#endif
-    }
-    if (ret != 0) {
-        DAUDIO_Close((void*) info, isSource);
-        info = NULL;
-    } else {
-        // set to non-blocking mode
-        snd_pcm_nonblock(info->handle, 1);
-        TRACE1("< DAUDIO_Open: Opened device successfully. Handle=%p\n",
-               (void*) info->handle);
-    }
-    return (void*) info;
-}
-
-#ifdef USE_TRACE
-void printState(snd_pcm_state_t state) {
-    if (state == SND_PCM_STATE_OPEN) {
-        TRACE0("State: SND_PCM_STATE_OPEN\n");
-    }
-    else if (state == SND_PCM_STATE_SETUP) {
-        TRACE0("State: SND_PCM_STATE_SETUP\n");
-    }
-    else if (state == SND_PCM_STATE_PREPARED) {
-        TRACE0("State: SND_PCM_STATE_PREPARED\n");
-    }
-    else if (state == SND_PCM_STATE_RUNNING) {
-        TRACE0("State: SND_PCM_STATE_RUNNING\n");
-    }
-    else if (state == SND_PCM_STATE_XRUN) {
-        TRACE0("State: SND_PCM_STATE_XRUN\n");
-    }
-    else if (state == SND_PCM_STATE_DRAINING) {
-        TRACE0("State: SND_PCM_STATE_DRAINING\n");
-    }
-    else if (state == SND_PCM_STATE_PAUSED) {
-        TRACE0("State: SND_PCM_STATE_PAUSED\n");
-    }
-    else if (state == SND_PCM_STATE_SUSPENDED) {
-        TRACE0("State: SND_PCM_STATE_SUSPENDED\n");
-    }
-}
-#endif
-
-int DAUDIO_Start(void* id, int isSource) {
-    AlsaPcmInfo* info = (AlsaPcmInfo*) id;
-    int ret;
-    snd_pcm_state_t state;
-
-    TRACE0("> DAUDIO_Start\n");
-    // set to blocking mode
-    snd_pcm_nonblock(info->handle, 0);
-    // set start mode so that it always starts as soon as data is there
-    setStartThreshold(info, TRUE /* use threshold */);
-    state = snd_pcm_state(info->handle);
-    if (state == SND_PCM_STATE_PAUSED) {
-        // in case it was stopped previously
-        TRACE0("  Un-pausing...\n");
-        ret = snd_pcm_pause(info->handle, FALSE);
-        if (ret != 0) {
-            ERROR2("  NOTE: error in snd_pcm_pause:%d: %s\n", ret, snd_strerror(ret));
-        }
-    }
-    if (state == SND_PCM_STATE_SUSPENDED) {
-        TRACE0("  Resuming...\n");
-        ret = snd_pcm_resume(info->handle);
-        if (ret < 0) {
-            if ((ret != -EAGAIN) && (ret != -ENOSYS)) {
-                ERROR2("  ERROR: error in snd_pcm_resume:%d: %s\n", ret, snd_strerror(ret));
-            }
-        }
-    }
-    if (state == SND_PCM_STATE_SETUP) {
-        TRACE0("need to call prepare again...\n");
-        // prepare device
-        ret = snd_pcm_prepare(info->handle);
-        if (ret < 0) {
-            ERROR1("ERROR: snd_pcm_prepare: %s\n", snd_strerror(ret));
-        }
-    }
-    // in case there is still data in the buffers
-    ret = snd_pcm_start(info->handle);
-    if (ret != 0) {
-        if (ret != -EPIPE) {
-            ERROR2("  NOTE: error in snd_pcm_start: %d: %s\n", ret, snd_strerror(ret));
-        }
-    }
-    // set to non-blocking mode
-    ret = snd_pcm_nonblock(info->handle, 1);
-    if (ret != 0) {
-        ERROR1("  ERROR in snd_pcm_nonblock: %s\n", snd_strerror(ret));
-    }
-    state = snd_pcm_state(info->handle);
-#ifdef USE_TRACE
-    printState(state);
-#endif
-    ret = (state == SND_PCM_STATE_PREPARED)
-        || (state == SND_PCM_STATE_RUNNING)
-        || (state == SND_PCM_STATE_XRUN)
-        || (state == SND_PCM_STATE_SUSPENDED);
-    if (ret) {
-        info->isRunning = 1;
-        // source line should keep isFlushed value until Write() is called;
-        // for target data line reset it right now.
-        if (!isSource) {
-            info->isFlushed = 0;
-        }
-    }
-    TRACE1("< DAUDIO_Start %s\n", ret?"success":"error");
-    return ret?TRUE:FALSE;
-}
-
-int DAUDIO_Stop(void* id, int isSource) {
-    AlsaPcmInfo* info = (AlsaPcmInfo*) id;
-    int ret;
-
-    TRACE0("> DAUDIO_Stop\n");
-    // set to blocking mode
-    snd_pcm_nonblock(info->handle, 0);
-    setStartThreshold(info, FALSE /* don't use threshold */); // device will not start after buffer xrun
-    ret = snd_pcm_pause(info->handle, 1);
-    // set to non-blocking mode
-    snd_pcm_nonblock(info->handle, 1);
-    if (ret != 0) {
-        ERROR1("ERROR in snd_pcm_pause: %s\n", snd_strerror(ret));
-        return FALSE;
-    }
-    info->isRunning = 0;
-    TRACE0("< DAUDIO_Stop success\n");
-    return TRUE;
-}
-
-void DAUDIO_Close(void* id, int isSource) {
-    AlsaPcmInfo* info = (AlsaPcmInfo*) id;
-
-    TRACE0("DAUDIO_Close\n");
-    if (info != NULL) {
-        if (info->handle != NULL) {
-            snd_pcm_close(info->handle);
-        }
-        if (info->hwParams) {
-            snd_pcm_hw_params_free(info->hwParams);
-        }
-        if (info->swParams) {
-            snd_pcm_sw_params_free(info->swParams);
-        }
-#ifdef GET_POSITION_METHOD2
-        if (info->positionStatus) {
-            snd_pcm_status_free(info->positionStatus);
-        }
-#endif
-        free(info);
-    }
-}
-
-/*
- * Underrun and suspend recovery
- * returns
- * 0:  exit native and return 0
- * 1:  try again to write/read
- * -1: error - exit native with return value -1
- */
-int xrun_recovery(AlsaPcmInfo* info, int err) {
-    int ret;
-
-    if (err == -EPIPE) {    /* underrun / overflow */
-        TRACE0("xrun_recovery: underrun/overflow.\n");
-        ret = snd_pcm_prepare(info->handle);
-        if (ret < 0) {
-            ERROR1("Can't recover from underrun/overflow, prepare failed: %s\n", snd_strerror(ret));
-            return -1;
-        }
-        return 1;
-    } else if (err == -ESTRPIPE) {
-        TRACE0("xrun_recovery: suspended.\n");
-        ret = snd_pcm_resume(info->handle);
-        if (ret < 0) {
-            if (ret == -EAGAIN) {
-                return 0; /* wait until the suspend flag is released */
-            }
-            return -1;
-        }
-        ret = snd_pcm_prepare(info->handle);
-        if (ret < 0) {
-            ERROR1("Can't recover from underrun/overflow, prepare failed: %s\n", snd_strerror(ret));
-            return -1;
-        }
-        return 1;
-    } else if (err == -EAGAIN) {
-        TRACE0("xrun_recovery: EAGAIN try again flag.\n");
-        return 0;
-    }
-
-    TRACE2("xrun_recovery: unexpected error %d: %s\n", err, snd_strerror(err));
-    return -1;
-}
-
-// returns -1 on error
-int DAUDIO_Write(void* id, char* data, int byteSize) {
-    AlsaPcmInfo* info = (AlsaPcmInfo*) id;
-    int ret, count;
-    snd_pcm_sframes_t frameSize, writtenFrames;
-
-    TRACE1("> DAUDIO_Write %d bytes\n", byteSize);
-
-    /* sanity */
-    if (byteSize <= 0 || info->frameSize <= 0) {
-        ERROR2(" DAUDIO_Write: byteSize=%d, frameSize=%d!\n",
-               (int) byteSize, (int) info->frameSize);
-        TRACE0("< DAUDIO_Write returning -1\n");
-        return -1;
-    }
-
-    count = 2; // maximum number of trials to recover from underrun
-    //frameSize = snd_pcm_bytes_to_frames(info->handle, byteSize);
-    frameSize = (snd_pcm_sframes_t) (byteSize / info->frameSize);
-    do {
-        writtenFrames = snd_pcm_writei(info->handle, (const void*) data, (snd_pcm_uframes_t) frameSize);
-
-        if (writtenFrames < 0) {
-            ret = xrun_recovery(info, (int) writtenFrames);
-            if (ret <= 0) {
-                TRACE1("DAUDIO_Write: xrun recovery returned %d -> return.\n", ret);
-                return ret;
-            }
-            if (count-- <= 0) {
-                ERROR0("DAUDIO_Write: too many attempts to recover from xrun/suspend\n");
-                return -1;
-            }
-        } else {
-            break;
-        }
-    } while (TRUE);
-    //ret =  snd_pcm_frames_to_bytes(info->handle, writtenFrames);
-
-    if (writtenFrames > 0) {
-        // reset "flushed" flag
-        info->isFlushed = 0;
-    }
-
-    ret =  (int) (writtenFrames * info->frameSize);
-    TRACE1("< DAUDIO_Write: returning %d bytes.\n", ret);
-    return ret;
-}
-
-// returns -1 on error
-int DAUDIO_Read(void* id, char* data, int byteSize) {
-    AlsaPcmInfo* info = (AlsaPcmInfo*) id;
-    int ret, count;
-    snd_pcm_sframes_t frameSize, readFrames;
-
-    TRACE1("> DAUDIO_Read %d bytes\n", byteSize);
-    /*TRACE3("  info=%p, data=%p, byteSize=%d\n",
-      (void*) info, (void*) data, (int) byteSize);
-      TRACE2("  info->frameSize=%d, info->handle=%p\n",
-      (int) info->frameSize, (void*) info->handle);
-    */
-    /* sanity */
-    if (byteSize <= 0 || info->frameSize <= 0) {
-        ERROR2(" DAUDIO_Read: byteSize=%d, frameSize=%d!\n",
-               (int) byteSize, (int) info->frameSize);
-        TRACE0("< DAUDIO_Read returning -1\n");
-        return -1;
-    }
-    if (!info->isRunning && info->isFlushed) {
-        // PCM has nothing to read
-        return 0;
-    }
-
-    count = 2; // maximum number of trials to recover from error
-    //frameSize = snd_pcm_bytes_to_frames(info->handle, byteSize);
-    frameSize = (snd_pcm_sframes_t) (byteSize / info->frameSize);
-    do {
-        readFrames = snd_pcm_readi(info->handle, (void*) data, (snd_pcm_uframes_t) frameSize);
-        if (readFrames < 0) {
-            ret = xrun_recovery(info, (int) readFrames);
-            if (ret <= 0) {
-                TRACE1("DAUDIO_Read: xrun recovery returned %d -> return.\n", ret);
-                return ret;
-            }
-            if (count-- <= 0) {
-                ERROR0("DAUDIO_Read: too many attempts to recover from xrun/suspend\n");
-                return -1;
-            }
-        } else {
-            break;
-        }
-    } while (TRUE);
-    //ret =  snd_pcm_frames_to_bytes(info->handle, readFrames);
-    ret =  (int) (readFrames * info->frameSize);
-    TRACE1("< DAUDIO_Read: returning %d bytes.\n", ret);
-    return ret;
-}
-
-
-int DAUDIO_GetBufferSize(void* id, int isSource) {
-    AlsaPcmInfo* info = (AlsaPcmInfo*) id;
-
-    return info->bufferSizeInBytes;
-}
-
-int DAUDIO_StillDraining(void* id, int isSource) {
-    AlsaPcmInfo* info = (AlsaPcmInfo*) id;
-    snd_pcm_state_t state;
-
-    state = snd_pcm_state(info->handle);
-    //printState(state);
-    //TRACE1("Still draining: %s\n", (state != SND_PCM_STATE_XRUN)?"TRUE":"FALSE");
-    return (state == SND_PCM_STATE_RUNNING)?TRUE:FALSE;
-}
-
-
-int DAUDIO_Flush(void* id, int isSource) {
-    AlsaPcmInfo* info = (AlsaPcmInfo*) id;
-    int ret;
-
-    TRACE0("DAUDIO_Flush\n");
-
-    if (info->isFlushed) {
-        // nothing to drop
-        return 1;
-    }
-
-    ret = snd_pcm_drop(info->handle);
-    if (ret != 0) {
-        ERROR1("ERROR in snd_pcm_drop: %s\n", snd_strerror(ret));
-        return FALSE;
-    }
-
-    info->isFlushed = 1;
-    if (info->isRunning) {
-        ret = DAUDIO_Start(id, isSource);
-    }
-    return ret;
-}
-
-int DAUDIO_GetAvailable(void* id, int isSource) {
-    AlsaPcmInfo* info = (AlsaPcmInfo*) id;
-    snd_pcm_sframes_t availableInFrames;
-    snd_pcm_state_t state;
-    int ret;
-
-    state = snd_pcm_state(info->handle);
-    if (info->isFlushed || state == SND_PCM_STATE_XRUN) {
-        // if in xrun state then we have the entire buffer available,
-        // not 0 as alsa reports
-        ret = info->bufferSizeInBytes;
-    } else {
-        availableInFrames = snd_pcm_avail_update(info->handle);
-        if (availableInFrames < 0) {
-            ret = 0;
-        } else {
-            //ret = snd_pcm_frames_to_bytes(info->handle, availableInFrames);
-            ret = (int) (availableInFrames * info->frameSize);
-        }
-    }
-    TRACE1("DAUDIO_GetAvailable returns %d bytes\n", ret);
-    return ret;
-}
-
-INT64 estimatePositionFromAvail(AlsaPcmInfo* info, int isSource, INT64 javaBytePos, int availInBytes) {
-    // estimate the current position with the buffer size and
-    // the available bytes to read or write in the buffer.
-    // not an elegant solution - bytePos will stop on xruns,
-    // and in race conditions it may jump backwards
-    // Advantage is that it is indeed based on the samples that go through
-    // the system (rather than time-based methods)
-    if (isSource) {
-        // javaBytePos is the position that is reached when the current
-        // buffer is played completely
-        return (INT64) (javaBytePos - info->bufferSizeInBytes + availInBytes);
-    } else {
-        // javaBytePos is the position that was when the current buffer was empty
-        return (INT64) (javaBytePos + availInBytes);
-    }
-}
-
-INT64 DAUDIO_GetBytePosition(void* id, int isSource, INT64 javaBytePos) {
-    AlsaPcmInfo* info = (AlsaPcmInfo*) id;
-    int ret;
-    INT64 result = javaBytePos;
-    snd_pcm_state_t state;
-    state = snd_pcm_state(info->handle);
-
-    if (!info->isFlushed && state != SND_PCM_STATE_XRUN) {
-#ifdef GET_POSITION_METHOD2
-        snd_timestamp_t* ts;
-        snd_pcm_uframes_t framesAvail;
-
-        // note: slight race condition if this is called simultaneously from 2 threads
-        ret = snd_pcm_status(info->handle, info->positionStatus);
-        if (ret != 0) {
-            ERROR1("ERROR in snd_pcm_status: %s\n", snd_strerror(ret));
-            result = javaBytePos;
-        } else {
-            // calculate from time value, or from available bytes
-            framesAvail = snd_pcm_status_get_avail(info->positionStatus);
-            result = estimatePositionFromAvail(info, isSource, javaBytePos, framesAvail * info->frameSize);
-        }
-#endif
-#ifdef GET_POSITION_METHOD3
-        snd_pcm_uframes_t framesAvail;
-        ret = snd_pcm_avail(info->handle, &framesAvail);
-        if (ret != 0) {
-            ERROR1("ERROR in snd_pcm_avail: %s\n", snd_strerror(ret));
-            result = javaBytePos;
-        } else {
-            result = estimatePositionFromAvail(info, isSource, javaBytePos, framesAvail * info->frameSize);
-        }
-#endif
-#ifdef GET_POSITION_METHOD1
-        result = estimatePositionFromAvail(info, isSource, javaBytePos, DAUDIO_GetAvailable(id, isSource));
-#endif
-    }
-    //printf("getbyteposition: javaBytePos=%d , return=%d\n", (int) javaBytePos, (int) result);
-    return result;
-}
-
-
-
-void DAUDIO_SetBytePosition(void* id, int isSource, INT64 javaBytePos) {
-    /* save to ignore, since GetBytePosition
-     * takes the javaBytePos param into account
-     */
-}
-
-int DAUDIO_RequiresServicing(void* id, int isSource) {
-    // never need servicing on Bsd
-    return FALSE;
-}
-
-void DAUDIO_Service(void* id, int isSource) {
-    // never need servicing on Bsd
-}
-
-
-#endif // USE_DAUDIO