Added libspeexresampler-based resampler and thus bring a working AudioStreamIn
authorPaul Kocialkowski <contact@paulk.fr>
Sun, 15 Jul 2012 20:17:40 +0000 (22:17 +0200)
committerPaul Kocialkowski <contact@paulk.fr>
Sun, 15 Jul 2012 20:17:40 +0000 (22:17 +0200)
Signed-off-by: Paul Kocialkowski <contact@paulk.fr>
Android.mk
AudioHardware.cpp
AudioHardware.h
AudioResampler.cpp [new file with mode: 0644]
AudioResampler.h [new file with mode: 0644]
AudioStreamIn.cpp
AudioStreamIn.h
AudioStreamOut.cpp
AudioStreamOut.h

index 375ac27..46d3422 100644 (file)
@@ -6,16 +6,20 @@ include $(CLEAR_VARS)
 
 LOCAL_SRC_FILES := \
        AudioHardware.cpp \
+       AudioResampler.cpp \
        AudioStreamOut.cpp \
        AudioStreamIn.cpp \
        Mixer.cpp
 
 LOCAL_C_INCLUDES += \
        external/expat/lib \
-       external/tinyalsa/include
+       external/tinyalsa/include \
+       external/speex/include \
+       hardware/tinyalsa-audio/include
 
 LOCAL_STATIC_LIBRARIES += libaudiointerface
 LOCAL_SHARED_LIBRARIES := \
+       libspeexresampler \
        libtinyalsa \
        libexpat \
        libcutils \
index 0b1f256..49aabaf 100644 (file)
@@ -70,10 +70,7 @@ 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;
+       return BAD_VALUE;
 }
 
 status_t AudioHardware::setMode(int mode)
@@ -97,6 +94,7 @@ status_t AudioHardware::getMicMute(bool* state)
        LOGD("getMicMute()");
 
        *state = mMicMute;
+
        return NO_ERROR;
 }
 
@@ -128,40 +126,45 @@ size_t AudioHardware::getInputBufferSize(uint32_t sampleRate, int format, int ch
                return 0;
        }
 
