Path: blob/master/dep/cubeb/src/cubeb_resampler_internal.h
4246 views
/*1* Copyright © 2016 Mozilla Foundation2*3* This program is made available under an ISC-style license. See the4* accompanying file LICENSE for details.5*/67#if !defined(CUBEB_RESAMPLER_INTERNAL)8#define CUBEB_RESAMPLER_INTERNAL910#include <algorithm>11#include <cassert>12#include <cmath>13#include <memory>14#ifdef CUBEB_GECKO_BUILD15#include "mozilla/UniquePtr.h"16// In libc++, symbols such as std::unique_ptr may be defined in std::__1.17// The _LIBCPP_BEGIN_NAMESPACE_STD and _LIBCPP_END_NAMESPACE_STD macros18// will expand to the correct namespace.19#ifdef _LIBCPP_BEGIN_NAMESPACE_STD20#define MOZ_BEGIN_STD_NAMESPACE _LIBCPP_BEGIN_NAMESPACE_STD21#define MOZ_END_STD_NAMESPACE _LIBCPP_END_NAMESPACE_STD22#else23#define MOZ_BEGIN_STD_NAMESPACE namespace std {24#define MOZ_END_STD_NAMESPACE }25#endif26MOZ_BEGIN_STD_NAMESPACE27using mozilla::DefaultDelete;28using mozilla::UniquePtr;29#define default_delete DefaultDelete30#define unique_ptr UniquePtr31MOZ_END_STD_NAMESPACE32#endif33#include "cubeb-speex-resampler.h"34#include "cubeb/cubeb.h"35#include "cubeb_log.h"36#include "cubeb_resampler.h"37#include "cubeb_utils.h"38#include <stdio.h>3940/* This header file contains the internal C++ API of the resamplers, for41* testing. */4243// When dropping audio input frames to prevent building44// an input delay, this function returns the number of frames45// to keep in the buffer.46// @parameter sample_rate The sample rate of the stream.47// @return A number of frames to keep.48uint32_t49min_buffered_audio_frame(uint32_t sample_rate);5051int52to_speex_quality(cubeb_resampler_quality q);5354struct cubeb_resampler {55virtual long fill(void * input_buffer, long * input_frames_count,56void * output_buffer, long frames_needed) = 0;57virtual long latency() = 0;58virtual ~cubeb_resampler() {}59};6061/** Base class for processors. This is just used to share methods for now. */62class processor {63public:64explicit processor(uint32_t channels) : channels(channels) {}6566protected:67size_t frames_to_samples(size_t frames) const { return frames * channels; }68size_t samples_to_frames(size_t samples) const69{70assert(!(samples % channels));71return samples / channels;72}73/** The number of channel of the audio buffers to be resampled. */74const uint32_t channels;75};7677template <typename T>78class passthrough_resampler : public cubeb_resampler, public processor {79public:80passthrough_resampler(cubeb_stream * s, cubeb_data_callback cb, void * ptr,81uint32_t input_channels, uint32_t sample_rate);8283virtual long fill(void * input_buffer, long * input_frames_count,84void * output_buffer, long output_frames);8586virtual long latency() { return 0; }8788void drop_audio_if_needed()89{90uint32_t to_keep = min_buffered_audio_frame(sample_rate);91uint32_t available = samples_to_frames(internal_input_buffer.length());92if (available > to_keep) {93ALOGV("Dropping %u frames", available - to_keep);94internal_input_buffer.pop(nullptr,95frames_to_samples(available - to_keep));96}97}9899private:100cubeb_stream * const stream;101const cubeb_data_callback data_callback;102void * const user_ptr;103/* This allows to buffer some input to account for the fact that we buffer104* some inputs. */105auto_array<T> internal_input_buffer;106uint32_t sample_rate;107};108109/** Bidirectional resampler, can resample an input and an output stream, or just110* an input stream or output stream. In this case a delay is inserted in the111* opposite direction to keep the streams synchronized. */112template <typename T, typename InputProcessing, typename OutputProcessing>113class cubeb_resampler_speex : public cubeb_resampler {114public:115cubeb_resampler_speex(InputProcessing * input_processor,116OutputProcessing * output_processor, cubeb_stream * s,117cubeb_data_callback cb, void * ptr);118119virtual ~cubeb_resampler_speex();120121virtual long fill(void * input_buffer, long * input_frames_count,122void * output_buffer, long output_frames_needed);123124virtual long latency()125{126if (input_processor && output_processor) {127assert(input_processor->latency() == output_processor->latency());128return input_processor->latency();129} else if (input_processor) {130return input_processor->latency();131} else {132return output_processor->latency();133}134}135136private:137typedef long (cubeb_resampler_speex::*processing_callback)(138T * input_buffer, long * input_frames_count, T * output_buffer,139long output_frames_needed);140141long fill_internal_duplex(T * input_buffer, long * input_frames_count,142T * output_buffer, long output_frames_needed);143long fill_internal_input(T * input_buffer, long * input_frames_count,144T * output_buffer, long output_frames_needed);145long fill_internal_output(T * input_buffer, long * input_frames_count,146T * output_buffer, long output_frames_needed);147148std::unique_ptr<InputProcessing> input_processor;149std::unique_ptr<OutputProcessing> output_processor;150processing_callback fill_internal;151cubeb_stream * const stream;152const cubeb_data_callback data_callback;153void * const user_ptr;154bool draining = false;155};156157/** Handles one way of a (possibly) duplex resampler, working on interleaved158* audio buffers of type T. This class is designed so that the number of frames159* coming out of the resampler can be precisely controled. It manages its own160* input buffer, and can use the caller's output buffer, or allocate its own. */161template <typename T> class cubeb_resampler_speex_one_way : public processor {162public:163/** The sample type of this resampler, either 16-bit integers or 32-bit164* floats. */165typedef T sample_type;166/** Construct a resampler resampling from #source_rate to #target_rate, that167* can be arbitrary, strictly positive number.168* @parameter channels The number of channels this resampler will resample.169* @parameter source_rate The sample-rate of the audio input.170* @parameter target_rate The sample-rate of the audio output.171* @parameter quality A number between 0 (fast, low quality) and 10 (slow,172* high quality). */173cubeb_resampler_speex_one_way(uint32_t channels, uint32_t source_rate,174uint32_t target_rate, int quality)175: processor(channels),176resampling_ratio(static_cast<float>(source_rate) / target_rate),177source_rate(source_rate), additional_latency(0), leftover_samples(0)178{179int r;180speex_resampler =181speex_resampler_init(channels, source_rate, target_rate, quality, &r);182assert(r == RESAMPLER_ERR_SUCCESS && "resampler allocation failure");183184uint32_t input_latency = speex_resampler_get_input_latency(speex_resampler);185const size_t LATENCY_SAMPLES = 8192;186T input_buffer[LATENCY_SAMPLES] = {};187T output_buffer[LATENCY_SAMPLES] = {};188uint32_t input_frame_count = input_latency;189uint32_t output_frame_count = LATENCY_SAMPLES;190assert(input_latency * channels <= LATENCY_SAMPLES);191speex_resample(input_buffer, &input_frame_count, output_buffer,192&output_frame_count);193}194195/** Destructor, deallocate the resampler */196virtual ~cubeb_resampler_speex_one_way()197{198speex_resampler_destroy(speex_resampler);199}200201/* Fill the resampler with `input_frame_count` frames. */202void input(T * input_buffer, size_t input_frame_count)203{204resampling_in_buffer.push(input_buffer,205frames_to_samples(input_frame_count));206}207208/** Outputs exactly `output_frame_count` into `output_buffer`.209* `output_buffer` has to be at least `output_frame_count` long. */210size_t output(T * output_buffer, size_t output_frame_count)211{212uint32_t in_len = samples_to_frames(resampling_in_buffer.length());213uint32_t out_len = output_frame_count;214215speex_resample(resampling_in_buffer.data(), &in_len, output_buffer,216&out_len);217218/* This shifts back any unresampled samples to the beginning of the input219buffer. */220resampling_in_buffer.pop(nullptr, frames_to_samples(in_len));221222return out_len;223}224225size_t output_for_input(uint32_t input_frames)226{227return (size_t)floorf(228(input_frames + samples_to_frames(resampling_in_buffer.length())) /229resampling_ratio);230}231232/** Returns a buffer containing exactly `output_frame_count` resampled frames.233* The consumer should not hold onto the pointer. */234T * output(size_t output_frame_count, size_t * input_frames_used)235{236if (resampling_out_buffer.capacity() <237frames_to_samples(output_frame_count)) {238resampling_out_buffer.reserve(frames_to_samples(output_frame_count));239}240241uint32_t in_len = samples_to_frames(resampling_in_buffer.length());242uint32_t out_len = output_frame_count;243244speex_resample(resampling_in_buffer.data(), &in_len,245resampling_out_buffer.data(), &out_len);246247if (out_len < output_frame_count) {248LOGV("underrun during resampling: got %u frames, expected %zu",249(unsigned)out_len, output_frame_count);250// silence the rightmost part251T * data = resampling_out_buffer.data();252for (uint32_t i = frames_to_samples(out_len);253i < frames_to_samples(output_frame_count); i++) {254data[i] = 0;255}256}257258/* This shifts back any unresampled samples to the beginning of the input259buffer. */260resampling_in_buffer.pop(nullptr, frames_to_samples(in_len));261*input_frames_used = in_len;262263return resampling_out_buffer.data();264}265266/** Get the latency of the resampler, in output frames. */267uint32_t latency() const268{269/* The documentation of the resampler talks about "samples" here, but it270* only consider a single channel here so it's the same number of frames. */271int latency = 0;272273latency = speex_resampler_get_output_latency(speex_resampler) +274additional_latency;275276assert(latency >= 0);277278return latency;279}280281/** Returns the number of frames to pass in the input of the resampler to have282* exactly `output_frame_count` resampled frames. This can return a number283* slightly bigger than what is strictly necessary, but it guaranteed that the284* number of output frames will be exactly equal. */285uint32_t input_needed_for_output(int32_t output_frame_count) const286{287assert(output_frame_count >= 0); // Check overflow288int32_t unresampled_frames_left =289samples_to_frames(resampling_in_buffer.length());290int32_t resampled_frames_left =291samples_to_frames(resampling_out_buffer.length());292float input_frames_needed =293(output_frame_count - unresampled_frames_left) * resampling_ratio -294resampled_frames_left;295if (input_frames_needed < 0) {296return 0;297}298return (uint32_t)ceilf(input_frames_needed);299}300301/** Returns a pointer to the input buffer, that contains empty space for at302* least `frame_count` elements. This is useful so that consumer can directly303* write into the input buffer of the resampler. The pointer returned is304* adjusted so that leftover data are not overwritten.305*/306T * input_buffer(size_t frame_count)307{308leftover_samples = resampling_in_buffer.length();309resampling_in_buffer.reserve(leftover_samples +310frames_to_samples(frame_count));311return resampling_in_buffer.data() + leftover_samples;312}313314/** This method works with `input_buffer`, and allows to inform the processor315how much frames have been written in the provided buffer. */316void written(size_t written_frames)317{318resampling_in_buffer.set_length(leftover_samples +319frames_to_samples(written_frames));320}321322void drop_audio_if_needed()323{324// Keep at most 100ms buffered.325uint32_t available = samples_to_frames(resampling_in_buffer.length());326uint32_t to_keep = min_buffered_audio_frame(source_rate);327if (available > to_keep) {328ALOGV("Dropping %u frames", available - to_keep);329resampling_in_buffer.pop(nullptr, frames_to_samples(available - to_keep));330}331}332333private:334/** Wrapper for the speex resampling functions to have a typed335* interface. */336void speex_resample(float * input_buffer, uint32_t * input_frame_count,337float * output_buffer, uint32_t * output_frame_count)338{339#ifndef NDEBUG340int rv;341rv =342#endif343speex_resampler_process_interleaved_float(344speex_resampler, input_buffer, input_frame_count, output_buffer,345output_frame_count);346assert(rv == RESAMPLER_ERR_SUCCESS);347}348349void speex_resample(short * input_buffer, uint32_t * input_frame_count,350short * output_buffer, uint32_t * output_frame_count)351{352#ifndef NDEBUG353int rv;354rv =355#endif356speex_resampler_process_interleaved_int(357speex_resampler, input_buffer, input_frame_count, output_buffer,358output_frame_count);359assert(rv == RESAMPLER_ERR_SUCCESS);360}361/** The state for the speex resampler used internaly. */362SpeexResamplerState * speex_resampler;363/** Source rate / target rate. */364const float resampling_ratio;365const uint32_t source_rate;366/** Storage for the input frames, to be resampled. Also contains367* any unresampled frames after resampling. */368auto_array<T> resampling_in_buffer;369/* Storage for the resampled frames, to be passed back to the caller. */370auto_array<T> resampling_out_buffer;371/** Additional latency inserted into the pipeline for synchronisation. */372uint32_t additional_latency;373/** When `input_buffer` is called, this allows tracking the number of samples374that were in the buffer. */375uint32_t leftover_samples;376};377378/** This class allows delaying an audio stream by `frames` frames. */379template <typename T> class delay_line : public processor {380public:381/** Constructor382* @parameter frames the number of frames of delay.383* @parameter channels the number of channels of this delay line.384* @parameter sample_rate sample-rate of the audio going through this delay385* line */386delay_line(uint32_t frames, uint32_t channels, uint32_t sample_rate)387: processor(channels), length(frames), leftover_samples(0),388sample_rate(sample_rate)389{390/* Fill the delay line with some silent frames to add latency. */391delay_input_buffer.push_silence(frames * channels);392}393/** Push some frames into the delay line.394* @parameter buffer the frames to push.395* @parameter frame_count the number of frames in #buffer. */396void input(T * buffer, uint32_t frame_count)397{398delay_input_buffer.push(buffer, frames_to_samples(frame_count));399}400/** Pop some frames from the internal buffer, into a internal output buffer.401* @parameter frames_needed the number of frames to be returned.402* @return a buffer containing the delayed frames. The consumer should not403* hold onto the pointer. */404T * output(uint32_t frames_needed, size_t * input_frames_used)405{406if (delay_output_buffer.capacity() < frames_to_samples(frames_needed)) {407delay_output_buffer.reserve(frames_to_samples(frames_needed));408}409410delay_output_buffer.clear();411delay_output_buffer.push(delay_input_buffer.data(),412frames_to_samples(frames_needed));413delay_input_buffer.pop(nullptr, frames_to_samples(frames_needed));414*input_frames_used = frames_needed;415416return delay_output_buffer.data();417}418/** Get a pointer to the first writable location in the input buffer>419* @parameter frames_needed the number of frames the user needs to write into420* the buffer.421* @returns a pointer to a location in the input buffer where #frames_needed422* can be writen. */423T * input_buffer(uint32_t frames_needed)424{425leftover_samples = delay_input_buffer.length();426delay_input_buffer.reserve(leftover_samples +427frames_to_samples(frames_needed));428return delay_input_buffer.data() + leftover_samples;429}430/** This method works with `input_buffer`, and allows to inform the processor431how much frames have been written in the provided buffer. */432void written(size_t frames_written)433{434delay_input_buffer.set_length(leftover_samples +435frames_to_samples(frames_written));436}437/** Drains the delay line, emptying the buffer.438* @parameter output_buffer the buffer in which the frames are written.439* @parameter frames_needed the maximum number of frames to write.440* @return the actual number of frames written. */441size_t output(T * output_buffer, uint32_t frames_needed)442{443uint32_t in_len = samples_to_frames(delay_input_buffer.length());444uint32_t out_len = frames_needed;445446uint32_t to_pop = std::min(in_len, out_len);447448delay_input_buffer.pop(output_buffer, frames_to_samples(to_pop));449450return to_pop;451}452/** Returns the number of frames one needs to input into the delay line to get453* #frames_needed frames back.454* @parameter frames_needed the number of frames one want to write into the455* delay_line456* @returns the number of frames one will get. */457uint32_t input_needed_for_output(int32_t frames_needed) const458{459assert(frames_needed >= 0); // Check overflow460return frames_needed;461}462/** Returns the number of frames produces for `input_frames` frames in input463*/464size_t output_for_input(uint32_t input_frames) { return input_frames; }465/** The number of frames this delay line delays the stream by.466* @returns The number of frames of delay. */467size_t latency() { return length; }468469void drop_audio_if_needed()470{471size_t available = samples_to_frames(delay_input_buffer.length());472uint32_t to_keep = min_buffered_audio_frame(sample_rate);473if (available > to_keep) {474ALOGV("Dropping %u frames", available - to_keep);475delay_input_buffer.pop(nullptr, frames_to_samples(available - to_keep));476}477}478479private:480/** The length, in frames, of this delay line */481uint32_t length;482/** When `input_buffer` is called, this allows tracking the number of samples483that where in the buffer. */484uint32_t leftover_samples;485/** The input buffer, where the delay is applied. */486auto_array<T> delay_input_buffer;487/** The output buffer. This is only ever used if using the ::output with a488* single argument. */489auto_array<T> delay_output_buffer;490uint32_t sample_rate;491};492493/** This sits behind the C API and is more typed. */494template <typename T>495cubeb_resampler *496cubeb_resampler_create_internal(cubeb_stream * stream,497cubeb_stream_params * input_params,498cubeb_stream_params * output_params,499unsigned int target_rate,500cubeb_data_callback callback, void * user_ptr,501cubeb_resampler_quality quality,502cubeb_resampler_reclock reclock)503{504std::unique_ptr<cubeb_resampler_speex_one_way<T>> input_resampler = nullptr;505std::unique_ptr<cubeb_resampler_speex_one_way<T>> output_resampler = nullptr;506std::unique_ptr<delay_line<T>> input_delay = nullptr;507std::unique_ptr<delay_line<T>> output_delay = nullptr;508509assert((input_params || output_params) &&510"need at least one valid parameter pointer.");511512/* All the streams we have have a sample rate that matches the target513sample rate, use a no-op resampler, that simply forwards the buffers to the514callback. */515if (((input_params && input_params->rate == target_rate) &&516(output_params && output_params->rate == target_rate)) ||517(input_params && !output_params && (input_params->rate == target_rate)) ||518(output_params && !input_params &&519(output_params->rate == target_rate))) {520LOG("Input and output sample-rate match, target rate of %dHz", target_rate);521return new passthrough_resampler<T>(522stream, callback, user_ptr, input_params ? input_params->channels : 0,523target_rate);524}525526/* Determine if we need to resampler one or both directions, and create the527resamplers. */528if (output_params && (output_params->rate != target_rate)) {529output_resampler.reset(new cubeb_resampler_speex_one_way<T>(530output_params->channels, target_rate, output_params->rate,531to_speex_quality(quality)));532if (!output_resampler) {533return NULL;534}535}536537if (input_params && (input_params->rate != target_rate)) {538input_resampler.reset(new cubeb_resampler_speex_one_way<T>(539input_params->channels, input_params->rate, target_rate,540to_speex_quality(quality)));541if (!input_resampler) {542return NULL;543}544}545546/* If we resample only one direction but we have a duplex stream, insert a547* delay line with a length equal to the resampler latency of the548* other direction so that the streams are synchronized. */549if (input_resampler && !output_resampler && input_params && output_params) {550output_delay.reset(new delay_line<T>(input_resampler->latency(),551output_params->channels,552output_params->rate));553if (!output_delay) {554return NULL;555}556} else if (output_resampler && !input_resampler && input_params &&557output_params) {558input_delay.reset(new delay_line<T>(output_resampler->latency(),559input_params->channels,560output_params->rate));561if (!input_delay) {562return NULL;563}564}565566if (input_resampler && output_resampler) {567LOG("Resampling input (%d) and output (%d) to target rate of %dHz",568input_params->rate, output_params->rate, target_rate);569return new cubeb_resampler_speex<T, cubeb_resampler_speex_one_way<T>,570cubeb_resampler_speex_one_way<T>>(571input_resampler.release(), output_resampler.release(), stream, callback,572user_ptr);573} else if (input_resampler) {574LOG("Resampling input (%d) to target and output rate of %dHz",575input_params->rate, target_rate);576return new cubeb_resampler_speex<T, cubeb_resampler_speex_one_way<T>,577delay_line<T>>(input_resampler.release(),578output_delay.release(),579stream, callback, user_ptr);580} else {581LOG("Resampling output (%dHz) to target and input rate of %dHz",582output_params->rate, target_rate);583return new cubeb_resampler_speex<T, delay_line<T>,584cubeb_resampler_speex_one_way<T>>(585input_delay.release(), output_resampler.release(), stream, callback,586user_ptr);587}588}589590#endif /* CUBEB_RESAMPLER_INTERNAL */591592593