OSDN Git Service

Initial Contribution
authorXiaopeng Yang <xiaopeng.yang@windriver.com>
Thu, 20 Nov 2008 02:39:50 +0000 (10:39 +0800)
committerXiaopeng Yang <xiaopeng.yang@windriver.com>
Thu, 20 Nov 2008 02:39:50 +0000 (10:39 +0800)
Android.mk [new file with mode: 0644]
AudioHardwareALSA.cpp [new file with mode: 0644]
AudioHardwareALSA.h [new file with mode: 0644]
AudioHardwareInterface.cpp
MODULE_LICENSE_APACHE2 [new file with mode: 0644]
NOTICE [new file with mode: 0644]

diff --git a/Android.mk b/Android.mk
new file mode 100644 (file)
index 0000000..14c0216
--- /dev/null
@@ -0,0 +1,35 @@
+# hardware/libaudio-alsa/Android.mk
+#
+# Copyright 2008 Wind River Systems
+#
+
+ifeq ($(strip $(BOARD_USES_ALSA_AUDIO)),true)
+
+  LOCAL_PATH := $(call my-dir)
+
+  include $(CLEAR_VARS)
+
+  LOCAL_ARM_MODE := arm
+  LOCAL_CFLAGS = -fno-short-enums
+  LOCAL_WHOLE_STATIC_LIBRARIES := libasound
+
+  LOCAL_C_INCLUDES += external/alsa-lib/include
+
+  LOCAL_SRC_FILES := \
+    AudioHardwareInterface.cpp \
+    AudioHardwareStub.cpp \
+    AudioHardwareALSA.cpp
+
+  LOCAL_MODULE := libaudio
+
+  LOCAL_SHARED_LIBRARIES := \
+    libcutils \
+    libutils \
+    libmedia \
+    libhardware \
+    libdl \
+    libc
+
+  include $(BUILD_SHARED_LIBRARY)
+
+endif
diff --git a/AudioHardwareALSA.cpp b/AudioHardwareALSA.cpp
new file mode 100644 (file)
index 0000000..5fe7fd1
--- /dev/null
@@ -0,0 +1,1299 @@
+/* AudioHardwareALSA.cpp
+**
+** Copyright 2008 Wind River Systems
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include <errno.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#define LOG_TAG "AudioHardwareALSA"
+#include <utils/Log.h>
+#include <utils/String8.h>
+
+#include <cutils/properties.h>
+#include <media/AudioRecord.h>
+#include <hardware/power.h>
+
+#include <alsa/asoundlib.h>
+#include "AudioHardwareALSA.h"
+
+#define SND_MIXER_VOL_RANGE_MIN  (0)
+#define SND_MIXER_VOL_RANGE_MAX  (1000)
+
+extern "C" {
+
+extern int ffs(int i);
+
+//
+// Make sure this prototype is consistent with what's in
+// external/libasound/alsa-lib-1.0.16/src/pcm/pcm_null.c!
+//
+extern int snd_pcm_null_open(snd_pcm_t **pcmp,
+                             const char *name,
+                             snd_pcm_stream_t stream,
+                             int mode);
+
+//
+// Function for dlsym() to look up for creating a new AudioHardwareInterface.
+//
+android::AudioHardwareInterface *createAudioHardware(void)
+{
+    return new android::AudioHardwareALSA();
+}
+
+} // extern "C"
+
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+static const char _nullALSADeviceName[] = "NULL_Device";
+
+static void ALSAErrorHandler(const char *file,
+                             int line,
+                             const char *function,
+                             int err,
+                             const char *fmt,
+                             ...)
+{
+    char buf[BUFSIZ];
+    va_list arg;
+    int l;
+
+    va_start(arg, fmt);
+    l = snprintf(buf, BUFSIZ, "%s:%i:(%s) ", file, line, function);
+    vsnprintf(buf + l, BUFSIZ - l, fmt, arg);
+    buf[BUFSIZ-1] = '\0';
+    LOG(LOG_ERROR, "ALSALib", buf);
+    va_end(arg);
+}
+
+// ----------------------------------------------------------------------------
+
+struct alsa_properties_t {
+       const char *propName;
+       const char *propDefault;
+};
+
+static const alsa_properties_t masterPlaybackProp = {
+       "alsa.mixer.playback.master", "PCM"
+};
+
+static const alsa_properties_t masterCaptureProp = {
+       "alsa.mixer.capture.master", "Capture"
+};
+
+/* The following table(s) need to match in order of the route bits
+ */
+static const char *deviceSuffix[] = {
+       /* ROUTE_EARPIECE  */ "_Earpiece",
+    /* ROUTE_SPEAKER   */ "_Speaker",
+    /* ROUTE_BLUETOOTH */ "_Bluetooth",
+    /* ROUTE_HEADSET   */ "_Headset",
+};
+
+static const int deviceSuffixLen = (sizeof(deviceSuffix) / sizeof(char *));
+
+static const alsa_properties_t
+       mixerMasterProp[SND_PCM_STREAM_LAST+1] =
+{
+       { "alsa.mixer.playback.master",  "PCM" },
+       { "alsa.mixer.capture.master",   "Capture" }
+};
+
+static const alsa_properties_t
+       mixerProp[SND_PCM_STREAM_LAST+1][ALSAMixer::MIXER_LAST+1] =
+{
+    {
+       {"alsa.mixer.playback.earpiece",  "Earpiece"},
+               {"alsa.mixer.playback.speaker",   "Speaker"},
+               {"alsa.mixer.playback.bluetooth", "Bluetooth"},
+               {"alsa.mixer.playback.headset",   "Headphone"}
+       },
+       {
+               {"alsa.mixer.capture.earpiece",  "Capture"},
+               {"alsa.mixer.capture.speaker",   ""},
+               {"alsa.mixer.capture.bluetooth", "Bluetooth Capture"},
+               {"alsa.mixer.capture.headset",   "Capture"}
+       }
+};
+
+// ----------------------------------------------------------------------------
+
+AudioHardwareALSA::AudioHardwareALSA() :
+    mOutput(0),
+    mInput(0)
+{
+    snd_lib_error_set_handler(&ALSAErrorHandler);
+    mMixer = new ALSAMixer;
+}
+
+AudioHardwareALSA::~AudioHardwareALSA()
+{
+    if (mOutput) delete mOutput;
+    if (mInput) delete mInput;
+    if (mMixer) delete mMixer;
+}
+
+status_t AudioHardwareALSA::initCheck()
+{
+       if (mMixer && mMixer->isValid())
+               return NO_ERROR;
+       else
+               return NO_INIT;
+}
+
+status_t AudioHardwareALSA::standby()
+{
+       if (mOutput)
+               return mOutput->standby();
+
+    return NO_ERROR;
+}
+
+status_t AudioHardwareALSA::setVoiceVolume(float volume)
+{
+    // The voice volume is used by the VOICE_CALL audio stream.
+       if (mMixer)
+               return mMixer->setVolume(ALSAMixer::MIXER_EARPIECE, volume);
+       else
+               return INVALID_OPERATION;
+}
+
+status_t AudioHardwareALSA::setMasterVolume(float volume)
+{
+       if (mMixer)
+               return mMixer->setMasterVolume(volume);
+       else
+               return INVALID_OPERATION;
+}
+
+AudioStreamOut *AudioHardwareALSA::openOutputStream(int      format,
+                                                    int      channelCount,
+                                                    uint32_t sampleRate)
+{
+    AutoMutex lock(mLock);
+
+    // only one output stream allowed
+    if (mOutput)
+        return 0;
+
+    AudioStreamOutALSA *out = new AudioStreamOutALSA(this);
+
+    if (out->set(format, channelCount, sampleRate) == NO_ERROR) {
+        mOutput = out;
+        // Some information is expected to be available immediately after
+        // the device is open.
+           uint32_t routes = mRoutes[mMode];
+        mOutput->setDevice(mMode, routes);
+    } else {
+        delete out;
+    }
+
+    return mOutput;
+}
+
+AudioStreamIn *AudioHardwareALSA::openInputStream(int      format,
+                                                  int      channelCount,
+                                                  uint32_t sampleRate)
+{
+    AutoMutex lock(mLock);
+
+    // only one input stream allowed
+    if (mInput)
+        return 0;
+
+    AudioStreamInALSA *in = new AudioStreamInALSA(this);
+
+    if (in->set(format, channelCount, sampleRate) == NO_ERROR) {
+        mInput = in;
+        // Now, actually open the device. Only 1 route used
+        mInput->setDevice(0, 0);
+    } else {
+        delete in;
+    }
+    return mInput;
+}
+
+status_t AudioHardwareALSA::doRouting()
+{
+    uint32_t routes;
+
+    AutoMutex lock(mLock);
+
+    if (mOutput) {
+        routes = mRoutes[mMode];
+        return mOutput->setDevice(mMode, routes);
+    }
+    return NO_INIT;
+}
+
+status_t AudioHardwareALSA::setMicMute(bool state)
+{
+       ALSAMixer::mixer_types mixer_type =
+               static_cast<ALSAMixer::mixer_types>(ffs(AudioSystem::ROUTE_EARPIECE) - 1);
+
+    if (mMixer)
+        return mMixer->setCaptureMuteState(mixer_type, state);
+
+    return NO_INIT;
+}
+
+status_t AudioHardwareALSA::getMicMute(bool *state)
+{
+       ALSAMixer::mixer_types mixer_type =
+               static_cast<ALSAMixer::mixer_types>(ffs(AudioSystem::ROUTE_EARPIECE) - 1);
+
+    if (mMixer)
+        return mMixer->getCaptureMuteState(mixer_type, state);
+
+    return NO_ERROR;
+}
+
+status_t AudioHardwareALSA::dump(int fd, const Vector<String16>& args)
+{
+    return NO_ERROR;
+}
+
+// ----------------------------------------------------------------------------
+
+ALSAStreamOps::ALSAStreamOps() :
+    mHandle(0),
+    mHardwareParams(0),
+    mSoftwareParams(0),
+    mMode(-1),
+    mDevice(-1)
+{
+    if (snd_pcm_hw_params_malloc(&mHardwareParams) < 0) {
+        LOG_ALWAYS_FATAL("Failed to allocate ALSA hardware parameters!");
+    }
+
+    if (snd_pcm_sw_params_malloc(&mSoftwareParams) < 0) {
+        LOG_ALWAYS_FATAL("Failed to allocate ALSA software parameters!");
+    }
+}
+
+ALSAStreamOps::~ALSAStreamOps()
+{
+       AutoMutex lock(mLock);
+
+    close();
+
+    if (mHardwareParams)
+        snd_pcm_hw_params_free(mHardwareParams);
+
+    if (mSoftwareParams)
+        snd_pcm_sw_params_free(mSoftwareParams);
+}
+
+status_t ALSAStreamOps::set(int      format,
+                            int      channels,
+                            uint32_t rate)
+{
+    if (channels != 0)
+        mDefaults->channels = channels;
+
+    if (rate != 0)
+        mDefaults->sampleRate = rate;
+
+    switch(format) {
+    case AudioSystem::DEFAULT:  // format == 0
+        break;
+
+    case AudioSystem::PCM_16_BIT:
+        mDefaults->format = SND_PCM_FORMAT_S16_LE;
+        break;
+
+    case AudioSystem::PCM_8_BIT:
+        mDefaults->format = SND_PCM_FORMAT_S8;
+        break;
+
+    default:
+        LOGE("Unknown PCM format %i. Forcing default", format);
+        break;
+    }
+
+       return NO_ERROR;
+}
+
+uint32_t ALSAStreamOps::sampleRate() const
+{
+    unsigned int rate;
+    int err;
+
+    if (! mHandle)
+        return NO_INIT;
+
+    return snd_pcm_hw_params_get_rate(mHardwareParams, &rate, 0) < 0
+           ? 0 : static_cast<uint32_t>(rate);
+}
+
+status_t ALSAStreamOps::sampleRate(uint32_t rate)
+{
+    const char *stream;
+    unsigned int requestedRate;
+    int err;
+
+    if (!mHandle)
+        return NO_INIT;
+
+    stream = streamName();
+    requestedRate = rate;
+    err = snd_pcm_hw_params_set_rate_near(mHandle,
+                                          mHardwareParams,
+                                          &requestedRate,
+                                          0);
+
+    if (err < 0) {
+        LOGE("Unable to set %s sample rate to %u: %s",
+             stream, rate, snd_strerror(err));
+        return BAD_VALUE;
+    }
+    if (requestedRate != rate) {
+        // Some devices have a fixed sample rate, and can not be changed.
+        // This may cause resampling problems; i.e. PCM playback will be too
+        // slow or fast.
+        LOGW("Requested rate (%u HZ) does not match actual rate (%u HZ)",
+             rate, requestedRate);
+    } else {
+        LOGD("Set %s sample rate to %u HZ", stream, requestedRate);
+    }
+    return NO_ERROR;
+}
+
+//
+// Return the number of bytes (not frames)
+//
+size_t ALSAStreamOps::bufferSize() const
+{
+    snd_pcm_uframes_t periodSize;
+    int err;
+
+    if (!mHandle)
+        return -1;
+
+    err = snd_pcm_hw_params_get_period_size(mHardwareParams,
+                                            &periodSize,
+                                            0);
+    if (err < 0)
+        return -1;
+
+    return static_cast<size_t>(snd_pcm_frames_to_bytes(mHandle, periodSize));
+}
+
+int ALSAStreamOps::format() const
+{
+    snd_pcm_format_t ALSAFormat;
+    int pcmFormatBitWidth;
+    int audioSystemFormat;
+
+    if (!mHandle)
+        return -1;
+
+    if (snd_pcm_hw_params_get_format(mHardwareParams, &ALSAFormat) < 0) {
+        return -1;
+    }
+
+    pcmFormatBitWidth = snd_pcm_format_physical_width(ALSAFormat);
+    audioSystemFormat = AudioSystem::DEFAULT;
+    switch(pcmFormatBitWidth)
+    {
+    case 8:
+        audioSystemFormat = AudioSystem::PCM_8_BIT;
+        break;
+
+    case 16:
+        audioSystemFormat = AudioSystem::PCM_16_BIT;
+        break;
+
+    default:
+        LOG_FATAL("Unknown AudioSystem bit width %i!", pcmFormatBitWidth);
+    }
+
+    return audioSystemFormat;
+}
+
+int ALSAStreamOps::channelCount() const
+{
+    unsigned int val;
+    int err;
+
+    if (!mHandle)
+        return -1;
+
+    err = snd_pcm_hw_params_get_channels(mHardwareParams, &val);
+    if (err < 0) {
+        LOGE("Unable to get device channel count: %s",
+             snd_strerror(err));
+        return -1;
+    }
+
+    return val;
+}
+
+status_t ALSAStreamOps::channelCount(int channels)
+{
+    int err;
+
+    if (!mHandle)
+        return NO_INIT;
+
+    err = snd_pcm_hw_params_set_channels(mHandle, mHardwareParams, channels);
+    if (err < 0) {
+        LOGE("Unable to set channel count to %i: %s",
+             channels, snd_strerror(err));
+        return BAD_VALUE;
+    }
+
+    LOGD("Using %i %s for %s.",
+         channels, channels == 1 ? "channel" : "channels", streamName());
+
+    return NO_ERROR;
+}
+
+status_t ALSAStreamOps::open(int mode, int device)
+{
+    const char *stream = streamName();
+    const char *devName = deviceName(mode, device);
+
+    int         err;
+
+    // The PCM stream is opened in blocking mode, per ALSA defaults.  The
+    // AudioFlinger seems to assume blocking mode too, so asynchronous mode
+    // should not be used.
+    if ((err = snd_pcm_open(&mHandle, devName, mDefaults->direction, 0)) < 0) {
+
+        // Try without the mode.
+        devName  = deviceName(AudioSystem::MODE_INVALID, device);
+
+        err = snd_pcm_open(&mHandle, devName, mDefaults->direction, 0);
+        if (err < 0) {
+
+              // Try without mode or device.
+              devName  = deviceName(AudioSystem::MODE_INVALID, -1);
+
+              err = snd_pcm_open(&mHandle, devName, mDefaults->direction, 0);
+              if (err < 0) {
+
+                       err = snd_pcm_open(&mHandle, "hw:00,0", mDefaults->direction, 0);
+
+                       if (err < 0) {
+                           LOGE("Unable to open fallback %s device: %s",
+                                stream, snd_strerror(err));
+
+                           // Last resort is the NULL device (i.e. the bit bucket).
+                                   err = snd_pcm_null_open(&mHandle, _nullALSADeviceName,
+                                                                                       mDefaults->direction, 0);
+                                   if (err < 0) {
+                                       LOG_FATAL("Unable to open NULL ALSA device: %s",
+                                                 snd_strerror(err));
+                                   }
+                                   LOGD("Opened NULL %s device.", streamName());
+                                   return err;
+                       }
+               }
+        }
+    }
+
+       mMode   = mode;
+       mDevice = device;
+
+    LOGI("Initialized ALSA %s device %s", stream, devName);
+    return err;
+}
+
+void ALSAStreamOps::close()
+{
+       snd_pcm_t *handle = mHandle;
+    mHandle = NULL;
+
+    if (handle) {
+        snd_pcm_close(handle);
+           mMode   = -1;
+           mDevice = -1;
+    }
+}
+
+status_t ALSAStreamOps::setSoftwareParams()
+{
+    if (!mHandle)
+        return NO_INIT;
+
+    int err;
+
+    // Get the current software parameters
+    err = snd_pcm_sw_params_current(mHandle, mSoftwareParams);
+    if (err < 0) {
+        LOGE("Unable to get software parameters: %s", snd_strerror(err));
+        return NO_INIT;
+    }
+
+    snd_pcm_uframes_t bufferSize = 0;
+    snd_pcm_uframes_t periodSize = 0;
+    snd_pcm_uframes_t startThreshold;
+
+    // Configure ALSA to start the transfer when the buffer is almost full.
+    snd_pcm_get_params(mHandle, &bufferSize, &periodSize);
+
+    if (mDefaults->direction == SND_PCM_STREAM_PLAYBACK) {
+        // For playback, configure ALSA to start the transfer when the
+        // buffer is almost full.
+        startThreshold = (bufferSize / periodSize) * periodSize;
+    } else {
+        // For recording, configure ALSA to start the transfer on the
+        // first frame.
+        startThreshold = 1;
+    }
+
+    err = snd_pcm_sw_params_set_start_threshold(mHandle,
+                                                mSoftwareParams,
+                                                startThreshold);
+    if (err < 0) {
+        LOGE("Unable to set start threshold to %lu frames: %s",
+             startThreshold, snd_strerror(err));
+        return NO_INIT;
+    }
+
+    // Stop the transfer when the buffer is full.
+    err = snd_pcm_sw_params_set_stop_threshold(mHandle,
+                                               mSoftwareParams,
+                                               bufferSize);
+    if (err < 0) {
+        LOGE("Unable to set stop threshold to %lu frames: %s",
+             bufferSize, snd_strerror(err));
+        return NO_INIT;
+    }
+
+    // Allow the transfer to start when at least periodSize samples can be
+    // processed.
+    err = snd_pcm_sw_params_set_avail_min(mHandle,
+                                          mSoftwareParams,
+                                          periodSize);
+    if (err < 0) {
+        LOGE("Unable to configure available minimum to %lu: %s",
+             periodSize, snd_strerror(err));
+        return NO_INIT;
+    }
+
+    // Commit the software parameters back to the device.
+    err = snd_pcm_sw_params(mHandle, mSoftwareParams);
+    if (err < 0) {
+        LOGE("Unable to configure software parameters: %s",
+             snd_strerror(err));
+        return NO_INIT;
+    }
+
+    return NO_ERROR;
+}
+
+status_t ALSAStreamOps::setPCMFormat(snd_pcm_format_t format)
+{
+    const char *formatDesc;
+    const char *formatName;
+    bool validFormat;
+    int err;
+
+    // snd_pcm_format_description() and snd_pcm_format_name() do not perform
+    // proper bounds checking.
+    validFormat = (static_cast<int>(format) > SND_PCM_FORMAT_UNKNOWN) &&
+        (static_cast<int>(format) <= SND_PCM_FORMAT_LAST);
+    formatDesc = validFormat ?
+        snd_pcm_format_description(format) : "Invalid Format";
+    formatName = validFormat ?
+        snd_pcm_format_name(format) : "UNKNOWN";
+
+    err = snd_pcm_hw_params_set_format(mHandle, mHardwareParams, format);
+    if (err < 0) {
+        LOGE("Unable to configure PCM format %s (%s): %s",
+             formatName, formatDesc, snd_strerror(err));
+        return NO_INIT;
+    }
+
+    LOGD("Set %s PCM format to %s (%s)", streamName(), formatName, formatDesc);
+    return NO_ERROR;
+}
+
+status_t ALSAStreamOps::setHardwareResample(bool resample)
+{
+    int err;
+
+    err = snd_pcm_hw_params_set_rate_resample(mHandle,
+                                              mHardwareParams,
+                                              static_cast<int>(resample));
+    if (err < 0) {
+        LOGE("Unable to %s hardware resampling: %s",
+             resample ? "enable" : "disable",
+             snd_strerror(err));
+        return NO_INIT;
+    }
+    return NO_ERROR;
+}
+
+const char *ALSAStreamOps::streamName()
+{
+    // Don't use snd_pcm_stream(mHandle), as the PCM stream may not be
+    // opened yet.  In such case, snd_pcm_stream() will abort().
+    return snd_pcm_stream_name(mDefaults->direction);
+}
+
+//
+// Set playback or capture PCM device.  It's possible to support audio output
+// or input from multiple devices by using the ALSA plugins, but this is
+// not supported for simplicity.
+//
+// The AudioHardwareALSA API does not allow one to set the input routing.
+//
+// If the "routes" value does not map to a valid device, the default playback
+// device is used.
+//
+status_t ALSAStreamOps::setDevice(int mode, uint32_t device)
+{
+    // Close off previously opened device.
+    // It would be nice to determine if the underlying device actually
+    // changes, but we might be manipulating mixer settings (see asound.conf).
+    //
+    close();
+
+    const char *stream = streamName();
+
+    status_t    status = open (mode, device);
+    int                        err;
+
+    if (status != NO_ERROR)
+        return status;
+
+    err = snd_pcm_hw_params_any(mHandle, mHardwareParams);
+    if (err < 0) {
+        LOGE("Unable to configure hardware: %s", snd_strerror(err));
+        return NO_INIT;
+    }
+
+    // Set the interleaved read and write format.
+    err = snd_pcm_hw_params_set_access(mHandle, mHardwareParams,
+                                       SND_PCM_ACCESS_RW_INTERLEAVED);
+    if (err < 0) {
+        LOGE("Unable to configure PCM read/write format: %s",
+             snd_strerror(err));
+        return NO_INIT;
+    }
+
+    status = setPCMFormat(mDefaults->format);
+
+    //
+    // Some devices do not have the default two channels.  Force an error to
+    // prevent AudioMixer from crashing and taking the whole system down.
+    //
+    // Note that some devices will return an -EINVAL if the channel count
+    // is queried before it has been set.  i.e. calling channelCount()
+    // before channelCount(channels) may return -EINVAL.
+    //
+    status = channelCount(mDefaults->channels);
+    if (status != NO_ERROR)
+        return status;
+
+    // Don't check for failure; some devices do not support the default
+    // 44100 Hz rate.
+    sampleRate(mDefaults->sampleRate);
+
+    // Disable hardware resampling.
+    status = setHardwareResample(false);
+    if (status != NO_ERROR)
+        return status;
+
+    unsigned int     bufferTime;
+    unsigned int     periodTime;
+
+    // Set the buffer time.
+    bufferTime = mDefaults->bufferTime;
+    err = snd_pcm_hw_params_set_buffer_time_near(mHandle,
+                                                 mHardwareParams,
+                                                 &bufferTime,
+                                                 0);
+    if (err < 0) {
+        LOGE("Unable to set buffer time to %u usec: %s",
+             bufferTime, snd_strerror(err));
+        return NO_INIT;
+    }
+
+    // Set the period time (i.e. the number of frames)
+    periodTime = mDefaults->periodTime;
+    err = snd_pcm_hw_params_set_period_time_near(mHandle,
+                                                 mHardwareParams,
+                                                 &periodTime,
+                                                 0);
+    if (err < 0) {
+        LOGE("Unable to set period time to %u usec: %s",
+             periodTime, snd_strerror(err));
+        return NO_INIT;
+    }
+
+    // Commit the hardware parameters back to the device.
+    err = snd_pcm_hw_params(mHandle, mHardwareParams);
+    if (err < 0) {
+        LOGE("Unable to set hardware parameters: %s", snd_strerror(err));
+        return NO_INIT;
+    }
+
+    status = setSoftwareParams();
+
+    return status;
+}
+
+// ----------------------------------------------------------------------------
+
+AudioStreamOutALSA::AudioStreamOutALSA(AudioHardwareALSA *parent) :
+       mParent(parent),
+       mPowerLock(false)
+{
+    static StreamDefaults _defaults =
+    {
+        deviceName     : "AndroidPlayback",
+        direction      : SND_PCM_STREAM_PLAYBACK,
+        format         : SND_PCM_FORMAT_S16_LE, // AudioSystem::PCM_16_BIT
+        channels       : 2,
+        sampleRate     : 44100,
+        bufferTime     : 500000, // Ring buffer length in usec, 1/2 second
+        periodTime     : 100000, // Period time in usec
+    };
+
+    setStreamDefaults(&_defaults);
+}
+
+AudioStreamOutALSA::~AudioStreamOutALSA()
+{
+       standby();
+       mParent->mOutput = NULL;
+}
+
+int AudioStreamOutALSA::channelCount() const
+{
+    int c;
+
+    c = ALSAStreamOps::channelCount();
+
+    // AudioMixer will seg fault if it doesn't have two channels.
+    LOGW_IF(c != 2,
+            "AudioMixer expects two channels, but only %i found!", c);
+    return c;
+}
+
+status_t AudioStreamOutALSA::setVolume(float volume)
+{
+       if (! mParent->mMixer || mDevice < 0)
+               return NO_INIT;
+
+       ALSAMixer::mixer_types mixer_type = static_cast<ALSAMixer::mixer_types>(mDevice);
+
+       return mParent->mMixer->setVolume (mixer_type, volume);
+}
+
+ssize_t AudioStreamOutALSA::write(const void *buffer, size_t bytes)
+{
+    snd_pcm_sframes_t n;
+    status_t          err;
+
+    AutoMutex lock(mLock);
+
+    if (isStandby())
+       return 0;
+
+    if (!mPowerLock) {
+        acquire_wake_lock (PARTIAL_WAKE_LOCK, "AudioLock");
+        ALSAStreamOps::setDevice(mMode, mDevice);
+        mPowerLock = true;
+    }
+
+    n = snd_pcm_writei(mHandle,
+                       buffer,
+                       snd_pcm_bytes_to_frames(mHandle, bytes));
+    if (n < 0 && mHandle) {
+        // snd_pcm_recover() will return 0 if successful in recovering from
+        // an error, or -errno if the error was unrecoverable.
+        n = snd_pcm_recover(mHandle, n, 0);
+    }
+
+    return static_cast<ssize_t>(n);
+}
+
+status_t AudioStreamOutALSA::dump(int fd, const Vector<String16>& args)
+{
+    return NO_ERROR;
+}
+
+status_t AudioStreamOutALSA::setDevice(int mode, uint32_t newDevice)
+{
+    uint32_t dev;
+
+    //
+    // Output to only one device.  The new device is the first selected bit
+    // in newDevice (per IAudioFlinger::ROUTE_*).
+    //
+    // It's possible to not output to any device (i.e. newDevice is 0).
+    //
+    dev = newDevice ? (ffs(static_cast<int>(newDevice)) - 1) : -1;
+
+    AutoMutex lock(mLock);
+
+    return ALSAStreamOps::setDevice(mode, dev);
+}
+
+const char *AudioStreamOutALSA::deviceName(int mode, int device)
+{
+    static char devString[PROPERTY_VALUE_MAX];
+       int hasDevExt = 0;
+
+       strcpy (devString, mDefaults->deviceName);
+
+    if (device >= 0 && device < deviceSuffixLen) {
+        strcat (devString, deviceSuffix[device]);
+        hasDevExt = 1;
+    }
+
+       if (hasDevExt)
+           switch (mode) {
+                       case AudioSystem::MODE_NORMAL:
+                       strcat (devString, "_normal");
+                               break;
+               case AudioSystem::MODE_RINGTONE:
+                       strcat (devString, "_ringtone");
+                               break;
+               case AudioSystem::MODE_IN_CALL:
+                       strcat (devString, "_incall");
+                               break;
+           };
+
+       return devString;
+}
+
+status_t AudioStreamOutALSA::standby()
+{
+    AutoMutex lock(mLock);
+
+    if (mHandle)
+        snd_pcm_drain (mHandle);
+
+    if (mPowerLock) {
+        release_wake_lock ("AudioLock");
+        mPowerLock = false;
+    }
+
+    return NO_ERROR;
+}
+
+bool AudioStreamOutALSA::isStandby()
+{
+    return (!mHandle);
+}
+
+// ----------------------------------------------------------------------------
+
+AudioStreamInALSA::AudioStreamInALSA(AudioHardwareALSA *parent) :
+       mParent(parent)
+{
+    static StreamDefaults _defaults =
+    {
+        deviceName     : "AndroidRecord",
+        direction      : SND_PCM_STREAM_CAPTURE,
+        format         : SND_PCM_FORMAT_S16_LE, // AudioSystem::PCM_16_BIT
+        channels       : 1,
+        sampleRate     : AudioRecord::DEFAULT_SAMPLE_RATE,
+        bufferTime     : 500000, // Ring buffer length in usec, 1/2 second
+        periodTime     : 100000, // Period time in usec
+    };
+
+    setStreamDefaults(&_defaults);
+}
+
+AudioStreamInALSA::~AudioStreamInALSA()
+{
+       mParent->mInput = NULL;
+}
+
+status_t AudioStreamInALSA::setGain(float gain)
+{
+       if (mParent->mMixer)
+               return mParent->mMixer->setMasterGain (gain);
+       else
+               return NO_INIT;
+}
+
+ssize_t AudioStreamInALSA::read(void *buffer, ssize_t bytes)
+{
+    snd_pcm_sframes_t n;
+    status_t          err;
+
+    AutoMutex lock(mLock);
+
+    n = snd_pcm_readi(mHandle,
+                      buffer,
+                      snd_pcm_bytes_to_frames(mHandle, bytes));
+    if (n < 0 && mHandle) {
+        n = snd_pcm_recover(mHandle, n, 0);
+    }
+
+    return static_cast<ssize_t>(n);
+}
+
+status_t AudioStreamInALSA::dump(int fd, const Vector<String16>& args)
+{
+    return NO_ERROR;
+}
+
+status_t AudioStreamInALSA::setDevice(int mode, uint32_t newDevice)
+{
+    AutoMutex lock(mLock);
+
+    // The AudioHardwareALSA API does not allow one to set the input routing.
+    // Only one input device (the microphone) is currently supported.
+    //
+    return ALSAStreamOps::setDevice(mode, AudioRecord::MIC_INPUT);
+}
+
+const char *AudioStreamInALSA::deviceName(int mode, int device)
+{
+    static char devString[PROPERTY_VALUE_MAX];
+
+       strcpy (devString, mDefaults->deviceName);
+    strcat (devString, "_Microphone");
+
+    return devString;
+}
+
+// ----------------------------------------------------------------------------
+
+struct ALSAMixer::mixer_info_t {
+       mixer_info_t() :
+           elem(0), min(0), max(100), mute(false)
+       {
+       }
+       snd_mixer_elem_t        *elem;
+       long                     min;
+       long                     max;
+       long                     volume;
+       bool                     mute;
+       char                     name[PROPERTY_VALUE_MAX];
+};
+
+static int initMixer (snd_mixer_t **mixer, const char *name)
+{
+       int err;
+
+    if ((err = snd_mixer_open(mixer, 0)) < 0) {
+        LOGE("Unable to open mixer: %s", snd_strerror(err));
+        return err;
+    }
+
+    if ((err = snd_mixer_attach(*mixer, name)) < 0) {
+        LOGE("Unable to attach mixer to device %s: %s",
+             name, snd_strerror(err));
+
+           if ((err = snd_mixer_attach(*mixer, "hw:00")) < 0) {
+               LOGE("Unable to attach mixer to device default: %s",
+                            snd_strerror(err));
+
+                       snd_mixer_close (*mixer);
+                       *mixer = NULL;
+                       return err;
+           }
+    }
+
+    if ((err = snd_mixer_selem_register(*mixer, NULL, NULL)) < 0) {
+        LOGE("Unable to register mixer elements: %s", snd_strerror(err));
+               snd_mixer_close (*mixer);
+               *mixer = NULL;
+               return err;
+    }
+
+    // Get the mixer controls from the kernel
+    if ((err = snd_mixer_load(*mixer)) < 0) {
+        LOGE("Unable to load mixer elements: %s", snd_strerror(err));
+               snd_mixer_close (*mixer);
+               *mixer = NULL;
+               return err;
+    }
+
+       return 0;
+}
+
+typedef int (*hasVolume_t)(snd_mixer_elem_t*);
+
+static hasVolume_t hasVolume[] =
+{
+       snd_mixer_selem_has_playback_volume,
+       snd_mixer_selem_has_capture_volume
+};
+
+typedef int (*getVolumeRange_t)(snd_mixer_elem_t*, long int*, long int*);
+
+static getVolumeRange_t getVolumeRange[] =
+{
+       snd_mixer_selem_get_playback_volume_range,
+       snd_mixer_selem_get_capture_volume_range
+};
+
+typedef int (*setVolume_t)(snd_mixer_elem_t*, long int);
+
+static setVolume_t setVol[] =
+{
+       snd_mixer_selem_set_playback_volume_all,
+       snd_mixer_selem_set_capture_volume_all
+};
+
+ALSAMixer::ALSAMixer()
+{
+    int err;
+
+       initMixer (&mMixer[SND_PCM_STREAM_PLAYBACK], "AndroidPlayback");
+       initMixer (&mMixer[SND_PCM_STREAM_CAPTURE], "AndroidRecord");
+
+    snd_mixer_selem_id_t *sid;
+    snd_mixer_selem_id_alloca(&sid);
+
+       for (int i = 0; i <= SND_PCM_STREAM_LAST; i++) {
+
+               mMaster[i] = new mixer_info_t;
+
+           property_get (mixerMasterProp[i].propName,
+                                         mMaster[i]->name,
+                                         mixerMasterProp[i].propDefault);
+
+               for (snd_mixer_elem_t *elem = snd_mixer_first_elem(mMixer[i]);
+                        elem;
+                elem = snd_mixer_elem_next(elem)) {
+
+                       if (!snd_mixer_selem_is_active(elem))
+                               continue;
+
+               snd_mixer_selem_get_id(elem, sid);
+
+               // Find PCM playback volume control element.
+               const char *elementName = snd_mixer_selem_id_get_name(sid);
+
+                       if (mMaster[i]->elem == NULL &&
+                           strcmp(elementName, mMaster[i]->name) == 0 &&
+                           hasVolume[i] (elem)) {
+
+                               mMaster[i]->elem = elem;
+                               getVolumeRange[i] (elem, &mMaster[i]->min, &mMaster[i]->max);
+                               mMaster[i]->volume = mMaster[i]->max;
+                           setVol[i] (elem, mMaster[i]->volume);
+                           if (i == SND_PCM_STREAM_PLAYBACK &&
+                               snd_mixer_selem_has_playback_switch (elem))
+                                               snd_mixer_selem_set_playback_switch_all (elem, 1);
+                           break;
+                       }
+        }
+
+               for (int j = 0; j <= MIXER_LAST; j++) {
+
+                       mInfo[i][j] = new mixer_info_t;
+
+                   property_get (mixerProp[i][j].propName,
+                                                 mInfo[i][j]->name,
+                                                 mixerProp[i][j].propDefault);
+
+                   for (snd_mixer_elem_t *elem = snd_mixer_first_elem(mMixer[i]);
+                                elem;
+                        elem = snd_mixer_elem_next(elem)) {
+
+                               if (!snd_mixer_selem_is_active(elem))
+                                       continue;
+
+                       snd_mixer_selem_get_id(elem, sid);
+
+                       // Find PCM playback volume control element.
+                       const char *elementName = snd_mixer_selem_id_get_name(sid);
+
+                               if (mInfo[i][j]->elem == NULL &&
+                                   strcmp(elementName, mInfo[i][j]->name) == 0 &&
+                                   hasVolume[i] (elem)) {
+
+                                       mInfo[i][j]->elem = elem;
+                                       getVolumeRange[i] (elem, &mInfo[i][j]->min, &mInfo[i][j]->max);
+                                       mInfo[i][j]->volume = mInfo[i][j]->max;
+                                   setVol[i] (elem, mInfo[i][j]->volume);
+                                   if (i == SND_PCM_STREAM_PLAYBACK &&
+                                       snd_mixer_selem_has_playback_switch (elem))
+                                                       snd_mixer_selem_set_playback_switch_all (elem, 1);
+                                   break;
+                               }
+                       }
+               }
+       }
+       LOGD("mixer initialized.");
+}
+
+ALSAMixer::~ALSAMixer()
+{
+       for (int i = 0; i <= SND_PCM_STREAM_LAST; i++) {
+           if (mMixer[i]) snd_mixer_close (mMixer[i]);
+           if (mMaster[i]) delete mMaster[i];
+               for (int j = 0; j <= MIXER_LAST; j++) {
+                   if (mInfo[i][j]) delete mInfo[i][j];
+               }
+       }
+    LOGD("mixer destroyed.");
+}
+
+status_t ALSAMixer::setMasterVolume(float volume)
+{
+       mixer_info_t *info = mMaster[SND_PCM_STREAM_PLAYBACK];
+    if (!info || !info->elem) return INVALID_OPERATION;
+
+    long minVol = info->min;
+    long maxVol = info->max;
+
+    // Make sure volume is between bounds.
+    long vol = minVol + volume * (maxVol - minVol);
+    if (vol > maxVol) vol = maxVol;
+    if (vol < minVol) vol = minVol;
+
+    info->volume = vol;
+    snd_mixer_selem_set_playback_volume_all (info->elem, vol);
+
+    return NO_ERROR;
+}
+
+status_t ALSAMixer::setMasterGain(float gain)
+{
+       mixer_info_t *info = mMaster[SND_PCM_STREAM_CAPTURE];
+    if (!info || !info->elem) return INVALID_OPERATION;
+
+    long minVol = info->min;
+    long maxVol = info->max;
+
+    // Make sure volume is between bounds.
+    long vol = minVol + gain * (maxVol - minVol);
+    if (vol > maxVol) vol = maxVol;
+    if (vol < minVol) vol = minVol;
+
+    info->volume = vol;
+    snd_mixer_selem_set_capture_volume_all (info->elem, vol);
+
+    return NO_ERROR;
+}
+
+status_t ALSAMixer::setVolume(mixer_types mixer, float volume)
+{
+       mixer_info_t *info = mInfo[mixer][SND_PCM_STREAM_PLAYBACK];
+    if (!info || !info->elem) return INVALID_OPERATION;
+
+    long minVol = info->min;
+    long maxVol = info->max;
+
+    // Make sure volume is between bounds.
+    long vol = minVol + volume * (maxVol - minVol);
+    if (vol > maxVol) vol = maxVol;
+    if (vol < minVol) vol = minVol;
+
+    info->volume = vol;
+    snd_mixer_selem_set_playback_volume_all (info->elem, vol);
+
+    return NO_ERROR;
+}
+
+status_t ALSAMixer::setGain(mixer_types mixer, float gain)
+{
+       mixer_info_t *info = mInfo[mixer][SND_PCM_STREAM_CAPTURE];
+    if (!info || !info->elem) return INVALID_OPERATION;
+
+    long minVol = info->min;
+    long maxVol = info->max;
+
+    // Make sure volume is between bounds.
+    long vol = minVol + gain * (maxVol - minVol);
+    if (vol > maxVol) vol = maxVol;
+    if (vol < minVol) vol = minVol;
+
+    info->volume = vol;
+    snd_mixer_selem_set_capture_volume_all (info->elem, vol);
+
+    return NO_ERROR;
+}
+
+status_t ALSAMixer::setCaptureMuteState(mixer_types mixer, bool state)
+{
+       mixer_info_t *info = mInfo[mixer][SND_PCM_STREAM_CAPTURE];
+    if (!info || !info->elem) return INVALID_OPERATION;
+
+    if (info->mute == state) return NO_ERROR;
+
+    if (snd_mixer_selem_has_capture_switch (info->elem)) {
+
+       int err = snd_mixer_selem_set_capture_switch_all (info->elem, static_cast<int>(!state));
+       if (err < 0) {
+           LOGE("Unable to %s capture mixer switch %s",
+                state ? "enable" : "disable", info->name);
+           return INVALID_OPERATION;
+       }
+    }
+
+    info->mute = state;
+    return NO_ERROR;
+}
+
+status_t ALSAMixer::getCaptureMuteState(mixer_types mixer, bool *state)
+{
+       mixer_info_t *info = mInfo[mixer][SND_PCM_STREAM_CAPTURE];
+    if (!info || !info->elem) return INVALID_OPERATION;
+
+    if (! state) return BAD_VALUE;
+
+    *state = info->mute;
+
+    return NO_ERROR;
+}
+
+status_t ALSAMixer::setPlaybackMuteState(mixer_types mixer, bool state)
+{
+       mixer_info_t *info = mInfo[mixer][SND_PCM_STREAM_PLAYBACK];
+    if (!info || !info->elem) return INVALID_OPERATION;
+
+    if (snd_mixer_selem_has_playback_switch (info->elem)) {
+
+       int err = snd_mixer_selem_set_playback_switch_all (info->elem, static_cast<int>(!state));
+       if (err < 0) {
+           LOGE("Unable to %s playback mixer switch %s",
+                state ? "enable" : "disable", info->name);
+           return INVALID_OPERATION;
+       }
+    }
+
+    info->mute = state;
+    return NO_ERROR;
+}
+
+status_t ALSAMixer::getPlaybackMuteState(mixer_types mixer, bool *state)
+{
+       mixer_info_t *info = mInfo[SND_PCM_STREAM_PLAYBACK][mixer];
+    if (!info || !info->elem) return INVALID_OPERATION;
+
+    if (! state) return BAD_VALUE;
+
+    *state = info->mute;
+
+    return NO_ERROR;
+}
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
diff --git a/AudioHardwareALSA.h b/AudioHardwareALSA.h
new file mode 100644 (file)
index 0000000..12d875c
--- /dev/null
@@ -0,0 +1,266 @@
+/* AudioHardwareALSA.h
+**
+** Copyright 2008, Wind River Systems
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef ANDROID_AUDIO_HARDWARE_ALSA_H
+#define ANDROID_AUDIO_HARDWARE_ALSA_H
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <alsa/asoundlib.h>
+
+#include <hardware/AudioHardwareInterface.h>
+
+namespace android {
+
+class AudioHardwareALSA;
+
+// ----------------------------------------------------------------------------
+
+class ALSAMixer
+{
+public:
+    //
+    // Keep this in sync with AudioSystem::audio_routes
+    //
+    enum mixer_types {
+        MIXER_EARPIECE  = 0,
+        MIXER_SPEAKER   = 1,
+        MIXER_BLUETOOTH = 2,
+        MIXER_HEADSET   = 3,
+        MIXER_LAST      = MIXER_HEADSET
+    };
+
+                                         ALSAMixer();
+    virtual                     ~ALSAMixer();
+
+                bool                     isValid() { return !!mMixer[SND_PCM_STREAM_PLAYBACK]; }
+                status_t                 setMasterVolume(float volume);
+            status_t             setMasterGain(float gain);
+
+        status_t                 setVolume(mixer_types mixer, float volume);
+            status_t             setGain(mixer_types mixer, float gain);
+
+            status_t             setCaptureMuteState(mixer_types mixer, bool state);
+            status_t         getCaptureMuteState(mixer_types mixer, bool *state);
+            status_t             setPlaybackMuteState(mixer_types mixer, bool state);
+            status_t         getPlaybackMuteState(mixer_types mixer, bool *state);
+
+private:
+    snd_mixer_t             *mMixer[SND_PCM_STREAM_LAST+1];
+
+    struct mixer_info_t;
+    mixer_info_t *mMaster[SND_PCM_STREAM_LAST+1];
+    mixer_info_t *mInfo[SND_PCM_STREAM_LAST+1][MIXER_LAST+1];
+};
+
+class ALSAStreamOps
+{
+public:
+    struct StreamDefaults
+    {
+        const char *            deviceName;
+        snd_pcm_stream_t        direction;  // playback or capture
+        snd_pcm_format_t        format;
+        int                                     channels;
+        uint32_t                        sampleRate;
+        unsigned int            bufferTime; // Ring buffer length in usec
+        unsigned int            periodTime; // Period time in usec
+    };
+
+                             ALSAStreamOps();
+    virtual                  ~ALSAStreamOps();
+
+            status_t         set(int format,
+                                 int channels,
+                                 uint32_t rate);
+    virtual uint32_t         sampleRate() const;
+            status_t         sampleRate(uint32_t rate);
+    virtual size_t           bufferSize() const;
+    virtual int              format() const;
+    virtual int              channelCount() const;
+            status_t         channelCount(int channels);
+            const char      *streamName();
+    virtual status_t         setDevice(int mode, uint32_t device);
+
+    virtual const char      *deviceName(int mode, int device) = 0;
+
+protected:
+    friend class AudioStreamOutALSA;
+    friend class AudioStreamInALSA;
+
+    status_t                 open(int mode, int device);
+    void                     close();
+    status_t                 setSoftwareParams();
+    status_t                 setPCMFormat(snd_pcm_format_t format);
+    status_t                 setHardwareResample(bool resample);
+
+    void                     setStreamDefaults(StreamDefaults *dev)
+    {
+        mDefaults = dev;
+    }
+
+    Mutex                    mLock;
+
+private:
+    snd_pcm_t               *mHandle;
+    snd_pcm_hw_params_t     *mHardwareParams;
+    snd_pcm_sw_params_t     *mSoftwareParams;
+    int                      mMode;
+    int                      mDevice;
+
+    StreamDefaults                      *mDefaults;
+};
+
+// ----------------------------------------------------------------------------
+
+class AudioStreamOutALSA : public AudioStreamOut, public ALSAStreamOps
+{
+public:
+                             AudioStreamOutALSA(AudioHardwareALSA *parent);
+    virtual                  ~AudioStreamOutALSA();
+
+    status_t                 set(int format          = 0,
+                                 int channelCount    = 0,
+                                 uint32_t sampleRate = 0)
+    {
+        return ALSAStreamOps::set(format, channelCount, sampleRate);
+    }
+
+    virtual uint32_t         sampleRate() const
+    {
+        return ALSAStreamOps::sampleRate();
+    }
+
+    virtual size_t           bufferSize() const
+    {
+        return ALSAStreamOps::bufferSize();
+    }
+
+    virtual int              channelCount() const;
+
+    virtual int              format() const
+    {
+        return ALSAStreamOps::format();
+    }
+
+    virtual ssize_t          write(const void *buffer, size_t bytes);
+    virtual status_t         dump(int fd, const Vector<String16>& args);
+    virtual status_t         setDevice(int mode, uint32_t newDevice);
+
+                        status_t         setVolume(float volume);
+
+    virtual const char      *deviceName(int mode, int device);
+
+    status_t                 standby();
+    bool                     isStandby();
+
+private:
+        AudioHardwareALSA       *mParent;
+        bool                     mPowerLock;
+};
+
+
+class AudioStreamInALSA : public AudioStreamIn, public ALSAStreamOps
+{
+public:
+                             AudioStreamInALSA(AudioHardwareALSA *parent);
+    virtual                  ~AudioStreamInALSA();
+
+    status_t                 set(int      format       = 0,
+                                 int      channelCount = 0,
+                                 uint32_t sampleRate   = 0)
+    {
+        return ALSAStreamOps::set(format, channelCount, sampleRate);
+    }
+
+    virtual uint32_t         sampleRate()
+    {
+        return ALSAStreamOps::sampleRate();
+    }
+
+    virtual size_t           bufferSize() const
+    {
+        return ALSAStreamOps::bufferSize();
+    }
+
+    virtual int              channelCount() const
+    {
+        return ALSAStreamOps::channelCount();
+    }
+
+    virtual int              format() const
+    {
+        return ALSAStreamOps::format();
+    }
+
+    virtual ssize_t          read(void* buffer, ssize_t bytes);
+    virtual status_t         dump(int fd, const Vector<String16>& args);
+    virtual status_t         setDevice(int mode, uint32_t newDevice);
+
+        virtual status_t         setGain(float gain);
+
+    virtual const char      *deviceName(int mode, int device);
+
+private:
+        AudioHardwareALSA *mParent;
+};
+
+
+class AudioHardwareALSA : public AudioHardwareInterface
+{
+public:
+                             AudioHardwareALSA();
+    virtual                  ~AudioHardwareALSA();
+
+    virtual status_t         initCheck();
+    virtual status_t         standby();
+    virtual status_t         setVoiceVolume(float volume);
+    virtual status_t         setMasterVolume(float volume);
+
+    virtual AudioStreamOut  *openOutputStream(int format          = 0,
+                                              int channelCount    = 0,
+                                              uint32_t sampleRate = 0);
+
+    virtual AudioStreamIn   *openInputStream (int format          = 0,
+                                              int channelCount    = 0,
+                                              uint32_t sampleRate = 0);
+
+    // Microphone mute
+    virtual status_t         setMicMute(bool state);
+    virtual status_t         getMicMute(bool *state);
+
+protected:
+    // audio routing
+    virtual status_t         doRouting();
+    virtual status_t         dump(int fd, const Vector<String16>& args);
+
+    friend class AudioStreamOutALSA;
+    friend class AudioStreamInALSA;
+
+    ALSAMixer               *mMixer;
+    AudioStreamOutALSA      *mOutput;
+    AudioStreamInALSA       *mInput;
+
+private:
+    Mutex                    mLock;
+};
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // ANDROID_AUDIO_HARDWARE_ALSA_H
index 7387b3d..68fa70e 100644 (file)
@@ -1,6 +1,7 @@
 /*
 **
 ** Copyright 2007, The Android Open Source Project
+** Copyright 2008 Wind River Systems
 **
 ** Licensed under the Apache License, Version 2.0 (the "License"); 
 ** you may not use this file except in compliance with the License. 
@@ -24,7 +25,7 @@
 #include <utils/String8.h>
 
 #include "AudioHardwareStub.h"
-#include "AudioHardwareGeneric.h"
+#include "AudioHardwareALSA.h"
 
 // #define DUMP_FLINGER_OUT        // if defined allows recording samples in a file
 #ifdef DUMP_FLINGER_OUT
@@ -92,19 +93,8 @@ AudioHardwareInterface* AudioHardwareInterface::create()
     AudioHardwareInterface* hw = 0;
     char value[PROPERTY_VALUE_MAX];
 
-#ifdef GENERIC_AUDIO
-    hw = new AudioHardwareGeneric();
-#else
-    // if running in emulation - use the emulator driver
-    if (property_get("ro.kernel.qemu", value, 0)) {
-        LOGD("Running in emulation - using generic audio driver");
-        hw = new AudioHardwareGeneric();
-    }
-    else {
-        LOGV("Creating Vendor Specific AudioHardware");
-        hw = createAudioHardware();
-    }
-#endif
+    hw = new AudioHardwareALSA();
+
     if (hw->initCheck() != NO_ERROR) {
         LOGW("Using stubbed audio hardware. No sound will be produced.");
         delete hw;
diff --git a/MODULE_LICENSE_APACHE2 b/MODULE_LICENSE_APACHE2
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/NOTICE b/NOTICE
new file mode 100644 (file)
index 0000000..ada44e1
--- /dev/null
+++ b/NOTICE
@@ -0,0 +1,191 @@
+
+   Copyright (c) 2005-2008, The Android Open Source Project
+   Copyright 2008 Wind River Systems
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+