-       return mInput->getInputBufferSize(sampleRate, format, channelCount);
+       return mInput->bufferSize();
 }
 
 AudioStreamOut* AudioHardware::openOutputStream(
        uint32_t devices, int *format, uint32_t *channels,
        uint32_t *sampleRate, status_t *status)
 {
-       status_t o_status;
+       status_t set_status;
 
        AutoMutex lock(mLock);
 
        LOGD("openOutputStream()");
 
+       // Check for valid input source
+       if(!AudioSystem::isOutputDevice((AudioSystem::audio_devices) devices)) {
+               LOGE("openOutputStream() failed: invalid device");
+               return NULL;
+       }
+
        if(mOutput) {
-               if (status)
-                       *status = INVALID_OPERATION;
+               *status = INVALID_OPERATION;
 
-               LOGE("There is already an active outputStream!");
+               LOGE("openOutputStream() failed: 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;
-       }
+       set_status = output->set(this, devices, format, channels, sampleRate);
+
+       *status = set_status;
 
-       if(o_status == NO_ERROR) {
-               mOutput = output;
-       } else {
+       if(set_status != NO_ERROR) {
                delete output;
+               return NULL;
        }
 
+       mOutput = output;
+
        return output;
 }
 
@@ -170,39 +173,39 @@ AudioStreamIn* AudioHardware::openInputStream(
        uint32_t *sampleRate, status_t *status,
        AudioSystem::audio_in_acoustics acoustic_flags)
 {
-       status_t i_status;
+       status_t set_status;
 
        AutoMutex lock(mLock);
 
        LOGD("openInputStream()");
 
-       // check for valid input source
-       if(!AudioSystem::isInputDevice((AudioSystem::audio_devices)devices)) {
+       // Check for valid input source
+       if(!AudioSystem::isInputDevice((AudioSystem::audio_devices) devices)) {
+               LOGE("openInputStream() failed: invalid device");
                return NULL;
        }
 
        if(mInput) {
-               if (status)
-                       *status = INVALID_OPERATION;
+               *status = INVALID_OPERATION;
 
-               LOGE("There is already an active inputStream!");
+               LOGE("openInputStream() failed: there is already an active inputStream!");
                return NULL;
        }
 
        TinyALSAAudioStreamIn *input = new TinyALSAAudioStreamIn();
 
-       i_status = input->set(this, devices, format, channels, sampleRate,
+       set_status = input->set(this, devices, format, channels, sampleRate,
                acoustic_flags);
-       if(i_status) {
-               *status = i_status;
-       }
 
-       if(i_status == NO_ERROR) {
-               mInput = input;
-       } else {
+       *status = set_status;
+
+       if(set_status != NO_ERROR) {
                delete input;
+               return NULL;
        }
 
+       mInput = input;
+
        return input;
 }
 
index f8e5d9c..8c0c161 100644 (file)
@@ -64,6 +64,9 @@ public:
        virtual void closeOutputStream(AudioStreamOut* out);
        virtual void closeInputStream(AudioStreamIn* in);
 
+       TinyALSAAudioStreamOut *mOutput;
+       TinyALSAAudioStreamIn *mInput;
+
 protected:
        virtual status_t dump(int fd, const Vector<String16>& args);
 
@@ -73,8 +76,6 @@ private:
        unsigned int mOutDevices;
        unsigned int mInDevices;
        bool mMicMute;
-       TinyALSAAudioStreamOut *mOutput;
-       TinyALSAAudioStreamIn *mInput;
 };
 
 }; // namespace android
diff --git a/AudioResampler.cpp b/AudioResampler.cpp
new file mode 100644 (file)
index 0000000..99cfcb7
--- /dev/null
@@ -0,0 +1,445 @@
+/*
+ * Copyright (C) 2012 Paul Kocialkowski <contact@paulk.fr>
+ *
+ * This is based on Android 4.0 resampler
+ * Copyright 2011, The Android Open-Source Project
+ *
+ * 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 "AudioResampler"
+
+#include "AudioResampler.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <ctype.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+namespace android {
+
+/*
+ * Android 4.0 resampler (begin)
+ */
+
+struct resampler {
+    struct resampler_itfe itfe;
+    SpeexResamplerState *speex_resampler;       // handle on speex resampler
+    struct resampler_buffer_provider *provider; // buffer provider installed by client
+    uint32_t in_sample_rate;                    // input sampling rate in Hz
+    uint32_t out_sample_rate;                   // output sampling rate in Hz
+    uint32_t channel_count;                     // number of channels (interleaved)
+    int16_t *in_buf;                            // input buffer
+    size_t in_buf_size;                         // input buffer size
+    size_t frames_in;                           // number of frames in input buffer
+    size_t frames_rq;                           // cached number of output frames
+    size_t frames_needed;                       // minimum number of input frames to produce
+                                                // frames_rq output frames
+    int32_t speex_delay_ns;                     // delay introduced by speex resampler in ns
+};
+
+
+//------------------------------------------------------------------------------
+// speex based resampler
+//------------------------------------------------------------------------------
+
+static void resampler_reset(struct resampler_itfe *resampler)
+{
+    struct resampler *rsmp = (struct resampler *)resampler;
+
+    rsmp->frames_in = 0;
+    rsmp->frames_rq = 0;
+
+    if (rsmp != NULL && rsmp->speex_resampler != NULL) {
+        speex_resampler_reset_mem(rsmp->speex_resampler);
+    }
+}
+
+static int32_t resampler_delay_ns(struct resampler_itfe *resampler)
+{
+    struct resampler *rsmp = (struct resampler *)resampler;
+
+    int32_t delay = (int32_t)((1000000000 * (int64_t)rsmp->frames_in) / rsmp->in_sample_rate);
+    delay += rsmp->speex_delay_ns;
+
+    return delay;
+}
+
+// outputs a number of frames less or equal to *outFrameCount and updates *outFrameCount
+// with the actual number of frames produced.
+int resampler_resample_from_provider(struct resampler_itfe *resampler,
+                       int16_t *out,
+                       size_t *outFrameCount)
+{
+    struct resampler *rsmp = (struct resampler *)resampler;
+
+    if (rsmp == NULL || out == NULL || outFrameCount == NULL) {
+        return -EINVAL;
+    }
+    if (rsmp->provider == NULL) {
+        *outFrameCount = 0;
+        return -ENOSYS;
+    }
+
+    size_t framesRq = *outFrameCount;
+    // update and cache the number of frames needed at the input sampling rate to produce
+    // the number of frames requested at the output sampling rate
+    if (framesRq != rsmp->frames_rq) {
+        rsmp->frames_needed = (framesRq * rsmp->out_sample_rate) / rsmp->in_sample_rate + 1;
+        rsmp->frames_rq = framesRq;
+    }
+
+    size_t framesWr = 0;
+    size_t inFrames = 0;
+    while (framesWr < framesRq) {
+        if (rsmp->frames_in < rsmp->frames_needed) {
+            // make sure that the number of frames present in rsmp->in_buf (rsmp->frames_in) is at
+            // least the number of frames needed to produce the number of frames requested at
+            // the output sampling rate
+            if (rsmp->in_buf_size < rsmp->frames_needed) {
+                rsmp->in_buf_size = rsmp->frames_needed;
+                rsmp->in_buf = (int16_t *)realloc(rsmp->in_buf,
+                                        rsmp->in_buf_size * rsmp->channel_count * sizeof(int16_t));
+            }
+            struct resampler_buffer buf;
+            buf.frame_count = rsmp->frames_needed - rsmp->frames_in;
+            rsmp->provider->get_next_buffer(rsmp->provider, &buf);
+            if (buf.raw == NULL) {
+                break;
+            }
+            memcpy(rsmp->in_buf + rsmp->frames_in * rsmp->channel_count,
+                    buf.raw,
+                    buf.frame_count * rsmp->channel_count * sizeof(int16_t));
+            rsmp->frames_in += buf.frame_count;
+            rsmp->provider->release_buffer(rsmp->provider, &buf);
+        }
+
+        size_t outFrames = framesRq - framesWr;
+        inFrames = rsmp->frames_in;
+        if (rsmp->channel_count == 1) {
+            speex_resampler_process_int(rsmp->speex_resampler,
+                                        0,
+                                        rsmp->in_buf,
+                                        &inFrames,
+                                        out + framesWr,
+                                        &outFrames);
+        } else {
+            speex_resampler_process_interleaved_int(rsmp->speex_resampler,
+                                        rsmp->in_buf,
+                                        &inFrames,
+                                        out + framesWr * rsmp->channel_count,
+                                        &outFrames);
+        }
+        framesWr += outFrames;
+        rsmp->frames_in -= inFrames;
+        LOGW_IF((framesWr != framesRq) && (rsmp->frames_in != 0),
+                "ReSampler::resample() remaining %d frames in and %d frames out",
+                rsmp->frames_in, (framesRq - framesWr));
+    }
+    if (rsmp->frames_in) {
+        memmove(rsmp->in_buf,
+                rsmp->in_buf + inFrames * rsmp->channel_count,
+                rsmp->frames_in * rsmp->channel_count * sizeof(int16_t));
+    }
+    *outFrameCount = framesWr;
+
+    return 0;
+}
+
+int resampler_resample_from_input(struct resampler_itfe *resampler,
+                                  int16_t *in,
+                                  size_t *inFrameCount,
+                                  int16_t *out,
+                                  size_t *outFrameCount)
+{
+    struct resampler *rsmp = (struct resampler *)resampler;
+
+    if (rsmp == NULL || in == NULL || inFrameCount == NULL ||
+            out == NULL || outFrameCount == NULL) {
+        return -EINVAL;
+    }
+    if (rsmp->provider != NULL) {
+        *outFrameCount = 0;
+        return -ENOSYS;
+    }
+
+    if (rsmp->channel_count == 1) {
+        speex_resampler_process_int(rsmp->speex_resampler,
+                                    0,
+                                    in,
+                                    inFrameCount,
+                                    out,
+                                    outFrameCount);
+    } else {
+        speex_resampler_process_interleaved_int(rsmp->speex_resampler,
+                                                in,
+                                                inFrameCount,
+                                                out,
+                                                outFrameCount);
+    }
+
+    LOGV("resampler_resample_from_input() DONE in %d out % d", *inFrameCount, *outFrameCount);
+
+    return 0;
+}
+
+int create_resampler(uint32_t inSampleRate,
+                    uint32_t outSampleRate,
+                    uint32_t channelCount,
+                    uint32_t quality,
+                    struct resampler_buffer_provider* provider,
+                    struct resampler_itfe **resampler)
+{
+    int error;
+    struct resampler *rsmp;
+
+    LOGV("create_resampler() In SR %d Out SR %d channels %d",
+         inSampleRate, outSampleRate, channelCount);
+
+    if (resampler == NULL) {
+        return -EINVAL;
+    }
+
+    *resampler = NULL;
+
+    if (quality <= RESAMPLER_QUALITY_MIN || quality >= RESAMPLER_QUALITY_MAX) {
+        return -EINVAL;
+    }
+
+    rsmp = (struct resampler *)calloc(1, sizeof(struct resampler));
+
+    rsmp->speex_resampler = speex_resampler_init(channelCount,
+                                      inSampleRate,
+                                      outSampleRate,
+                                      quality,
+                                      &error);
+    if (rsmp->speex_resampler == NULL) {
+        LOGW("ReSampler: Cannot create speex resampler: %s", speex_resampler_strerror(error));
+        return -ENODEV;
+    }
+
+    rsmp->itfe.reset = resampler_reset;
+    rsmp->itfe.resample_from_provider = resampler_resample_from_provider;
+    rsmp->itfe.resample_from_input = resampler_resample_from_input;
+    rsmp->itfe.delay_ns = resampler_delay_ns;
+
+    rsmp->provider = provider;
+    rsmp->in_sample_rate = inSampleRate;
+    rsmp->out_sample_rate = outSampleRate;
+    rsmp->channel_count = channelCount;
+    rsmp->in_buf = NULL;
+    rsmp->in_buf_size = 0;
+
+    resampler_reset(&rsmp->itfe);
+
+    int frames = speex_resampler_get_input_latency(rsmp->speex_resampler);
+    rsmp->speex_delay_ns = (int32_t)((1000000000 * (int64_t)frames) / rsmp->in_sample_rate);
+    frames = speex_resampler_get_output_latency(rsmp->speex_resampler);
+    rsmp->speex_delay_ns += (int32_t)((1000000000 * (int64_t)frames) / rsmp->out_sample_rate);
+
+    *resampler = &rsmp->itfe;
+    LOGV("create_resampler() DONE rsmp %p &rsmp->itfe %p speex %p",
+         rsmp, &rsmp->itfe, rsmp->speex_resampler);
+    return 0;
+}
+
+void release_resampler(struct resampler_itfe *resampler)
+{
+    struct resampler *rsmp = (struct resampler *)resampler;
+
+    if (rsmp == NULL) {
+        return;
+    }
+
+    free(rsmp->in_buf);
+
+    if (rsmp->speex_resampler != NULL) {
+        speex_resampler_destroy(rsmp->speex_resampler);
+    }
+    free(rsmp);
+}
+
+/*
+ * Android 4.0 resampler (end)
+ */
+
+int tinyalsa_audio_resampler_get_next_buffer(
+       struct resampler_buffer_provider *buffer_provider,
+       struct resampler_buffer* buffer)
+{
+       TinyALSAAudioResampler *r =
+               (TinyALSAAudioResampler *) buffer_provider->pdata;
+
+       char *addr = NULL;
+       int rc;
+
+       if(r->mFramesIn == 0) {
+               rc = pcm_read(r->mPcm, r->mPcmReadBuffer,
+                       r->mPcmReadFrames * r->mFrameSizeIn);
+
+               r->mFramesIn = r->mPcmReadFrames;
+       }
+
+       addr = (char *) r->mPcmReadBuffer +
+               (r->mPcmReadFrames - r->mFramesIn) * r->mFrameSizeIn;
+
+       buffer->frame_count = (buffer->frame_count > r->mFramesIn) ?
+               r->mFramesIn : buffer->frame_count;
+       buffer->i16 = (short int *) addr;
+
+       return 0;
+}
+
+void tinyalsa_audio_resampler_release_buffer(
+       struct resampler_buffer_provider *buffer_provider,
+       struct resampler_buffer* buffer)
+{
+       TinyALSAAudioResampler *r =
+               (TinyALSAAudioResampler *) buffer_provider->pdata;
+
+       r->mFramesIn -= buffer->frame_count;
+}
+
+TinyALSAAudioResampler::TinyALSAAudioResampler(
+       struct pcm *Pcm, int PcmReadFrames,
+       uint32_t sampleRateIn, uint32_t channelsIn,
+       uint32_t sampleRateOut, uint32_t channelsOut) :
+       mPcm(Pcm), mPcmReadFrames(PcmReadFrames),
+       mSampleRateIn(sampleRateIn), mChannelsIn(channelsIn),
+       mSampleRateOut(sampleRateOut), mChannelsOut(channelsOut)
+{
+       LOGD("TinyALSAAudioResampler(%d, %d, %d, %d)",
+               sampleRateIn, channelsIn, sampleRateOut, channelsOut);
+
+       mFrameSizeIn = mChannelsIn * sizeof(int16_t);
+       mFrameSizeOut = mChannelsOut * sizeof(int16_t);
+
+       mResampledFrames = 320;
+       mFramesIn = 0;
+
+       mPcmReadBuffer = malloc(mPcmReadFrames * mFrameSizeIn);
+       mResampledBuffer = malloc(mResampledFrames * mFrameSizeIn);
+
+       if(mFrameSizeIn != mFrameSizeOut) {
+
+               mOutBuffer = malloc(mResampledFrames * mFrameSizeOut);
+       } else {
+               mOutBuffer = mResampledBuffer;
+       }
+
+       mResamplerProvider.get_next_buffer =
+               tinyalsa_audio_resampler_get_next_buffer;
+       mResamplerProvider.release_buffer =
+               tinyalsa_audio_resampler_release_buffer;
+       mResamplerProvider.pdata = (void *) this;
+
+       create_resampler(sampleRateIn, sampleRateOut, channelsIn,
+               RESAMPLER_QUALITY_DEFAULT, &mResamplerProvider, &mResampler);
+}
+
+TinyALSAAudioResampler::~TinyALSAAudioResampler()
+{
+       LOGD("~TinyALSAAudioResampler()");
+
+       release_resampler(mResampler);
+
+       if(mOutBuffer != NULL && mOutBuffer != mResampledBuffer)
+               free(mOutBuffer);
+       if(mResampledBuffer != NULL)
+               free(mResampledBuffer);
+       if(mPcmReadBuffer != NULL)
+               free(mPcmReadBuffer);
+}
+
+void TinyALSAAudioResampler::convertChannels(void *inBuffer, void *outBuffer,
+       int inChannels, int outChannels, int frames)
+{
+       int16_t *in = (int16_t *) inBuffer;
+       int16_t *out = (int16_t *) outBuffer;
+       int16_t d;
+       int i;
+
+       if(inChannels == 2 && outChannels == 1) {
+               for(i=0 ; i < frames ; i++) {
+                       d = (in[i*2] + in[i*2+1]) / 2;
+                       out[i] = d;
+               }
+       }
+}
+
+ssize_t TinyALSAAudioResampler::resample(void *buffer, int length)
+{
+       size_t frames = length / mFrameSizeOut;
+       ssize_t bytes = 0;
+       int rc;
+
+       if(frames > mResampledFrames) {
+               LOGE("TinyALSAAudioResampler::resample() failed: asked to read more frames than what the buffer can hold");
+               return -ENOMEM;
+       }
+
+       rc = mResampler->resample_from_provider(mResampler,
+               (int16_t *) mResampledBuffer, &frames);
+
+       if(rc < 0) {
+               LOGE("TinyALSAAudioResampler::resample() failed");
+               return 0;
+       }
+
+       if(mFrameSizeIn != mFrameSizeOut) {
+               convertChannels(mResampledBuffer, mOutBuffer, mChannelsIn, mChannelsOut, frames);
+       }
+
+       bytes = frames * mFrameSizeOut > length ?
+               length : frames * mFrameSizeOut;
+
+       memcpy(buffer, mOutBuffer, length);
+
+       return bytes;
+}
+
+uint32_t TinyALSAAudioResampler::sampleRate(void) const
+{
+       return (uint32_t) mSampleRateOut;
+}
+
+size_t TinyALSAAudioResampler::bufferSize(void) const
+{
+       return (size_t) mResampledFrames * mFrameSizeOut;
+}
+
+uint32_t TinyALSAAudioResampler::channels(void) const
+{
+       switch(mChannelsOut) {
+               case 1:
+                       return AudioSystem::CHANNEL_OUT_MONO;
+               case 2:
+                       return AudioSystem::CHANNEL_OUT_STEREO;
+               case 4:
+                       return AudioSystem::CHANNEL_OUT_QUAD;
+               case 6:
+                       return AudioSystem::CHANNEL_OUT_5POINT1;
+               default:
+                       return AudioSystem::CHANNEL_OUT_STEREO;
+       }
+}
+
+
+}; // namespace android
diff --git a/AudioResampler.h b/AudioResampler.h
new file mode 100644 (file)
index 0000000..52f0d7e
--- /dev/null
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2012 Paul Kocialkowski <contact@paulk.fr>
+ *
+ * This is based on Android 4.0 resampler
+ * Copyright 2011, The Android Open-Source Project
+ *
+ * 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_RESAMPLER_H
+#define TINYALSA_AUDIO_RESAMPLER_H
+
+#include <stdint.h>
+#include <sys/time.h>
+
+#include <hardware_legacy/AudioHardwareBase.h>
+#include <speex/speex_resampler.h>
+#include "AudioHardware.h"
+
+#define RESAMPLER_QUALITY_MAX 10
+#define RESAMPLER_QUALITY_MIN 0
+#define RESAMPLER_QUALITY_DEFAULT 4
+#define RESAMPLER_QUALITY_VOIP 3
+#define RESAMPLER_QUALITY_DESKTOP 5
+
+namespace android {
+
+struct resampler_buffer {
+    union {
+        void*       raw;
+        short*      i16;
+        int8_t*     i8;
+    };
+    size_t frame_count;
+};
+
+/* call back interface used by the resampler to get new data */
+struct resampler_buffer_provider
+{
+    /**
+     *  get a new buffer of data:
+     *   as input: buffer->frame_count is the number of frames requested
+     *   as output: buffer->frame_count is the number of frames returned
+     *              buffer->raw points to data returned
+     */
+    int (*get_next_buffer)(struct resampler_buffer_provider *provider,
+            struct resampler_buffer *buffer);
+    /**
+     *  release a consumed buffer of data:
+     *   as input: buffer->frame_count is the number of frames released
+     *             buffer->raw points to data released
+     */
+    void (*release_buffer)(struct resampler_buffer_provider *provider,
+            struct resampler_buffer *buffer);
+
+    void *pdata;
+};
+
+/* resampler interface */
+struct resampler_itfe {
+    /**
+     * reset resampler state
+     */
+    void (*reset)(struct resampler_itfe *resampler);
+    /**
+     * resample input from buffer provider and output at most *outFrameCount to out buffer.
+     * *outFrameCount is updated with the actual number of frames produced.
+     */
+    int (*resample_from_provider)(struct resampler_itfe *resampler,
+                    int16_t *out,
+                    size_t *outFrameCount);
+    /**
+     * resample at most *inFrameCount frames from in buffer and output at most
+     * *outFrameCount to out buffer. *inFrameCount and *outFrameCount are updated respectively
+     * with the number of frames remaining in input and written to output.
+     */
+    int (*resample_from_input)(struct resampler_itfe *resampler,
+                    int16_t *in,
+                    size_t *inFrameCount,
+                    int16_t *out,
+                    size_t *outFrameCount);
+    /**
+     * return the latency introduced by the resampler in ns.
+     */
+    int32_t (*delay_ns)(struct resampler_itfe *resampler);
+};
+
+/**
+ * create a resampler according to input parameters passed.
+ * If resampler_buffer_provider is not NULL only resample_from_provider() can be called.
+ * If resampler_buffer_provider is NULL only resample_from_input() can be called.
+ */
+int create_resampler(uint32_t inSampleRate,
+          uint32_t outSampleRate,
+          uint32_t channelCount,
+          uint32_t quality,
+          struct resampler_buffer_provider *provider,
+          struct resampler_itfe **);
+
+/**
+ * release resampler resources.
+ */
+void release_resampler(struct resampler_itfe *);
+
+class TinyALSAAudioResampler
+{
+public:
+       TinyALSAAudioResampler(
+               struct pcm *Pcm, int PcmReadFrames,
+               uint32_t sampleRateIn, uint32_t channelsIn,
+               uint32_t sampleRateOut, uint32_t channelsOut);
+       virtual ~TinyALSAAudioResampler();
+
+       virtual void convertChannels(void *inBuffer, void *outBuffer,
+       int inChannels, int outChannels, int frames);
+       virtual ssize_t resample(void *buffer, int length);
+
+       virtual uint32_t sampleRate(void) const;
+       virtual size_t bufferSize(void) const;
+       virtual uint32_t channels(void) const;
+
+       struct pcm *mPcm;
+
+       int mFrameSizeIn;
+       int mFrameSizeOut;
+
+       void *mPcmReadBuffer;
+
+       int mPcmReadFrames;
+       int mFramesIn;
+
+private:
+       uint32_t mSampleRateIn;
+       uint32_t mChannelsIn;
+       uint32_t mSampleRateOut;
+       uint32_t mChannelsOut;
+
+       void *mResampledBuffer;
+       int mResampledFrames;
+       void *mOutBuffer;
+
+       struct resampler_itfe *mResampler;
+       struct resampler_buffer_provider mResamplerProvider;
+};
+
+}; // namespace android
+
+#endif
index 7fbfe68..75b1c5e 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Copyright (C) 2012 Paul Kocialkowski <contact@paulk.fr>
- * 
+ *
  * This is based on TinyHardware:
  * Copyright 2011 Wolfson Microelectronics plc
  *
 
 #include "AudioHardware.h"
 #include "AudioStreamIn.h"
-
-/* 
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <poll.h>
+/*
  * Missing:
  * - Mixer support
  * - Downsampler to record at the rate we want with out stream opened
@@ -37,6 +44,7 @@ namespace android {
 
 TinyALSAAudioStreamIn::TinyALSAAudioStreamIn() :
        mAudioHardware(NULL),
+       mAudioResampler(NULL),
        mPcm(NULL),
        mDevice(0)
 {
@@ -49,99 +57,126 @@ TinyALSAAudioStreamIn::~TinyALSAAudioStreamIn()
 
        if(mPcm)
                pcm_close(mPcm);
+       if(mAudioResampler)
+               delete mAudioResampler;
 }
 
 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;
+       struct pcm *Pcm;
+       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;
+       // Setting PcmConfig
+       memset(&PcmConfig, 0, sizeof(struct pcm_config));
+       PcmConfig.channels = AudioSystem::popCount(*pChannels);
+       PcmConfig.rate = *pRate;
+       PcmConfig.format = PCM_FORMAT_S16_LE;
+       PcmConfig.period_size = 1056;
+       PcmConfig.period_count = 2;
 
-       mSampleRate = 44100;
+       Pcm = pcm_open(0, 0, PCM_IN, &PcmConfig);
 
-       if(*pFormat != format() || *pChannels != channels() || 
-               *pRate != sampleRate()) {
+       if(!Pcm || !pcm_is_ready(Pcm)) {
+               LOGE("TinyALSAAudioStreamIn::set() failed: %s",
+                       pcm_get_error(Pcm));
 
-               LOGE("Format mismatch opening input: %x/%x, %x/%x, %d/%d",
-                       *pFormat, format(), *pChannels, channels(),
-                       *pRate, sampleRate());
+               pcm_close(Pcm);
 
-               *pFormat = format();
-               *pChannels = channels();
-               *pRate = sampleRate();
+               LOGD("TinyALSAAudioStreamIn::set(): trying output params");
 
-               return BAD_VALUE;
-       }
+               PcmConfig.channels =
+                       AudioSystem::popCount(hw->mOutput->channels());
+               PcmConfig.rate = hw->mOutput->sampleRate();
 
-       memset(&pcmConfig, 0, sizeof(pcmConfig));
-       pcmConfig.channels = mChannelCount;
-       pcmConfig.period_size = 1024;
-       pcmConfig.period_count = 4;
-       pcmConfig.format = PCM_FORMAT_S16_LE;
+               Pcm = pcm_open(0, 0, PCM_IN, &PcmConfig);
 
-       //TODO: get to know the rate correctly
-       pcmConfig.rate = mSampleRate;
+               if(!Pcm || !pcm_is_ready(Pcm)) {
+                       LOGE("TinyALSAAudioStreamIn::set() failed: %s",
+                               pcm_get_error(Pcm));
 
-       mPcm = pcm_open(0, 0, PCM_IN, &pcmConfig);
+                       return BAD_VALUE;
+               }
 
-       if(!mPcm || !::pcm_is_ready(mPcm)) {
-               LOGE("TinyALSAAudioStreamIn::set() failed: %s\n",
-                       ::pcm_get_error(mPcm));
-               pcm_close(mPcm);
-
-               return BAD_VALUE;
+               mAudioResampler = new TinyALSAAudioResampler(Pcm,
+                       PcmConfig.period_size * PcmConfig.period_count,
+                       PcmConfig.rate, PcmConfig.channels,
+                       *pRate, AudioSystem::popCount(*pChannels));
        }
 
-       //TODO: Sort the controls
+       // Do the routing
+       mDevice = devices;
+       //mMixer->route(mDevice);
+
+       // Copy the PCM infos
+       mPcm = Pcm;
+       memcpy(&mPcmConfig, &PcmConfig, sizeof(struct pcm_config));
+
+       *pRate = sampleRate();
+       *pFormat = format();
+       *pChannels = channels();
 
        return NO_ERROR;
 }
 
-ssize_t TinyALSAAudioStreamIn::read(void* buffer, ssize_t bytes)
+uint32_t TinyALSAAudioStreamIn::sampleRate(void) const
 {
-       AutoMutex lock(mLock);
+       if(mAudioResampler != NULL)
+               return mAudioResampler->sampleRate();
 
-       if(!::pcm_is_ready(mPcm)) {
-               // This should never happen...
-               return BAD_VALUE;
-       }
+       return (uint32_t) mPcmConfig.rate;
+}
 
-       if(pcm_read(mPcm, buffer, bytes) != 0) {
-               LOGE("TinyALSAAudioStreamIn::read() failed: %s",
-                       ::pcm_get_error(mPcm));
-               return BAD_VALUE;
-       }
+size_t TinyALSAAudioStreamIn::bufferSize(void) const
+{
+       if(mAudioResampler != NULL)
+               return mAudioResampler->bufferSize();
 
-       return NO_ERROR;
+       return (size_t) pcm_get_buffer_size(mPcm);
 }
 
-size_t TinyALSAAudioStreamIn::getInputBufferSize(uint32_t sampleRate, 
-       int inputFormat, int channelCount)
+uint32_t TinyALSAAudioStreamIn::channels(void) const
 {
-       LOGD("TinyALSAAudioStreamIn::getInputBufferSize()");
+       if(mAudioResampler != NULL)
+               return mAudioResampler->channels();
+
+       switch(mPcmConfig.channels) {
+               case 1:
+                       return AudioSystem::CHANNEL_OUT_MONO;
+               case 2:
+                       return AudioSystem::CHANNEL_OUT_STEREO;
+               case 4:
+                       return AudioSystem::CHANNEL_OUT_QUAD;
+               case 6:
+                       return AudioSystem::CHANNEL_OUT_5POINT1;
+               default:
+                       return AudioSystem::CHANNEL_OUT_STEREO;
+       }
+}
 
-       if(!mPcm) {
-               LOGE("PCM wasn't initialized yet!");
-               return 0;
+int TinyALSAAudioStreamIn::format(void) const
+{
+       switch(mPcmConfig.format) {
+               case PCM_FORMAT_S16_LE:
+                       return AudioSystem::PCM_16_BIT;
+               default:
+                       return AudioSystem::PCM_16_BIT;
        }
+}
 
-       return pcm_get_buffer_size(mPcm);
+ssize_t TinyALSAAudioStreamIn::read(void* buffer, ssize_t bytes)
+{
+       return mAudioResampler->resample(buffer, bytes);
 }
 
-status_t TinyALSAAudioStreamIn::dump(int fd, const Vector<String16>& args)
+status_t TinyALSAAudioStreamIn::standby()
 {
        return NO_ERROR;
 }
@@ -183,4 +218,19 @@ String8 TinyALSAAudioStreamIn::getParameters(const String8& keys)
        return param.toString();
 }
 
+unsigned int TinyALSAAudioStreamIn::getInputFramesLost() const
+{
+       return 0;
+}
+
+status_t TinyALSAAudioStreamIn::setGain(float gain)
+{
+       return INVALID_OPERATION;
+}
+
+status_t TinyALSAAudioStreamIn::dump(int fd, const Vector<String16>& args)
+{
+       return NO_ERROR;
+}
+
 }; // namespace android
index 2b835e0..fc41aa5 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Copyright (C) 2012 Paul Kocialkowski <contact@paulk.fr>
- * 
+ *
  * This is based on TinyHardware:
  * Copyright 2011 Wolfson Microelectronics plc
  *
 
 #include <hardware_legacy/AudioHardwareBase.h>
 #include "AudioHardware.h"
+#include "AudioResampler.h"
 
 namespace android {
 
 class AudioHardware;
+class TinyALSAAudioResampler;
 
 class TinyALSAAudioStreamIn : public AudioStreamIn
 {
@@ -40,29 +42,30 @@ public:
                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 uint32_t sampleRate(void) const;
+       virtual size_t bufferSize(void) const;
+       virtual uint32_t channels(void) const;
+       virtual int format(void) const;
 
        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 standby();
+
        virtual status_t setParameters(const String8& keyValuePairs);
        virtual String8 getParameters(const String8& keys);
 
+       virtual unsigned int getInputFramesLost() const;
+       virtual status_t setGain(float gain);
+       virtual status_t dump(int fd, const Vector<String16>& args);
+
 private:
        Mutex mLock;
        AudioHardware *mAudioHardware;
+       TinyALSAAudioResampler *mAudioResampler;
+
        struct pcm *mPcm;
+       struct pcm_config mPcmConfig;
+
        uint32_t mDevice;
-       int mChannels;
-       int mChannelCount;
-       int mSampleRate;
 };
 
 }; // namespace android
index 02c62b2..0a87f6b 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Copyright (C) 2012 Paul Kocialkowski <contact@paulk.fr>
- * 
+ *
  * This is based on TinyHardware:
  * Copyright 2011 Wolfson Microelectronics plc
  *
@@ -38,6 +38,8 @@ TinyALSAAudioStreamOut::TinyALSAAudioStreamOut() :
 {
        LOGD("TinyALSAAudioStreamOut()");
 
+       memset(&mPcmConfig, 0, sizeof(struct pcm_config));
+
        mMixer = new TinyALSAMixer();
 }
 
@@ -54,7 +56,8 @@ TinyALSAAudioStreamOut::~TinyALSAAudioStreamOut()
 status_t TinyALSAAudioStreamOut::set(AudioHardware *hw, uint32_t devices,
        int *pFormat, uint32_t *pChannels, uint32_t *pRate)
 {
-       struct pcm_config pcmConfig;
+       struct pcm *Pcm = NULL;
+       struct pcm_config PcmConfig;
 
        LOGD("TinyALSAAudioStreamOut::set()");
 
@@ -62,70 +65,125 @@ status_t TinyALSAAudioStreamOut::set(AudioHardware *hw, uint32_t devices,
        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;
+       // Setting PcmConfig
+       memset(&PcmConfig, 0, sizeof(struct pcm_config));
+       PcmConfig.channels = AudioSystem::popCount(*pChannels);
+       PcmConfig.rate = *pRate;
+       PcmConfig.format = PCM_FORMAT_S16_LE;
+       PcmConfig.period_size = 1056;
+       PcmConfig.period_count = 4;
 
-       // TODO: read rate, if not try 44100 then 48000 
-       pcmConfig.rate = 44100;
+       Pcm = pcm_open(0, 0, PCM_OUT, &PcmConfig);
 
-       mPcm = pcm_open(0, 0, PCM_OUT, &pcmConfig);
+       if(!Pcm || !pcm_is_ready(Pcm)) {
+               LOGE("TinyALSAAudioStreamOut::set() failed: %s",
+                       pcm_get_error(Pcm));
 
-       if(!mPcm || !::pcm_is_ready(mPcm)) {
-               LOGE("TinyALSAAudioStreamOut::set() failed: %s\n", 
-                       ::pcm_get_error(mPcm));
-               pcm_close(mPcm);
+               pcm_close(Pcm);
 
-               return BAD_VALUE;
-       }
+               LOGD("TinyALSAAudioStreamOut::set(): trying fixed params");
+
+               // TODO: Get known-to-work values from Mixer config
+               PcmConfig.channels = 2;
+               PcmConfig.rate = 44100;
+
+               Pcm = pcm_open(0, 0, PCM_OUT, &PcmConfig);
 
-       mSampleRate = pcmConfig.rate;
-       *pRate = pcmConfig.rate;
-       *pFormat = AudioSystem::PCM_16_BIT;
-       *pChannels = AudioSystem::CHANNEL_OUT_STEREO;
+               if(!Pcm || !pcm_is_ready(Pcm)) {
+                       LOGE("TinyALSAAudioStreamOut::set() failed: %s",
+                               pcm_get_error(Pcm));
 
+                       return BAD_VALUE;
+               }
+       }
+
+       // Do the routing
+       mDevice = devices;
        mMixer->route(mDevice);
 
+       // Copy the PCM infos
+       mPcm = Pcm;
+       memcpy(&mPcmConfig, &PcmConfig, sizeof(struct pcm_config));
+
+       *pRate = sampleRate();
+       *pFormat = format();
+       *pChannels = channels();
+
        return NO_ERROR;
 }
 
-ssize_t TinyALSAAudioStreamOut::write(const void* buffer, size_t bytes)
+uint32_t TinyALSAAudioStreamOut::sampleRate(void) const
+{
+       return (uint32_t) mPcmConfig.rate;
+}
+
+size_t TinyALSAAudioStreamOut::bufferSize(void) const
+{
+       return (size_t) pcm_get_buffer_size(mPcm);
+}
+
+uint32_t TinyALSAAudioStreamOut::channels(void) const
+{
+       switch(mPcmConfig.channels) {
+               case 1:
+                       return AudioSystem::CHANNEL_OUT_MONO;
+               case 2:
+                       return AudioSystem::CHANNEL_OUT_STEREO;
+               case 4:
+                       return AudioSystem::CHANNEL_OUT_QUAD;
+               case 6:
+                       return AudioSystem::CHANNEL_OUT_5POINT1;
+               default:
+                       return AudioSystem::CHANNEL_OUT_STEREO;
+       }
+}
+
+int TinyALSAAudioStreamOut::format(void) const
+{
+       switch(mPcmConfig.format) {
+               case PCM_FORMAT_S16_LE:
+                       return AudioSystem::PCM_16_BIT;
+               default:
+                       return AudioSystem::PCM_16_BIT;
+       }
+}
+
+uint32_t TinyALSAAudioStreamOut::latency(void) const
+{
+       return (mPcmConfig.period_size * mPcmConfig.period_count * 1000) / mPcmConfig.rate;
+}
+
+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_is_ready(mPcm)) {
+               LOGE("TinyALSAAudioStreamOut::write() failed: PCM is not ready!");
+
+               return 0;
        }
 
        if(pcm_write(mPcm, buffer, bytes) != 0) {
-               LOGE("TinyALSAAudioStreamOut::write() failed: %s\n",
-                       ::pcm_get_error(mPcm));
-               return BAD_VALUE;
+               LOGE("TinyALSAAudioStreamOut::write() failed: %s",
+                       pcm_get_error(mPcm));
+
+               return 0;
        }
 
-       return NO_ERROR;
+       return bytes;
 }
 
 status_t TinyALSAAudioStreamOut::standby()
 {
        if(pcm_stop(mPcm) != 0) {
-               LOGE("TinyALSAAudioStreamOut::standby() failed: %s\n", 
-                       ::pcm_get_error(mPcm));
+               LOGE("TinyALSAAudioStreamOut::standby() failed: %s",
+                       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);
@@ -166,8 +224,18 @@ String8 TinyALSAAudioStreamOut::getParameters(const String8& keys)
 
 status_t TinyALSAAudioStreamOut::getRenderPosition(uint32_t *dspFrames)
 {
-       // Don't bother just yet, there are no actual users of this.
        return INVALID_OPERATION;
 }
 
+
+status_t TinyALSAAudioStreamOut::setVolume(float left, float right)
+{
+       return INVALID_OPERATION;
+}
+
+status_t TinyALSAAudioStreamOut::dump(int fd, const Vector<String16>& args)
+{
+       return NO_ERROR;
+}
+
 }; // namespace android
index c9cf305..ab550c5 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Copyright (C) 2012 Paul Kocialkowski <contact@paulk.fr>
- * 
+ *
  * This is based on TinyHardware:
  * Copyright 2011 Wolfson Microelectronics plc
  *
@@ -40,27 +40,31 @@ public:
        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 uint32_t sampleRate(void) const;
+       virtual size_t bufferSize(void) const;
+       virtual uint32_t channels(void) const;
+       virtual int format(void) const;
+       virtual uint32_t latency(void) const;
 
        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 standby(void);
+
        virtual status_t setParameters(const String8& keyValuePairs);
        virtual String8 getParameters(const String8& keys);
+
        virtual status_t getRenderPosition(uint32_t *dspFrames);
+       virtual status_t setVolume(float left, float right);
+       virtual status_t dump(int fd, const Vector<String16>& args);
 
 private:
        Mutex mLock;
        AudioHardware *mAudioHardware;
        TinyALSAMixer *mMixer;
+
        struct pcm *mPcm;
+       struct pcm_config mPcmConfig;
+
        uint32_t mDevice;
-       uint32_t mSampleRate;
 };
 
 }; // namespace android