diff options
Diffstat (limited to 'media/libcubeb/src/cubeb_resampler_internal.h')
-rw-r--r-- | media/libcubeb/src/cubeb_resampler_internal.h | 348 |
1 files changed, 156 insertions, 192 deletions
diff --git a/media/libcubeb/src/cubeb_resampler_internal.h b/media/libcubeb/src/cubeb_resampler_internal.h index 2eabb7c4c4..3c37a04b9c 100644 --- a/media/libcubeb/src/cubeb_resampler_internal.h +++ b/media/libcubeb/src/cubeb_resampler_internal.h @@ -8,9 +8,9 @@ #if !defined(CUBEB_RESAMPLER_INTERNAL) #define CUBEB_RESAMPLER_INTERNAL -#include <algorithm> -#include <cassert> #include <cmath> +#include <cassert> +#include <algorithm> #include <memory> #ifdef CUBEB_GECKO_BUILD #include "mozilla/UniquePtr.h" @@ -25,32 +25,21 @@ #define MOZ_END_STD_NAMESPACE } #endif MOZ_BEGIN_STD_NAMESPACE -using mozilla::DefaultDelete; -using mozilla::UniquePtr; -#define default_delete DefaultDelete -#define unique_ptr UniquePtr + 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 "cubeb-speex-resampler.h" +#include "cubeb_resampler.h" #include <stdio.h> -/* This header file contains the internal C++ API of the resamplers, for - * testing. */ +/* This header file contains the internal C++ API of the resamplers, for testing. */ -// When dropping audio input frames to prevent building -// an input delay, this function returns the number of frames -// to keep in the buffer. -// @parameter sample_rate The sample rate of the stream. -// @return A number of frames to keep. -uint32_t -min_buffered_audio_frame(uint32_t sample_rate); - -int -to_speex_quality(cubeb_resampler_quality q); +int to_speex_quality(cubeb_resampler_quality q); struct cubeb_resampler { virtual long fill(void * input_buffer, long * input_frames_count, @@ -59,62 +48,62 @@ struct cubeb_resampler { virtual ~cubeb_resampler() {} }; -/** Base class for processors. This is just used to share methods for now. */ -class processor { +class noop_resampler : public cubeb_resampler { 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 + noop_resampler(cubeb_stream * s, + cubeb_data_callback cb, + void * ptr) + : stream(s) + , data_callback(cb) + , user_ptr(ptr) { - assert(!(samples % channels)); - return samples / channels; } - /** The number of channel of the audio buffers to be resampled. */ - 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; } - - void drop_audio_if_needed() + virtual long latency() { - uint32_t to_keep = min_buffered_audio_frame(sample_rate); - uint32_t available = samples_to_frames(internal_input_buffer.length()); - if (available > to_keep) { - internal_input_buffer.pop(nullptr, - frames_to_samples(available - to_keep)); - } + return 0; } private: cubeb_stream * const stream; const cubeb_data_callback data_callback; void * const user_ptr; - /* This allows to buffer some input to account for the fact that we buffer - * some inputs. */ - auto_array<T> internal_input_buffer; - uint32_t sample_rate; +}; + +/** Base class for processors. This is just used to share methods for now. */ +class processor { +public: + explicit processor(uint32_t channels) + : channels(channels) + {} +protected: + size_t frames_to_samples(size_t frames) + { + return frames * channels; + } + size_t samples_to_frames(size_t samples) + { + assert(!(samples % channels)); + return samples / channels; + } + /** The number of channel of the audio buffers to be resampled. */ + const uint32_t channels; }; /** Bidirectional resampler, can resample an input and an output stream, or just * an input stream or output stream. In this case a delay is inserted in the * opposite direction to keep the streams synchronized. */ -template <typename T, typename InputProcessing, typename OutputProcessing> +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); + OutputProcessing * output_processor, + cubeb_stream * s, + cubeb_data_callback cb, + void * ptr); virtual ~cubeb_resampler_speex(); @@ -134,9 +123,7 @@ public: } private: - typedef long (cubeb_resampler_speex::*processing_callback)( - T * input_buffer, long * input_frames_count, T * output_buffer, - long output_frames_needed); + 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); @@ -151,14 +138,14 @@ private: cubeb_stream * const stream; const cubeb_data_callback data_callback; void * const user_ptr; - bool draining = false; }; /** Handles one way of a (possibly) duplex resampler, working on interleaved * audio buffers of type T. This class is designed so that the number of frames * coming out of the resampler can be precisely controled. It manages its own * input buffer, and can use the caller's output buffer, or allocate its own. */ -template <typename T> class cubeb_resampler_speex_one_way : public processor { +template<typename T> +class cubeb_resampler_speex_one_way : public processor { public: /** The sample type of this resampler, either 16-bit integers or 32-bit * floats. */ @@ -170,26 +157,19 @@ public: * @parameter target_rate The sample-rate of the audio output. * @parameter quality A number between 0 (fast, low quality) and 10 (slow, * high quality). */ - 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), additional_latency(0), leftover_samples(0) + 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) + , additional_latency(0) + , leftover_samples(0) { int r; - speex_resampler = - speex_resampler_init(channels, source_rate, target_rate, quality, &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] = {}; - uint32_t input_frame_count = input_latency; - uint32_t output_frame_count = LATENCY_SAMPLES; - assert(input_latency * channels <= LATENCY_SAMPLES); - speex_resample(input_buffer, &input_frame_count, output_buffer, - &output_frame_count); } /** Destructor, deallocate the resampler */ @@ -198,6 +178,17 @@ public: speex_resampler_destroy(speex_resampler); } + /** Sometimes, it is necessary to add latency on one way of a two-way + * resampler so that the stream are synchronized. This must be called only on + * a fresh resampler, otherwise, silent samples will be inserted in the + * stream. + * @param frames the number of frames of latency to add. */ + void add_latency(size_t frames) + { + additional_latency += frames; + resampling_in_buffer.push_silence(frames_to_samples(frames)); + } + /* Fill the resampler with `input_frame_count` frames. */ void input(T * input_buffer, size_t input_frame_count) { @@ -206,14 +197,14 @@ public: } /** Outputs exactly `output_frame_count` into `output_buffer`. - * `output_buffer` has to be at least `output_frame_count` long. */ + * `output_buffer` has to be at least `output_frame_count` long. */ 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); + speex_resample(resampling_in_buffer.data(), &in_len, + output_buffer, &out_len); /* This shifts back any unresampled samples to the beginning of the input buffer. */ @@ -224,17 +215,15 @@ public: size_t output_for_input(uint32_t input_frames) { - return (size_t)floorf( - (input_frames + samples_to_frames(resampling_in_buffer.length())) / - resampling_ratio); + return (size_t)floorf((input_frames + samples_to_frames(resampling_in_buffer.length())) + / resampling_ratio); } /** Returns a buffer containing exactly `output_frame_count` resampled frames. - * The consumer should not hold onto the pointer. */ - T * output(size_t output_frame_count, size_t * input_frames_used) + * The consumer should not hold onto the pointer. */ + T * output(size_t output_frame_count) { - if (resampling_out_buffer.capacity() < - frames_to_samples(output_frame_count)) { + if (resampling_out_buffer.capacity() < frames_to_samples(output_frame_count)) { resampling_out_buffer.reserve(frames_to_samples(output_frame_count)); } @@ -244,21 +233,11 @@ public: 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); - // silence the rightmost part - 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; - } - } + assert(out_len == output_frame_count); /* This shifts back any unresampled samples to the beginning of the input buffer. */ resampling_in_buffer.pop(nullptr, frames_to_samples(in_len)); - *input_frames_used = in_len; return resampling_out_buffer.data(); } @@ -270,8 +249,8 @@ public: * only consider a single channel here so it's the same number of frames. */ int latency = 0; - latency = speex_resampler_get_output_latency(speex_resampler) + - additional_latency; + latency = + speex_resampler_get_output_latency(speex_resampler) + additional_latency; assert(latency >= 0); @@ -282,16 +261,13 @@ public: * exactly `output_frame_count` resampled frames. This can return a number * slightly bigger than what is strictly necessary, but it guaranteed that the * number of output frames will be exactly equal. */ - uint32_t input_needed_for_output(int32_t output_frame_count) const + uint32_t input_needed_for_output(uint32_t output_frame_count) { - assert(output_frame_count >= 0); // Check overflow - int32_t unresampled_frames_left = - samples_to_frames(resampling_in_buffer.length()); - int32_t resampled_frames_left = - samples_to_frames(resampling_out_buffer.length()); + int32_t unresampled_frames_left = samples_to_frames(resampling_in_buffer.length()); + int32_t resampled_frames_left = samples_to_frames(resampling_out_buffer.length()); float input_frames_needed = - (output_frame_count - unresampled_frames_left) * resampling_ratio - - resampled_frames_left; + (output_frame_count - unresampled_frames_left) * resampling_ratio + - resampled_frames_left; if (input_frames_needed < 0) { return 0; } @@ -318,20 +294,9 @@ public: resampling_in_buffer.set_length(leftover_samples + frames_to_samples(written_frames)); } - - void drop_audio_if_needed() - { - // Keep at most 100ms buffered. - uint32_t available = samples_to_frames(resampling_in_buffer.length()); - uint32_t to_keep = min_buffered_audio_frame(source_rate); - if (available > to_keep) { - resampling_in_buffer.pop(nullptr, frames_to_samples(available - to_keep)); - } - } - private: /** Wrapper for the speex resampling functions to have a typed - * interface. */ + * interface. */ void speex_resample(float * input_buffer, uint32_t * input_frame_count, float * output_buffer, uint32_t * output_frame_count) { @@ -339,9 +304,11 @@ private: int rv; rv = #endif - speex_resampler_process_interleaved_float( - speex_resampler, input_buffer, input_frame_count, output_buffer, - output_frame_count); + speex_resampler_process_interleaved_float(speex_resampler, + input_buffer, + input_frame_count, + output_buffer, + output_frame_count); assert(rv == RESAMPLER_ERR_SUCCESS); } @@ -352,16 +319,17 @@ private: int rv; rv = #endif - speex_resampler_process_interleaved_int( - speex_resampler, input_buffer, input_frame_count, output_buffer, - output_frame_count); + speex_resampler_process_interleaved_int(speex_resampler, + input_buffer, + input_frame_count, + output_buffer, + output_frame_count); assert(rv == RESAMPLER_ERR_SUCCESS); } /** The state for the speex resampler used internaly. */ SpeexResamplerState * speex_resampler; /** Source rate / target rate. */ const float resampling_ratio; - const uint32_t source_rate; /** Storage for the input frames, to be resampled. Also contains * any unresampled frames after resampling. */ auto_array<T> resampling_in_buffer; @@ -375,20 +343,27 @@ private: }; /** This class allows delaying an audio stream by `frames` frames. */ -template <typename T> class delay_line : public processor { +template<typename T> +class delay_line : public processor { public: /** Constructor * @parameter frames the number of frames of delay. - * @parameter channels the number of channels of this delay line. - * @parameter sample_rate sample-rate of the audio going through this delay - * line */ - delay_line(uint32_t frames, uint32_t channels, uint32_t sample_rate) - : processor(channels), length(frames), leftover_samples(0), - sample_rate(sample_rate) + * @parameter channels the number of channels of this delay line. */ + delay_line(uint32_t frames, uint32_t channels) + : processor(channels) + , length(frames) + , leftover_samples(0) { /* Fill the delay line with some silent frames to add latency. */ delay_input_buffer.push_silence(frames * channels); } + /* Add some latency to the delay line. + * @param frames the number of frames of latency to add. */ + void add_latency(size_t frames) + { + length += frames; + delay_input_buffer.push_silence(frames_to_samples(frames)); + } /** Push some frames into the delay line. * @parameter buffer the frames to push. * @parameter frame_count the number of frames in #buffer. */ @@ -400,7 +375,7 @@ public: * @parameter frames_needed the number of frames to be returned. * @return a buffer containing the delayed frames. The consumer should not * hold onto the pointer. */ - T * output(uint32_t frames_needed, size_t * input_frames_used) + T * output(uint32_t frames_needed) { if (delay_output_buffer.capacity() < frames_to_samples(frames_needed)) { delay_output_buffer.reserve(frames_to_samples(frames_needed)); @@ -410,7 +385,6 @@ public: delay_output_buffer.push(delay_input_buffer.data(), frames_to_samples(frames_needed)); delay_input_buffer.pop(nullptr, frames_to_samples(frames_needed)); - *input_frames_used = frames_needed; return delay_output_buffer.data(); } @@ -422,8 +396,7 @@ public: T * input_buffer(uint32_t frames_needed) { leftover_samples = delay_input_buffer.length(); - delay_input_buffer.reserve(leftover_samples + - frames_to_samples(frames_needed)); + delay_input_buffer.reserve(leftover_samples + frames_to_samples(frames_needed)); return delay_input_buffer.data() + leftover_samples; } /** This method works with `input_buffer`, and allows to inform the processor @@ -453,27 +426,21 @@ public: * @parameter frames_needed the number of frames one want to write into the * delay_line * @returns the number of frames one will get. */ - uint32_t input_needed_for_output(int32_t frames_needed) const + size_t input_needed_for_output(uint32_t frames_needed) { - assert(frames_needed >= 0); // Check overflow return frames_needed; } - /** Returns the number of frames produces for `input_frames` frames in input - */ - size_t output_for_input(uint32_t input_frames) { return input_frames; } + /** Returns the number of frames produces for `input_frames` frames in input */ + size_t output_for_input(uint32_t input_frames) + { + return input_frames; + } /** The number of frames this delay line delays the stream by. * @returns The number of frames of delay. */ - size_t latency() { return length; } - - void drop_audio_if_needed() + size_t latency() { - size_t available = samples_to_frames(delay_input_buffer.length()); - uint32_t to_keep = min_buffered_audio_frame(sample_rate); - if (available > to_keep) { - delay_input_buffer.pop(nullptr, frames_to_samples(available - to_keep)); - } + return length; } - private: /** The length, in frames, of this delay line */ uint32_t length; @@ -485,17 +452,17 @@ private: /** The output buffer. This is only ever used if using the ::output with a * single argument. */ auto_array<T> delay_output_buffer; - uint32_t sample_rate; }; /** This sits behind the C API and is more typed. */ -template <typename T> +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_data_callback callback, + void * user_ptr, cubeb_resampler_quality quality) { std::unique_ptr<cubeb_resampler_speex_one_way<T>> input_resampler = nullptr; @@ -510,31 +477,31 @@ cubeb_resampler_create_internal(cubeb_stream * stream, sample rate, use a no-op resampler, that simply forwards the buffers to the callback. */ if (((input_params && input_params->rate == target_rate) && - (output_params && output_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); + (output_params && !input_params && (output_params->rate == target_rate))) { + return new noop_resampler(stream, callback, user_ptr); } /* Determine if we need to resampler one or both directions, and create the resamplers. */ 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))); + output_resampler.reset( + new cubeb_resampler_speex_one_way<T>(output_params->channels, + target_rate, + output_params->rate, + to_speex_quality(quality))); if (!output_resampler) { return NULL; } } 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))); + input_resampler.reset( + new cubeb_resampler_speex_one_way<T>(input_params->channels, + input_params->rate, + target_rate, + to_speex_quality(quality))); if (!input_resampler) { return NULL; } @@ -545,42 +512,39 @@ cubeb_resampler_create_internal(cubeb_stream * stream, * other direction so that the streams are synchronized. */ if (input_resampler && !output_resampler && input_params && output_params) { output_delay.reset(new delay_line<T>(input_resampler->latency(), - output_params->channels, - output_params->rate)); + output_params->channels)); if (!output_delay) { return NULL; } - } else if (output_resampler && !input_resampler && input_params && - output_params) { + } else if (output_resampler && !input_resampler && input_params && output_params) { input_delay.reset(new delay_line<T>(output_resampler->latency(), - input_params->channels, - output_params->rate)); + input_params->channels)); if (!input_delay) { return NULL; } } 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); + 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); } else if (input_resampler) { - LOG("Resampling input (%d) to target and output rate of %dHz", - input_params->rate, target_rate); - return new cubeb_resampler_speex<T, cubeb_resampler_speex_one_way<T>, - delay_line<T>>(input_resampler.release(), - output_delay.release(), - stream, callback, user_ptr); + return new cubeb_resampler_speex<T, + cubeb_resampler_speex_one_way<T>, + delay_line<T>> + (input_resampler.release(), + output_delay.release(), + stream, callback, user_ptr); } else { - LOG("Resampling output (%dHz) to target and input rate of %dHz", - output_params->rate, target_rate); - return new cubeb_resampler_speex<T, delay_line<T>, - cubeb_resampler_speex_one_way<T>>( - input_delay.release(), output_resampler.release(), stream, callback, - user_ptr); + return new cubeb_resampler_speex<T, + delay_line<T>, + cubeb_resampler_speex_one_way<T>> + (input_delay.release(), + output_resampler.release(), + stream, callback, user_ptr); } } |