/*
** Copyright 2011, 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_NDEBUG 0
#define LOG_TAG "resampler"
#include <errno.h>
#include <stdlib.h>
#include <cutils/log.h>
#include <system/audio.h>
#include <audio_utils/resampler.h>
#include <speex/speex_resampler.h>
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->in_sample_rate) / rsmp->out_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;
ALOGW_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);
}
ALOGV("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;
ALOGV("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) {
ALOGW("ReSampler: Cannot create speex resampler: %s", speex_resampler_strerror(error));
free(rsmp);
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;
ALOGV("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);
}