+static bool speex_is_fixed_point(void);
+
+static pa_resample_method_t choose_auto_resampler(pa_resample_flags_t flags) {
+ pa_resample_method_t method;
+
+ if (pa_resample_method_supported(PA_RESAMPLER_SPEEX_FLOAT_BASE + 1))
+ method = PA_RESAMPLER_SPEEX_FLOAT_BASE + 1;
+ else if (flags & PA_RESAMPLER_VARIABLE_RATE)
+ method = PA_RESAMPLER_TRIVIAL;
+ else
+ method = PA_RESAMPLER_FFMPEG;
+
+ return method;
+}
+
+static pa_resample_method_t fix_method(
+ pa_resample_flags_t flags,
+ pa_resample_method_t method,
+ const uint32_t rate_a,
+ const uint32_t rate_b) {
+
+ pa_assert(pa_sample_rate_valid(rate_a));
+ pa_assert(pa_sample_rate_valid(rate_b));
+ pa_assert(method >= 0);
+ pa_assert(method < PA_RESAMPLER_MAX);
+
+ if (!(flags & PA_RESAMPLER_VARIABLE_RATE) && rate_a == rate_b) {
+ pa_log_info("Forcing resampler 'copy', because of fixed, identical sample rates.");
+ method = PA_RESAMPLER_COPY;
+ }
+
+ if (!pa_resample_method_supported(method)) {
+ pa_log_warn("Support for resampler '%s' not compiled in, reverting to 'auto'.", pa_resample_method_to_string(method));
+ method = PA_RESAMPLER_AUTO;
+ }
+
+ switch (method) {
+ case PA_RESAMPLER_COPY:
+ if (rate_a != rate_b) {
+ pa_log_info("Resampler 'copy' cannot change sampling rate, reverting to resampler 'auto'.");
+ method = PA_RESAMPLER_AUTO;
+ break;
+ }
+ /* Else fall through */
+ case PA_RESAMPLER_FFMPEG:
+ if (flags & PA_RESAMPLER_VARIABLE_RATE) {
+ pa_log_info("Resampler '%s' cannot do variable rate, reverting to resampler 'auto'.", pa_resample_method_to_string(method));
+ method = PA_RESAMPLER_AUTO;
+ }
+ break;
+
+ /* The Peaks resampler only supports downsampling.
+ * Revert to auto if we are upsampling */
+ case PA_RESAMPLER_PEAKS:
+ if (rate_a < rate_b) {
+ pa_log_warn("The 'peaks' resampler only supports downsampling, reverting to resampler 'auto'.");
+ method = PA_RESAMPLER_AUTO;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if (method == PA_RESAMPLER_AUTO)
+ method = choose_auto_resampler(flags);
+
+ /* At this point, method is supported in the sense that it
+ * has an init function and supports the required flags. However,
+ * speex-float implementation in PulseAudio relies on the
+ * assumption that is invalid if speex has been compiled with
+ * --enable-fixed-point. Besides, speex-fixed is more efficient
+ * in this configuration. So use it instead.
+ */
+ if (method >= PA_RESAMPLER_SPEEX_FLOAT_BASE && method <= PA_RESAMPLER_SPEEX_FLOAT_MAX) {
+ if (speex_is_fixed_point()) {
+ pa_log_info("Speex appears to be compiled with --enable-fixed-point. "
+ "Switching to a fixed-point resampler because it should be faster.");
+ method = method - PA_RESAMPLER_SPEEX_FLOAT_BASE + PA_RESAMPLER_SPEEX_FIXED_BASE;
+ }
+ }
+ return method;
+}
+
+/* Return true if a is a more precise sample format than b, else return false */
+static bool sample_format_more_precise(pa_sample_format_t a, pa_sample_format_t b) {
+ pa_assert(pa_sample_format_valid(a));
+ pa_assert(pa_sample_format_valid(b));
+
+ switch (a) {
+ case PA_SAMPLE_U8:
+ case PA_SAMPLE_ALAW:
+ case PA_SAMPLE_ULAW:
+ return false;
+ break;
+
+ case PA_SAMPLE_S16LE:
+ case PA_SAMPLE_S16BE:
+ if (b == PA_SAMPLE_ULAW || b == PA_SAMPLE_ALAW || b == PA_SAMPLE_U8)
+ return true;
+ else
+ return false;
+ break;
+
+ case PA_SAMPLE_S24LE:
+ case PA_SAMPLE_S24BE:
+ case PA_SAMPLE_S24_32LE:
+ case PA_SAMPLE_S24_32BE:
+ if (b == PA_SAMPLE_ULAW || b == PA_SAMPLE_ALAW || b == PA_SAMPLE_U8 ||
+ b == PA_SAMPLE_S16LE || b == PA_SAMPLE_S16BE)
+ return true;
+ else
+ return false;
+ break;
+
+ case PA_SAMPLE_FLOAT32LE:
+ case PA_SAMPLE_FLOAT32BE:
+ case PA_SAMPLE_S32LE:
+ case PA_SAMPLE_S32BE:
+ if (b == PA_SAMPLE_FLOAT32LE || b == PA_SAMPLE_FLOAT32BE ||
+ b == PA_SAMPLE_S32LE || b == PA_SAMPLE_FLOAT32BE)
+ return false;
+ else
+ return true;
+ break;
+
+ default:
+ return false;
+ }
+}
+
+static pa_sample_format_t choose_work_format(
+ pa_resample_method_t method,
+ pa_sample_format_t a,
+ pa_sample_format_t b,
+ bool map_required) {
+ pa_sample_format_t work_format;
+
+ pa_assert(pa_sample_format_valid(a));
+ pa_assert(pa_sample_format_valid(b));
+ pa_assert(method >= 0);
+ pa_assert(method < PA_RESAMPLER_MAX);
+
+ if (method >= PA_RESAMPLER_SPEEX_FIXED_BASE && method <= PA_RESAMPLER_SPEEX_FIXED_MAX)
+ method = PA_RESAMPLER_SPEEX_FIXED_BASE;
+
+ switch (method) {
+ /* This block is for resampling functions that only
+ * support the S16 sample format. */
+ case PA_RESAMPLER_SPEEX_FIXED_BASE: /* fall through */
+ case PA_RESAMPLER_FFMPEG:
+ work_format = PA_SAMPLE_S16NE;
+ break;
+
+ /* This block is for resampling functions that support
+ * any sample format. */
+ case PA_RESAMPLER_COPY: /* fall through */
+ case PA_RESAMPLER_TRIVIAL:
+ if (!map_required && a == b) {
+ work_format = a;
+ break;
+ }
+ /* Else fall trough */
+ case PA_RESAMPLER_PEAKS:
+ if (a == PA_SAMPLE_S16NE || b == PA_SAMPLE_S16NE)
+ work_format = PA_SAMPLE_S16NE;
+ else if (sample_format_more_precise(a, PA_SAMPLE_S16NE) ||
+ sample_format_more_precise(b, PA_SAMPLE_S16NE))
+ work_format = PA_SAMPLE_FLOAT32NE;
+ else
+ work_format = PA_SAMPLE_S16NE;
+ break;
+
+ default:
+ work_format = PA_SAMPLE_FLOAT32NE;
+ }
+
+ return work_format;
+}
+