summaryrefslogtreecommitdiffstats
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.cpp299
1 files changed, 299 insertions, 0 deletions
diff --git a/media/libcubeb/src/cubeb_resampler.cpp b/media/libcubeb/src/cubeb_resampler.cpp
new file mode 100644
index 000000000..f6676946c
--- /dev/null
+++ b/media/libcubeb/src/cubeb_resampler.cpp
@@ -0,0 +1,299 @@
+/*
+ * Copyright © 2014 Mozilla Foundation
+ *
+ * This program is made available under an ISC-style license. See the
+ * accompanying file LICENSE for details.
+ */
+#ifndef NOMINMAX
+#define NOMINMAX
+#endif // NOMINMAX
+
+#include <algorithm>
+#include <cmath>
+#include <cassert>
+#include <cstring>
+#include <cstddef>
+#include <cstdio>
+#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) {
+ case CUBEB_RESAMPLER_QUALITY_VOIP:
+ return SPEEX_RESAMPLER_QUALITY_VOIP;
+ case CUBEB_RESAMPLER_QUALITY_DEFAULT:
+ return SPEEX_RESAMPLER_QUALITY_DEFAULT;
+ case CUBEB_RESAMPLER_QUALITY_DESKTOP:
+ return SPEEX_RESAMPLER_QUALITY_DESKTOP;
+ default:
+ assert(false);
+ return 0XFFFFFFFF;
+ }
+}
+
+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 &&
+ *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;
+ }
+
+ if (input_buffer && *input_frames_count != output_frames) {
+ assert(*input_frames_count > output_frames);
+ *input_frames_count = output_frames;
+ }
+
+ return data_callback(stream, user_ptr,
+ input_buffer, output_buffer, output_frames);
+}
+
+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) {
+ fill_internal = &cubeb_resampler_speex::fill_internal_input;
+ } 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>
+long
+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);
+}
+
+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)
+{
+ assert(!input_buffer && (!input_frames_count || *input_frames_count == 0) &&
+ output_buffer && output_frames_needed);
+
+ 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);
+
+ out_unprocessed =
+ output_processor->input_buffer(output_frames_before_processing);
+
+ got = data_callback(stream, user_ptr,
+ nullptr, out_unprocessed,
+ output_frames_before_processing);
+
+ 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. */
+ return output_processor->output(output_buffer, output_frames_needed);
+}
+
+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*/)
+{
+ assert(input_buffer && input_frames_count && *input_frames_count &&
+ !output_buffer);
+
+ /* 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);
+
+ /* process the input, and present exactly `output_frames_needed` in the
+ * callback. */
+ input_processor->input(input_buffer, *input_frames_count);
+ resampled_input = input_processor->output(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. */
+ return (*input_frames_count) * (got / resampled_frame_count);
+}
+
+
+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)
+{
+ /* 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;
+ size_t output_frames_before_processing = 0;
+ /* The number of frames returned from the callback. */
+ long got = 0;
+
+ /* We need to determine how much frames to present to the consumer.
+ * - If we have a two way stream, but we're only resampling input, we resample
+ * the input to the number of output frames.
+ * - If we have a two way stream, but we're only resampling the output, we
+ * resize the input buffer of the output resampler to the number of input
+ * frames, and we resample it afterwards.
+ * - If we resample both ways, we resample the input to the number of frames
+ * we would need to pass down to the consumer (before resampling the output),
+ * get the output data, and resample it to the number of frames needed by the
+ * 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 */
+ out_unprocessed =
+ output_processor->input_buffer(output_frames_before_processing);
+
+ if (in_buffer) {
+ /* process the input, and present exactly `output_frames_needed` in the
+ * callback. */
+ input_processor->input(in_buffer, *input_frames_count);
+ resampled_input =
+ input_processor->output(output_frames_before_processing);
+ } else {
+ resampled_input = nullptr;
+ }
+
+ got = data_callback(stream, user_ptr,
+ resampled_input, out_unprocessed,
+ output_frames_before_processing);
+
+ 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. */
+ return output_processor->output(out_buffer, output_frames_needed);
+}
+
+/* Resampler C API */
+
+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)
+{
+ cubeb_sample_format format;
+
+ assert(input_params || output_params);
+
+ if (input_params) {
+ format = input_params->format;
+ } else {
+ 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;
+ }
+}
+
+long
+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);
+}
+
+void
+cubeb_resampler_destroy(cubeb_resampler * resampler)
+{
+ delete resampler;
+}
+
+long
+cubeb_resampler_latency(cubeb_resampler * resampler)
+{
+ return resampler->latency();
+}