mirror of
https://github.com/sle118/squeezelite-esp32.git
synced 2025-12-08 04:27:12 +03:00
add resample
consumes A LOT of CPU. Need alos to change the command line to set the sample rate (-r) to a fixed value (BT) but accept igher (-Z) from LMS and enable resampling (-R) -R -Z 96000 -r \"44100-44100\" or similar
This commit is contained in:
@@ -8,7 +8,9 @@ COMPONENT_ADD_LDFLAGS=-l$(COMPONENT_NAME) \
|
|||||||
$(COMPONENT_PATH)/lib/libhelix-aac.a \
|
$(COMPONENT_PATH)/lib/libhelix-aac.a \
|
||||||
$(COMPONENT_PATH)/lib/libvorbisidec.a \
|
$(COMPONENT_PATH)/lib/libvorbisidec.a \
|
||||||
$(COMPONENT_PATH)/lib/libogg.a \
|
$(COMPONENT_PATH)/lib/libogg.a \
|
||||||
$(COMPONENT_PATH)/lib/libalac.a
|
$(COMPONENT_PATH)/lib/libalac.a \
|
||||||
|
$(COMPONENT_PATH)/lib/libsoxr.a
|
||||||
|
|
||||||
|
|
||||||
#$(COMPONENT_PATH)/lib/libfaad.a
|
#$(COMPONENT_PATH)/lib/libfaad.a
|
||||||
#$(COMPONENT_PATH)/lib/libvorbisidec.a
|
#$(COMPONENT_PATH)/lib/libvorbisidec.a
|
||||||
|
|||||||
348
components/codecs/inc/soxr/soxr.h
Normal file
348
components/codecs/inc/soxr/soxr.h
Normal file
@@ -0,0 +1,348 @@
|
|||||||
|
/* SoX Resampler Library Copyright (c) 2007-13 robs@users.sourceforge.net
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2.1 of the License, or (at
|
||||||
|
* your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with this library; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* -------------------------------- Gubbins --------------------------------- */
|
||||||
|
|
||||||
|
#if !defined soxr_included
|
||||||
|
#define soxr_included
|
||||||
|
|
||||||
|
|
||||||
|
#if defined __cplusplus
|
||||||
|
#include <cstddef>
|
||||||
|
extern "C" {
|
||||||
|
#else
|
||||||
|
#include <stddef.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined SOXR_DLL
|
||||||
|
#if defined soxr_EXPORTS
|
||||||
|
#define SOXR __declspec(dllexport)
|
||||||
|
#else
|
||||||
|
#define SOXR __declspec(dllimport)
|
||||||
|
#endif
|
||||||
|
#elif defined SOXR_VISIBILITY && defined __GNUC__ && (__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ >= 1)
|
||||||
|
#define SOXR __attribute__ ((visibility("default")))
|
||||||
|
#else
|
||||||
|
#define SOXR
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct soxr_io_spec soxr_io_spec_t;
|
||||||
|
typedef struct soxr_quality_spec soxr_quality_spec_t;
|
||||||
|
typedef struct soxr_runtime_spec soxr_runtime_spec_t;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* ---------------------------- API conventions --------------------------------
|
||||||
|
|
||||||
|
Buffer lengths (and occupancies) are expressed as the number of contained
|
||||||
|
samples per channel.
|
||||||
|
|
||||||
|
Parameter names for buffer lengths have the suffix `len'.
|
||||||
|
|
||||||
|
A single-character `i' or 'o' is often used in names to give context as
|
||||||
|
input or output (e.g. ilen, olen). */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* --------------------------- Version management --------------------------- */
|
||||||
|
|
||||||
|
/* E.g. #if SOXR_THIS_VERSION >= SOXR_VERSION(0,1,1) ... */
|
||||||
|
|
||||||
|
#define SOXR_VERSION(x,y,z) (((x)<<16)|((y)<<8)|(z))
|
||||||
|
#define SOXR_THIS_VERSION SOXR_VERSION(0,1,2)
|
||||||
|
#define SOXR_THIS_VERSION_STR "0.1.2"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* --------------------------- Type declarations ---------------------------- */
|
||||||
|
|
||||||
|
typedef struct soxr * soxr_t; /* A resampler for 1 or more channels. */
|
||||||
|
typedef char const * soxr_error_t; /* 0:no-error; non-0:error. */
|
||||||
|
|
||||||
|
typedef void * soxr_buf_t; /* 1 buffer of channel-interleaved samples. */
|
||||||
|
typedef void const * soxr_cbuf_t; /* Ditto; read-only. */
|
||||||
|
|
||||||
|
typedef soxr_buf_t const * soxr_bufs_t;/* Or, a separate buffer for each ch. */
|
||||||
|
typedef soxr_cbuf_t const * soxr_cbufs_t; /* Ditto; read-only. */
|
||||||
|
|
||||||
|
typedef void const * soxr_in_t; /* Either a soxr_cbuf_t or soxr_cbufs_t,
|
||||||
|
depending on itype in soxr_io_spec_t. */
|
||||||
|
typedef void * soxr_out_t; /* Either a soxr_buf_t or soxr_bufs_t,
|
||||||
|
depending on otype in soxr_io_spec_t. */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* --------------------------- API main functions --------------------------- */
|
||||||
|
|
||||||
|
SOXR char const * soxr_version(void); /* Query library version: "libsoxr-x.y.z" */
|
||||||
|
|
||||||
|
#define soxr_strerror(e) /* Soxr counterpart to strerror. */ \
|
||||||
|
((e)?(e):"no error")
|
||||||
|
|
||||||
|
|
||||||
|
/* Create a stream resampler: */
|
||||||
|
|
||||||
|
SOXR soxr_t soxr_create(
|
||||||
|
double input_rate, /* Input sample-rate. */
|
||||||
|
double output_rate, /* Output sample-rate. */
|
||||||
|
unsigned num_channels, /* Number of channels to be used. */
|
||||||
|
/* All following arguments are optional (may be set to NULL). */
|
||||||
|
soxr_error_t *, /* To report any error during creation. */
|
||||||
|
soxr_io_spec_t const *, /* To specify non-default I/O formats. */
|
||||||
|
soxr_quality_spec_t const *, /* To specify non-default resampling quality.*/
|
||||||
|
soxr_runtime_spec_t const *);/* To specify non-default runtime resources.
|
||||||
|
|
||||||
|
Default io_spec is per soxr_io_spec(SOXR_FLOAT32_I, SOXR_FLOAT32_I)
|
||||||
|
Default quality_spec is per soxr_quality_spec(SOXR_HQ, 0)
|
||||||
|
Default runtime_spec is per soxr_runtime_spec(1) */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* If not using an app-supplied input function, after creating a stream
|
||||||
|
* resampler, repeatedly call: */
|
||||||
|
|
||||||
|
SOXR soxr_error_t soxr_process(
|
||||||
|
soxr_t resampler, /* As returned by soxr_create. */
|
||||||
|
/* Input (to be resampled): */
|
||||||
|
soxr_in_t in, /* Input buffer(s); may be NULL (see below). */
|
||||||
|
size_t ilen, /* Input buf. length (samples per channel). */
|
||||||
|
size_t * idone, /* To return actual # samples used (<= ilen). */
|
||||||
|
/* Output (resampled): */
|
||||||
|
soxr_out_t out, /* Output buffer(s).*/
|
||||||
|
size_t olen, /* Output buf. length (samples per channel). */
|
||||||
|
size_t * odone); /* To return actual # samples out (<= olen).
|
||||||
|
|
||||||
|
Note that no special meaning is associated with ilen or olen equal to
|
||||||
|
zero. End-of-input (i.e. no data is available nor shall be available)
|
||||||
|
may be indicated by seting `in' to NULL. */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* If using an app-supplied input function, it must look and behave like this:*/
|
||||||
|
|
||||||
|
typedef size_t /* data_len */
|
||||||
|
(* soxr_input_fn_t)( /* Supply data to be resampled. */
|
||||||
|
void * input_fn_state, /* As given to soxr_set_input_fn (below). */
|
||||||
|
soxr_in_t * data, /* Returned data; see below. N.B. ptr to ptr(s)*/
|
||||||
|
size_t requested_len); /* Samples per channel, >= returned data_len.
|
||||||
|
|
||||||
|
data_len *data Indicates Meaning
|
||||||
|
------- ------- ------------ -------------------------
|
||||||
|
!=0 !=0 Success *data contains data to be
|
||||||
|
input to the resampler.
|
||||||
|
0 !=0 (or End-of-input No data is available nor
|
||||||
|
not set) shall be available.
|
||||||
|
0 0 Failure An error occurred whilst trying to
|
||||||
|
source data to be input to the resampler. */
|
||||||
|
|
||||||
|
/* and be registered with a previously created stream resampler using: */
|
||||||
|
|
||||||
|
SOXR soxr_error_t soxr_set_input_fn(/* Set (or reset) an input function.*/
|
||||||
|
soxr_t resampler, /* As returned by soxr_create. */
|
||||||
|
soxr_input_fn_t, /* Function to supply data to be resampled.*/
|
||||||
|
void * input_fn_state, /* If needed by the input function. */
|
||||||
|
size_t max_ilen); /* Maximum value for input fn. requested_len.*/
|
||||||
|
|
||||||
|
/* then repeatedly call: */
|
||||||
|
|
||||||
|
SOXR size_t /*odone*/ soxr_output(/* Resample and output a block of data.*/
|
||||||
|
soxr_t resampler, /* As returned by soxr_create. */
|
||||||
|
soxr_out_t data, /* App-supplied buffer(s) for resampled data.*/
|
||||||
|
size_t olen); /* Amount of data to output; >= odone. */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Common stream resampler operations: */
|
||||||
|
|
||||||
|
SOXR soxr_error_t soxr_error(soxr_t); /* Query error status. */
|
||||||
|
SOXR size_t * soxr_num_clips(soxr_t); /* Query int. clip counter (for R/W). */
|
||||||
|
SOXR double soxr_delay(soxr_t); /* Query current delay in output samples.*/
|
||||||
|
SOXR char const * soxr_engine(soxr_t p); /* Query resampling engine name. */
|
||||||
|
|
||||||
|
SOXR soxr_error_t soxr_clear(soxr_t); /* Ready for fresh signal, same config. */
|
||||||
|
SOXR void soxr_delete(soxr_t); /* Free resources. */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* `Short-cut', single call to resample a (probably short) signal held entirely
|
||||||
|
* in memory. See soxr_create and soxr_process above for parameter details.
|
||||||
|
* Note that unlike soxr_create however, the default quality spec. for
|
||||||
|
* soxr_oneshot is per soxr_quality_spec(SOXR_LQ, 0). */
|
||||||
|
|
||||||
|
SOXR soxr_error_t soxr_oneshot(
|
||||||
|
double input_rate,
|
||||||
|
double output_rate,
|
||||||
|
unsigned num_channels,
|
||||||
|
soxr_in_t in , size_t ilen, size_t * idone,
|
||||||
|
soxr_out_t out, size_t olen, size_t * odone,
|
||||||
|
soxr_io_spec_t const *,
|
||||||
|
soxr_quality_spec_t const *,
|
||||||
|
soxr_runtime_spec_t const *);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* For variable-rate resampling. See example # 5 for how to create a
|
||||||
|
* variable-rate resampler and how to use this function. */
|
||||||
|
|
||||||
|
SOXR soxr_error_t soxr_set_io_ratio(soxr_t, double io_ratio, size_t slew_len);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* -------------------------- API type definitions -------------------------- */
|
||||||
|
|
||||||
|
typedef enum { /* Datatypes supported for I/O to/from the resampler: */
|
||||||
|
/* Internal; do not use: */
|
||||||
|
SOXR_FLOAT32, SOXR_FLOAT64, SOXR_INT32, SOXR_INT16, SOXR_SPLIT = 4,
|
||||||
|
|
||||||
|
/* Use for interleaved channels: */
|
||||||
|
SOXR_FLOAT32_I = SOXR_FLOAT32, SOXR_FLOAT64_I, SOXR_INT32_I, SOXR_INT16_I,
|
||||||
|
|
||||||
|
/* Use for split channels: */
|
||||||
|
SOXR_FLOAT32_S = SOXR_SPLIT , SOXR_FLOAT64_S, SOXR_INT32_S, SOXR_INT16_S
|
||||||
|
|
||||||
|
} soxr_datatype_t;
|
||||||
|
|
||||||
|
#define soxr_datatype_size(x) /* Returns `sizeof' a soxr_datatype_t sample. */\
|
||||||
|
((unsigned char *)"\4\10\4\2")[(x)&3]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
struct soxr_io_spec { /* Typically */
|
||||||
|
soxr_datatype_t itype; /* Input datatype. SOXR_FLOAT32_I */
|
||||||
|
soxr_datatype_t otype; /* Output datatype. SOXR_FLOAT32_I */
|
||||||
|
double scale; /* Linear gain to apply during resampling. 1 */
|
||||||
|
void * e; /* Reserved for internal use 0 */
|
||||||
|
unsigned long flags; /* Per the following #defines. 0 */
|
||||||
|
};
|
||||||
|
|
||||||
|
#define SOXR_TPDF 0 /* Applicable only if otype is INT16. */
|
||||||
|
#define SOXR_NO_DITHER 8u /* Disable the above. */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
struct soxr_quality_spec { /* Typically */
|
||||||
|
double precision; /* Conversion precision (in bits). 20 */
|
||||||
|
double phase_response; /* 0=minimum, ... 50=linear, ... 100=maximum 50 */
|
||||||
|
double passband_end; /* 0dB pt. bandwidth to preserve; nyquist=1 0.913*/
|
||||||
|
double stopband_begin; /* Aliasing/imaging control; > passband_end 1 */
|
||||||
|
void * e; /* Reserved for internal use. 0 */
|
||||||
|
unsigned long flags; /* Per the following #defines. 0 */
|
||||||
|
};
|
||||||
|
|
||||||
|
#define SOXR_ROLLOFF_SMALL 0u /* <= 0.01 dB */
|
||||||
|
#define SOXR_ROLLOFF_MEDIUM 1u /* <= 0.35 dB */
|
||||||
|
#define SOXR_ROLLOFF_NONE 2u /* For Chebyshev bandwidth. */
|
||||||
|
|
||||||
|
#define SOXR_MAINTAIN_3DB_PT 4u /* Reserved for internal use. */
|
||||||
|
#define SOXR_HI_PREC_CLOCK 8u /* Increase `irrational' ratio accuracy. */
|
||||||
|
#define SOXR_DOUBLE_PRECISION 16u /* Use D.P. calcs even if precision <= 20. */
|
||||||
|
#define SOXR_VR 32u /* Variable-rate resampling. */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
struct soxr_runtime_spec { /* Typically */
|
||||||
|
unsigned log2_min_dft_size;/* For DFT efficiency. [8,15] 10 */
|
||||||
|
unsigned log2_large_dft_size;/* For DFT efficiency. [16,20] 17 */
|
||||||
|
unsigned coef_size_kbytes; /* For SOXR_COEF_INTERP_AUTO (below). 400 */
|
||||||
|
unsigned num_threads; /* If built so. 0 means `automatic'. 1 */
|
||||||
|
void * e; /* Reserved for internal use. 0 */
|
||||||
|
unsigned long flags; /* Per the following #defines. 0 */
|
||||||
|
};
|
||||||
|
/* For `irrational' ratios only: */
|
||||||
|
#define SOXR_COEF_INTERP_AUTO 0u /* Auto select coef. interpolation. */
|
||||||
|
#define SOXR_COEF_INTERP_LOW 2u /* Man. select: less CPU, more memory. */
|
||||||
|
#define SOXR_COEF_INTERP_HIGH 3u /* Man. select: more CPU, less memory. */
|
||||||
|
|
||||||
|
#define SOXR_STRICT_BUFFERING 4u /* Reserved for future use. */
|
||||||
|
#define SOXR_NOSMALLINTOPT 8u /* For test purposes only. */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* -------------------------- API type constructors ------------------------- */
|
||||||
|
|
||||||
|
/* These functions allow setting of the most commonly-used structure
|
||||||
|
* parameters, with other parameters being given default values. The default
|
||||||
|
* values may then be overridden, directly in the structure, if needed. */
|
||||||
|
|
||||||
|
SOXR soxr_quality_spec_t soxr_quality_spec(
|
||||||
|
unsigned long recipe, /* Per the #defines immediately below. */
|
||||||
|
unsigned long flags); /* As soxr_quality_spec_t.flags. */
|
||||||
|
|
||||||
|
/* The 5 standard qualities found in SoX: */
|
||||||
|
#define SOXR_QQ 0 /* 'Quick' cubic interpolation. */
|
||||||
|
#define SOXR_LQ 1 /* 'Low' 16-bit with larger rolloff. */
|
||||||
|
#define SOXR_MQ 2 /* 'Medium' 16-bit with medium rolloff. */
|
||||||
|
#define SOXR_HQ SOXR_20_BITQ /* 'High quality'. */
|
||||||
|
#define SOXR_VHQ SOXR_28_BITQ /* 'Very high quality'. */
|
||||||
|
|
||||||
|
#define SOXR_16_BITQ 3
|
||||||
|
#define SOXR_20_BITQ 4
|
||||||
|
#define SOXR_24_BITQ 5
|
||||||
|
#define SOXR_28_BITQ 6
|
||||||
|
#define SOXR_32_BITQ 7
|
||||||
|
/* Libsamplerate equivalent qualities: */
|
||||||
|
#define SOXR_LSR0Q 8 /* 'Best sinc'. */
|
||||||
|
#define SOXR_LSR1Q 9 /* 'Medium sinc'. */
|
||||||
|
#define SOXR_LSR2Q 10 /* 'Fast sinc'. */
|
||||||
|
|
||||||
|
#define SOXR_LINEAR_PHASE 0x00
|
||||||
|
#define SOXR_INTERMEDIATE_PHASE 0x10
|
||||||
|
#define SOXR_MINIMUM_PHASE 0x30
|
||||||
|
#define SOXR_STEEP_FILTER 0x40
|
||||||
|
#define SOXR_ALLOW_ALIASING 0x80 /* Reserved for future use. */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
SOXR soxr_runtime_spec_t soxr_runtime_spec(
|
||||||
|
unsigned num_threads);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
SOXR soxr_io_spec_t soxr_io_spec(
|
||||||
|
soxr_datatype_t itype,
|
||||||
|
soxr_datatype_t otype);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* --------------------------- Advanced use only ---------------------------- */
|
||||||
|
|
||||||
|
/* For new designs, the following functions/usage will probably not be needed.
|
||||||
|
* They might be useful when adding soxr into an existing design where values
|
||||||
|
* for the resampling-rate and/or number-of-channels parameters to soxr_create
|
||||||
|
* are not available when that function will be called. In such cases, the
|
||||||
|
* relevant soxr_create parameter(s) can be given as 0, then one or both of the
|
||||||
|
* following (as appropriate) later invoked (but prior to calling soxr_process
|
||||||
|
* or soxr_output):
|
||||||
|
*
|
||||||
|
* soxr_set_error(soxr, soxr_set_io_ratio(soxr, io_ratio, 0));
|
||||||
|
* soxr_set_error(soxr, soxr_set_num_channels(soxr, num_channels));
|
||||||
|
*/
|
||||||
|
|
||||||
|
SOXR soxr_error_t soxr_set_error(soxr_t, soxr_error_t);
|
||||||
|
SOXR soxr_error_t soxr_set_num_channels(soxr_t, unsigned);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#undef SOXR
|
||||||
|
|
||||||
|
#if defined __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
BIN
components/codecs/lib/libsoxr.a
Normal file
BIN
components/codecs/lib/libsoxr.a
Normal file
Binary file not shown.
@@ -99,7 +99,6 @@ menu "Squeezelite-ESP32"
|
|||||||
help
|
help
|
||||||
Include FAAD library for aac decoding.
|
Include FAAD library for aac decoding.
|
||||||
config INCLUDE_MAD
|
config INCLUDE_MAD
|
||||||
depends on SPIRAM_SUPPORT
|
|
||||||
bool "MAD"
|
bool "MAD"
|
||||||
default 1
|
default 1
|
||||||
help
|
help
|
||||||
|
|||||||
@@ -329,14 +329,14 @@ static decode_state alac_decode(void) {
|
|||||||
bytes = min(_buf_used(streambuf), _buf_cont_read(streambuf));
|
bytes = min(_buf_used(streambuf), _buf_cont_read(streambuf));
|
||||||
|
|
||||||
LOG_INFO("setting track_start");
|
LOG_INFO("setting track_start");
|
||||||
LOCK_O_not_direct;
|
LOCK_O;
|
||||||
|
|
||||||
output.next_sample_rate = decode_newstream(l->sample_rate, output.supported_rates);
|
output.next_sample_rate = decode_newstream(l->sample_rate, output.supported_rates);
|
||||||
output.track_start = outputbuf->writep;
|
output.track_start = outputbuf->writep;
|
||||||
if (output.fade_mode) _checkfade(true);
|
if (output.fade_mode) _checkfade(true);
|
||||||
decode.new_stream = false;
|
decode.new_stream = false;
|
||||||
|
|
||||||
UNLOCK_O_not_direct;
|
UNLOCK_O;
|
||||||
} else if (found == -1) {
|
} else if (found == -1) {
|
||||||
LOG_WARN("[%p]: error reading stream header");
|
LOG_WARN("[%p]: error reading stream header");
|
||||||
UNLOCK_S;
|
UNLOCK_S;
|
||||||
|
|||||||
@@ -2,12 +2,13 @@
|
|||||||
# "main" pseudo-component makefile.
|
# "main" pseudo-component makefile.
|
||||||
#
|
#
|
||||||
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
|
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
|
||||||
CFLAGS += -O3 -DPOSIX -DLINKALL -DLOOPBACK -DNO_FAAD -DEMBEDDED -DTREMOR_ONLY -DBYTES_PER_FRAME=4 \
|
CFLAGS += -O3 -DPOSIX -DLINKALL -DLOOPBACK -DNO_FAAD -DRESAMPLE -DEMBEDDED -DTREMOR_ONLY -DBYTES_PER_FRAME=4 \
|
||||||
-I$(COMPONENT_PATH)/../components/codecs/inc \
|
-I$(COMPONENT_PATH)/../components/codecs/inc \
|
||||||
-I$(COMPONENT_PATH)/../components/codecs/inc/mad \
|
-I$(COMPONENT_PATH)/../components/codecs/inc/mad \
|
||||||
-I$(COMPONENT_PATH)/../components/codecs/inc/alac \
|
-I$(COMPONENT_PATH)/../components/codecs/inc/alac \
|
||||||
-I$(COMPONENT_PATH)/../components/codecs/inc/helix-aac \
|
-I$(COMPONENT_PATH)/../components/codecs/inc/helix-aac \
|
||||||
-I$(COMPONENT_PATH)/../components/codecs/inc/vorbis \
|
-I$(COMPONENT_PATH)/../components/codecs/inc/vorbis \
|
||||||
|
-I$(COMPONENT_PATH)/../components/codecs/inc/soxr \
|
||||||
-I$(COMPONENT_PATH)/../components/platform_esp32
|
-I$(COMPONENT_PATH)/../components/platform_esp32
|
||||||
|
|
||||||
LDFLAGS += -s
|
LDFLAGS += -s
|
||||||
|
|||||||
194
main/process.c
Normal file
194
main/process.c
Normal file
@@ -0,0 +1,194 @@
|
|||||||
|
/*
|
||||||
|
* Squeezelite - lightweight headless squeezebox emulator
|
||||||
|
*
|
||||||
|
* (c) Adrian Smith 2012-2015, triode1@btinternet.com
|
||||||
|
* Ralph Irving 2015-2017, ralph_irving@hotmail.com
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// sample processing - only included when building with PROCESS set
|
||||||
|
|
||||||
|
#include "squeezelite.h"
|
||||||
|
|
||||||
|
#if PROCESS
|
||||||
|
|
||||||
|
extern log_level loglevel;
|
||||||
|
|
||||||
|
extern struct buffer *outputbuf;
|
||||||
|
extern struct decodestate decode;
|
||||||
|
struct processstate process;
|
||||||
|
extern struct codec *codec;
|
||||||
|
|
||||||
|
#define LOCK_D mutex_lock(decode.mutex);
|
||||||
|
#define UNLOCK_D mutex_unlock(decode.mutex);
|
||||||
|
#define LOCK_O mutex_lock(outputbuf->mutex)
|
||||||
|
#define UNLOCK_O mutex_unlock(outputbuf->mutex)
|
||||||
|
|
||||||
|
// macros to map to processing functions - currently only resample.c
|
||||||
|
// this can be made more generic when multiple processing mechanisms get added
|
||||||
|
#if RESAMPLE
|
||||||
|
#define SAMPLES_FUNC resample_samples
|
||||||
|
#define DRAIN_FUNC resample_drain
|
||||||
|
#define NEWSTREAM_FUNC resample_newstream
|
||||||
|
#define FLUSH_FUNC resample_flush
|
||||||
|
#define INIT_FUNC resample_init
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
// transfer all processed frames to the output buf
|
||||||
|
static void _write_samples(void) {
|
||||||
|
frames_t frames = process.out_frames;
|
||||||
|
ISAMPLE_T *iptr = (ISAMPLE_T *) process.outbuf;
|
||||||
|
unsigned cnt = 10;
|
||||||
|
|
||||||
|
LOCK_O;
|
||||||
|
|
||||||
|
while (frames > 0) {
|
||||||
|
|
||||||
|
frames_t f = min(_buf_space(outputbuf), _buf_cont_write(outputbuf)) / BYTES_PER_FRAME;
|
||||||
|
ISAMPLE_T *optr = (ISAMPLE_T*) outputbuf->writep;
|
||||||
|
|
||||||
|
if (f > 0) {
|
||||||
|
|
||||||
|
f = min(f, frames);
|
||||||
|
|
||||||
|
memcpy(optr, iptr, f * BYTES_PER_FRAME);
|
||||||
|
|
||||||
|
frames -= f;
|
||||||
|
|
||||||
|
_buf_inc_writep(outputbuf, f * BYTES_PER_FRAME);
|
||||||
|
iptr += f * BYTES_PER_FRAME / sizeof(*iptr);
|
||||||
|
|
||||||
|
} else if (cnt--) {
|
||||||
|
|
||||||
|
// there should normally be space in the output buffer, but may need to wait during drain phase
|
||||||
|
UNLOCK_O;
|
||||||
|
usleep(10000);
|
||||||
|
LOCK_O;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// bail out if no space found after 100ms to avoid locking
|
||||||
|
LOG_ERROR("unable to get space in output buffer");
|
||||||
|
UNLOCK_O;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UNLOCK_O;
|
||||||
|
}
|
||||||
|
|
||||||
|
// process samples - called with decode mutex set
|
||||||
|
void process_samples(void) {
|
||||||
|
|
||||||
|
SAMPLES_FUNC(&process);
|
||||||
|
|
||||||
|
_write_samples();
|
||||||
|
|
||||||
|
process.in_frames = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// drain at end of track - called with decode mutex set
|
||||||
|
void process_drain(void) {
|
||||||
|
bool done;
|
||||||
|
|
||||||
|
do {
|
||||||
|
|
||||||
|
done = DRAIN_FUNC(&process);
|
||||||
|
|
||||||
|
_write_samples();
|
||||||
|
|
||||||
|
} while (!done);
|
||||||
|
|
||||||
|
LOG_DEBUG("processing track complete - frames in: %lu out: %lu", process.total_in, process.total_out);
|
||||||
|
}
|
||||||
|
|
||||||
|
// new stream - called with decode mutex set
|
||||||
|
unsigned process_newstream(bool *direct, unsigned raw_sample_rate, unsigned supported_rates[]) {
|
||||||
|
|
||||||
|
bool active = NEWSTREAM_FUNC(&process, raw_sample_rate, supported_rates);
|
||||||
|
|
||||||
|
LOG_INFO("processing: %s", active ? "active" : "inactive");
|
||||||
|
|
||||||
|
*direct = !active;
|
||||||
|
|
||||||
|
if (active) {
|
||||||
|
|
||||||
|
unsigned max_in_frames, max_out_frames;
|
||||||
|
|
||||||
|
process.in_frames = process.out_frames = 0;
|
||||||
|
process.total_in = process.total_out = 0;
|
||||||
|
|
||||||
|
max_in_frames = codec->min_space / BYTES_PER_FRAME ;
|
||||||
|
|
||||||
|
// increase size of output buffer by 10% as output rate is not an exact multiple of input rate
|
||||||
|
if (process.out_sample_rate % process.in_sample_rate == 0) {
|
||||||
|
max_out_frames = max_in_frames * (process.out_sample_rate / process.in_sample_rate);
|
||||||
|
} else {
|
||||||
|
max_out_frames = (int)(1.1 * (float)max_in_frames * (float)process.out_sample_rate / (float)process.in_sample_rate);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (process.max_in_frames != max_in_frames) {
|
||||||
|
LOG_DEBUG("creating process buf in frames: %u", max_in_frames);
|
||||||
|
if (process.inbuf) free(process.inbuf);
|
||||||
|
process.inbuf = malloc(max_in_frames * BYTES_PER_FRAME);
|
||||||
|
process.max_in_frames = max_in_frames;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (process.max_out_frames != max_out_frames) {
|
||||||
|
LOG_DEBUG("creating process buf out frames: %u", max_out_frames);
|
||||||
|
if (process.outbuf) free(process.outbuf);
|
||||||
|
process.outbuf = malloc(max_out_frames * BYTES_PER_FRAME);
|
||||||
|
process.max_out_frames = max_out_frames;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!process.inbuf || !process.outbuf) {
|
||||||
|
LOG_ERROR("malloc fail creating process buffers");
|
||||||
|
*direct = true;
|
||||||
|
return raw_sample_rate;
|
||||||
|
}
|
||||||
|
|
||||||
|
return process.out_sample_rate;
|
||||||
|
}
|
||||||
|
|
||||||
|
return raw_sample_rate;
|
||||||
|
}
|
||||||
|
|
||||||
|
// process flush - called with decode mutex set
|
||||||
|
void process_flush(void) {
|
||||||
|
|
||||||
|
LOG_INFO("process flush");
|
||||||
|
|
||||||
|
FLUSH_FUNC();
|
||||||
|
|
||||||
|
process.in_frames = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// init - called with no mutex
|
||||||
|
void process_init(char *opt) {
|
||||||
|
|
||||||
|
bool enabled = INIT_FUNC(opt);
|
||||||
|
|
||||||
|
memset(&process, 0, sizeof(process));
|
||||||
|
|
||||||
|
if (enabled) {
|
||||||
|
LOCK_D;
|
||||||
|
decode.process = true;
|
||||||
|
UNLOCK_D;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // #if PROCESS
|
||||||
377
main/resample.c
Normal file
377
main/resample.c
Normal file
@@ -0,0 +1,377 @@
|
|||||||
|
/*
|
||||||
|
* Squeezelite - lightweight headless squeezebox emulator
|
||||||
|
*
|
||||||
|
* (c) Adrian Smith 2012-2015, triode1@btinternet.com
|
||||||
|
* Ralph Irving 2015-2017, ralph_irving@hotmail.com
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// upsampling using libsoxr - only included if RESAMPLE set
|
||||||
|
|
||||||
|
#include "squeezelite.h"
|
||||||
|
|
||||||
|
#if RESAMPLE
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
#include <soxr.h>
|
||||||
|
|
||||||
|
extern log_level loglevel;
|
||||||
|
|
||||||
|
struct soxr {
|
||||||
|
soxr_t resampler;
|
||||||
|
size_t old_clips;
|
||||||
|
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;
|
||||||
|
#if !LINKALL
|
||||||
|
// soxr symbols to be dynamically loaded
|
||||||
|
soxr_io_spec_t (* soxr_io_spec)(soxr_datatype_t itype, soxr_datatype_t otype);
|
||||||
|
soxr_quality_spec_t (* soxr_quality_spec)(unsigned long recipe, unsigned long flags);
|
||||||
|
soxr_t (* soxr_create)(double, double, unsigned, soxr_error_t *,
|
||||||
|
soxr_io_spec_t const *, soxr_quality_spec_t const *, soxr_runtime_spec_t const *);
|
||||||
|
void (* soxr_delete)(soxr_t);
|
||||||
|
soxr_error_t (* soxr_process)(soxr_t, soxr_in_t, size_t, size_t *, soxr_out_t, size_t olen, size_t *);
|
||||||
|
size_t *(* soxr_num_clips)(soxr_t);
|
||||||
|
#if RESAMPLE_MP
|
||||||
|
soxr_runtime_spec_t (* soxr_runtime_spec)(unsigned num_threads);
|
||||||
|
#endif
|
||||||
|
// soxr_strerror is a macro so not included here
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct soxr *r;
|
||||||
|
|
||||||
|
#if LINKALL
|
||||||
|
#define SOXR(h, fn, ...) (soxr_ ## fn)(__VA_ARGS__)
|
||||||
|
#else
|
||||||
|
#define SOXR(h, fn, ...) (h)->soxr_##fn(__VA_ARGS__)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
void resample_samples(struct processstate *process) {
|
||||||
|
size_t idone, odone;
|
||||||
|
size_t clip_cnt;
|
||||||
|
|
||||||
|
soxr_error_t error =
|
||||||
|
SOXR(r, process, r->resampler, process->inbuf, process->in_frames, &idone, process->outbuf, process->max_out_frames, &odone);
|
||||||
|
if (error) {
|
||||||
|
LOG_INFO("soxr_process error: %s", soxr_strerror(error));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (idone != process->in_frames) {
|
||||||
|
// should not get here if buffers are big enough...
|
||||||
|
LOG_ERROR("should not get here - partial sox process: %u of %u processed %u of %u out",
|
||||||
|
(unsigned)idone, process->in_frames, (unsigned)odone, process->max_out_frames);
|
||||||
|
}
|
||||||
|
|
||||||
|
process->out_frames = odone;
|
||||||
|
process->total_in += idone;
|
||||||
|
process->total_out += odone;
|
||||||
|
|
||||||
|
clip_cnt = *(SOXR(r, num_clips, r->resampler));
|
||||||
|
if (clip_cnt - r->old_clips) {
|
||||||
|
LOG_SDEBUG("resampling clips: %u", (unsigned)(clip_cnt - r->old_clips));
|
||||||
|
r->old_clips = clip_cnt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool resample_drain(struct processstate *process) {
|
||||||
|
size_t odone;
|
||||||
|
size_t clip_cnt;
|
||||||
|
|
||||||
|
soxr_error_t error = SOXR(r, process, r->resampler, NULL, 0, NULL, process->outbuf, process->max_out_frames, &odone);
|
||||||
|
if (error) {
|
||||||
|
LOG_INFO("soxr_process error: %s", soxr_strerror(error));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
process->out_frames = odone;
|
||||||
|
process->total_out += odone;
|
||||||
|
|
||||||
|
clip_cnt = *(SOXR(r, num_clips, r->resampler));
|
||||||
|
if (clip_cnt - r->old_clips) {
|
||||||
|
LOG_DEBUG("resampling clips: %u", (unsigned)(clip_cnt - r->old_clips));
|
||||||
|
r->old_clips = clip_cnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (odone == 0) {
|
||||||
|
|
||||||
|
LOG_INFO("resample track complete - total track clips: %u", r->old_clips);
|
||||||
|
|
||||||
|
SOXR(r, delete, r->resampler);
|
||||||
|
r->resampler = NULL;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool resample_newstream(struct processstate *process, unsigned raw_sample_rate, unsigned supported_rates[]) {
|
||||||
|
unsigned outrate = 0;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (r->exception) {
|
||||||
|
// find direct match - avoid resampling
|
||||||
|
for (i = 0; supported_rates[i]; i++) {
|
||||||
|
if (raw_sample_rate == supported_rates[i]) {
|
||||||
|
outrate = raw_sample_rate;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// else find next highest sync sample rate
|
||||||
|
while (!outrate && i >= 0) {
|
||||||
|
if (supported_rates[i] > raw_sample_rate && supported_rates[i] % raw_sample_rate == 0) {
|
||||||
|
outrate = supported_rates[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
i--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!outrate) {
|
||||||
|
if (r->max_rate) {
|
||||||
|
// resample to max rate for device
|
||||||
|
outrate = supported_rates[0];
|
||||||
|
} else {
|
||||||
|
// resample to max sync sample rate
|
||||||
|
for (i = 0; supported_rates[i]; i++) {
|
||||||
|
if (supported_rates[i] % raw_sample_rate == 0 || raw_sample_rate % supported_rates[i] == 0) {
|
||||||
|
outrate = supported_rates[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!outrate) {
|
||||||
|
outrate = supported_rates[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
process->in_sample_rate = raw_sample_rate;
|
||||||
|
process->out_sample_rate = outrate;
|
||||||
|
|
||||||
|
if (r->resampler) {
|
||||||
|
SOXR(r, delete, r->resampler);
|
||||||
|
r->resampler = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (raw_sample_rate != outrate) {
|
||||||
|
|
||||||
|
soxr_io_spec_t io_spec;
|
||||||
|
soxr_quality_spec_t q_spec;
|
||||||
|
soxr_error_t error;
|
||||||
|
#if RESAMPLE_MP
|
||||||
|
soxr_runtime_spec_t r_spec;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
LOG_INFO("resampling from %u -> %u", raw_sample_rate, outrate);
|
||||||
|
|
||||||
|
#if BYTES_PER_FRAME == 4
|
||||||
|
io_spec = SOXR(r, io_spec, SOXR_INT16_I, SOXR_INT16_I);
|
||||||
|
#else
|
||||||
|
io_spec = SOXR(r, io_spec, SOXR_INT32_I, SOXR_INT32_I);
|
||||||
|
#endif
|
||||||
|
io_spec.scale = r->scale;
|
||||||
|
|
||||||
|
q_spec = SOXR(r, quality_spec, r->q_recipe, r->q_flags);
|
||||||
|
if (r->q_precision > 0) {
|
||||||
|
q_spec.precision = r->q_precision;
|
||||||
|
}
|
||||||
|
if (r->q_passband_end > 0) {
|
||||||
|
q_spec.passband_end = r->q_passband_end;
|
||||||
|
}
|
||||||
|
if (r->q_stopband_begin > 0) {
|
||||||
|
q_spec.stopband_begin = r->q_stopband_begin;
|
||||||
|
}
|
||||||
|
if (r->q_phase_response > -1) {
|
||||||
|
q_spec.phase_response = r->q_phase_response;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if RESAMPLE_MP
|
||||||
|
r_spec = SOXR(r, runtime_spec, 0); // make use of libsoxr OpenMP support allowing parallel execution if multiple cores
|
||||||
|
#endif
|
||||||
|
|
||||||
|
LOG_DEBUG("resampling with soxr_quality_spec_t[precision: %03.1f, passband_end: %03.6f, stopband_begin: %03.6f, "
|
||||||
|
"phase_response: %03.1f, flags: 0x%02x], soxr_io_spec_t[scale: %03.2f]", q_spec.precision,
|
||||||
|
q_spec.passband_end, q_spec.stopband_begin, q_spec.phase_response, q_spec.flags, io_spec.scale);
|
||||||
|
|
||||||
|
#if RESAMPLE_MP
|
||||||
|
r->resampler = SOXR(r, create, raw_sample_rate, outrate, 2, &error, &io_spec, &q_spec, &r_spec);
|
||||||
|
#else
|
||||||
|
r->resampler = SOXR(r, create, raw_sample_rate, outrate, 2, &error, &io_spec, &q_spec, NULL);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
LOG_INFO("soxr_create error: %s", soxr_strerror(error));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
r->old_clips = 0;
|
||||||
|
return true;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
LOG_INFO("disable resampling - rates match");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void resample_flush(void) {
|
||||||
|
if (r->resampler) {
|
||||||
|
SOXR(r, delete, r->resampler);
|
||||||
|
r->resampler = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool load_soxr(void) {
|
||||||
|
#if !LINKALL
|
||||||
|
void *handle = dlopen(LIBSOXR, RTLD_NOW);
|
||||||
|
char *err;
|
||||||
|
|
||||||
|
if (!handle) {
|
||||||
|
LOG_INFO("dlerror: %s", dlerror());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
r->soxr_io_spec = dlsym(handle, "soxr_io_spec");
|
||||||
|
r->soxr_quality_spec = dlsym(handle, "soxr_quality_spec");
|
||||||
|
r->soxr_create = dlsym(handle, "soxr_create");
|
||||||
|
r->soxr_delete = dlsym(handle, "soxr_delete");
|
||||||
|
r->soxr_process = dlsym(handle, "soxr_process");
|
||||||
|
r->soxr_num_clips = dlsym(handle, "soxr_num_clips");
|
||||||
|
#if RESAMPLE_MP
|
||||||
|
r->soxr_runtime_spec = dlsym(handle, "soxr_runtime_spec");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if ((err = dlerror()) != NULL) {
|
||||||
|
LOG_INFO("dlerror: %s", err);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_INFO("loaded "LIBSOXR);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool resample_init(char *opt) {
|
||||||
|
char *recipe = NULL, *flags = NULL;
|
||||||
|
char *atten = NULL;
|
||||||
|
char *precision = NULL, *passband_end = NULL, *stopband_begin = NULL, *phase_response = NULL;
|
||||||
|
|
||||||
|
r = malloc(sizeof(struct soxr));
|
||||||
|
if (!r) {
|
||||||
|
LOG_WARN("resampling disabled");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
r->resampler = NULL;
|
||||||
|
r->old_clips = 0;
|
||||||
|
r->max_rate = false;
|
||||||
|
r->exception = false;
|
||||||
|
|
||||||
|
if (!load_soxr()) {
|
||||||
|
LOG_WARN("resampling disabled");
|
||||||
|
return 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, ':');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#if BYTES_PER_FRAME == 4
|
||||||
|
// default to LQ (16 bit) if not user specified
|
||||||
|
r->q_recipe = SOXR_LQ;
|
||||||
|
#else
|
||||||
|
// default to HQ (20 bit) if not user specified
|
||||||
|
r->q_recipe = SOXR_HQ;
|
||||||
|
#endif
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_INFO("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",
|
||||||
|
r->q_recipe, r->q_flags, r->scale, r->q_precision, r->q_passband_end, r->q_stopband_begin, r->q_phase_response);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // #if RESAMPLE
|
||||||
Reference in New Issue
Block a user