Initial commit: TinyALSA-Audio libaudio for gingerbread
authorPaulK <contact@paulk.fr>
Sun, 6 May 2012 14:09:41 +0000 (16:09 +0200)
committerPaulK <contact@paulk.fr>
Sun, 6 May 2012 14:09:41 +0000 (16:09 +0200)
This is based on TinyHardware and Nexus S libaudio, with XML configuration.
Some features are missing from this release: see the Missing sections.

Signed-off-by: PaulK <contact@paulk.fr>
Android.mk [new file with mode: 0644]
AudioHardware.cpp [new file with mode: 0644]
AudioHardware.h [new file with mode: 0644]
AudioPolicyManager.cpp [new file with mode: 0644]
AudioPolicyManager.h [new file with mode: 0644]
AudioStreamIn.cpp [new file with mode: 0644]
AudioStreamIn.h [new file with mode: 0644]
AudioStreamOut.cpp [new file with mode: 0644]
AudioStreamOut.h [new file with mode: 0644]
Mixer.cpp [new file with mode: 0644]
Mixer.h [new file with mode: 0644]

diff --git a/Android.mk b/Android.mk
new file mode 100644 (file)
index 0000000..375ac27
--- /dev/null
@@ -0,0 +1,51 @@
+LOCAL_PATH := $(call my-dir)
+
+ifeq  ($(strip $(BOARD_USES_TINYALSA_LIBAUDIO)),true)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+       AudioHardware.cpp \
+       AudioStreamOut.cpp \
+       AudioStreamIn.cpp \
+       Mixer.cpp
+
+LOCAL_C_INCLUDES += \
+       external/expat/lib \
+       external/tinyalsa/include
+
+LOCAL_STATIC_LIBRARIES += libaudiointerface
+LOCAL_SHARED_LIBRARIES := \
+       libtinyalsa \
+       libexpat \
+       libcutils \
+       libutils \
+       libmedia \
+       libhardware \
+       libhardware_legacy \
+       libc
+
+LOCAL_PRELINK_MODULE := false
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE := libaudio
+
+include $(BUILD_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+       AudioPolicyManager.cpp
+
+LOCAL_WHOLE_STATIC_LIBRARIES += libaudiopolicybase
+LOCAL_SHARED_LIBRARIES := \
+       libcutils \
+       libutils \
+       libmedia
+
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE := libaudiopolicy
+
+include $(BUILD_SHARED_LIBRARY)
+
+endif
diff --git a/AudioHardware.cpp b/AudioHardware.cpp
new file mode 100644 (file)
index 0000000..0b1f256
--- /dev/null
@@ -0,0 +1,242 @@
+/*
+ * Copyright (C) 2012 Paul Kocialkowski <contact@paulk.fr>
+ * 
+ * This is based on TinyHardware:
+ * Copyright 2011 Wolfson Microelectronics plc
+ *
+ * This is based on Nexus S AudioHardware implementation:
+ * Copyright 2010, The Android Open-Source Project
+ *
+ * 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.
+ */
+
+#define LOG_TAG "AudioHardware"
+
+#include "tinyalsa/asoundlib.h"
+
+#include "AudioHardware.h"
+#include "AudioStreamOut.h"
+#include "AudioStreamIn.h"
+
+namespace android {
+
+AudioHardware::AudioHardware() :
+       mInit(false),
+       mInput(NULL),
+       mOutput(NULL),
+       mInDevices(0),
+       mOutDevices(0),
+       mMicMute(false)
+{
+       LOGD("AudioHardware()");
+
+       mInit = true;
+}
+
+AudioHardware::~AudioHardware()
+{
+       LOGD("~AudioHardware()");
+
+       mInit = false;
+       closeOutputStream((AudioStreamOut *) mOutput);
+       closeInputStream((AudioStreamIn *) mInput);
+}
+
+status_t AudioHardware::initCheck()
+{
+       LOGD("initCheck()");
+
+       return mInit ? NO_ERROR : NO_INIT;
+}
+
+status_t AudioHardware::setVoiceVolume(float volume)
+{
+       LOGD("setVoiceVolume(%f)", volume);
+
+       return NO_ERROR;
+}
+
+status_t AudioHardware::setMasterVolume(float volume)
+{
+       LOGD("setMasterVolume(%f)", volume);
+
+       // We return an error code here to let the audioflinger do in-software
+       // volume on top of the maximum volume that we set through the SND API.
+       // return error - software mixer will handle it
+       return -1;
+}
+
+status_t AudioHardware::setMode(int mode)
+{
+       LOGD("setMode(%d)", mode);
+
+       return NO_ERROR;
+}
+
+status_t AudioHardware::setMicMute(bool state)
+{
+       LOGD("setMicMute(%d)", state);
+
+       mMicMute = state;
+
+       return NO_ERROR;
+}
+
+status_t AudioHardware::getMicMute(bool* state)
+{
+       LOGD("getMicMute()");
+
+       *state = mMicMute;
+       return NO_ERROR;
+}
+
+status_t AudioHardware::setParameters(const String8& keyValuePairs)
+{
+       LOGD("setParameters()");
+
+       AudioParameter param = AudioParameter(keyValuePairs);
+
+       return NO_ERROR;
+}
+
+String8 AudioHardware::getParameters(const String8& keys)
+{
+       LOGD("getParameters()");
+
+       AudioParameter request = AudioParameter(keys);
+       AudioParameter reply = AudioParameter();
+
+       return reply.toString();
+}
+
+size_t AudioHardware::getInputBufferSize(uint32_t sampleRate, int format, int channelCount)
+{
+       LOGD("getInputBufferSize(%d, %d, %d)", sampleRate, format, channelCount);
+
+       if(!mInput) {
+               LOGE("Input wasn't initialized yet!");
+               return 0;
+       }
+
+       return mInput->getInputBufferSize(sampleRate, format, channelCount);
+}
+
+AudioStreamOut* AudioHardware::openOutputStream(
+       uint32_t devices, int *format, uint32_t *channels,
+       uint32_t *sampleRate, status_t *status)
+{
+       status_t o_status;
+
+       AutoMutex lock(mLock);
+
+       LOGD("openOutputStream()");
+
+       if(mOutput) {
+               if (status)
+                       *status = INVALID_OPERATION;
+
+               LOGE("There is already an active outputStream!");
+               return NULL;
+       }
+
+       TinyALSAAudioStreamOut *output = new TinyALSAAudioStreamOut();
+
+       o_status = output->set(this, devices, format, channels, sampleRate);
+       if(o_status) {
+               *status = o_status;
+       }
+
+       if(o_status == NO_ERROR) {
+               mOutput = output;
+       } else {
+               delete output;
+       }
+
+       return output;
+}
+
+AudioStreamIn* AudioHardware::openInputStream(
+       uint32_t devices, int *format, uint32_t *channels,
+       uint32_t *sampleRate, status_t *status,
+       AudioSystem::audio_in_acoustics acoustic_flags)
+{
+       status_t i_status;
+
+       AutoMutex lock(mLock);
+
+       LOGD("openInputStream()");
+
+       // check for valid input source
+       if(!AudioSystem::isInputDevice((AudioSystem::audio_devices)devices)) {
+               return NULL;
+       }
+
+       if(mInput) {
+               if (status)
+                       *status = INVALID_OPERATION;
+
+               LOGE("There is already an active inputStream!");
+               return NULL;
+       }
+
+       TinyALSAAudioStreamIn *input = new TinyALSAAudioStreamIn();
+
+       i_status = input->set(this, devices, format, channels, sampleRate,
+               acoustic_flags);
+       if(i_status) {
+               *status = i_status;
+       }
+
+       if(i_status == NO_ERROR) {
+               mInput = input;
+       } else {
+               delete input;
+       }
+
+       return input;
+}
+
+void AudioHardware::closeOutputStream(AudioStreamOut* out)
+{
+       LOGD("closeOutputStream()");
+
+       if(mOutput && mOutput == out) {
+               delete mOutput;
+               mOutput = NULL;
+       }
+}
+
+void AudioHardware::closeInputStream(AudioStreamIn* in)
+{
+       LOGD("closeInputStream()");
+
+       if(mInput && mInput == in) {
+               delete mInput;
+               mInput = NULL;
+       }
+}
+
+status_t AudioHardware::dump(int fd, const Vector<String16>& args)
+{
+       LOGD("dump()");
+
+       return NO_ERROR;
+}
+
+// Main function called to return AudioHardware object
+extern "C" AudioHardwareInterface* createAudioHardware(void)
+{
+       return new AudioHardware();
+}
+
+}; // namespace android
diff --git a/AudioHardware.h b/AudioHardware.h
new file mode 100644 (file)
index 0000000..f8e5d9c
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2012 Paul Kocialkowski <contact@paulk.fr>
+ * 
+ * This is based on TinyHardware:
+ * Copyright 2011 Wolfson Microelectronics plc
+ *
+ * This is based on Nexus S AudioHardware implementation:
+ * Copyright 2010, The Android Open-Source Project
+ *
+ * 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 TINYALSA_AUDIO_HARDWARE_H
+#define TINYALSA_AUDIO_HARDWARE_H
+
+#include <hardware_legacy/AudioHardwareBase.h>
+#include "AudioStreamOut.h"
+#include "AudioStreamIn.h"
+
+namespace android {
+
+class TinyALSAAudioStreamOut;
+
+class AudioHardware : public AudioHardwareBase
+{
+public:
+       AudioHardware();
+       virtual ~AudioHardware();
+       virtual status_t initCheck();
+
+       virtual status_t setVoiceVolume(float volume);
+       virtual status_t setMasterVolume(float volume);
+
+       virtual status_t setMode(int mode);
+
+       virtual status_t setMicMute(bool state);
+       virtual status_t getMicMute(bool* state);
+
+       virtual status_t setParameters(const String8& keyValuePairs);
+       virtual String8 getParameters(const String8& keys);
+
+       virtual size_t getInputBufferSize(
+               uint32_t sampleRate, int format, int channelCount);
+
+       virtual AudioStreamOut* openOutputStream(
+               uint32_t devices, int *format=0, uint32_t *channels=0,
+               uint32_t *sampleRate=0, status_t *status=0);
+
+       virtual AudioStreamIn* openInputStream(
+               uint32_t devices, int *format, uint32_t *channels,
+               uint32_t *sampleRate, status_t *status,
+               AudioSystem::audio_in_acoustics acoustics);
+
+       virtual void closeOutputStream(AudioStreamOut* out);
+       virtual void closeInputStream(AudioStreamIn* in);
+
+protected:
+       virtual status_t dump(int fd, const Vector<String16>& args);
+
+private:
+       Mutex mLock;
+       bool mInit;
+       unsigned int mOutDevices;
+       unsigned int mInDevices;
+       bool mMicMute;
+       TinyALSAAudioStreamOut *mOutput;
+       TinyALSAAudioStreamIn *mInput;
+};
+
+}; // namespace android
+
+#endif
diff --git a/AudioPolicyManager.cpp b/AudioPolicyManager.cpp
new file mode 100644 (file)
index 0000000..099e69a
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2012 Paul Kocialkowski <contact@paulk.fr>
+ * 
+ * This is based on TinyHardware:
+ * Copyright 2011 Wolfson Microelectronics plc
+ *
+ * This is based on Nexus S AudioHardware implementation:
+ * Copyright 2010, The Android Open-Source Project
+ *
+ * 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.
+ */
+
+#define LOG_TAG "AudioPolicyManager"
+
+#include <utils/Log.h>
+#include "AudioPolicyManager.h"
+
+namespace android {
+
+// Nothing currently different between the Base implementation.
+
+AudioPolicyManager::AudioPolicyManager
+       (AudioPolicyClientInterface *clientInterface)
+       : AudioPolicyManagerBase(clientInterface)
+{
+}
+
+AudioPolicyManager::~AudioPolicyManager()
+{
+}
+
+extern "C" AudioPolicyInterface* createAudioPolicyManager
+       (AudioPolicyClientInterface *clientInterface)
+{
+       return new AudioPolicyManager(clientInterface);
+}
+
+extern "C" void destroyAudioPolicyManager(AudioPolicyInterface *interface)
+{
+       delete interface;
+}
+
+}; // namespace android
diff --git a/AudioPolicyManager.h b/AudioPolicyManager.h
new file mode 100644 (file)
index 0000000..f888627
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2012 Paul Kocialkowski <contact@paulk.fr>
+ * 
+ * This is based on TinyHardware:
+ * Copyright 2011 Wolfson Microelectronics plc
+ *
+ * This is based on Nexus S AudioHardware implementation:
+ * Copyright 2010, The Android Open-Source Project
+ *
+ * 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 <stdint.h>
+#include <sys/types.h>
+#include <utils/Timers.h>
+#include <utils/Errors.h>
+#include <utils/KeyedVector.h>
+#include <hardware_legacy/AudioPolicyManagerBase.h>
+
+namespace android {
+
+// Nothing currently different between the Base implementation.
+
+class AudioPolicyManager: public AudioPolicyManagerBase
+{
+public:
+       AudioPolicyManager(AudioPolicyClientInterface *clientInterface);
+       virtual ~AudioPolicyManager();
+};
+
+}; // namespace android
diff --git a/AudioStreamIn.cpp b/AudioStreamIn.cpp
new file mode 100644 (file)
index 0000000..7fbfe68
--- /dev/null
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2012 Paul Kocialkowski <contact@paulk.fr>
+ * 
+ * This is based on TinyHardware:
+ * Copyright 2011 Wolfson Microelectronics plc
+ *
+ * This is based on Nexus S AudioHardware implementation:
+ * Copyright 2010, The Android Open-Source Project
+ *
+ * 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.
+ */
+
+#define LOG_TAG "AudioHardware"
+
+#include "tinyalsa/asoundlib.h"
+
+#include "AudioHardware.h"
+#include "AudioStreamIn.h"
+
+/* 
+ * Missing:
+ * - Mixer support
+ * - Downsampler to record at the rate we want with out stream opened
+ */
+
+namespace android {
+
+TinyALSAAudioStreamIn::TinyALSAAudioStreamIn() :
+       mAudioHardware(NULL),
+       mPcm(NULL),
+       mDevice(0)
+{
+       LOGD("TinyALSAAudioStreamIn()");
+}
+
+TinyALSAAudioStreamIn::~TinyALSAAudioStreamIn()
+{
+       LOGD("~TinyALSAAudioStreamIn()");
+
+       if(mPcm)
+               pcm_close(mPcm);
+}
+
+status_t TinyALSAAudioStreamIn::set(AudioHardware *hw, uint32_t devices,
+       int *pFormat, uint32_t *pChannels, uint32_t *pRate,
+       AudioSystem::audio_in_acoustics acoustics)
+{
+       struct pcm_config pcmConfig;
+
+       LOGD("TinyALSAAudioStreamIn::set()");
+
+       if (pFormat == 0 || pChannels == 0 || pRate == 0)
+               return BAD_VALUE;
+
+       // Setting globals
+       mAudioHardware = hw;
+       mDevice = devices;
+
+       mChannels = *pChannels;
+       mChannelCount = AudioSystem::popCount(mChannels);
+       mSampleRate = *pRate;
+
+       mSampleRate = 44100;
+
+       if(*pFormat != format() || *pChannels != channels() || 
+               *pRate != sampleRate()) {
+
+               LOGE("Format mismatch opening input: %x/%x, %x/%x, %d/%d",
+                       *pFormat, format(), *pChannels, channels(),
+                       *pRate, sampleRate());
+
+               *pFormat = format();
+               *pChannels = channels();
+               *pRate = sampleRate();
+
+               return BAD_VALUE;
+       }
+
+       memset(&pcmConfig, 0, sizeof(pcmConfig));
+       pcmConfig.channels = mChannelCount;
+       pcmConfig.period_size = 1024;
+       pcmConfig.period_count = 4;
+       pcmConfig.format = PCM_FORMAT_S16_LE;
+
+       //TODO: get to know the rate correctly
+       pcmConfig.rate = mSampleRate;
+
+       mPcm = pcm_open(0, 0, PCM_IN, &pcmConfig);
+
+       if(!mPcm || !::pcm_is_ready(mPcm)) {
+               LOGE("TinyALSAAudioStreamIn::set() failed: %s\n",
+                       ::pcm_get_error(mPcm));
+               pcm_close(mPcm);
+
+               return BAD_VALUE;
+       }
+
+       //TODO: Sort the controls
+
+       return NO_ERROR;
+}
+
+ssize_t TinyALSAAudioStreamIn::read(void* buffer, ssize_t bytes)
+{
+       AutoMutex lock(mLock);
+
+       if(!::pcm_is_ready(mPcm)) {
+               // This should never happen...
+               return BAD_VALUE;
+       }
+
+       if(pcm_read(mPcm, buffer, bytes) != 0) {
+               LOGE("TinyALSAAudioStreamIn::read() failed: %s",
+                       ::pcm_get_error(mPcm));
+               return BAD_VALUE;
+       }
+
+       return NO_ERROR;
+}
+
+size_t TinyALSAAudioStreamIn::getInputBufferSize(uint32_t sampleRate, 
+       int inputFormat, int channelCount)
+{
+       LOGD("TinyALSAAudioStreamIn::getInputBufferSize()");
+
+       if(!mPcm) {
+               LOGE("PCM wasn't initialized yet!");
+               return 0;
+       }
+
+       return pcm_get_buffer_size(mPcm);
+}
+
+status_t TinyALSAAudioStreamIn::dump(int fd, const Vector<String16>& args)
+{
+       return NO_ERROR;
+}
+
+status_t TinyALSAAudioStreamIn::setParameters(const String8& keyValuePairs)
+{
+       AudioParameter param = AudioParameter(keyValuePairs);
+       String8 key = String8(AudioParameter::keyRouting);
+       int device;
+
+       LOGD("TinyALSAAudioStreamIn::setParameters(%s)",
+               keyValuePairs.string());
+
+       if(param.getInt(key, device) == NO_ERROR) {
+               // TODO: change routing
+               mDevice = device;
+               param.remove(key);
+       }
+
+       if(param.size()) {
+               return BAD_VALUE;
+       }
+
+       return NO_ERROR;
+}
+
+String8 TinyALSAAudioStreamIn::getParameters(const String8& keys)
+{
+       AudioParameter param = AudioParameter(keys);
+       String8 key = String8(AudioParameter::keyRouting);
+       String8 value;
+
+       LOGD("TinyALSAAudioStreamIn::getParameters()");
+
+       if(param.get(key, value) == NO_ERROR) {
+               param.addInt(key, (int) mDevice);
+       }
+
+       return param.toString();
+}
+
+}; // namespace android
diff --git a/AudioStreamIn.h b/AudioStreamIn.h
new file mode 100644 (file)
index 0000000..2b835e0
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2012 Paul Kocialkowski <contact@paulk.fr>
+ * 
+ * This is based on TinyHardware:
+ * Copyright 2011 Wolfson Microelectronics plc
+ *
+ * This is based on Nexus S AudioHardware implementation:
+ * Copyright 2010, The Android Open-Source Project
+ *
+ * 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 TINYALSA_AUDIO_STREAM_IN_H
+#define TINYALSA_AUDIO_STREAM_IN_H
+
+#include <hardware_legacy/AudioHardwareBase.h>
+#include "AudioHardware.h"
+
+namespace android {
+
+class AudioHardware;
+
+class TinyALSAAudioStreamIn : public AudioStreamIn
+{
+public:
+       TinyALSAAudioStreamIn();
+       virtual ~TinyALSAAudioStreamIn();
+
+       virtual status_t set(AudioHardware *hw, uint32_t devices,
+               int *pFormat, uint32_t *pChannels, uint32_t *pRate,
+               AudioSystem::audio_in_acoustics acoustics);
+
+       virtual uint32_t sampleRate() const { return mSampleRate; }
+       virtual size_t bufferSize() const { return 320; }
+       virtual uint32_t channels() const { return AudioSystem::CHANNEL_IN_MONO; }
+       virtual int format() const { return AudioSystem::PCM_16_BIT; }
+       virtual status_t setGain(float gain) { return INVALID_OPERATION; }
+       virtual status_t standby() { return NO_ERROR; }
+       virtual unsigned int getInputFramesLost() const { return 0; }
+
+       virtual ssize_t read(void* buffer, ssize_t bytes);
+       virtual status_t dump(int fd, const Vector<String16>& args);
+       virtual size_t getInputBufferSize(uint32_t sampleRate, int inputFormat,
+               int channelCount);
+       virtual status_t setParameters(const String8& keyValuePairs);
+       virtual String8 getParameters(const String8& keys);
+
+private:
+       Mutex mLock;
+       AudioHardware *mAudioHardware;
+       struct pcm *mPcm;
+       uint32_t mDevice;
+       int mChannels;
+       int mChannelCount;
+       int mSampleRate;
+};
+
+}; // namespace android
+
+#endif
diff --git a/AudioStreamOut.cpp b/AudioStreamOut.cpp
new file mode 100644 (file)
index 0000000..02c62b2
--- /dev/null
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2012 Paul Kocialkowski <contact@paulk.fr>
+ * 
+ * This is based on TinyHardware:
+ * Copyright 2011 Wolfson Microelectronics plc
+ *
+ * This is based on Nexus S AudioHardware implementation:
+ * Copyright 2010, The Android Open-Source Project
+ *
+ * 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.
+ */
+
+#define LOG_TAG "AudioHardware"
+
+#include "tinyalsa/asoundlib.h"
+
+#include "AudioHardware.h"
+#include "AudioStreamOut.h"
+#include "Mixer.h"
+
+namespace android {
+
+TinyALSAAudioStreamOut::TinyALSAAudioStreamOut() :
+       mAudioHardware(NULL),
+       mMixer(NULL),
+       mPcm(NULL),
+       mDevice(0)
+{
+       LOGD("TinyALSAAudioStreamOut()");
+
+       mMixer = new TinyALSAMixer();
+}
+
+TinyALSAAudioStreamOut::~TinyALSAAudioStreamOut()
+{
+       LOGD("~TinyALSAAudioStreamOut()");
+
+       if(mPcm)
+               pcm_close(mPcm);
+
+       delete mMixer;
+}
+
+status_t TinyALSAAudioStreamOut::set(AudioHardware *hw, uint32_t devices,
+       int *pFormat, uint32_t *pChannels, uint32_t *pRate)
+{
+       struct pcm_config pcmConfig;
+
+       LOGD("TinyALSAAudioStreamOut::set()");
+
+       // Setting globals
+       mAudioHardware = hw;
+       mDevice = devices;
+
+       // set mPcmFormat and mPcmSize
+       memset(&pcmConfig, 0, sizeof(pcmConfig));
+       pcmConfig.channels = 2;
+       pcmConfig.period_size = 2048;
+       pcmConfig.period_count = 6;
+       pcmConfig.format = PCM_FORMAT_S16_LE;
+
+       // TODO: read rate, if not try 44100 then 48000 
+       pcmConfig.rate = 44100;
+
+       mPcm = pcm_open(0, 0, PCM_OUT, &pcmConfig);
+
+       if(!mPcm || !::pcm_is_ready(mPcm)) {
+               LOGE("TinyALSAAudioStreamOut::set() failed: %s\n", 
+                       ::pcm_get_error(mPcm));
+               pcm_close(mPcm);
+
+               return BAD_VALUE;
+       }
+
+       mSampleRate = pcmConfig.rate;
+       *pRate = pcmConfig.rate;
+       *pFormat = AudioSystem::PCM_16_BIT;
+       *pChannels = AudioSystem::CHANNEL_OUT_STEREO;
+
+       mMixer->route(mDevice);
+
+       return NO_ERROR;
+}
+
+ssize_t TinyALSAAudioStreamOut::write(const void* buffer, size_t bytes)
+{
+       AutoMutex lock(mLock);
+
+       if (!::pcm_is_ready(mPcm)) {
+               // This should never happen...
+               return BAD_VALUE;
+       }
+
+       if(pcm_write(mPcm, buffer, bytes) != 0) {
+               LOGE("TinyALSAAudioStreamOut::write() failed: %s\n",
+                       ::pcm_get_error(mPcm));
+               return BAD_VALUE;
+       }
+
+       return NO_ERROR;
+}
+
+status_t TinyALSAAudioStreamOut::standby()
+{
+       if(pcm_stop(mPcm) != 0) {
+               LOGE("TinyALSAAudioStreamOut::standby() failed: %s\n", 
+                       ::pcm_get_error(mPcm));
+               return BAD_VALUE;
+       }
+
+       return NO_ERROR;
+}
+
+status_t TinyALSAAudioStreamOut::dump(int fd, const Vector<String16>& args)
+{
+       return NO_ERROR;
+}
+
+status_t TinyALSAAudioStreamOut::setParameters(const String8& keyValuePairs)
+{
+       AudioParameter param = AudioParameter(keyValuePairs);
+       String8 key = String8(AudioParameter::keyRouting);
+       int device;
+
+       LOGD("TinyALSAAudioStreamOut::setParameters(%s)",
+               keyValuePairs.string());
+
+       if(param.getInt(key, device) == NO_ERROR) {
+               mDevice = device;
+               mMixer->route(mDevice);
+
+               param.remove(key);
+       }
+
+       if(param.size()) {
+               return BAD_VALUE;
+       }
+
+       return NO_ERROR;
+}
+
+String8 TinyALSAAudioStreamOut::getParameters(const String8& keys)
+{
+       AudioParameter param = AudioParameter(keys);
+       String8 key = String8(AudioParameter::keyRouting);
+       String8 value;
+
+       LOGD("TinyALSAAudioStreamOut::getParameters()");
+
+       if(param.get(key, value) == NO_ERROR) {
+               param.addInt(key, (int) mDevice);
+       }
+
+       return param.toString();
+}
+
+status_t TinyALSAAudioStreamOut::getRenderPosition(uint32_t *dspFrames)
+{
+       // Don't bother just yet, there are no actual users of this.
+       return INVALID_OPERATION;
+}
+
+}; // namespace android
diff --git a/AudioStreamOut.h b/AudioStreamOut.h
new file mode 100644 (file)
index 0000000..c9cf305
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2012 Paul Kocialkowski <contact@paulk.fr>
+ * 
+ * This is based on TinyHardware:
+ * Copyright 2011 Wolfson Microelectronics plc
+ *
+ * This is based on Nexus S AudioHardware implementation:
+ * Copyright 2010, The Android Open-Source Project
+ *
+ * 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 TINYALSA_AUDIO_STREAM_OUT_H
+#define TINYALSA_AUDIO_STREAM_OUT_H
+
+#include <hardware_legacy/AudioHardwareBase.h>
+#include "AudioHardware.h"
+#include "Mixer.h"
+
+namespace android {
+
+class AudioHardware;
+
+class TinyALSAAudioStreamOut : public AudioStreamOut
+{
+public:
+       TinyALSAAudioStreamOut();
+       virtual ~TinyALSAAudioStreamOut();
+
+       virtual status_t set(AudioHardware *hw, uint32_t devices,
+               int *pFormat, uint32_t *pChannels, uint32_t *pRate);
+
+       virtual uint32_t sampleRate() const { return mSampleRate; }
+       virtual size_t bufferSize() const { return 2048; }
+       virtual uint32_t channels() const { return AudioSystem::CHANNEL_OUT_STEREO; }
+       virtual int format() const { return AudioSystem::PCM_16_BIT; }
+       virtual uint32_t latency() const { return 20; }
+       virtual status_t setVolume(float left, float right) { return INVALID_OPERATION; }
+
+       virtual ssize_t write(const void* buffer, size_t bytes);
+       virtual status_t standby();
+       virtual status_t dump(int fd, const Vector<String16>& args);
+       virtual status_t setParameters(const String8& keyValuePairs);
+       virtual String8 getParameters(const String8& keys);
+       virtual status_t getRenderPosition(uint32_t *dspFrames);
+
+private:
+       Mutex mLock;
+       AudioHardware *mAudioHardware;
+       TinyALSAMixer *mMixer;
+       struct pcm *mPcm;
+       uint32_t mDevice;
+       uint32_t mSampleRate;
+};
+
+}; // namespace android
+
+#endif
diff --git a/Mixer.cpp b/Mixer.cpp
new file mode 100644 (file)
index 0000000..d0c0805
--- /dev/null
+++ b/Mixer.cpp
@@ -0,0 +1,544 @@
+/*
+ * Copyright (C) 2012 Paul Kocialkowski <contact@paulk.fr>
+ * 
+ * This is based on TinyHardware:
+ * Copyright 2011 Wolfson Microelectronics plc
+ *
+ * This is based on Nexus S AudioHardware implementation:
+ * Copyright 2010, The Android Open-Source Project
+ *
+ * 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.
+ */
+
+#define LOG_TAG "AudioHardware"
+
+#include <cutils/log.h>
+#include <media/AudioSystem.h>
+#include <expat.h>
+
+#include "Mixer.h"
+
+/* 
+ * Missing:
+ * - Input handling (parsing/allocating/names/routing)
+ * - "write" element handling (as well as ctl), for type MIXER_DATA_TYPE_WRITE
+ */
+
+namespace android {
+
+struct tinyalsa_mixer_device_name mixer_output_devices_names[] = {
+       { AudioSystem::DEVICE_OUT_EARPIECE, "earpiece" },
+       { AudioSystem::DEVICE_OUT_SPEAKER, "speaker" },
+       { AudioSystem::DEVICE_OUT_WIRED_HEADSET, "headset" },
+       { AudioSystem::DEVICE_OUT_WIRED_HEADPHONE, "headphone" },
+       { AudioSystem::DEVICE_OUT_BLUETOOTH_SCO, "bluetooth-sco" },
+       { AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_HEADSET, "bluetooth-sco-headset" },
+       { AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_CARKIT, "bluetooth-sco-carkit" },
+       { AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP, "bluetooth-a2dp" },
+       { AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES, "bluetooth-a2dp-headphones" },
+       { AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER, "bluetooth-a2dp-speaker" },
+       { AudioSystem::DEVICE_OUT_AUX_DIGITAL, "aux-digital" },
+       { AudioSystem::DEVICE_OUT_HDMI, "hdmi" }
+};
+
+struct tinyalsa_mixer_device mixer_init_device;
+struct tinyalsa_mixer_device *mixer_output_devices = NULL;
+struct tinyalsa_mixer_device *mixer_current_output_device = NULL;
+
+status_t tinyalsa_mixer_devices_alloc(void)
+{
+       int i, c;
+
+       if(mixer_output_devices != NULL) {
+               LOGE("Error: mixer_output_devices are already allocated");
+               return BAD_VALUE;
+       }
+
+       c = sizeof(mixer_output_devices_names) /
+               sizeof(mixer_output_devices_names[0]);
+       mixer_output_devices = (struct tinyalsa_mixer_device *)
+               calloc(c, sizeof(*mixer_output_devices));
+
+       LOGD("Alloc %d elements (%d bytes) to mixer_output_devices", c,
+               (sizeof(*mixer_output_devices) * c));
+
+       for(i=0 ; i < c ; i++) {
+               mixer_output_devices[i].type =
+                       mixer_output_devices_names[i].type;
+       }
+
+       memset(&mixer_init_device, 0, sizeof(mixer_init_device));
+       mixer_current_output_device = NULL;
+
+       return NO_ERROR;
+}
+
+void tinyalsa_mixer_devices_free(void)
+{
+       if(mixer_output_devices != NULL) 
+               free(mixer_output_devices);
+
+       mixer_current_output_device = NULL;
+}
+
+void list_head_dump(struct list_head *list_p)
+{
+       struct tinyalsa_mixer_data *mixer_data;
+       struct list_head *list = list_p;
+
+       LOGD("Dump from top to bottom");
+
+       while(list) {
+               mixer_data = (struct tinyalsa_mixer_data *) list->data;
+               LOGD("* %s:%s", mixer_data->name, mixer_data->value);
+
+               if(list->next != NULL)
+                       list = list->next;
+               else
+                       break;
+       }
+
+       LOGD("Dump from bottom to top");
+
+       while(list) {
+               mixer_data = (struct tinyalsa_mixer_data *) list->data;
+               LOGD("* %s:%s", mixer_data->name, mixer_data->value);
+
+               if(list->prev != NULL)
+                       list = list->prev;
+               else
+                       break;
+       }
+
+}
+
+struct list_head *list_head_alloc(void)
+{
+       struct list_head *list = (struct list_head *)
+               calloc(1, sizeof(struct list_head));
+
+
+       return list;
+}
+
+struct tinyalsa_mixer_data *tinyalsa_mixer_data_alloc(void)
+{
+       struct tinyalsa_mixer_data *mixer_data = (struct tinyalsa_mixer_data *)
+               calloc(1, sizeof(struct tinyalsa_mixer_data));
+
+       return mixer_data;
+}
+
+void tinyalsa_mixer_device_list_free(struct list_head *list)
+{
+       struct tinyalsa_mixer_data *mixer_data;
+
+       while(list) {
+               mixer_data = (struct tinyalsa_mixer_data *) list->data;
+               if(mixer_data->name)
+                       free(mixer_data->name);
+               if(mixer_data->value)
+                       free(mixer_data->value);
+
+               if(list->next != NULL)
+                       list = list->next;
+               else
+                       break;
+       }
+}
+
+void tinyalsa_mixer_device_free(struct tinyalsa_mixer_device *device)
+{
+       if(device->enable != NULL)
+               tinyalsa_mixer_device_list_free(device->enable);
+       if(device->disable != NULL)
+               tinyalsa_mixer_device_list_free(device->disable);
+}
+
+struct tinyalsa_mixer_device *tinyalsa_mixer_device_get(
+       struct tinyalsa_mixer_device_name *devices_names, 
+       struct tinyalsa_mixer_device *devices, int devices_count, 
+       char *device_name, int device_type)
+{
+       int type = 0;
+       int i;
+
+       if(device_type != 0) {
+               type = device_type;
+               goto seek_type;
+       }
+
+       if(device_name == NULL) {
+               LOGE("Unable to retrieve device: no data given!");
+               return NULL;
+       }
+
+       for(i=0 ; i < devices_count ; i++) {
+               if(strcmp(devices_names[i].name, device_name) == 0) {
+                       type = mixer_output_devices_names[i].type;
+                       break;
+               }
+       }
+
+seek_type:
+       for(i=0 ; i < devices_count ; i++) {
+               if(devices[i].type == type) {
+                       return &devices[i];
+               }
+       }
+
+       return NULL;
+}
+
+struct tinyalsa_mixer_device *tinyalsa_mixer_device_get_with_type(
+       char *device_name, char *device_type)
+{
+       int c;
+
+       if(strcmp(device_type, "output") == 0) {
+               c = sizeof(mixer_output_devices_names) /
+                       sizeof(mixer_output_devices_names[0]);
+
+               return tinyalsa_mixer_device_get(mixer_output_devices_names, 
+                       mixer_output_devices, c, device_name, 0);
+       } else { //TODO: input
+               LOGE("Unknown device type: %s", device_type);
+               return NULL;
+       }
+}
+
+status_t tinyalsa_mixer_route_set(struct mixer *mixer, struct list_head *list)
+{
+       struct tinyalsa_mixer_data *mixer_data;
+       struct mixer_ctl *ctl;
+       status_t status = BAD_VALUE;
+       int value;
+       int type;
+       int rc;
+       int i;
+
+       while(list) {
+               mixer_data = (struct tinyalsa_mixer_data *) list->data;
+//--- split to another function based on switch on type with MIXER_DATA_TYPE_CTL
+
+               ctl = mixer_get_ctl_by_name(mixer, mixer_data->name);
+
+               type = mixer_ctl_get_type(ctl);
+
+               switch(type) {
+                       case MIXER_CTL_TYPE_BOOL:
+                               value = strcmp(mixer_data->value, "on") == 0 ?
+                                       1 : 0;
+                               break;
+                       case MIXER_CTL_TYPE_INT:
+                               value = atoi(mixer_data->value);
+                               break;
+                       case MIXER_CTL_TYPE_BYTE:
+                               value = atoi(mixer_data->value) & 0xff;
+                               break;
+                       case MIXER_CTL_TYPE_ENUM:
+                       case MIXER_CTL_TYPE_UNKNOWN:
+                               rc = mixer_ctl_set_enum_by_string(ctl, mixer_data->value);
+                               if(!rc)
+                                       status = NO_ERROR;
+               }
+
+               if(type == MIXER_CTL_TYPE_BOOL || type == MIXER_CTL_TYPE_INT ||
+                       type == MIXER_CTL_TYPE_BYTE) {
+                               for(i=0 ; i < mixer_ctl_get_num_values(ctl) ; i++) {
+                                       rc = mixer_ctl_set_value(ctl, i, value);
+                                       if(!rc)
+                                               status = NO_ERROR;
+                               }
+               }
+
+               LOGD("Setting %s to %s", mixer_data->name, mixer_data->value);
+
+               if(list->next != NULL)
+                       list = list->next;
+               else
+                       break;
+       }
+
+       return status;
+}
+
+status_t tinyalsa_mixer_route_init(struct mixer *mixer)
+{
+       status_t status;
+
+       status = tinyalsa_mixer_route_set(mixer, mixer_init_device.enable);
+       if(status != NO_ERROR) {
+               LOGE("Failed to set init route!");
+               goto error;
+       }
+
+error:
+       return BAD_VALUE;
+}
+
+status_t tinyalsa_mixer_route(struct mixer *mixer, int type) {
+       struct tinyalsa_mixer_device *device;
+       status_t status;
+       int c;
+
+       if(AudioSystem::isOutputDevice((AudioSystem::audio_devices) type)) {
+               c = sizeof(mixer_output_devices_names) /
+                       sizeof(mixer_output_devices_names[0]);
+
+               device = tinyalsa_mixer_device_get(mixer_output_devices_names,
+                       mixer_output_devices, c, NULL, type);
+
+               if(!device)
+                       goto error_device;
+
+               if(mixer_current_output_device != NULL) {
+                       status = tinyalsa_mixer_route_set(mixer, 
+                               mixer_current_output_device->disable);
+                       mixer_current_output_device = NULL;
+                       if(status != NO_ERROR) {
+                               LOGE("Failed to set disable route for current device!");
+                               goto error;
+                       }
+               }
+
+               status = tinyalsa_mixer_route_set(mixer, device->enable);
+               if(status != NO_ERROR) {
+                       LOGE("Failed to set enable route for device type: %d!", type);
+                       goto error;
+               }
+               mixer_current_output_device = device;
+       } else if(AudioSystem::isInputDevice((AudioSystem::audio_devices) type)) {
+               //TODO: input
+       } else {
+               goto error_device;
+       }
+
+       return NO_ERROR;
+
+error_device:
+       LOGE("Error: device of type %d not found!", type);
+error:
+       return BAD_VALUE;
+}
+
+void tinyalsa_mixer_config_free(void)
+{
+       int output_devices_count;
+       int i;
+
+       // free init device
+       tinyalsa_mixer_device_free(&mixer_init_device);
+
+       // free output devices
+       output_devices_count = sizeof(mixer_output_devices_names) /
+               sizeof(mixer_output_devices_names[0]);
+
+       for(i=0 ; i < output_devices_count ; i++) {
+               tinyalsa_mixer_device_free(&mixer_output_devices[i]);
+       }
+
+       //TODO: input
+}
+
+void tinyalsa_mixer_config_start(void *data, const XML_Char *elem,
+       const XML_Char **attr)
+{
+       struct tinyalsa_mixer_config_data *config_data;
+       struct tinyalsa_mixer_data *mixer_data;
+       struct list_head *list_prev;
+       struct list_head *list;
+
+       const XML_Char *device_name = NULL;
+       const XML_Char *device_type = NULL;
+       const XML_Char *data_name = NULL;
+       const XML_Char *data_value = NULL;
+       int i;
+
+       config_data = (struct tinyalsa_mixer_config_data *) data;
+
+       if(strcmp(elem, "tinyalsa-audio") == 0) {
+               for(i=0 ; attr[i] && attr[i+1] ; i++) {
+                       if(strcmp(attr[i], "device") == 0) {
+                               i++;
+                               LOGD("Parsing config for device: %s", attr[i]);
+                       }
+               }
+       } else if(strcmp(elem, "device") == 0) {
+               for(i=0 ; attr[i] && attr[i+1] ; i++) {
+                       if(strcmp(attr[i], "type") == 0) {
+                               i++;
+                               device_type = attr[i];
+                       } else if(strcmp(attr[i], "name") == 0) {
+                               i++;
+                               device_name = attr[i];
+                       }
+               }
+
+               if(strcmp(device_type, "init") == 0) {
+                       config_data->device = &mixer_init_device;
+               } else if(device_name != NULL) {
+                       config_data->device = 
+                               tinyalsa_mixer_device_get_with_type((char *) device_name, (char *) device_type);
+               } else {
+                       LOGE("Missing device name!");
+               }
+       } else if(strcmp(elem, "path") == 0) {
+               for(i=0 ; attr[i] && attr[i+1] ; i++) {
+                       if(strcmp(attr[i], "name") == 0) {
+                               i++;
+                               if(strcmp(attr[i], "enable") == 0) {
+                                       config_data->list_start =
+                                               &config_data->device->enable;
+                               } else if(strcmp(attr[i], "disable") == 0) {
+                                       config_data->list_start =
+                                               &config_data->device->disable;
+                               }
+                       }
+               }
+       } else if(strcmp(elem, "ctl") == 0) {
+               for(i=0 ; attr[i] && attr[i+1] ; i++) {
+                       if(strcmp(attr[i], "name") == 0) {
+                               i++;
+                               data_name = attr[i];
+                       } else if(strcmp(attr[i], "value") == 0) {
+                               i++;
+                               data_value = attr[i];
+                       }
+               }
+
+               if(data_name && data_value) {
+                       list = list_head_alloc();
+
+                       if(config_data->list_start != NULL) {
+                               *config_data->list_start = list;
+                               config_data->list_start = NULL;
+                       } else {
+                               config_data->list->next = list;
+                               list->prev = config_data->list;
+                       }
+
+                       mixer_data = tinyalsa_mixer_data_alloc();
+                       list->data = (void *) mixer_data;
+
+                       mixer_data->name = strdup((char *) data_name);
+                       mixer_data->value = strdup((char *) data_value);
+                       mixer_data->type = MIXER_DATA_TYPE_CTL; 
+
+                       config_data->list = list;
+               }
+       }
+}
+
+void tinyalsa_mixer_config_end(void *data, const XML_Char *name)
+{
+       struct tinyalsa_mixer_config_data *config_data;
+
+       config_data = (struct tinyalsa_mixer_config_data *) data;
+
+       if(strcmp(name, "tinyalsa-audio") == 0) {
+               LOGI("Config parsing done");
+       } else if(strcmp(name, "device") == 0) {
+               config_data->device = NULL;
+       } else if(strcmp(name, "path") == 0) {
+               config_data->list_start = NULL;
+               config_data->list = NULL;
+       }
+}
+
+status_t tinyalsa_mixer_config_parse(void)
+{
+       struct tinyalsa_mixer_config_data config_data;
+
+       char buf[80];
+       XML_Parser p;
+       FILE *f;
+
+       int eof = 0;
+       int len = 0;
+
+       f = fopen(TINYALSA_MIXER_CONFIG_FILE, "r");
+       if(!f) {
+               LOGE("Failed to open tinyalsa-audio config file!");
+               return BAD_VALUE;
+       }
+
+       p = XML_ParserCreate(NULL);
+       if(!p) {
+               LOGE("Failed to create XML parser!");
+               goto error_file;
+       }
+
+       memset(&config_data, 0, sizeof(config_data));
+
+       XML_SetUserData(p, &config_data);
+       XML_SetElementHandler(p, tinyalsa_mixer_config_start, tinyalsa_mixer_config_end);
+
+       while(!eof) {
+               len = fread(buf, 1, sizeof(buf), f);
+               if(ferror(f)) {
+                       LOGE("Failed to read config file!");
+                       goto error_xml_parser;
+               }
+
+               eof = feof(f);
+
+               if(XML_Parse(p, buf, len, eof) == XML_STATUS_ERROR) {
+                       LOGE("Failed to parse line %d: %s", 
+                               XML_GetCurrentLineNumber(p),
+                               XML_ErrorString(XML_GetErrorCode(p)));
+                       goto error_xml_parser;
+               }
+       }
+
+       return NO_ERROR;
+
+error_xml_parser:
+       XML_ParserFree(p);
+
+error_file:
+       fclose(f);
+
+       return BAD_VALUE;
+}
+
+TinyALSAMixer::TinyALSAMixer() :
+       mMixer(NULL)
+{
+       LOGD("Mixer()");
+
+       tinyalsa_mixer_devices_alloc();
+       tinyalsa_mixer_config_parse();
+
+       mMixer = mixer_open(0);
+
+       tinyalsa_mixer_route_init(mMixer);
+}
+
+TinyALSAMixer::~TinyALSAMixer()
+{
+       LOGD("~Mixer()");
+
+       tinyalsa_mixer_config_free();
+       tinyalsa_mixer_devices_free();
+
+       if(mMixer)
+               mixer_close(mMixer);
+}
+
+status_t TinyALSAMixer::route(int type)
+{
+       return tinyalsa_mixer_route(mMixer, type);
+}
+
+}; // namespace android
diff --git a/Mixer.h b/Mixer.h
new file mode 100644 (file)
index 0000000..fa4c768
--- /dev/null
+++ b/Mixer.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2012 Paul Kocialkowski <contact@paulk.fr>
+ * 
+ * This is based on TinyHardware:
+ * Copyright 2011 Wolfson Microelectronics plc
+ *
+ * This is based on Nexus S AudioHardware implementation:
+ * Copyright 2010, The Android Open-Source Project
+ *
+ * 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 TINYALSA_MIXER_H
+#define TINYALSA_MIXER_H
+
+#include <media/AudioSystem.h>
+
+#include "tinyalsa/asoundlib.h"
+#include <expat.h>
+
+namespace android {
+
+#define TINYALSA_MIXER_CONFIG_FILE     "/system/etc/tinyalsa-audio.xml"
+
+enum tinyalsa_mixer_data_type {
+       MIXER_DATA_TYPE_CTL,
+       MIXER_DATA_TYPE_WRITE,
+       MIXER_DATA_TYPE_MAX
+};
+
+struct list_head {
+       struct list_head *prev;
+       struct list_head *next;
+
+       void *data;
+};
+
+struct tinyalsa_mixer_data {
+       char *name;
+       char *value;
+
+       tinyalsa_mixer_data_type type;
+};
+
+struct tinyalsa_mixer_device_name {
+       int type;
+       char *name;
+};
+
+struct tinyalsa_mixer_device {
+       int type;
+
+       struct list_head *enable;
+       struct list_head *disable;
+};
+
+struct tinyalsa_mixer_config_data {
+       struct tinyalsa_mixer_device *device;
+       struct list_head **list_start;
+       struct list_head *list;
+};
+
+class TinyALSAMixer
+{
+public:
+       TinyALSAMixer();
+       virtual ~TinyALSAMixer();
+       status_t route(int type);
+
+private:
+       struct mixer *mMixer;
+
+};
+
+}; // namespace android
+
+#endif