The following warnings occurred:
Warning [2] count(): Parameter must be an array or an object that implements Countable - Line: 906 - File: showthread.php PHP 7.2.34 (Linux)
File Line Function
/showthread.php 906 errorHandler->error



Thank you for your donation!


Cloudsmith graciously provides open-source package management and distribution for our project.


Custom soxr settings
#1
I've added the attached patch to allow custom soxr resampling.
It works great to upsample with custom param and you can also attenuate the signal.

The code is compatible with PiCorePlayer so you can enter strings like this:
http://archimago.blogspot.com/2018/11/mu....html#more

It fall back to the custom if the settings is not in the default supported list:

mpd.conf:

resampler {
plugin "soxr"
quality "v::4:28:99.5:100:73"
threads "0"
}

There is currently no GUI support so it will not show in the GUI.


Code:
diff --git a/src/pcm/SoxrResampler.cxx b/src/pcm/SoxrResampler.cxx
index 411e79b..a830d28 100644
--- a/src/pcm/SoxrResampler.cxx
+++ b/src/pcm/SoxrResampler.cxx
@@ -28,6 +28,22 @@

 #include <assert.h>
 #include <string.h>
+#include <math.h>
+
+struct soxr {
+       unsigned long q_recipe;
+       unsigned long q_flags;
+       double q_precision;         /* Conversion precision (in bits).           20    */
+       double q_phase_response;    /* 0=minimum, ... 50=linear, ... 100=maximum 50    */
+       double q_passband_end;      /* 0dB pt. bandwidth to preserve; nyquist=1  0.913 */
+       double q_stopband_begin;    /* Aliasing/imaging control; > passband_end   1    */
+       double scale;
+       bool max_rate;
+       bool exception;
+};
+
+static struct soxr soxr_custom_settings;
+char soxr_custom_string[200];

 static constexpr Domain soxr_domain("soxr");

@@ -40,6 +56,7 @@ static constexpr unsigned long SOXR_INVALID_RECIPE = -1;

 static soxr_quality_spec_t soxr_quality;
 static soxr_runtime_spec_t soxr_runtime;
