summaryrefslogtreecommitdiff
path: root/media/libcubeb/src/cubeb_resampler.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'media/libcubeb/src/cubeb_resampler.cpp')
-rw-r--r--media/libcubeb/src/cubeb_resampler.cpp349
1 files changed, 138 insertions, 211 deletions
diff --git a/media/libcubeb/src/cubeb_resampler.cpp b/media/libcubeb/src/cubeb_resampler.cpp
index d61b1daf26..f6676946c0 100644
--- a/media/libcubeb/src/cubeb_resampler.cpp
+++ b/media/libcubeb/src/cubeb_resampler.cpp
@@ -8,21 +8,21 @@
#define NOMINMAX
#endif // NOMINMAX
-#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 <cassert>
+#include <cstring>
#include <cstddef>
#include <cstdio>
-#include <cstring>
+#include "cubeb_resampler.h"
+#include "cubeb-speex-resampler.h"
+#include "cubeb_resampler_internal.h"
+#include "cubeb_utils.h"
int
to_speex_quality(cubeb_resampler_quality q)
{
- switch (q) {
+ switch(q) {
case CUBEB_RESAMPLER_QUALITY_VOIP:
return SPEEX_RESAMPLER_QUALITY_VOIP;
case CUBEB_RESAMPLER_QUALITY_DEFAULT:
@@ -35,230 +35,157 @@ to_speex_quality(cubeb_resampler_quality q)
}
}
-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)
+long noop_resampler::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));
-
- // When we have no pending input data and exactly as much input
- // as output data, we don't need to copy it into the internal buffer
- // and can directly forward it to the callback.
- void * in_buf = input_buffer;
- unsigned long pop_input_count = 0u;
- if (input_buffer && !output_buffer) {
+ assert((input_buffer && output_buffer &&
+ *input_frames_count >= output_frames) ||
+ (!input_buffer && (!input_frames_count || *input_frames_count == 0)) ||
+ (!output_buffer && output_frames == 0));
+
+ if (output_buffer == nullptr) {
+ assert(input_buffer);
output_frames = *input_frames_count;
- } else if (input_buffer) {
- if (internal_input_buffer.length() != 0 ||
- *input_frames_count < output_frames) {
- // If we have pending input data left and have to first append the input
- // so we can pass it as one pointer to the callback. Or this is a glitch.
- // It can happen when system's performance is poor. Audible silence is
- // being pushed at the end of the short input buffer. An improvement for
- // the future is to resample to the output number of frames, when that
- // happens.
- 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)) {
- // This is unxpected but it can happen when a glitch occurs. Fill the
- // buffer with silence. First keep the actual number of input samples
- // used without the silence.
- 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) {
- // In this case we have more input that we need output and
- // fill the overflowing input into internal_input_buffer
- // Since we have no other pending data, we can nonetheless
- // pass the current input data directly to the callback
- 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 = samples_to_frames(pop_input_count);
- } else {
- *input_frames_count = output_frames;
- }
- drop_audio_if_needed();
+ if (input_buffer && *input_frames_count != output_frames) {
+ assert(*input_frames_count > output_frames);
+ *input_frames_count = output_frames;
}
- return rv;
+ return data_callback(stream, user_ptr,
+ input_buffer, output_buffer, output_frames);
}
-// Explicit instantiation of template class.
-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)
- : input_processor(input_processor), output_processor(output_processor),
- stream(s), data_callback(cb), user_ptr(ptr)
+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)
+ : input_processor(input_processor)
+ , output_processor(output_processor)
+ , stream(s)
+ , data_callback(cb)
+ , user_ptr(ptr)
{
if (input_processor && output_processor) {
+ // Add some delay on the processor that has the lowest delay so that the
+ // streams are synchronized.
+ uint32_t in_latency = input_processor->latency();
+ uint32_t out_latency = output_processor->latency();
+ if (in_latency > out_latency) {
+ uint32_t latency_diff = in_latency - out_latency;
+ output_processor->add_latency(latency_diff);
+ } else if (in_latency < out_latency) {
+ uint32_t latency_diff = out_latency - in_latency;
+ input_processor->add_latency(latency_diff);
+ }
fill_internal = &cubeb_resampler_speex::fill_internal_duplex;
- } else if (input_processor) {
+ } else if (input_processor) {
fill_internal = &cubeb_resampler_speex::fill_internal_input;
- } else if (output_processor) {
+ } else if (output_processor) {
fill_internal = &cubeb_resampler_speex::fill_internal_output;
}
}
-template <typename T, typename InputProcessor, typename OutputProcessor>
-cubeb_resampler_speex<T, InputProcessor,
- OutputProcessor>::~cubeb_resampler_speex()
-{
-}
+template<typename T, typename InputProcessor, typename OutputProcessor>
+cubeb_resampler_speex<T, InputProcessor, OutputProcessor>
+ ::~cubeb_resampler_speex()
+{ }
-template <typename T, typename InputProcessor, typename OutputProcessor>
+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)
+cubeb_resampler_speex<T, InputProcessor, OutputProcessor>
+::fill(void * input_buffer, long * input_frames_count,
+ void * output_buffer, long output_frames_needed)
{
/* Input and output buffers, typed */
- 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);
+ 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>
+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)
+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;
-
- /* fill directly the input buffer of the output processor to save a copy */
- output_frames_before_processing =
- output_processor->input_needed_for_output(output_frames_needed);
+ long got = 0;
+ T * out_unprocessed = nullptr;
+ long output_frames_before_processing = 0;
- out_unprocessed =
- output_processor->input_buffer(output_frames_before_processing);
- got = data_callback(stream, user_ptr, nullptr, out_unprocessed,
- output_frames_before_processing);
+ /* fill directly the input buffer of the output processor to save a copy */
+ output_frames_before_processing =
+ output_processor->input_needed_for_output(output_frames_needed);
- if (got < output_frames_before_processing) {
- draining = true;
+ out_unprocessed =
+ output_processor->input_buffer(output_frames_before_processing);
- if (got < 0) {
- return got;
- }
- }
+ got = data_callback(stream, user_ptr,
+ nullptr, out_unprocessed,
+ output_frames_before_processing);
- output_processor->written(got);
+ if (got < 0) {
+ return got;
}
+ output_processor->written(got);
+
/* Process the output. If not enough frames have been returned from the
- * callback, drain the processors. */
+ * callback, drain the processors. */
return output_processor->output(output_buffer, output_frames_needed);
}
-template <typename T, typename InputProcessor, typename OutputProcessor>
+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 /*output_frames_needed*/)
+cubeb_resampler_speex<T, InputProcessor, OutputProcessor>
+::fill_internal_input(T * input_buffer, long * input_frames_count,
+ T * output_buffer, long /*output_frames_needed*/)
{
assert(input_buffer && input_frames_count && *input_frames_count &&
!output_buffer);
- /* The input data, after eventual resampling. This is passed to the callback.
- */
+ /* The input data, after eventual resampling. This is passed to the callback. */
T * resampled_input = nullptr;
- uint32_t resampled_frame_count =
- input_processor->output_for_input(*input_frames_count);
+ uint32_t resampled_frame_count = input_processor->output_for_input(*input_frames_count);
/* process the input, and present exactly `output_frames_needed` in the
- * callback. */
+ * callback. */
input_processor->input(input_buffer, *input_frames_count);
+ resampled_input = input_processor->output(resampled_frame_count);
- /* resampled_frame_count == 0 happens if the resampler
- * doesn't have enough input frames buffered to produce 1 resampled frame. */
- if (resampled_frame_count == 0) {
- return *input_frames_count;
- }
-
- size_t frames_resampled = 0;
- resampled_input =
- input_processor->output(resampled_frame_count, &frames_resampled);
- *input_frames_count = frames_resampled;
-
- long got = data_callback(stream, user_ptr, resampled_input, nullptr,
- resampled_frame_count);
+ long got = data_callback(stream, user_ptr,
+ resampled_input, nullptr, resampled_frame_count);
/* Return the number of initial input frames or part of it.
- * Since output_frames_needed == 0 in input scenario, the only
- * available number outside resampler is the initial number of frames. */
+ * Since output_frames_needed == 0 in input scenario, the only
+ * available number outside resampler is the initial number of frames. */
return (*input_frames_count) * (got / resampled_frame_count);
}
-template <typename T, typename InputProcessor, typename OutputProcessor>
+
+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)
+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) {
- // discard input and drain any signal remaining in the resampler.
- return output_processor->output(out_buffer, output_frames_needed);
- }
-
- /* The input data, after eventual resampling. This is passed to the callback.
- */
+ /* The input data, after eventual resampling. This is passed to the callback. */
T * resampled_input = nullptr;
/* The output buffer passed down in the callback, that might be resampled. */
T * out_unprocessed = nullptr;
- long output_frames_before_processing = 0;
+ size_t output_frames_before_processing = 0;
/* The number of frames returned from the callback. */
long got = 0;
@@ -274,46 +201,34 @@ cubeb_resampler_speex<T, InputProcessor, OutputProcessor>::fill_internal_duplex(
* caller. */
output_frames_before_processing =
- output_processor->input_needed_for_output(output_frames_needed);
- /* fill directly the input buffer of the output processor to save a copy */
+ output_processor->input_needed_for_output(output_frames_needed);
+ /* fill directly the input buffer of the output processor to save a copy */
out_unprocessed =
- output_processor->input_buffer(output_frames_before_processing);
+ output_processor->input_buffer(output_frames_before_processing);
if (in_buffer) {
/* process the input, and present exactly `output_frames_needed` in the
- * callback. */
+ * callback. */
input_processor->input(in_buffer, *input_frames_count);
-
- size_t frames_resampled = 0;
- resampled_input = input_processor->output(output_frames_before_processing,
- &frames_resampled);
- *input_frames_count = frames_resampled;
+ resampled_input =
+ input_processor->output(output_frames_before_processing);
} else {
resampled_input = nullptr;
}
- got = data_callback(stream, user_ptr, resampled_input, out_unprocessed,
+ 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 (got < 0) {
+ return got;
}
output_processor->written(got);
- input_processor->drop_audio_if_needed();
-
/* Process the output. If not enough frames have been returned from the
* callback, drain the processors. */
- got = output_processor->output(out_buffer, output_frames_needed);
-
- output_processor->drop_audio_if_needed();
-
- return got;
+ return output_processor->output(out_buffer, output_frames_needed);
}
/* Resampler C API */
@@ -322,8 +237,10 @@ 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)
+ unsigned int target_rate,
+ cubeb_data_callback callback,
+ void * user_ptr,
+ cubeb_resampler_quality quality)
{
cubeb_sample_format format;
@@ -335,28 +252,38 @@ cubeb_resampler_create(cubeb_stream * stream,
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);
- case CUBEB_SAMPLE_FLOAT32NE:
- return cubeb_resampler_create_internal<float>(stream, input_params,
- output_params, target_rate,
- callback, user_ptr, quality);
- default:
- assert(false);
- return nullptr;
+ switch(format) {
+ case CUBEB_SAMPLE_S16NE:
+ return cubeb_resampler_create_internal<short>(stream,
+ input_params,
+ output_params,
+ target_rate,
+ callback,
+ user_ptr,
+ quality);
+ case CUBEB_SAMPLE_FLOAT32NE:
+ return cubeb_resampler_create_internal<float>(stream,
+ input_params,
+ output_params,
+ target_rate,
+ callback,
+ user_ptr,
+ quality);
+ default:
+ assert(false);
+ return nullptr;
}
}
long
-cubeb_resampler_fill(cubeb_resampler * resampler, void * input_buffer,
- long * input_frames_count, void * output_buffer,
+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);
+ return resampler->fill(input_buffer, input_frames_count,
+ output_buffer, output_frames_needed);
}
void