summaryrefslogtreecommitdiff
path: root/media/libcubeb/src/cubeb_resampler_internal.h
diff options
context:
space:
mode:
Diffstat (limited to 'media/libcubeb/src/cubeb_resampler_internal.h')
-rw-r--r--media/libcubeb/src/cubeb_resampler_internal.h348
1 files changed, 192 insertions, 156 deletions
diff --git a/media/libcubeb/src/cubeb_resampler_internal.h b/media/libcubeb/src/cubeb_resampler_internal.h
index 3c37a04b9c..2eabb7c4c4 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 <cmath>
-#include <cassert>
#include <algorithm>
+#include <cassert>
+#include <cmath>
#include <memory>
#ifdef CUBEB_GECKO_BUILD
#include "mozilla/UniquePtr.h"
@@ -25,21 +25,32 @@
#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/cubeb.h"
-#include "cubeb_utils.h"
#include "cubeb-speex-resampler.h"
+#include "cubeb/cubeb.h"
+#include "cubeb_log.h"
#include "cubeb_resampler.h"
+#include "cubeb_utils.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. */
-int to_speex_quality(cubeb_resampler_quality q);
+// 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);
struct cubeb_resampler {
virtual long fill(void * input_buffer, long * input_frames_count,
@@ -48,62 +59,62 @@ struct cubeb_resampler {
virtual ~cubeb_resampler() {}
};
-class noop_resampler : public cubeb_resampler {
+/** Base class for processors. This is just used to share methods for now. */
+class processor {
public:
- noop_resampler(cubeb_stream * s,
- cubeb_data_callback cb,
- void * ptr)
- : stream(s)
- , data_callback(cb)
- , user_ptr(ptr)
+ 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;
}
+ /** 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()
+ virtual long latency() { return 0; }
+
+ void drop_audio_if_needed()
{
- return 0;
+ 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));
+ }
}
private:
cubeb_stream * const stream;
const cubeb_data_callback data_callback;
void * const user_ptr;
-};
-
-/** 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;
+ /* 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;
};
/** 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();
@@ -123,7 +134,9 @@ 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);
@@ -138,14 +151,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. */
@@ -157,19 +170,26 @@ 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)
- , 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),
+ source_rate(source_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 */
@@ -178,17 +198,6 @@ 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)
{
@@ -197,14 +206,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. */
@@ -215,15 +224,17 @@ 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)
+ * The consumer should not hold onto the pointer. */
+ T * output(size_t output_frame_count, size_t * input_frames_used)
{
- 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));
}
@@ -233,11 +244,21 @@ public:
speex_resample(resampling_in_buffer.data(), &in_len,
resampling_out_buffer.data(), &out_len);
- assert(out_len == output_frame_count);
+ 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;
+ }
+ }
/* 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();
}
@@ -249,8 +270,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);
@@ -261,13 +282,16 @@ 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(uint32_t output_frame_count)
+ uint32_t input_needed_for_output(int32_t output_frame_count) const
{
- int32_t unresampled_frames_left = samples_to_frames(resampling_in_buffer.length());
- int32_t resampled_frames_left = samples_to_frames(resampling_out_buffer.length());
+ 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());
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;
}
@@ -294,9 +318,20 @@ 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)
{
@@ -304,11 +339,9 @@ 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);
}
@@ -319,17 +352,16 @@ 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;
@@ -343,27 +375,20 @@ 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. */
- delay_line(uint32_t frames, uint32_t channels)
- : processor(channels)
- , length(frames)
- , leftover_samples(0)
+ * @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)
{
/* 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. */
@@ -375,7 +400,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)
+ T * output(uint32_t frames_needed, size_t * input_frames_used)
{
if (delay_output_buffer.capacity() < frames_to_samples(frames_needed)) {
delay_output_buffer.reserve(frames_to_samples(frames_needed));
@@ -385,6 +410,7 @@ 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();
}
@@ -396,7 +422,8 @@ 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
@@ -426,21 +453,27 @@ public:
* @parameter frames_needed the number of frames one want to write into the
* delay_line
* @returns the number of frames one will get. */
- size_t input_needed_for_output(uint32_t frames_needed)
+ uint32_t input_needed_for_output(int32_t frames_needed) const
{
+ 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()
+ size_t latency() { return length; }
+
+ void drop_audio_if_needed()
{
- return length;
+ 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));
+ }
}
+
private:
/** The length, in frames, of this delay line */
uint32_t length;
@@ -452,17 +485,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;
@@ -477,31 +510,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))) {
- return new noop_resampler(stream, callback, user_ptr);
+ (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);
}
/* 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;
}
@@ -512,39 +545,42 @@ 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->channels,
+ output_params->rate));
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));
+ input_params->channels,
+ output_params->rate));
if (!input_delay) {
return NULL;
}
}
if (input_resampler && output_resampler) {
- 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);
+ 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);
} else if (input_resampler) {
- 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);
+ 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);
} else {
- 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);
+ 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);
}
}