+static soxr_io_spec_t soxr_iospec;

 static constexpr struct {
        unsigned long recipe;
@@ -53,6 +70,107 @@ static constexpr struct {
        { SOXR_INVALID_RECIPE, nullptr }
 };

+static char *next_param(char *src, char c) {
+        static char *str = NULL;
+        char *ptr, *ret;
+        if (src) str = src;
+        if (str && (ptr = strchr(str, c))) {
+                ret = str;
+                *ptr = '\0';
+                str = ptr + 1;
+        } else {
+                ret = str;
+                str = NULL;
+        }
+
+        return ret && ret[0] ? ret : NULL;
+}
+
+static bool resample_init(char *opt, struct soxr *r) {
+       char *recipe = NULL, *flags = NULL;
+       char *atten = NULL;
+       char *precision = NULL, *passband_end = NULL, *stopband_begin = NULL, *phase_response = NULL;
+
+       if (!r) {
+               return false;
+       }
+
+       r->max_rate = false;
+       r->exception = false;
+
+       if (opt) {
+               recipe = next_param(opt, ':');
+               flags = next_param(NULL, ':');
+               atten = next_param(NULL, ':');
+               precision = next_param(NULL, ':');
+               passband_end = next_param(NULL, ':');
+               stopband_begin = next_param(NULL, ':');
+               phase_response = next_param(NULL, ':');
+       }
+
+       // default to HQ (20 bit) if not user specified
+       r->q_recipe = SOXR_HQ;
+       r->q_flags = 0;
+       // default to 1db of attenuation if not user specified
+       r->scale = pow(10, -1.0 / 20);
+       // override recipe derived values with user specified values
+       r->q_precision = 0;
+       r->q_passband_end = 0;
+       r->q_stopband_begin = 0;
+       r->q_phase_response = -1;
+
+       if (recipe && recipe[0] != '\0') {
+               if (strchr(recipe, 'v')) r->q_recipe = SOXR_VHQ;
+               if (strchr(recipe, 'h')) r->q_recipe = SOXR_HQ;
+               if (strchr(recipe, 'm')) r->q_recipe = SOXR_MQ;
+               if (strchr(recipe, 'l')) r->q_recipe = SOXR_LQ;
+               if (strchr(recipe, 'q')) r->q_recipe = SOXR_QQ;
+               if (strchr(recipe, 'L')) r->q_recipe |= SOXR_LINEAR_PHASE;
+               if (strchr(recipe, 'I')) r->q_recipe |= SOXR_INTERMEDIATE_PHASE;
+               if (strchr(recipe, 'M')) r->q_recipe |= SOXR_MINIMUM_PHASE;
+               if (strchr(recipe, 's')) r->q_recipe |= SOXR_STEEP_FILTER;
+               // X = async resampling to max_rate
+               if (strchr(recipe, 'X')) r->max_rate = true;
+               // E = exception, only resample if native rate is not supported
+               if (strchr(recipe, 'E')) r->exception = true;
+       }
+
+       if (flags) {
+               r->q_flags = strtoul(flags, 0, 16);
+       }
+
+       if (atten) {
+               double scale = pow(10, -atof(atten) / 20);
+               if (scale > 0 && scale <= 1.0) {
+                       r->scale = scale;
+               }
+       }
+
+       if (precision) {
+               r->q_precision = atof(precision);
+       }
+
+       if (passband_end) {
+               r->q_passband_end = atof(passband_end) / 100;
+       }
+
+       if (stopband_begin) {
+               r->q_stopband_begin = atof(stopband_begin) / 100;
+       }
+
+       if (phase_response) {
+               r->q_phase_response = atof(phase_response);
+       }
+
+       snprintf(soxr_custom_string, sizeof(soxr_custom_string),
+               "resampling %s recipe: 0x%02x, flags: 0x%02x, scale: %03.2f, precision: %03.1f, passband_end: %03.5f, stopband_begin: %03.5f, phase_response: %03.1f",
+               r->max_rate ? "async" : "sync",
+               (unsigned int)r->q_recipe, (unsigned int)r->q_flags, r->scale, r->q_precision, r->q_passband_end, r->q_stopband_begin, r->q_phase_response);
+
+       return true;
+}
+
+
 gcc_const
 static const char *
 soxr_quality_name(unsigned long recipe) noexcept
@@ -84,15 +202,35 @@ pcm_resample_soxr_global_init(const ConfigBlock &block)
 {
        const char *quality_string = block.GetBlockValue("quality");
        unsigned long recipe = soxr_parse_quality(quality_string);
-       if (recipe == SOXR_INVALID_RECIPE) {
-               assert(quality_string != nullptr);

-               throw FormatRuntimeError("unknown quality setting '%s' in line %d",
-                                        quality_string, block.line);
+       if (recipe == SOXR_INVALID_RECIPE && resample_init((char*)quality_string, &soxr_custom_settings)) {
+                soxr_quality = soxr_quality_spec(soxr_custom_settings.q_recipe, soxr_custom_settings.q_flags);
+                if (soxr_custom_settings.q_precision > 0) {
+                        soxr_quality.precision = soxr_custom_settings.q_precision;
+                }
+                if (soxr_custom_settings.q_passband_end > 0) {
+                        soxr_quality.passband_end = soxr_custom_settings.q_passband_end;
+                }
+                if (soxr_custom_settings.q_stopband_begin > 0) {
+                        soxr_quality.stopband_begin = soxr_custom_settings.q_stopband_begin;
+                }
+                if (soxr_custom_settings.q_phase_response > -1) {
+                        soxr_quality.phase_response = soxr_custom_settings.q_phase_response;
+                }
+               soxr_iospec = soxr_io_spec(SOXR_FLOAT32_I, SOXR_FLOAT32_I);
+               soxr_iospec.scale = soxr_custom_settings.scale;
+       } else {
+               if (recipe == SOXR_INVALID_RECIPE) {
+                       assert(quality_string != nullptr);
+
+                       throw FormatRuntimeError("unknown quality setting '%s' in line %d",
+                                                quality_string, block.line);
+               }
+
+               soxr_quality = soxr_quality_spec(recipe, 0);
+               soxr_iospec.scale = 0;
        }

-       soxr_quality = soxr_quality_spec(recipe, 0);
-
        FormatDebug(soxr_domain,
                    "soxr converter '%s'",
                    soxr_quality_name(recipe));
@@ -110,12 +248,12 @@ SoxrPcmResampler::Open(AudioFormat &af, unsigned new_sample_rate)
        soxr_error_t e;
        soxr = soxr_create(af.sample_rate, new_sample_rate,
                           af.channels, &e,
-                          nullptr, &soxr_quality, &soxr_runtime);
+                          soxr_iospec.scale > 0 ? &soxr_iospec : nullptr, &soxr_quality, &soxr_runtime);
        if (soxr == nullptr)
                throw FormatRuntimeError("soxr initialization has failed: %s",
                                         e);

-       FormatDebug(soxr_domain, "soxr engine '%s'", soxr_engine(soxr));
+       FormatDebug(soxr_domain, "soxr engine '%s' soxr_quality '%s'", soxr_engine(soxr), soxr_custom_string);

        channels = af.channels;


I hope it is useful for someone else Smile
Reply


Messages In This Thread
Custom soxr settings - by zeb5274 - 01-18-2020, 01:36 PM
RE: Custom soxr settings - by Tim Curtis - 01-18-2020, 02:06 PM
RE: Custom soxr settings - by TheOldPresbyope - 01-18-2020, 03:06 PM
RE: Custom soxr settings - by zeb5274 - 01-18-2020, 03:59 PM
RE: Custom soxr settings - by Tim Curtis - 01-18-2020, 07:58 PM
RE: Custom soxr settings - by cryptout - 01-18-2020, 06:29 PM
RE: Custom soxr settings - by zeb5274 - 01-18-2020, 11:19 PM
RE: Custom soxr settings - by zeb5274 - 01-18-2020, 11:22 PM
RE: Custom soxr settings - by zeb5274 - 01-20-2020, 09:46 PM
RE: Custom soxr settings - by cryptout - 01-23-2020, 06:55 AM
RE: Custom soxr settings - by Tim Curtis - 01-20-2020, 10:32 PM

Forum Jump: