| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427 |
- ////////////////////////////////////////////////////////////////////////////
- // **** RESAMPLER **** //
- // Sinc-based Audio Resampling //
- // Copyright (c) 2006 - 2023 David Bryant. //
- // All Rights Reserved. //
- // Distributed under the BSD Software License (see license.txt) //
- ////////////////////////////////////////////////////////////////////////////
- // resampler.c
- #include "resampler.h"
- #ifdef ENABLE_EXTRAPOLATION
- #include "extrapolator.h"
- #endif
- static void init_filter(Resample *cxt, artsample_t *filter, double fraction);
- static double subsample_no_interpolate_precise(Resample *cxt,
- artsample_t *source,
- double offset);
- static double subsample_interpolate_precise(Resample *cxt, artsample_t *source,
- double offset);
- static double subsample_no_interpolate(Resample *cxt, artsample_t *source,
- double offset);
- static double subsample_interpolate(Resample *cxt, artsample_t *source,
- double offset);
- static unsigned long gcd(unsigned long a, unsigned long b);
- // There are now two functions to initialize a resampler context. The legacy
- // version is for arbitrary resampling operations with no fixed ratio (i.e.,
- // ASRCs), and its API is unchanged from previous versions of the resampler. The
- // resampler initially targeted this use case.
- //
- // A new version of the initialization function, resamplerFixedRatioInit(), is
- // for fixed-ratio sample rate conversions (i.e., conversions from one specific
- // sample rate to another) with no fractional phase shift. This version can
- // provide significantly improved performance and accuracy for such conversions.
- // See the new function's description below.
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- // Initialize a resampler context with the specified characteristics. The
- // returned context pointer is used for all subsequent calls to the resampler
- // (and should not be dereferenced). A NULL return indicates an error. For the
- // flags parameter, note that SUBSAMPLE_INTERPOLATE and BLACKMAN_HARRIS are
- // recommended for most applications. This function assumes that the "ratio"
- // parameter will be appropriately specified for every call to the resampler.
- // The parameters are:
- //
- // numChannels: the number of audio channels present
- //
- // numTaps: the number of taps for each sinc interpolation filter
- // - must be a multiple of 4, from 4 - 1024 taps
- // - affects quality by controlling cutoff sharpness of
- // filters
- // - linearly affects memory usage and CPU load of resampling
- //
- // numFilters: the number of sinc filters generated
- // - must be 1 - 1024
- // - affects quality of interpolated filtering
- // - linearly affects memory usage of resampler
- //
- // lowpassRatio: enable lowpass by specifying the ratio relative to the
- // Nyquist frequency of
- // the *input* samples (must be > 0.0 and < 1.0); required for
- // quality downsampling (except in special cases), but is
- // optional otherwise ex: lowpassRatio = lowpass freq in Hz /
- // (source rate in Hz / 2.0)
- //
- // flags: mask for optional feature configuration:
- //
- // SUBSAMPLE_INTERPOLATE: interpolate values from adjacent filters
- // - generally recommended except in special
- // situations
- // - approximately doubles the CPU load
- //
- // BLACKMAN_HARRIS: use 4-term Blackman Harris window function
- // - generally recommended except in special
- // situations
- // - if not specified, the default window is Hann
- // (raised cosine)
- // which has steeper cutoff but poorer stopband
- // rejection
- //
- // RESAMPLE_MULTITHREADED: use multiple threads for processing multiple
- // channels in parallel
- // - might be slower depending on CPU,
- // optimization, caching, etc.
- // - optional (define ENABLE_THREADS)
- //
- //
- // EXTRAPOLATE_ENDPOINTS enable sample extrapolation at the beginning and
- // end of conversion
- // - to use this properly, the resampleAdvance()
- // function must be
- // called BEFORE sending any data to the
- // resampler to delay the initial conversion
- // (otherwise samples will be generated
- // immediately when there aren't enough samples
- // to extrapolate)
- // - also, the new "flush" functionality must be
- // used instead of
- // just sending zeros to the resampler to
- // perform a final flush (although this should
- // be obvious)
- // - optional (define ENABLE_EXTRAPOLATION)
- //
- // EXTEND_CONVOLUTION_MATH use precise math in convolution (doubles, not
- // floats)
- // - this can improve resampling quality, but
- // just a few dB at best
- // - can result in significant performance hit on
- // some platforms
- // - might be worth it, but generally not
- // recommended
- // - non-operational when the 64-bit path
- // selected
- // Notes:
- //
- // 1. The same resampling instance can be used for upsampling, downsampling, or
- // simple (near-unity)
- // resampling (e.g., for asynchronous sample rate conversion, phase shifting,
- // or filtering). The behavior is controlled by the "ratio" parameter during
- // the actual resample processing call. To prevent aliasing, it's important
- // to specify a lowpassRatio if the resampler will be used for any
- // significant degree of downsampling (ratio < 1.0), but the lowpass can also
- // be used independently without any rate conversion (or even when
- // upsampling).
- //
- // 2. When the context is initialized (or reset) the sample histories are filled
- // with silence such
- // that the resampler is ready to generate output immediately. However, this
- // also means that there is an implicit signal delay equal to half the tap
- // length of the sinc filters in samples. If zero delay is desired then that
- // many samples can be ignored, or the resampleAdvancePosition() function can
- // be used to bypass them. Also, at the end of processing an equal length of
- // silence must be appended to the input audio to align the output with the
- // actual input.
- //
- // 3. Both the number of interpolation filters and the number of taps per filter
- // directly control
- // the fidelity of the resampling. The filter length has an approximately
- // linear affect on the the CPU load consumed by the resampler, and also on
- // the memory requirement (both for the filters themselves and also for the
- // sample history storage). On the other hand, the number of filters
- // allocated primarily affects just the memory footprint (it has little
- // affect on CPU load and so can be large on systems with lots of RAM).
- //
- // 4. For a fixed-ratio conversion (i.e., conversions from one specific sample
- // rate to another) with
- // no fractional phase shift, see resampleFixedRatioInit() below.
- Resample *resampleInit(int numChannels, int numTaps, int numFilters,
- double lowpassRatio, int flags) {
- Resample *cxt = calloc(1, sizeof(Resample));
- int i;
- if (lowpassRatio > 0.0 && lowpassRatio < 1.0) {
- flags |= INCLUDE_LOWPASS;
- } else {
- flags &= ~INCLUDE_LOWPASS;
- lowpassRatio = 1.0;
- }
- if ((numTaps & 3) || numTaps <= 0 || numTaps > 1024) {
- fprintf(stderr, "must 4-1024 filter taps, and a multiple of 4!\n");
- return NULL;
- }
- if (numFilters < 1 || numFilters > 1024) {
- fprintf(stderr, "must be 1-1024 filters!\n");
- return NULL;
- }
- cxt->lowpassRatio = lowpassRatio;
- cxt->numChannels = numChannels;
- cxt->numSamples = numTaps * 16;
- cxt->numFilters = numFilters;
- cxt->numTaps = numTaps;
- cxt->flags = flags;
- // note that we actually have one more than the specified number of filters
- cxt->filters =
- (artsample_t **)calloc(cxt->numFilters + 1, sizeof(artsample_t *));
- cxt->tempFilter = malloc(numTaps * sizeof(double));
- for (i = 0; i <= cxt->numFilters; ++i) {
- int j;
- cxt->filters[i] = calloc(cxt->numTaps, sizeof(artsample_t));
- if (i < cxt->numFilters) {
- init_filter(cxt, cxt->filters[i], (double)i / cxt->numFilters);
- } else {
- // the last filter is essentially identical to the first (just offset one
- // tap)
- for (j = 0; j < cxt->numTaps; ++j) {
- cxt->filters[cxt->numFilters][(j + 1) % cxt->numTaps] =
- cxt->filters[0][j];
- }
- }
- }
- // The first and last filters should actually have just an odd number of taps,
- // but Blackman-Harris doesn't go all the way to zero, so clear the outliers
- // here. This almost eliminates the tiny discrepancies caused by different
- // processing chunk sizes (those remaining come from math errors and
- // rounding).
- cxt->filters[0][cxt->numTaps - 1] = (artsample_t)0.0;
- cxt->filters[cxt->numFilters][0] = (artsample_t)0.0;
- free(cxt->tempFilter);
- cxt->tempFilter = NULL;
- cxt->buffers = (artsample_t **)calloc(numChannels, sizeof(artsample_t *));
- for (i = 0; i < numChannels; ++i) {
- cxt->buffers[i] = calloc(cxt->numSamples, sizeof(artsample_t));
- }
- cxt->outputOffset = numTaps / 2.0;
- cxt->inputIndex = numTaps;
- #ifdef ENABLE_EXTRAPOLATION
- if (cxt->flags & EXTRAPOLATE_ENDPOINTS) {
- cxt->flags |= EXTRAPOLATE_PREFILL;
- }
- #endif
- #ifdef ENABLE_THREADS
- if (numChannels > 1 && (flags & RESAMPLE_MULTITHREADED)) {
- cxt->workers = workersInit(numChannels);
- }
- #endif
- // extended math only makes sense if we have a 32-bit path
- if (sizeof(artsample_t) == 4 && (cxt->flags & EXTEND_CONVOLUTION_MATH)) {
- cxt->subsample = (cxt->flags & SUBSAMPLE_INTERPOLATE)
- ? subsample_interpolate_precise
- : subsample_no_interpolate_precise;
- } else {
- cxt->subsample = (cxt->flags & SUBSAMPLE_INTERPOLATE)
- ? subsample_interpolate
- : subsample_no_interpolate;
- }
- return cxt;
- }
- // Initialize a resampler context with the specified characteristics. The
- // returned context pointer is used for all subsequent calls to the resampler
- // (and should not be dereferenced). A NULL return indicates an error. For the
- // flags parameter, note that SUBSAMPLE_INTERPOLATE, BLACKMAN_HARRIS, and
- // INCLUDE_LOWPASS are all recommended for most applications.
- //
- // This function will determine whether the specified fixed-ratio conversion
- // operation is possible with a reduced number of filters that can be used
- // directly without interpolation (i.e., every calculation exactly aligns to a
- // single filter). If this is possible then several advantages result. First,
- // fewer filters are required which reduces the memory footprint. Also, the
- // performance is approximately doubled due to the elimination of the
- // interpolation step. And finally, the numerical accuracy of the resampling is
- // improved, also due to the lack of interpolation inaccuracies. Note that
- // subsample phase shifts are not allowed in this mode, so if these are required
- // set the NO_FILTER_REDUCTION bit in the flags to prevent this optimization.
- //
- // If the number of filters cannot be reduced because the sample rates are not
- // sufficiently related for the max number of filters specified, then that
- // specified maximum filter count will be used with the specfied interpolation
- // mode (recommended to be ON). If the number filters is reduced, then the
- // supplied interpolation mode flag is ignored.
- //
- // When using this version there is also the ability of the resampler to choose
- // an optimum lowpass cutoff frequency for downsampling operations only based on
- // the sampling rates and the number of filter taps. Simply set the
- // INCLUDE_LOWPASS bit in the flags parameter and set the lowpassFreq parameter
- // to zero. This is generally recommended because it does not introduce a
- // lowpass for upsampling.
- //
- // numChannels: the number of audio channels present
- //
- // numTaps: the number of taps for each sinc interpolation filter
- // - must be a multiple of 4, from 4 - 1024 taps
- // - affects quality by controlling cutoff sharpness of
- // filters
- // - linearly affects memory usage and CPU load of resampling
- //
- // maxFilters: the maximum number of sinc filters allowed
- // - must be 1 - 1024, will be reduced if possible
- // - affects quality of interpolated filtering
- // - linearly affects memory usage of resampler
- //
- // sourceRate: the fixed source and destination sample rates
- // destinRate: - the "ratio" parameter to other resampling functions is
- // ignored
- //
- // lowpassFreq: enable lowpass by specifying a lowpass frequency here
- // - to have the library determine an appropriate lowpass
- // frequency based
- // on the sample rates and filter length, leave this
- // parameter zero and set the INCLUDE_LOWPASS flag bit
- // below
- //
- // flags: mask for optional feature configuration:
- //
- // SUBSAMPLE_INTERPOLATE: interpolate values from adjacent filters
- // - recommended in case the reduced filter
- // determination fails
- // - will be ignored if a reduced filter count is
- // selected
- // - approximately doubles the CPU load (if used)
- //
- // NO_FILTER_REDUCTION: don't allow the automatic reduction of filter
- // count optimization
- // - this prevents the resampler from attempting
- // to reduce the
- // number of filters, which includes disabling
- // interpolation
- // - only necessary if subsample phase-shifts are
- // required, which
- // is not a normal use case for fixed-ratio
- // resampling
- //
- // BLACKMAN_HARRIS: use 4-term Blackman Harris window function
- // - generally recommended except in special
- // situations
- // - if not specified, the default window is Hann
- // (raised cosine)
- // which has steeper cutoff but poorer stopband
- // rejection
- //
- // RESAMPLE_MULTITHREADED: use multiple threads for processing multiple
- // channels in parallel
- // - might be slower depending on CPU,
- // optimization, caching, etc.
- // - optional (define ENABLE_THREADS)
- //
- // INCLUDE_LOWPASS: enable automatic calculation of appropriate
- // lowpass frequency
- // - also set the lowpassFreq parameter (above)
- // to zero
- // - calculation is based on sample rates and
- // filter length
- // - no lowpass is used for upsampling or
- // near-unity resampling,
- // but can always be enabled by setting the
- // lowpassFreq directly
- //
- // EXTRAPOLATE_ENDPOINTS enable sample extrapolation at the beginning and
- // end of conversion
- // - to use this properly, the resampleAdvance()
- // function must be
- // called BEFORE sending any data to the
- // resampler to delay the initial conversion
- // (otherwise samples will be generated
- // immediately when there aren't enough samples
- // to extrapolate)
- // - also, the new "flush" functionality must be
- // used instead of
- // just sending zeros to the resampler to
- // perform a final flush (although this should
- // be obvious)
- // - optional (define ENABLE_EXTRAPOLATION)
- //
- // EXTEND_CONVOLUTION_MATH use precise math in convolution (doubles, not
- // floats)
- // - this can improve resampling quality, but
- // just a few dB at best
- // - can result in significant performance hit on
- // some platforms
- // - might be worth it, but generally not
- // recommended
- // - non-operational when the 64-bit path
- // selected
- // Notes:
- //
- // 1. The resampling instance created by this call can only be used to perform
- // the specified
- // conversion as the "ratio" parameter in the processing functions is
- // ignored. Also subsample phase advances are not allowed.
- //
- // 2. When the context is initialized (or reset) the sample histories are filled
- // with silence such
- // that the resampler is ready to generate output immediately. However, this
- // also means that there is an implicit signal delay equal to half the tap
- // length of the sinc filters in samples. If zero delay is desired then that
- // many samples can be ignored, or the resampleAdvancePosition() function can
- // be used to bypass them. Also, at the end of processing an equal length of
- // silence must be appended to the input audio to align the output with the
- // actual input.
- //
- // 3. The number of taps per filter directly control the fidelity of the
- // resampling. The filter length
- // has an approximately linear affect on the the CPU load consumed by the
- // resampler, and also on the memory requirement (both for the filters
- // themselves and also for the sample history storage). It is assumed that
- // this version of the resampler instance has exactly the optimum number of
- // filters to eliminate the need for interpolation and provide the highest
- // possible accuracy, but otherwise the maximum number of filters specified
- // will be used.
- Resample *resampleFixedRatioInit(int numChannels, int numTaps, int maxFilters,
- double sourceRate, double destinRate,
- int lowpassFreq, int flags) {
- double lowpassRatio = lowpassFreq / (destinRate / 2.0);
- double resampleRatio = destinRate / sourceRate;
- Resample *cxt;
- if (lowpassFreq > destinRate / 2.0) {
- fprintf(stderr,
- "lowpass frequency must be lower than destination Nyquist!\n");
- return NULL;
- }
- // if we can use the exact number of filters for interpolation-free resampling
- // without exceeding the specified limit, do it
- if (sourceRate == floor(sourceRate) && destinRate == floor(destinRate) &&
- !(flags & NO_FILTER_REDUCTION)) {
- unsigned long factor =
- (unsigned long)destinRate /
- gcd((unsigned long)sourceRate, (unsigned long)destinRate);
- if (factor <= maxFilters) {
- flags &= ~SUBSAMPLE_INTERPOLATE;
- maxFilters = (int)factor;
- // if the number of filters is not a power of two, snap to the nearest
- // filter after each buffer
- if (maxFilters & (maxFilters - 1)) {
- flags |= RESAMPLER_SNAP_OFFSET;
- }
- }
- }
- // this is where we calculate an optimized lowpass ratio for the specified
- // rates and filter length current target is around 98 dB attenuation at the
- // Nyquist frequency, assuming a long enough filter
- if (!lowpassFreq && (flags & INCLUDE_LOWPASS) && destinRate < sourceRate) {
- lowpassRatio = 1.0 - (7.5 / numTaps / resampleRatio);
- if (lowpassRatio < 0.8) {
- lowpassRatio = 0.8;
- }
- if (lowpassRatio < resampleRatio) {
- lowpassRatio = resampleRatio;
- }
- }
- cxt =
- resampleInit(numChannels, numTaps, maxFilters,
- lowpassRatio * resampleRatio, flags | RESAMPLE_FIXED_RATIO);
- if (cxt) {
- cxt->fixedRatio = destinRate / sourceRate;
- }
- return cxt;
- }
- // Here are several functions to query the configuration of the resampler. These
- // were not previously required because the configuration was fully specfied by
- // the initialization call, but with the new fixed-ratio initialization some
- // configuration is indeterminate.
- // Note that lowpass ratio is relative to the Nyquist frequency of the *source*
- // sample rate with 1.0 indicating no lowpass configured.
- double resampleGetLowpassRatio(Resample *cxt) {
- return cxt->lowpassRatio;
- }
- int resampleGetNumFilters(Resample *cxt) {
- return cxt->numFilters;
- }
- int resampleInterpolationUsed(Resample *cxt) {
- return cxt->flags & SUBSAMPLE_INTERPOLATE;
- }
- // Reset a resampler context to its initialized state. Specifically, any history
- // is discarded and this should be used when an audio "flush" or other
- // discontinuity occurs.
- void resampleReset(Resample *cxt) {
- int i;
- for (i = 0; i < cxt->numChannels; ++i) {
- memset(cxt->buffers[i], 0, cxt->numSamples * sizeof(artsample_t));
- }
- cxt->outputOffset = cxt->numTaps / 2.0;
- cxt->inputIndex = cxt->numTaps;
- if (cxt->flags & EXTRAPOLATE_ENDPOINTS) {
- cxt->flags |= EXTRAPOLATE_PREFILL;
- }
- cxt->flags &=
- ~RESAMPLER_FLUSHED; // resampler can now be used again after flush
- }
- // Run the resampler context at the specified output ratio and return both the
- // number of input samples consumed and output samples generated (in the
- // ResampleResult structure). Over time the average number of output samples
- // will be equal to the number of input samples multiplied by the given ratio,
- // but of course in a single call only an integer number of samples can be
- // generated. The numInputFrames parameter indicates the number of samples
- // available at "input" and the numOutputFrames indicates the number of samples
- // at "output" that can be written. The resampling proceeds until EITHER the
- // input is exhausted or space at the output is exhausted (there is no other
- // limit).
- //
- // This is the "non-interleaved" version of the resampler where the audio sample
- // buffers for different channels are passed in as an array of float pointers.
- // There is also an "interleaved" version (see below).
- //
- // If this resampler was created with the resampleFixedRatioInit() function,
- // then the "ratio" parameter is ignored and the originally specified conversion
- // is performed.
- //
- // To perform a "flush" operation on the resampler, set the numInputFrames to -1
- // (the input pointer can be NULL in this case). This flush is required to align
- // the output of the resampler, which is delayed by half the sinc filter width,
- // with the input. Previously, this flush alignment was forced by feeding the
- // appropriate number of zeros into the resampler, but now this can be
- // accomplished explicitly, and more cleanly, this way. Also, if sample
- // extrapolation has been selected in the init call (with the
- // EXTRAPOLATE_ENDPOINTS flag), that is also performed during the flush.
- #ifdef ENABLE_THREADS
- static int resampleProcessChannelJob(void *ptr, void *sync_not_used);
- #endif
- #ifdef ENABLE_EXTRAPOLATION
- static void prefillAllChannels(Resample *cxt),
- postfillAllChannels(Resample *cxt);
- #else
- static void postfillAllChannels(Resample *cxt);
- #endif
- ResampleResult resampleProcess(Resample *cxt, const artsample_t *const *input,
- int numInputFrames, artsample_t *const *output,
- int numOutputFrames, double ratio) {
- if (cxt->flags & RESAMPLE_FIXED_RATIO) { // override any supplied ratio if
- // doing fixed ratio resampling
- ratio = cxt->fixedRatio;
- }
- if (cxt->flags &
- RESAMPLER_FLUSHED) { // ignore new input after flush but before reset
- numInputFrames = 0;
- }
- #ifdef ENABLE_THREADS
- if (cxt->workers) {
- Resample *worker_contexts = calloc(cxt->numChannels, sizeof(Resample));
- ResampleResult res = {0, 0};
- int ch;
- for (ch = 0; ch < cxt->numChannels; ++ch) {
- Resample *wcxt = worker_contexts + ch;
- *wcxt = *cxt;
- if (input) {
- wcxt->input = input[ch] - 1;
- }
- wcxt->numInputFrames = numInputFrames;
- wcxt->output = output[ch] - 1;
- wcxt->numOutputFrames = numOutputFrames;
- wcxt->cbuffer = cxt->buffers[ch];
- wcxt->ratio = ratio;
- wcxt->stride = 1;
- wcxt->res = res;
- workersEnqueueJob(cxt->workers, resampleProcessChannelJob, wcxt,
- ch < cxt->numChannels - 1 ? WaitForAvailableWorkerThread
- : DontUseWorkerThread);
- }
- workersWaitAllJobs(cxt->workers);
- res = worker_contexts[0].res;
- *cxt = worker_contexts[0];
- free(worker_contexts);
- return res;
- } else if (cxt->numChannels == 1) {
- if (input) {
- cxt->input = input[0] - 1;
- }
- cxt->numInputFrames = numInputFrames;
- cxt->output = output[0] - 1;
- cxt->numOutputFrames = numOutputFrames;
- cxt->cbuffer = cxt->buffers[0];
- cxt->ratio = ratio;
- cxt->stride = 1;
- cxt->res.output_generated = 0;
- cxt->res.input_used = 0;
- resampleProcessChannelJob(cxt, NULL);
- return cxt->res;
- } else {
- #endif
- int half_taps = cxt->numTaps / 2, i;
- ResampleResult res = {0, 0};
- double offset2 = 0.0;
- if (numInputFrames < 0) { // this is where flush is handled
- postfillAllChannels(cxt);
- }
- while (numOutputFrames > 0) {
- if (cxt->outputOffset + offset2 >= cxt->inputIndex - half_taps) {
- if (numInputFrames > 0) {
- if (cxt->inputIndex == cxt->numSamples) {
- for (i = 0; i < cxt->numChannels; ++i) {
- memmove(cxt->buffers[i],
- cxt->buffers[i] + cxt->numSamples - cxt->numTaps,
- cxt->numTaps * sizeof(artsample_t));
- }
- cxt->outputOffset -= cxt->numSamples - cxt->numTaps;
- cxt->inputIndex -= cxt->numSamples - cxt->numTaps;
- }
- for (i = 0; i < cxt->numChannels; ++i) {
- cxt->buffers[i][cxt->inputIndex] = input[i][res.input_used];
- }
- cxt->inputIndex++;
- res.input_used++;
- numInputFrames--;
- } else {
- break;
- }
- } else {
- #ifdef ENABLE_EXTRAPOLATION
- // if we are extrapolating backwards and haven't yet, now is the time
- if (cxt->flags & EXTRAPOLATE_PREFILL) {
- cxt->flags &= ~EXTRAPOLATE_PREFILL;
- prefillAllChannels(cxt);
- }
- #endif
- for (i = 0; i < cxt->numChannels; ++i) {
- output[i][res.output_generated] = (artsample_t)cxt->subsample(
- cxt, cxt->buffers[i], cxt->outputOffset + offset2);
- }
- offset2 = ++res.output_generated / ratio;
- numOutputFrames--;
- }
- }
- cxt->outputOffset += offset2;
- if (cxt->flags & RESAMPLER_SNAP_OFFSET) {
- cxt->outputOffset = floor(cxt->outputOffset) +
- floor((cxt->outputOffset - floor(cxt->outputOffset)) *
- cxt->numFilters +
- 0.5) /
- cxt->numFilters;
- }
- return res;
- #ifdef ENABLE_THREADS
- }
- #endif
- }
- // This is the "interleaved" version of the resampler where the audio samples
- // for different channels are passed in sequence in a single buffer. There is
- // also a "non-interleaved" version for independent buffers, which is otherwise
- // identical (see above).
- //
- // If this resampler was created with the resampleFixedRatioInit() function,
- // then the "ratio" parameter is ignored and the originally specified conversion
- // is performed.
- ResampleResult resampleProcessInterleaved(Resample *cxt,
- const artsample_t *input,
- int numInputFrames,
- artsample_t *output,
- int numOutputFrames, double ratio) {
- if (cxt->flags & RESAMPLE_FIXED_RATIO) { // override any supplied ratio if
- // doing fixed ratio resampling
- ratio = cxt->fixedRatio;
- }
- if (cxt->flags &
- RESAMPLER_FLUSHED) { // ignore new input after flush but before reset
- numInputFrames = 0;
- }
- #ifdef ENABLE_THREADS
- if (cxt->workers) {
- Resample *worker_contexts = calloc(cxt->numChannels, sizeof(Resample));
- ResampleResult res = {0, 0};
- int ch;
- for (ch = 0; ch < cxt->numChannels; ++ch) {
- Resample *wcxt = worker_contexts + ch;
- *wcxt = *cxt;
- if (input) {
- wcxt->input = input + ch - cxt->numChannels;
- }
- wcxt->numInputFrames = numInputFrames;
- wcxt->output = output + ch - cxt->numChannels;
- wcxt->numOutputFrames = numOutputFrames;
- wcxt->cbuffer = cxt->buffers[ch];
- wcxt->stride = cxt->numChannels;
- wcxt->ratio = ratio;
- wcxt->res = res;
- workersEnqueueJob(cxt->workers, resampleProcessChannelJob, wcxt,
- ch < cxt->numChannels - 1 ? WaitForAvailableWorkerThread
- : DontUseWorkerThread);
- }
- workersWaitAllJobs(cxt->workers);
- res = worker_contexts[0].res;
- *cxt = worker_contexts[0];
- free(worker_contexts);
- return res;
- } else if (cxt->numChannels == 1) {
- if (input) {
- cxt->input = input - 1;
- }
- cxt->numInputFrames = numInputFrames;
- cxt->output = output - 1;
- cxt->numOutputFrames = numOutputFrames;
- cxt->cbuffer = cxt->buffers[0];
- cxt->ratio = ratio;
- cxt->stride = 1;
- cxt->res.output_generated = 0;
- cxt->res.input_used = 0;
- resampleProcessChannelJob(cxt, NULL);
- return cxt->res;
- } else {
- #endif
- int half_taps = cxt->numTaps / 2, i;
- ResampleResult res = {0, 0};
- double offset2 = 0.0;
- if (numInputFrames < 0) { // this is where flush is handled
- postfillAllChannels(cxt);
- }
- while (numOutputFrames > 0) {
- if (cxt->outputOffset + offset2 >= cxt->inputIndex - half_taps) {
- if (numInputFrames > 0) {
- if (cxt->inputIndex == cxt->numSamples) {
- for (i = 0; i < cxt->numChannels; ++i) {
- memmove(cxt->buffers[i],
- cxt->buffers[i] + cxt->numSamples - cxt->numTaps,
- cxt->numTaps * sizeof(artsample_t));
- }
- cxt->outputOffset -= cxt->numSamples - cxt->numTaps;
- cxt->inputIndex -= cxt->numSamples - cxt->numTaps;
- }
- for (i = 0; i < cxt->numChannels; ++i) {
- cxt->buffers[i][cxt->inputIndex] = *input++;
- }
- cxt->inputIndex++;
- res.input_used++;
- numInputFrames--;
- } else {
- break;
- }
- } else {
- #ifdef ENABLE_EXTRAPOLATION
- // if we are extrapolating backwards and haven't yet, now is the time
- if (cxt->flags & EXTRAPOLATE_PREFILL) {
- cxt->flags &= ~EXTRAPOLATE_PREFILL;
- prefillAllChannels(cxt);
- }
- #endif
- for (i = 0; i < cxt->numChannels; ++i) {
- *output++ = (artsample_t)cxt->subsample(cxt, cxt->buffers[i],
- cxt->outputOffset + offset2);
- }
- offset2 = ++res.output_generated / ratio;
- numOutputFrames--;
- }
- }
- cxt->outputOffset += offset2;
- if (cxt->flags & RESAMPLER_SNAP_OFFSET) {
- cxt->outputOffset = floor(cxt->outputOffset) +
- floor((cxt->outputOffset - floor(cxt->outputOffset)) *
- cxt->numFilters +
- 0.5) /
- cxt->numFilters;
- }
- return res;
- #ifdef ENABLE_THREADS
- }
- #endif
- }
- // This is where we flush the resampler by simulating enough input (half the
- // number of filter taps) to align the output. We may fill with zeros, or
- // extrapolate the most recent samples if that option is selected.
- static void postfillAllChannels(Resample *cxt) {
- int c;
- if (cxt->numSamples - cxt->inputIndex < cxt->numTaps / 2) {
- for (c = 0; c < cxt->numChannels; ++c) {
- memmove(cxt->buffers[c], cxt->buffers[c] + cxt->numSamples - cxt->numTaps,
- cxt->numTaps * sizeof(artsample_t));
- }
- cxt->outputOffset -= cxt->numSamples - cxt->numTaps;
- cxt->inputIndex -= cxt->numSamples - cxt->numTaps;
- }
- for (c = 0; c < cxt->numChannels; ++c) {
- memset(cxt->buffers[c] + cxt->inputIndex, 0,
- (cxt->numSamples - cxt->inputIndex) * sizeof(artsample_t));
- #ifdef ENABLE_EXTRAPOLATION
- if (cxt->flags & EXTRAPOLATE_ENDPOINTS) {
- extrapolate_forward(cxt->buffers[c] + cxt->inputIndex - cxt->numTaps / 2,
- cxt->numTaps / 2, cxt->numTaps / 2);
- }
- #endif
- }
- cxt->flags |= RESAMPLER_FLUSHED;
- cxt->inputIndex += cxt->numTaps / 2;
- }
- // Extrapolate the samples received so far back into the sample buffers.
- #ifdef ENABLE_EXTRAPOLATION
- static void prefillAllChannels(Resample *cxt) {
- int num_samples = cxt->inputIndex - cxt->numTaps, c;
- if (num_samples >= 8) {
- for (c = 0; c < cxt->numChannels; ++c) {
- extrapolate_reverse(cxt->buffers[c] + cxt->inputIndex, num_samples,
- cxt->numTaps - num_samples);
- }
- }
- }
- #endif
- // These two convenience functions are extensions of resampleProcess() and
- // resampleProcessInterleaved() that additionally perform the final "flush"
- // operation on the specified resampler. Simply call them for the last block of
- // samples and the "flushed" samples will be appended to the generated output,
- // with the total number of samples generated still indicated by the
- // "output_generated" field of the ResampleResult. Be sure to have enough buffer
- // space available.
- //
- // Using these is equivalent to calling the regular resampling functions with
- // the final block to be resampled and then calling them again with
- // numInputFrames set to -1 to execute the flush. This simply combines the two
- // calls into one.
- ResampleResult resampleProcessAndFlush(Resample *cxt,
- const artsample_t *const *input,
- int numInputFrames,
- artsample_t *const *output,
- int numOutputFrames, double ratio) {
- artsample_t **output_array =
- (artsample_t **)calloc(sizeof(artsample_t *), cxt->numChannels);
- ResampleResult res = {0, 0}, fres;
- int c;
- for (c = 0; c < cxt->numChannels; ++c) {
- output_array[c] = output[c];
- }
- res = resampleProcess(cxt, input, numInputFrames, output_array,
- numOutputFrames, ratio);
- // if we didn't consume all the input or ran out of output space, we're
- // finished (and this is obviously an unforced error, but the caller will have
- // to sort that out)
- numInputFrames -= (int)res.input_used;
- numOutputFrames -= (int)res.output_generated;
- if (numInputFrames != 0 || numOutputFrames == 0) {
- free((void *)output_array);
- return res;
- }
- for (c = 0; c < cxt->numChannels; ++c) {
- output_array[c] += res.output_generated;
- }
- fres = resampleProcess(cxt, NULL, -1, output_array, numOutputFrames, ratio);
- res.output_generated += fres.output_generated;
- free((void *)output_array);
- return res;
- }
- ResampleResult
- resampleProcessAndFlushInterleaved(Resample *cxt, const artsample_t *input,
- int numInputFrames, artsample_t *output,
- int numOutputFrames, double ratio) {
- ResampleResult res = {0, 0}, fres;
- res = resampleProcessInterleaved(cxt, input, numInputFrames, output,
- numOutputFrames, ratio);
- // if we didn't consume all the input or ran out of output space, we're
- // finished (and this is obviously an unforced error, but the caller will have
- // to sort that out)
- numInputFrames -= (int)res.input_used;
- numOutputFrames -= (int)res.output_generated;
- if (numInputFrames != 0 || numOutputFrames == 0) {
- return res;
- }
- output += (size_t)res.output_generated * cxt->numChannels;
- fres =
- resampleProcessInterleaved(cxt, NULL, -1, output, numOutputFrames, ratio);
- res.output_generated += fres.output_generated;
- return res;
- }
- #ifdef ENABLE_THREADS
- // This is the resampler processing function to process a single channel. It can
- // be called directly or called from a worker thread (see workers.c) and is used
- // for both interleaved and non-interleaved channels (see the "stride" argument
- // in the context).
- static int resampleProcessChannelJob(void *ptr, void *sync_not_used) {
- Resample *cxt = ptr;
- int half_taps = cxt->numTaps / 2;
- double offset2 = 0.0;
- // This is where we flush the resampler by simulating enough input (half the
- // number of filter taps) to align the output. We may fill with zeros, or
- // extrapolate the most recent samples if that option is selected.
- if (cxt->numInputFrames < 0) {
- if (cxt->numSamples - cxt->inputIndex < cxt->numTaps / 2) {
- memmove(cxt->cbuffer, cxt->cbuffer + cxt->numSamples - cxt->numTaps,
- cxt->numTaps * sizeof(artsample_t));
- cxt->outputOffset -= cxt->numSamples - cxt->numTaps;
- cxt->inputIndex -= cxt->numSamples - cxt->numTaps;
- }
- memset(cxt->cbuffer + cxt->inputIndex, 0,
- (cxt->numSamples - cxt->inputIndex) * sizeof(artsample_t));
- #ifdef ENABLE_EXTRAPOLATION
- if (cxt->flags & EXTRAPOLATE_ENDPOINTS) {
- extrapolate_forward(cxt->cbuffer + cxt->inputIndex - cxt->numTaps / 2,
- cxt->numTaps / 2, cxt->numTaps / 2);
- }
- #endif
- cxt->flags |= RESAMPLER_FLUSHED;
- cxt->inputIndex += cxt->numTaps / 2;
- }
- while (cxt->numOutputFrames > 0) {
- if (cxt->outputOffset + offset2 >= cxt->inputIndex - half_taps) {
- if (cxt->numInputFrames > 0) {
- if (cxt->inputIndex == cxt->numSamples) {
- memmove(cxt->cbuffer, cxt->cbuffer + cxt->numSamples - cxt->numTaps,
- cxt->numTaps * sizeof(artsample_t));
- cxt->outputOffset -= cxt->numSamples - cxt->numTaps;
- cxt->inputIndex -= cxt->numSamples - cxt->numTaps;
- }
- cxt->cbuffer[cxt->inputIndex++] = *(cxt->input += cxt->stride);
- cxt->res.input_used++;
- cxt->numInputFrames--;
- } else {
- break;
- }
- } else {
- #ifdef ENABLE_EXTRAPOLATION
- // if we are extrapolating backwards and haven't yet, now is the time
- if (cxt->flags & EXTRAPOLATE_PREFILL) {
- int num_samples = cxt->inputIndex - cxt->numTaps;
- if (num_samples >= 8) {
- extrapolate_reverse(cxt->cbuffer + cxt->inputIndex, num_samples,
- cxt->numTaps - num_samples);
- }
- cxt->flags &= ~EXTRAPOLATE_PREFILL;
- }
- #endif
- *(cxt->output += cxt->stride) =
- cxt->subsample(cxt, cxt->cbuffer, cxt->outputOffset + offset2);
- offset2 = ++(cxt->res.output_generated) / cxt->ratio;
- cxt->numOutputFrames--;
- }
- }
- cxt->outputOffset += offset2;
- if (cxt->flags & RESAMPLER_SNAP_OFFSET) {
- cxt->outputOffset =
- floor(cxt->outputOffset) +
- floor((cxt->outputOffset - floor(cxt->outputOffset)) * cxt->numFilters +
- 0.5) /
- cxt->numFilters;
- }
- return 0;
- }
- #endif
- // These two functions are not required for any application, but might be
- // useful. Essentially they allow a "dry run" of the resampler to determine
- // beforehand how many input samples would be consumed to generate a given
- // output, or how many samples would be generated with a given input.
- //
- // Note that there is a tricky edge-case here for ratios just over 1.0. If a
- // query is made as to how many input samples are required to generate a given
- // output, that does NOT necessarily mean that exactly that many samples will be
- // generated with the indicated input (specifically an extra sample might be
- // generated). Therefore it is important to restrict the output with
- // numOutputFrames if an exact output count is desired (don't just assume the
- // input count can exactly determine the output count).
- //
- // If this resampler was created with the resampleFixedRatioInit() function,
- // then the "ratio" parameter is ignored and the originally specified conversion
- // is simulated.
- unsigned int resampleGetRequiredSamples(Resample *cxt, int numOutputFrames,
- double ratio) {
- int half_taps = cxt->numTaps / 2;
- int input_index = cxt->inputIndex;
- double offset = cxt->outputOffset;
- ResampleResult res = {0, 0};
- if (cxt->flags & RESAMPLE_FIXED_RATIO) { // override any supplied ratio if
- // doing fixed ratio resampling
- ratio = cxt->fixedRatio;
- }
- while (numOutputFrames > 0) {
- if (offset >= input_index - half_taps) {
- if (input_index == cxt->numSamples) {
- offset -= cxt->numSamples - cxt->numTaps;
- input_index -= cxt->numSamples - cxt->numTaps;
- }
- input_index++;
- res.input_used++;
- } else {
- offset += (1.0 / ratio);
- numOutputFrames--;
- }
- }
- return res.input_used;
- }
- unsigned int resampleGetExpectedOutput(Resample *cxt, int numInputFrames,
- double ratio) {
- int half_taps = cxt->numTaps / 2;
- int input_index = cxt->inputIndex;
- double offset = cxt->outputOffset;
- ResampleResult res = {0, 0};
- if (cxt->flags & RESAMPLE_FIXED_RATIO) { // override any supplied ratio if
- // doing fixed ratio resampling
- ratio = cxt->fixedRatio;
- }
- if (cxt->flags &
- RESAMPLER_FLUSHED) { // ignore new input after flush but before reset
- numInputFrames = 0;
- } else if (numInputFrames < 0) { // also check for this being a flush
- input_index += half_taps;
- }
- while (1) {
- if (offset >= input_index - half_taps) {
- if (numInputFrames > 0) {
- if (input_index == cxt->numSamples) {
- offset -= cxt->numSamples - cxt->numTaps;
- input_index -= cxt->numSamples - cxt->numTaps;
- }
- input_index++;
- numInputFrames--;
- } else {
- break;
- }
- } else {
- offset += (1.0 / ratio);
- res.output_generated++;
- }
- }
- return res.output_generated;
- }
- // Advance the resampler output without generating any output, with the units
- // referenced to the input sample array. This can be used to temporally align
- // the output to the input (by specifying half the sinc filter tap width), and
- // it can also be used to introduce a phase shift. The resampler cannot be
- // reversed. Although not strictly true, for the purpose of this function we
- // will not allow subsampling (non-integer advance) without interpolation being
- // enabled.
- void resampleAdvancePosition(Resample *cxt, double delta) {
- if (delta < 0.0) {
- fprintf(stderr, "resampleAdvancePosition() can only advance forward!\n");
- } else if (!(cxt->flags & SUBSAMPLE_INTERPOLATE) && floor(delta) != delta) {
- fprintf(stderr, "resampleAdvancePosition() cannot advance partial samples "
- "without interpolation!\n");
- } else {
- cxt->outputOffset += delta;
- }
- }
- // Get the subsample position of the resampler. This is initialized to zero when
- // the resampler is started (or reset) and moves around zero as the resampler
- // processes audio. Obtaining this value is generally not required, but can be
- // useful for applications that need accurate phase information from the
- // resampler such as asynchronous sample rate converter (ASRC) implementations.
- // The units are relative to the input samples, and a negative value indicates
- // that an output sample is ready (i.e., can be generated with no further input
- // read).
- //
- // To fully understand the meaning of the position value the following C-like
- // pseudo-code for the resampler is presented. Note that this code has the
- // length of the sinc filters and the actual interpolation abstracted away (like
- // they abstracted away from the user of the library):
- //
- // while (numOutputFrames > 0) {
- // if (position < 0.0) {
- // write (output);
- // numOutputFrames--;
- // position += (1.0 / ratio);
- // }
- // else if (numInputFrames > 0) {
- // read (input);
- // numInputFrames--;
- // position -= 1.0;
- // }
- // else
- // break;
- // }
- double resampleGetPosition(Resample *cxt) {
- return cxt->outputOffset + (cxt->numTaps / 2.0) - cxt->inputIndex;
- }
- // Free all resources associated with the resampler context, including the
- // context pointer itself. Do not use the context after this call.
- void resampleFree(Resample *cxt) {
- if (cxt) {
- int i;
- for (i = 0; i <= cxt->numFilters; ++i) {
- free(cxt->filters[i]);
- }
- free((void *)cxt->filters);
- for (i = 0; i < cxt->numChannels; ++i) {
- free(cxt->buffers[i]);
- }
- free((void *)cxt->buffers);
- #ifdef ENABLE_THREADS
- if (cxt->workers) {
- workersDeinit(cxt->workers);
- }
- #endif
- free(cxt);
- }
- }
- // greatest common divisor
- static unsigned long gcd(unsigned long a, unsigned long b) {
- while (b) {
- unsigned long t = (a %= b);
- a = b;
- b = t;
- }
- return a;
- }
- // This is the basic convolution operation that is the core of the resampler and
- // utilizes the bulk of the CPU load (assuming reasonably long filters). The
- // first version is the canonical form for reference, followed by two variations
- // that are more accurate and incorporate various degrees of parallelization
- // that can be utilized by optimizing compilers. Try 'em and use the fastest, or
- // rewrite them using SIMD. Note that changing the "sum" variable from a float
- // to a double improves the quality somewhat at the possible expense of speed.
- #if 0 // Version 1 (canonical, very simple but slow and less accurate, not
- // recommended)
- static double apply_filter (artsample_t *A, artsample_t *B, int num_taps)
- {
- artsample_t sum = 0.0;
- do sum += *A++ * *B++;
- while (--num_taps);
- return sum;
- }
- #endif
- #if defined(__XTENSA__) && !defined(PATH_WIDTH)
- // ESP32/Xtensa: pure float to use hardware FPU (MADD.S), avoid software double
- // emulation
- static double apply_filter(artsample_t *A, artsample_t *B, int num_taps) {
- float sum = 0.0f;
- do {
- sum += A[0] * B[0];
- A++;
- B++;
- } while (--num_taps);
- return sum;
- }
- static double apply_filter_precise(artsample_t *A, artsample_t *B,
- int num_taps) {
- float sum = 0.0f;
- do {
- sum += *A++ * *B++;
- } while (--num_taps);
- return sum;
- }
- #elif !defined(_MSC_VER) || defined(__clang__) || defined(__llvm__) || \
- defined(__INTEL_COMPILER) || defined(__INTEL_LLVM_COMPILER)
- // Version 2 (outside-in order, more accurate)
- // Works well with most compilers but MSVC works better with the next one
- // try "-O3 -mavx2 -fno-signed-zeros -fno-trapping-math -fassociative-math"
- static double apply_filter(artsample_t *A, artsample_t *B, int num_taps) {
- int i = num_taps - 1;
- artsample_t sum = (artsample_t)0.0;
- do {
- sum += (A[0] * B[0]) + (A[i] * B[i]);
- A++;
- B++;
- } while ((i -= 2) > 0);
- return sum;
- }
- // For the "precise" version the order is not important, so just letting the
- // compiler do all the parallelization seems to work better with gcc and clang
- static double apply_filter_precise(artsample_t *A, artsample_t *B,
- int num_taps) {
- double sum = 0.0;
- do {
- sum += (double)*A++ * *B++;
- } while (--num_taps);
- return sum;
- }
- #else
- // Version 3 (outside-in order, 2x unrolled loop)
- // Works well with MSVC, but others have trouble vectorizing it
- static double apply_filter(artsample_t *A, artsample_t *B, int num_taps) {
- int i = num_taps - 1;
- artsample_t sum = (artsample_t)0.0;
- do {
- sum +=
- (A[0] * B[0]) + (A[i] * B[i]) + (A[1] * B[1]) + (A[i - 1] * B[i - 1]);
- A += 2;
- B += 2;
- } while ((i -= 4) > 0);
- return sum;
- }
- static double apply_filter_precise(artsample_t *A, artsample_t *B,
- int num_taps) {
- int i = num_taps - 1;
- double sum = 0.0;
- do {
- sum += ((double)A[0] * B[0]) + ((double)A[i] * B[i]) +
- ((double)A[1] * B[1]) + ((double)A[i - 1] * B[i - 1]);
- A += 2;
- B += 2;
- } while ((i -= 4) > 0);
- return sum;
- }
- #endif
- static void init_filter(Resample *cxt, artsample_t *filter, double fraction) {
- double filter_sum = 0.0, scaler, error;
- const double a0 = 0.35875;
- const double a1 = 0.48829;
- const double a2 = 0.14128;
- const double a3 = 0.01168;
- int i;
- // "dist" is the absolute distance from the sinc maximum to the filter tap to
- // be calculated, in radians "ratio" is that distance divided by half the tap
- // count such that it reaches π at the window extremes
- // Note that with this scaling, the odd terms of the Blackman-Harris
- // calculation appear to be negated with respect to the reference formula
- // version.
- for (i = 0; i < cxt->numTaps; ++i) {
- double dist = fabs((cxt->numTaps / 2 - 1) + fraction - i) * M_PI;
- double ratio = dist / (cxt->numTaps / 2.0);
- double value;
- if (dist != 0.0) {
- value = sin(dist * cxt->lowpassRatio) / (dist * cxt->lowpassRatio);
- if (cxt->flags & BLACKMAN_HARRIS) {
- value *=
- a0 + a1 * cos(ratio) + a2 * cos(2 * ratio) + a3 * cos(3 * ratio);
- } else {
- value *= 0.5 * (1.0 + cos(ratio)); // Hann window
- }
- } else {
- value = 1.0;
- }
- filter_sum += cxt->tempFilter[i] = value;
- }
- // filter should have unity DC gain
- scaler = 1.0 / filter_sum;
- error = 0.0;
- for (i = cxt->numTaps / 2; i < cxt->numTaps;
- i = cxt->numTaps - i - (i >= cxt->numTaps / 2)) {
- filter[i] = (artsample_t)((cxt->tempFilter[i] *= scaler) - error);
- error += filter[i] - cxt->tempFilter[i];
- }
- }
- static double subsample_no_interpolate(Resample *cxt, artsample_t *source,
- double offset) {
- int fi = (int)floor((offset - floor(offset)) * cxt->numFilters + 0.5);
- source += (int)floor(offset);
- if (!(cxt->flags & INCLUDE_LOWPASS) && !(fi % cxt->numFilters)) {
- return source[fi / cxt->numFilters];
- }
- return apply_filter(cxt->filters[fi], source - cxt->numTaps / 2 + 1,
- cxt->numTaps);
- }
- static double subsample_interpolate(Resample *cxt, artsample_t *source,
- double offset) {
- double frac = offset - floor(offset);
- int fi = (int)floor(frac *= cxt->numFilters);
- frac -= fi;
- source += (int)floor(offset) - cxt->numTaps / 2 + 1;
- return (apply_filter(cxt->filters[fi], source, cxt->numTaps) * (1.0 - frac)) +
- (apply_filter(cxt->filters[fi + 1], source, cxt->numTaps) * frac);
- }
- static double subsample_no_interpolate_precise(Resample *cxt,
- artsample_t *source,
- double offset) {
- int fi = (int)floor((offset - floor(offset)) * cxt->numFilters + 0.5);
- source += (int)floor(offset);
- if (!(cxt->flags & INCLUDE_LOWPASS) && !(fi % cxt->numFilters)) {
- return source[fi / cxt->numFilters];
- }
- return apply_filter_precise(cxt->filters[fi], source - cxt->numTaps / 2 + 1,
- cxt->numTaps);
- }
- static double subsample_interpolate_precise(Resample *cxt, artsample_t *source,
- double offset) {
- double frac = offset - floor(offset);
- int fi = (int)floor(frac *= cxt->numFilters);
- frac -= fi;
- source += (int)floor(offset) - cxt->numTaps / 2 + 1;
- return (apply_filter_precise(cxt->filters[fi], source, cxt->numTaps) *
- (1.0 - frac)) +
- (apply_filter_precise(cxt->filters[fi + 1], source, cxt->numTaps) *
- frac);
- }
|