#ifndef NOMINMAX
#define NOMINMAX
#endif
#include "cubeb_resampler.h"
#include "cubeb-speex-resampler.h"
#include "cubeb_resampler_internal.h"
#include "cubeb_utils.h"
#include <algorithm>
#include <cassert>
#include <cmath>
#include <cstddef>
#include <cstdio>
#include <cstring>
int
to_speex_quality(cubeb_resampler_quality q)
{
switch (q) {
case CUBEB_RESAMPLER_QUALITY_VOIP:
return SPEEX_RESAMPLER_QUALITY_VOIP;
case CUBEB_RESAMPLER_QUALITY_DEFAULT:
return SPEEX_RESAMPLER_QUALITY_DEFAULT;
case CUBEB_RESAMPLER_QUALITY_DESKTOP:
return SPEEX_RESAMPLER_QUALITY_DESKTOP;
default:
assert(false);
return 0XFFFFFFFF;
}
}
uint32_t
min_buffered_audio_frame(uint32_t sample_rate)
{
return sample_rate / 20;
}
template <typename T>
passthrough_resampler<T>::passthrough_resampler(cubeb_stream * s,
cubeb_data_callback cb,
void * ptr,
uint32_t input_channels,
uint32_t sample_rate)
: processor(input_channels), stream(s), data_callback(cb), user_ptr(ptr),
sample_rate(sample_rate)
{
}
template <typename T>
long
passthrough_resampler<T>::fill(void * input_buffer, long * input_frames_count,
void * output_buffer, long output_frames)
{
if (input_buffer) {
assert(input_frames_count);
}
assert((input_buffer && output_buffer) ||
(output_buffer && !input_buffer &&
(!input_frames_count || *input_frames_count == 0)) ||
(input_buffer && !output_buffer && output_frames == 0));
void * in_buf = input_buffer;
unsigned long pop_input_count = 0u;
long original_input_frames_count = input_buffer ? *input_frames_count : 0;
if (input_buffer && !output_buffer) {
output_frames = *input_frames_count;
} else if (input_buffer) {
if (internal_input_buffer.length() != 0 ||
*input_frames_count < output_frames) {
internal_input_buffer.push(static_cast<T *>(input_buffer),
frames_to_samples(*input_frames_count));
if (internal_input_buffer.length() < frames_to_samples(output_frames)) {
pop_input_count = internal_input_buffer.length();
internal_input_buffer.push_silence(frames_to_samples(output_frames) -
internal_input_buffer.length());
} else {
pop_input_count = frames_to_samples(output_frames);
}
in_buf = internal_input_buffer.data();
} else if (*input_frames_count > output_frames) {
assert(pop_input_count == 0);
unsigned long samples_off = frames_to_samples(output_frames);
internal_input_buffer.push(
static_cast<T *>(input_buffer) + samples_off,
frames_to_samples(*input_frames_count - output_frames));
}
}
long rv =
data_callback(stream, user_ptr, in_buf, output_buffer, output_frames);
if (input_buffer) {
if (pop_input_count) {
internal_input_buffer.pop(nullptr, pop_input_count);
}
*input_frames_count = original_input_frames_count;
drop_audio_if_needed();
}
return rv;
}
template class passthrough_resampler<float>;
template class passthrough_resampler<short>;
template <typename T, typename InputProcessor, typename OutputProcessor>
cubeb_resampler_speex<T, InputProcessor, OutputProcessor>::
cubeb_resampler_speex(InputProcessor * input_processor,
OutputProcessor * output_processor, cubeb_stream * s,
cubeb_data_callback cb, void * ptr,
cubeb_resampler_direction direction,
uint32_t input_channels, uint32_t target_rate)
: input_processor(input_processor), output_processor(output_processor),
stream(s), data_callback(cb), user_ptr(ptr),
input_channels(input_channels), target_rate(target_rate)
{
switch (direction) {
case cubeb_resampler_direction::DUPLEX:
fill_internal = &cubeb_resampler_speex::fill_internal_duplex;
break;
case cubeb_resampler_direction::INPUT:
fill_internal = &cubeb_resampler_speex::fill_internal_input;
break;
case cubeb_resampler_direction::OUTPUT:
fill_internal = &cubeb_resampler_speex::fill_internal_output;
break;
}
}
template <typename T, typename InputProcessor, typename OutputProcessor>
cubeb_resampler_speex<T, InputProcessor,
OutputProcessor>::~cubeb_resampler_speex()
{
}
template <typename T, typename InputProcessor, typename OutputProcessor>
long
cubeb_resampler_speex<T, InputProcessor, OutputProcessor>::fill(
void * input_buffer, long * input_frames_count, void * output_buffer,
long output_frames_needed)
{
T * in_buffer = reinterpret_cast<T *>(input_buffer);
T * out_buffer = reinterpret_cast<T *>(output_buffer);
return (this->*fill_internal)(in_buffer, input_frames_count, out_buffer,
output_frames_needed);
}
template <typename T, typename InputProcessor, typename OutputProcessor>
long
cubeb_resampler_speex<T, InputProcessor, OutputProcessor>::fill_internal_output(
T * input_buffer, long * input_frames_count, T * output_buffer,
long output_frames_needed)
{
assert(!input_buffer && (!input_frames_count || *input_frames_count == 0) &&
output_buffer && output_frames_needed);
if (!draining) {
long got = 0;
T * out_unprocessed = nullptr;
long output_frames_before_processing = 0;
output_frames_before_processing =
output_processor->input_needed_for_output(output_frames_needed);
out_unprocessed =
output_processor->input_buffer(output_frames_before_processing);
got = data_callback(stream, user_ptr, nullptr, out_unprocessed,
output_frames_before_processing);
if (got < output_frames_before_processing) {
draining = true;
if (got < 0) {
return got;
}
}
output_processor->written(got);
}
return output_processor->output(output_buffer, output_frames_needed);
}
template <typename T, typename InputProcessor, typename OutputProcessor>
long
cubeb_resampler_speex<T, InputProcessor, OutputProcessor>::fill_internal_input(
T * input_buffer, long * input_frames_count, T * output_buffer,
long )
{
assert(input_buffer && input_frames_count && *input_frames_count &&
!output_buffer);
long original_count = *input_frames_count;
T * resampled_input = nullptr;
uint32_t resampled_frame_count =
input_processor->output_for_input(*input_frames_count);
input_processor->input(input_buffer, *input_frames_count);
if (resampled_frame_count == 0) {
*input_frames_count = original_count;
return original_count;
}
resampled_input = input_processor->output(resampled_frame_count, nullptr);
long got = data_callback(stream, user_ptr, resampled_input, nullptr,
resampled_frame_count);
if (got < 0) {
input_processor->drop_audio_if_needed();
*input_frames_count = original_count;
return got;
}
input_processor->drop_audio_if_needed();
*input_frames_count = original_count;
return original_count * (got / static_cast<long>(resampled_frame_count));
}
template <typename T, typename InputProcessor, typename OutputProcessor>
long
cubeb_resampler_speex<T, InputProcessor, OutputProcessor>::fill_internal_duplex(
T * in_buffer, long * input_frames_count, T * out_buffer,
long output_frames_needed)
{
if (draining) {
if (output_processor) {
return output_processor->output(out_buffer, output_frames_needed);
}
return 0;
}
T * resampled_input = nullptr;
T * out_unprocessed = nullptr;
long output_frames_before_processing = 0;
long got = 0;
if (output_processor) {
output_frames_before_processing =
output_processor->input_needed_for_output(output_frames_needed);
out_unprocessed =
output_processor->input_buffer(output_frames_before_processing);
} else {
output_frames_before_processing = output_frames_needed;
out_unprocessed = out_buffer;
}
if (in_buffer) {
if (input_processor) {
long original_count = *input_frames_count;
input_processor->input(in_buffer, *input_frames_count);
resampled_input =
input_processor->output(output_frames_before_processing, nullptr);
*input_frames_count = original_count;
} else {
long original_count = *input_frames_count;
input_queue.push(in_buffer, *input_frames_count * input_channels);
long available = static_cast<long>(input_queue.length() / input_channels);
if (available < output_frames_before_processing) {
input_queue.push_silence((output_frames_before_processing - available) *
input_channels);
}
resampled_input = input_queue.data();
*input_frames_count = original_count;
}
}
got = data_callback(stream, user_ptr, resampled_input, out_unprocessed,
output_frames_before_processing);
if (got < output_frames_before_processing) {
draining = true;
if (got < 0) {
return got;
}
}
if (output_processor) {
output_processor->written(got);
}
if (input_processor) {
input_processor->drop_audio_if_needed();
} else if (in_buffer) {
input_queue.pop(nullptr, output_frames_before_processing * input_channels);
uint32_t available =
static_cast<uint32_t>(input_queue.length() / input_channels);
uint32_t to_keep = min_buffered_audio_frame(target_rate);
if (available > to_keep) {
input_queue.pop(nullptr, (available - to_keep) * input_channels);
}
}
if (output_processor) {
got = output_processor->output(out_buffer, output_frames_needed);
output_processor->drop_audio_if_needed();
}
return got;
}
cubeb_resampler *
cubeb_resampler_create(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)
{
cubeb_sample_format format;
assert(input_params || output_params);
if (input_params) {
format = input_params->format;
} else {
format = output_params->format;
}
switch (format) {
case CUBEB_SAMPLE_S16NE:
return cubeb_resampler_create_internal<short>(
stream, input_params, output_params, target_rate, callback, user_ptr,
quality, reclock);
case CUBEB_SAMPLE_FLOAT32NE:
return cubeb_resampler_create_internal<float>(
stream, input_params, output_params, target_rate, callback, user_ptr,
quality, reclock);
default:
assert(false);
return nullptr;
}
}
long
cubeb_resampler_fill(cubeb_resampler * resampler, void * input_buffer,
long * input_frames_count, void * output_buffer,
long output_frames_needed)
{
return resampler->fill(input_buffer, input_frames_count, output_buffer,
output_frames_needed);
}
void
cubeb_resampler_destroy(cubeb_resampler * resampler)
{
delete resampler;
}
long
cubeb_resampler_latency(cubeb_resampler * resampler)
{
return resampler->latency();
}
long
cubeb_resampler_input_latency(cubeb_resampler * resampler)
{
return resampler->input_latency();
}
long
cubeb_resampler_input_needed_for_output(cubeb_resampler * resampler,
long output_frames)
{
return resampler->input_needed_for_output(output_frames);
}
cubeb_resampler_stats
cubeb_resampler_stats_get(cubeb_resampler * resampler)
{
return resampler->stats();
}