#if !defined(CUBEB_RESAMPLER_INTERNAL)
#define CUBEB_RESAMPLER_INTERNAL
#include <algorithm>
#include <cassert>
#include <cmath>
#include <memory>
#ifdef CUBEB_GECKO_BUILD
#include "mozilla/UniquePtr.h"
#ifdef _LIBCPP_BEGIN_NAMESPACE_STD
#define MOZ_BEGIN_STD_NAMESPACE _LIBCPP_BEGIN_NAMESPACE_STD
#define MOZ_END_STD_NAMESPACE _LIBCPP_END_NAMESPACE_STD
#else
#define MOZ_BEGIN_STD_NAMESPACE namespace std {
#define MOZ_END_STD_NAMESPACE }
#endif
MOZ_BEGIN_STD_NAMESPACE
using mozilla::DefaultDelete;
using mozilla::UniquePtr;
#define default_delete DefaultDelete
#define unique_ptr UniquePtr
MOZ_END_STD_NAMESPACE
#endif
#include "cubeb-speex-resampler.h"
#include "cubeb/cubeb.h"
#include "cubeb_log.h"
#include "cubeb_resampler.h"
#include "cubeb_utils.h"
#include <stdio.h>
enum class cubeb_resampler_direction { INPUT, OUTPUT, DUPLEX };
uint32_t
min_buffered_audio_frame(uint32_t sample_rate);
int
to_speex_quality(cubeb_resampler_quality q);
struct cubeb_resampler {
virtual long fill(void * input_buffer, long * input_frames_count,
void * output_buffer, long frames_needed) = 0;
virtual long latency() = 0;
virtual long input_latency() { return 0; }
virtual long input_needed_for_output(long output_frames)
{
return output_frames;
}
virtual cubeb_resampler_stats stats() = 0;
virtual ~cubeb_resampler() {}
};
class processor {
public:
explicit processor(uint32_t channels) : channels(channels) {}
protected:
size_t frames_to_samples(size_t frames) const { return frames * channels; }
size_t samples_to_frames(size_t samples) const
{
assert(!(samples % channels));
return samples / channels;
}
const uint32_t channels;
};
template <typename T>
class passthrough_resampler : public cubeb_resampler, public processor {
public:
passthrough_resampler(cubeb_stream * s, cubeb_data_callback cb, void * ptr,
uint32_t input_channels, uint32_t sample_rate);
virtual long fill(void * input_buffer, long * input_frames_count,
void * output_buffer, long output_frames);
virtual long latency() { return 0; }
virtual long input_latency() { return 0; }
virtual long input_needed_for_output(long output_frames)
{
if (channels == 0) {
return 0;
}
long queued =
static_cast<long>(samples_to_frames(internal_input_buffer.length()));
return std::max(0L, output_frames - queued);
}
virtual cubeb_resampler_stats stats()
{
cubeb_resampler_stats stats;
stats.input_input_buffer_size = internal_input_buffer.length();
stats.input_output_buffer_size = 0;
stats.output_input_buffer_size = 0;
stats.output_output_buffer_size = 0;
return stats;
}
void drop_audio_if_needed()
{
uint32_t to_keep = min_buffered_audio_frame(sample_rate);
uint32_t available = samples_to_frames(internal_input_buffer.length());
if (available > to_keep) {
ALOGV("Dropping %u frames", available - to_keep);
internal_input_buffer.pop(nullptr,
frames_to_samples(available - to_keep));
}
}
private:
cubeb_stream * const stream;
const cubeb_data_callback data_callback;
void * const user_ptr;
auto_array<T> internal_input_buffer;
uint32_t sample_rate;
};
template <typename T, typename InputProcessing, typename OutputProcessing>
class cubeb_resampler_speex : public cubeb_resampler {
public:
cubeb_resampler_speex(InputProcessing * input_processor,
OutputProcessing * output_processor, cubeb_stream * s,
cubeb_data_callback cb, void * ptr,
cubeb_resampler_direction direction,
uint32_t input_channels, uint32_t target_rate);
virtual ~cubeb_resampler_speex();
virtual long fill(void * input_buffer, long * input_frames_count,
void * output_buffer, long output_frames_needed);
virtual cubeb_resampler_stats stats()
{
cubeb_resampler_stats stats = {};
if (input_processor) {
stats.input_input_buffer_size = input_processor->input_buffer_size();
stats.input_output_buffer_size = input_processor->output_buffer_size();
} else {
stats.input_input_buffer_size = input_queue.length();
}
if (output_processor) {
stats.output_input_buffer_size = output_processor->input_buffer_size();
stats.output_output_buffer_size = output_processor->output_buffer_size();
}
return stats;
}
long output_latency() const
{
return output_processor ? output_processor->latency() : 0;
}
virtual long latency() { return output_latency(); }
virtual long input_latency()
{
return input_processor ? static_cast<long>(input_processor->latency()) : 0;
}
virtual long input_needed_for_output(long output_frames)
{
long target_frames =
output_processor
? static_cast<long>(output_processor->input_needed_for_output(
static_cast<int32_t>(output_frames)))
: output_frames;
if (input_processor) {
return static_cast<long>(input_processor->input_needed_for_output(
static_cast<int32_t>(target_frames)));
}
if (input_channels == 0) {
return 0;
}
long queued = static_cast<long>(input_queue.length() / input_channels);
return std::max(0L, target_frames - queued);
}
private:
typedef long (cubeb_resampler_speex::*processing_callback)(
T * input_buffer, long * input_frames_count, T * output_buffer,
long output_frames_needed);
long fill_internal_duplex(T * input_buffer, long * input_frames_count,
T * output_buffer, long output_frames_needed);
long fill_internal_input(T * input_buffer, long * input_frames_count,
T * output_buffer, long output_frames_needed);
long fill_internal_output(T * input_buffer, long * input_frames_count,
T * output_buffer, long output_frames_needed);
std::unique_ptr<InputProcessing> input_processor;
std::unique_ptr<OutputProcessing> output_processor;
processing_callback fill_internal;
cubeb_stream * const stream;
const cubeb_data_callback data_callback;
void * const user_ptr;
bool draining = false;
auto_array<T> input_queue;
const uint32_t input_channels;
const uint32_t target_rate;
};
template <typename T> class cubeb_resampler_speex_one_way : public processor {
public:
typedef T sample_type;
cubeb_resampler_speex_one_way(uint32_t channels, uint32_t source_rate,
uint32_t target_rate, int quality)
: processor(channels),
resampling_ratio(static_cast<float>(source_rate) / target_rate),
source_rate(source_rate), leftover_samples(0)
{
int r;
speex_resampler =
speex_resampler_init(channels, source_rate, target_rate, quality, &r);
assert(r == RESAMPLER_ERR_SUCCESS && "resampler allocation failure");
uint32_t input_latency = speex_resampler_get_input_latency(speex_resampler);
const size_t LATENCY_SAMPLES = 8192;
T input_buffer[LATENCY_SAMPLES] = {};
T output_buffer[LATENCY_SAMPLES] = {};
const uint32_t latency_frames =
LATENCY_SAMPLES / std::max<uint32_t>(channels, 1);
assert(latency_frames * channels <= LATENCY_SAMPLES);
uint32_t remaining = input_latency;
while (remaining > 0) {
uint32_t input_frame_count = std::min(remaining, latency_frames);
uint32_t output_frame_count = latency_frames;
speex_resample(input_buffer, &input_frame_count, output_buffer,
&output_frame_count);
remaining -= input_frame_count;
}
}
virtual ~cubeb_resampler_speex_one_way()
{
speex_resampler_destroy(speex_resampler);
}
void input(T * input_buffer, size_t input_frame_count)
{
resampling_in_buffer.push(input_buffer,
frames_to_samples(input_frame_count));
}
size_t output(T * output_buffer, size_t output_frame_count)
{
uint32_t in_len = samples_to_frames(resampling_in_buffer.length());
uint32_t out_len = output_frame_count;
speex_resample(resampling_in_buffer.data(), &in_len, output_buffer,
&out_len);
resampling_in_buffer.pop(nullptr, frames_to_samples(in_len));
return out_len;
}
size_t output_for_input(uint32_t input_frames)
{
return (size_t)floorf(
(input_frames + samples_to_frames(resampling_in_buffer.length())) /
resampling_ratio);
}
T * output(size_t output_frame_count, size_t * input_frames_used)
{
if (resampling_out_buffer.capacity() <
frames_to_samples(output_frame_count)) {
resampling_out_buffer.reserve(frames_to_samples(output_frame_count));
}
uint32_t in_len = samples_to_frames(resampling_in_buffer.length());
uint32_t out_len = output_frame_count;
speex_resample(resampling_in_buffer.data(), &in_len,
resampling_out_buffer.data(), &out_len);
if (out_len < output_frame_count) {
LOGV("underrun during resampling: got %u frames, expected %zu",
(unsigned)out_len, output_frame_count);
T * data = resampling_out_buffer.data();
for (uint32_t i = frames_to_samples(out_len);
i < frames_to_samples(output_frame_count); i++) {
data[i] = 0;
}
}
resampling_in_buffer.pop(nullptr, frames_to_samples(in_len));
if (input_frames_used) {
*input_frames_used = in_len;
}
return resampling_out_buffer.data();
}
uint32_t latency() const
{
return speex_resampler_get_output_latency(speex_resampler);
}
uint32_t input_needed_for_output(int32_t output_frame_count) const
{
assert(output_frame_count >= 0); if (output_frame_count == 0) {
return 0;
}
int32_t unresampled_frames_left =
samples_to_frames(resampling_in_buffer.length());
float input_frames_needed_frac =
static_cast<float>(output_frame_count) * resampling_ratio;
auto input_frame_needed =
1 + static_cast<int32_t>(ceilf(input_frames_needed_frac));
input_frame_needed -= std::min(unresampled_frames_left, input_frame_needed);
return input_frame_needed;
}
T * input_buffer(size_t frame_count)
{
leftover_samples = resampling_in_buffer.length();
resampling_in_buffer.reserve(leftover_samples +
frames_to_samples(frame_count));
return resampling_in_buffer.data() + leftover_samples;
}
void written(size_t written_frames)
{
resampling_in_buffer.set_length(leftover_samples +
frames_to_samples(written_frames));
}
void drop_audio_if_needed()
{
uint32_t available = samples_to_frames(resampling_in_buffer.length());
uint32_t to_keep = min_buffered_audio_frame(source_rate);
if (available > to_keep) {
ALOGV("Dropping %u frames", available - to_keep);
resampling_in_buffer.pop(nullptr, frames_to_samples(available - to_keep));
}
}
size_t input_buffer_size() const { return resampling_in_buffer.length(); }
size_t output_buffer_size() const { return resampling_out_buffer.length(); }
private:
void speex_resample(float * input_buffer, uint32_t * input_frame_count,
float * output_buffer, uint32_t * output_frame_count)
{
int rv = speex_resampler_process_interleaved_float(
speex_resampler, input_buffer, input_frame_count, output_buffer,
output_frame_count);
assert(rv == RESAMPLER_ERR_SUCCESS);
if (rv != RESAMPLER_ERR_SUCCESS) {
ALOG("speex_resampler_process_interleaved_float error: %d", rv);
}
}
void speex_resample(short * input_buffer, uint32_t * input_frame_count,
short * output_buffer, uint32_t * output_frame_count)
{
int rv = speex_resampler_process_interleaved_int(
speex_resampler, input_buffer, input_frame_count, output_buffer,
output_frame_count);
assert(rv == RESAMPLER_ERR_SUCCESS);
if (rv != RESAMPLER_ERR_SUCCESS) {
ALOG("speex_resampler_process_interleaved_int error: %d", rv);
}
}
SpeexResamplerState * speex_resampler;
const float resampling_ratio;
const uint32_t source_rate;
auto_array<T> resampling_in_buffer;
auto_array<T> resampling_out_buffer;
uint32_t leftover_samples;
};
template <typename T>
cubeb_resampler *
cubeb_resampler_create_internal(cubeb_stream * stream,
cubeb_stream_params * input_params,
cubeb_stream_params * output_params,
unsigned int target_rate,
cubeb_data_callback callback, void * user_ptr,
cubeb_resampler_quality quality,
cubeb_resampler_reclock reclock)
{
std::unique_ptr<cubeb_resampler_speex_one_way<T>> input_resampler = nullptr;
std::unique_ptr<cubeb_resampler_speex_one_way<T>> output_resampler = nullptr;
assert((input_params || output_params) &&
"need at least one valid parameter pointer.");
if (((input_params && input_params->rate == target_rate) &&
(output_params && output_params->rate == target_rate)) ||
(input_params && !output_params && (input_params->rate == target_rate)) ||
(output_params && !input_params &&
(output_params->rate == target_rate))) {
LOG("Input and output sample-rate match, target rate of %dHz", target_rate);
return new passthrough_resampler<T>(
stream, callback, user_ptr, input_params ? input_params->channels : 0,
target_rate);
}
if (output_params && (output_params->rate != target_rate)) {
output_resampler.reset(new cubeb_resampler_speex_one_way<T>(
output_params->channels, target_rate, output_params->rate,
to_speex_quality(quality)));
}
if (input_params && (input_params->rate != target_rate)) {
input_resampler.reset(new cubeb_resampler_speex_one_way<T>(
input_params->channels, input_params->rate, target_rate,
to_speex_quality(quality)));
}
auto direction = (input_params && output_params)
? cubeb_resampler_direction::DUPLEX
: (input_params ? cubeb_resampler_direction::INPUT
: cubeb_resampler_direction::OUTPUT);
uint32_t in_channels = input_params ? input_params->channels : 0;
if (input_resampler && output_resampler) {
LOG("Resampling input (%d) and output (%d) to target rate of %dHz",
input_params->rate, output_params->rate, target_rate);
return new cubeb_resampler_speex<T, cubeb_resampler_speex_one_way<T>,
cubeb_resampler_speex_one_way<T>>(
input_resampler.release(), output_resampler.release(), stream, callback,
user_ptr, direction, in_channels, target_rate);
} else if (input_resampler) {
LOG("Resampling input (%d) to target rate of %dHz", input_params->rate,
target_rate);
return new cubeb_resampler_speex<T, cubeb_resampler_speex_one_way<T>,
cubeb_resampler_speex_one_way<T>>(
input_resampler.release(), nullptr, stream, callback, user_ptr,
direction, in_channels, target_rate);
} else {
LOG("Resampling output (%dHz) to target rate of %dHz", output_params->rate,
target_rate);
return new cubeb_resampler_speex<T, cubeb_resampler_speex_one_way<T>,
cubeb_resampler_speex_one_way<T>>(
nullptr, output_resampler.release(), stream, callback, user_ptr,
direction, in_channels, target_rate);
}
}
#endif