Path: blob/master/dep/cubeb/src/cubeb_resampler.cpp
4246 views
/*1* Copyright © 2014 Mozilla Foundation2*3* This program is made available under an ISC-style license. See the4* accompanying file LICENSE for details.5*/6#ifndef NOMINMAX7#define NOMINMAX8#endif // NOMINMAX910#include "cubeb_resampler.h"11#include "cubeb-speex-resampler.h"12#include "cubeb_resampler_internal.h"13#include "cubeb_utils.h"14#include <algorithm>15#include <cassert>16#include <cmath>17#include <cstddef>18#include <cstdio>19#include <cstring>2021int22to_speex_quality(cubeb_resampler_quality q)23{24switch (q) {25case CUBEB_RESAMPLER_QUALITY_VOIP:26return SPEEX_RESAMPLER_QUALITY_VOIP;27case CUBEB_RESAMPLER_QUALITY_DEFAULT:28return SPEEX_RESAMPLER_QUALITY_DEFAULT;29case CUBEB_RESAMPLER_QUALITY_DESKTOP:30return SPEEX_RESAMPLER_QUALITY_DESKTOP;31default:32assert(false);33return 0XFFFFFFFF;34}35}3637uint32_t38min_buffered_audio_frame(uint32_t sample_rate)39{40return sample_rate / 20;41}4243template <typename T>44passthrough_resampler<T>::passthrough_resampler(cubeb_stream * s,45cubeb_data_callback cb,46void * ptr,47uint32_t input_channels,48uint32_t sample_rate)49: processor(input_channels), stream(s), data_callback(cb), user_ptr(ptr),50sample_rate(sample_rate)51{52}5354template <typename T>55long56passthrough_resampler<T>::fill(void * input_buffer, long * input_frames_count,57void * output_buffer, long output_frames)58{59if (input_buffer) {60assert(input_frames_count);61}62assert((input_buffer && output_buffer) ||63(output_buffer && !input_buffer &&64(!input_frames_count || *input_frames_count == 0)) ||65(input_buffer && !output_buffer && output_frames == 0));6667// When we have no pending input data and exactly as much input68// as output data, we don't need to copy it into the internal buffer69// and can directly forward it to the callback.70void * in_buf = input_buffer;71unsigned long pop_input_count = 0u;72if (input_buffer && !output_buffer) {73output_frames = *input_frames_count;74} else if (input_buffer) {75if (internal_input_buffer.length() != 0 ||76*input_frames_count < output_frames) {77// If we have pending input data left and have to first append the input78// so we can pass it as one pointer to the callback. Or this is a glitch.79// It can happen when system's performance is poor. Audible silence is80// being pushed at the end of the short input buffer. An improvement for81// the future is to resample to the output number of frames, when that82// happens.83internal_input_buffer.push(static_cast<T *>(input_buffer),84frames_to_samples(*input_frames_count));85if (internal_input_buffer.length() < frames_to_samples(output_frames)) {86// This is unxpected but it can happen when a glitch occurs. Fill the87// buffer with silence. First keep the actual number of input samples88// used without the silence.89pop_input_count = internal_input_buffer.length();90internal_input_buffer.push_silence(frames_to_samples(output_frames) -91internal_input_buffer.length());92} else {93pop_input_count = frames_to_samples(output_frames);94}95in_buf = internal_input_buffer.data();96} else if (*input_frames_count > output_frames) {97// In this case we have more input that we need output and98// fill the overflowing input into internal_input_buffer99// Since we have no other pending data, we can nonetheless100// pass the current input data directly to the callback101assert(pop_input_count == 0);102unsigned long samples_off = frames_to_samples(output_frames);103internal_input_buffer.push(104static_cast<T *>(input_buffer) + samples_off,105frames_to_samples(*input_frames_count - output_frames));106}107}108109long rv =110data_callback(stream, user_ptr, in_buf, output_buffer, output_frames);111112if (input_buffer) {113if (pop_input_count) {114internal_input_buffer.pop(nullptr, pop_input_count);115*input_frames_count = samples_to_frames(pop_input_count);116} else {117*input_frames_count = output_frames;118}119drop_audio_if_needed();120}121122return rv;123}124125// Explicit instantiation of template class.126template class passthrough_resampler<float>;127template class passthrough_resampler<short>;128129template <typename T, typename InputProcessor, typename OutputProcessor>130cubeb_resampler_speex<T, InputProcessor, OutputProcessor>::131cubeb_resampler_speex(InputProcessor * input_processor,132OutputProcessor * output_processor, cubeb_stream * s,133cubeb_data_callback cb, void * ptr)134: input_processor(input_processor), output_processor(output_processor),135stream(s), data_callback(cb), user_ptr(ptr)136{137if (input_processor && output_processor) {138fill_internal = &cubeb_resampler_speex::fill_internal_duplex;139} else if (input_processor) {140fill_internal = &cubeb_resampler_speex::fill_internal_input;141} else if (output_processor) {142fill_internal = &cubeb_resampler_speex::fill_internal_output;143}144}145146template <typename T, typename InputProcessor, typename OutputProcessor>147cubeb_resampler_speex<T, InputProcessor,148OutputProcessor>::~cubeb_resampler_speex()149{150}151152template <typename T, typename InputProcessor, typename OutputProcessor>153long154cubeb_resampler_speex<T, InputProcessor, OutputProcessor>::fill(155void * input_buffer, long * input_frames_count, void * output_buffer,156long output_frames_needed)157{158/* Input and output buffers, typed */159T * in_buffer = reinterpret_cast<T *>(input_buffer);160T * out_buffer = reinterpret_cast<T *>(output_buffer);161return (this->*fill_internal)(in_buffer, input_frames_count, out_buffer,162output_frames_needed);163}164165template <typename T, typename InputProcessor, typename OutputProcessor>166long167cubeb_resampler_speex<T, InputProcessor, OutputProcessor>::fill_internal_output(168T * input_buffer, long * input_frames_count, T * output_buffer,169long output_frames_needed)170{171assert(!input_buffer && (!input_frames_count || *input_frames_count == 0) &&172output_buffer && output_frames_needed);173174if (!draining) {175long got = 0;176T * out_unprocessed = nullptr;177long output_frames_before_processing = 0;178179/* fill directly the input buffer of the output processor to save a copy */180output_frames_before_processing =181output_processor->input_needed_for_output(output_frames_needed);182183out_unprocessed =184output_processor->input_buffer(output_frames_before_processing);185186got = data_callback(stream, user_ptr, nullptr, out_unprocessed,187output_frames_before_processing);188189if (got < output_frames_before_processing) {190draining = true;191192if (got < 0) {193return got;194}195}196197output_processor->written(got);198}199200/* Process the output. If not enough frames have been returned from the201* callback, drain the processors. */202return output_processor->output(output_buffer, output_frames_needed);203}204205template <typename T, typename InputProcessor, typename OutputProcessor>206long207cubeb_resampler_speex<T, InputProcessor, OutputProcessor>::fill_internal_input(208T * input_buffer, long * input_frames_count, T * output_buffer,209long /*output_frames_needed*/)210{211assert(input_buffer && input_frames_count && *input_frames_count &&212!output_buffer);213214/* The input data, after eventual resampling. This is passed to the callback.215*/216T * resampled_input = nullptr;217uint32_t resampled_frame_count =218input_processor->output_for_input(*input_frames_count);219220/* process the input, and present exactly `output_frames_needed` in the221* callback. */222input_processor->input(input_buffer, *input_frames_count);223224/* resampled_frame_count == 0 happens if the resampler225* doesn't have enough input frames buffered to produce 1 resampled frame. */226if (resampled_frame_count == 0) {227return *input_frames_count;228}229230size_t frames_resampled = 0;231resampled_input =232input_processor->output(resampled_frame_count, &frames_resampled);233*input_frames_count = frames_resampled;234235long got = data_callback(stream, user_ptr, resampled_input, nullptr,236resampled_frame_count);237238/* Return the number of initial input frames or part of it.239* Since output_frames_needed == 0 in input scenario, the only240* available number outside resampler is the initial number of frames. */241return (*input_frames_count) * (got / resampled_frame_count);242}243244template <typename T, typename InputProcessor, typename OutputProcessor>245long246cubeb_resampler_speex<T, InputProcessor, OutputProcessor>::fill_internal_duplex(247T * in_buffer, long * input_frames_count, T * out_buffer,248long output_frames_needed)249{250if (draining) {251// discard input and drain any signal remaining in the resampler.252return output_processor->output(out_buffer, output_frames_needed);253}254255/* The input data, after eventual resampling. This is passed to the callback.256*/257T * resampled_input = nullptr;258/* The output buffer passed down in the callback, that might be resampled. */259T * out_unprocessed = nullptr;260long output_frames_before_processing = 0;261/* The number of frames returned from the callback. */262long got = 0;263264/* We need to determine how much frames to present to the consumer.265* - If we have a two way stream, but we're only resampling input, we resample266* the input to the number of output frames.267* - If we have a two way stream, but we're only resampling the output, we268* resize the input buffer of the output resampler to the number of input269* frames, and we resample it afterwards.270* - If we resample both ways, we resample the input to the number of frames271* we would need to pass down to the consumer (before resampling the output),272* get the output data, and resample it to the number of frames needed by the273* caller. */274275output_frames_before_processing =276output_processor->input_needed_for_output(output_frames_needed);277/* fill directly the input buffer of the output processor to save a copy */278out_unprocessed =279output_processor->input_buffer(output_frames_before_processing);280281if (in_buffer) {282/* process the input, and present exactly `output_frames_needed` in the283* callback. */284input_processor->input(in_buffer, *input_frames_count);285286size_t frames_resampled = 0;287resampled_input = input_processor->output(output_frames_before_processing,288&frames_resampled);289*input_frames_count = frames_resampled;290} else {291resampled_input = nullptr;292}293294got = data_callback(stream, user_ptr, resampled_input, out_unprocessed,295output_frames_before_processing);296297if (got < output_frames_before_processing) {298draining = true;299300if (got < 0) {301return got;302}303}304305output_processor->written(got);306307input_processor->drop_audio_if_needed();308309/* Process the output. If not enough frames have been returned from the310* callback, drain the processors. */311got = output_processor->output(out_buffer, output_frames_needed);312313output_processor->drop_audio_if_needed();314315return got;316}317318/* Resampler C API */319320cubeb_resampler *321cubeb_resampler_create(cubeb_stream * stream,322cubeb_stream_params * input_params,323cubeb_stream_params * output_params,324unsigned int target_rate, cubeb_data_callback callback,325void * user_ptr, cubeb_resampler_quality quality,326cubeb_resampler_reclock reclock)327{328cubeb_sample_format format;329330assert(input_params || output_params);331332if (input_params) {333format = input_params->format;334} else {335format = output_params->format;336}337338switch (format) {339case CUBEB_SAMPLE_S16NE:340return cubeb_resampler_create_internal<short>(341stream, input_params, output_params, target_rate, callback, user_ptr,342quality, reclock);343case CUBEB_SAMPLE_FLOAT32NE:344return cubeb_resampler_create_internal<float>(345stream, input_params, output_params, target_rate, callback, user_ptr,346quality, reclock);347default:348assert(false);349return nullptr;350}351}352353long354cubeb_resampler_fill(cubeb_resampler * resampler, void * input_buffer,355long * input_frames_count, void * output_buffer,356long output_frames_needed)357{358return resampler->fill(input_buffer, input_frames_count, output_buffer,359output_frames_needed);360}361362void363cubeb_resampler_destroy(cubeb_resampler * resampler)364{365delete resampler;366}367368long369cubeb_resampler_latency(cubeb_resampler * resampler)370{371return resampler->latency();372}373374375