Giter VIP home page Giter VIP logo

avaneev / r8brain-free-src Goto Github PK

View Code? Open in Web Editor NEW
523.0 33.0 60.0 32.06 MB

High-quality pro audio resampler / sample rate converter C++ library. Very fast, for both audio resampling and time-series interpolation.

License: MIT License

C++ 65.56% Pascal 0.19% C 25.28% NASL 8.97%
audio-processing sample-rate-converter audio-library resampler sample-rate dsp resampling resample time-series interpolation

r8brain-free-src's People

Contributors

avaneev avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

r8brain-free-src's Issues

Will this work with format 16bit format?

I'm using the DLL in my windows project and I'm confused that the "process" function only takes in a buffer in the form of a pointer to a double. What I really want to do is pass in a pointer to 16 bit integers. Would I need to convert these 16 bit integers to doubles?

FYI I'm linking this to a rust project, so doubles are represented as 64bit floats.

Can r8brain resample bidirectionally without slipping?

I have a real-time use case where I am adapting a 48KHz audio interface to 44.1Khz process that both records and plays audio simultaneously.

I am using the CDSPResampler24 in both directions, with buffers to handle the varying number of samples produced by each submitted buffer. I have a similar question on stackoverflow, but I am opening this issue specifically for r8brain.

Here is the flow of data through the process.

48K Input -> [48K to 44.1K] -> [buffer] -> process -> [44.1K to 48K] -> [buffer] -> 48K Output

The InLenBeforeOutStart for the resamplers are 1668 and 1770. And my buffer size is 256, so I prime this pipeline with 1668 + 256 + 1770 + 256 samples total.

After priming, this pipeline appears to be stable and can run continuously without slipping. There is always an available output buffer for each input buffer at the 48K end of the process. The 44.1K processing is obviously running at a different rate. But, no buffer underruns or overruns occur.

This is exactly what I need! So why am I opening this issue? I am not sure this is by design or just luck. The results are only by experiment. I am opening this issue to ask if your library is expected to support a full-duplex scenario like this without extra code to deal with overflow/underflow conditions? Will this work for other conversion rates? Is there a risk of round-off errors that would cause the reverse conversion to yield more or less samples that the forward conversion?

I am attaching my test code here in case this helps. As a side note, the output buffer length only varies by 277 samples min to max, and the buffer lengths cycle after 160 buffer conversions, which gives me more confidence it is stable indefinitely.

`
cout << "Running mono loopback re-sampler demo..." << endl;

RingBuffer inboundBuffer(2048);
RingBuffer outboundBuffer(2048);

const int bufferSize = 256;

CDSPResampler24 *inboundResampler = new CDSPResampler24(48000.0, 44100.0, bufferSize);
int inboundBeforeStart = inboundResampler->getInLenBeforeOutStart();
cout << "Inbound sample length before first output: " << inboundBeforeStart << endl;

CDSPResampler24 *outboundResampler = new CDSPResampler24(44100.0, 48000.0, bufferSize);
int outboundBeforeStart = outboundResampler->getInLenBeforeOutStart();
cout << "Inbound sample length before first output: " << outboundBeforeStart << endl;

double sampleRate = 48000.0;
int inputSize = sampleRate * 30;
double *input = createToneBuffer(sampleRate, inputSize);
double *output = new double[inputSize + 4096];

int inputPosition = 0;
int outputPosition = 0;
const int callbackBufferSize = 256;
int primeLength = inboundBeforeStart + outboundBeforeStart + callbackBufferSize + callbackBufferSize;

double loopbackBuffer[callbackBufferSize];

std::ofstream ofs;
ofs.open ("log.txt", std::ofstream::out | std::ofstream::app);

int outboundBufferMin = 2048;
int outboundBufferMax = 0;

while (inputSize - inputPosition >= callbackBufferSize) {

    int inboundProcessCount = 0;
    int outboundProcessedCount = 0;

    double *inboundResampledBuffer;
    int inboundResampledLength = inboundResampler->process(input + inputPosition, callbackBufferSize, inboundResampledBuffer);


    if (inboundBuffer.writeCapacity() >= inboundResampledLength) {
        inboundBuffer.push(inboundResampledBuffer, inboundResampledLength);
    } else {
        cout << "Buffer overflow writing input position " << inputPosition << endl;
        return -1;
    }

    inboundProcessCount++;
    inputPosition += callbackBufferSize;

    while (inboundBuffer.readCapacity() >= callbackBufferSize) {

        inboundBuffer.pop(loopbackBuffer, callbackBufferSize);
        double *outboundResampledBuffer;
        int outboundResampledLength = outboundResampler->process(loopbackBuffer, callbackBufferSize, outboundResampledBuffer);
        outboundBuffer.push(outboundResampledBuffer, outboundResampledLength);
    }

    if (inputPosition > primeLength) {

        size_t readCapacity = outboundBuffer.readCapacity();
        outboundBufferMin = min(readCapacity, outboundBufferMin);
        outboundBufferMax = max(readCapacity, outboundBufferMax);

        if (readCapacity > callbackBufferSize) {

            outboundBuffer.pop(output + outputPosition, callbackBufferSize);
            outputPosition += callbackBufferSize;
            outboundProcessedCount++;
        } else {
            cout << "Buffer overflow reading output position " << outputPosition << endl;
        }
    }

    ofs << outboundBuffer.readCapacity() << " ";

}

ofs << endl;
ofs.close();

writeOutputFile(output, outputPosition, 48000.0, "duplex.wav");

cout << ">>> Converted " << inputSize << " in to " << outputPosition << " out." << endl;
cout << "outboundMin=" << outboundBufferMin << " outboundMax=" << outboundBufferMax << endl;
cout << "Done." << endl;

`

Realtime latency tips

I have been using this library for a few years in a quite popular Android piano app. Today I looked at this repo again and saw that a lot of work has been done. I tried the most recent version and it works even faster then before. So that's nice!

I am optimizing my app for as low latency as possible, and was wondering what the best approach would be for this regarding the resampler?

In my current implementation I feed the resampler empty input data until the number of samples available in the output buffer is not 0 anymore. This is done once after the initialization of the resamplers. Is this a good approach? Or should I feed the actual input audio immediately?

double * dummyInput = (double *) calloc(sizeof(double),channelLength);
for (int i = 0; i < channels; i++) {
   resamplers[i] = new r8b::CDSPResampler16((double) sampleRateIn, (double) sampleRateOut, channelLength);

   double * dummyOut;
   int processedCount = 0;
   while (processedCount == 0) {
      processedCount = resamplers[i]->process(dummyInput, channelLength, dummyOut);
    }
}
free(dummyInput);

I am using PFFFT with NEON on ARM64: #define R8B_PFFFT 1

Thanks for this great library!

Passing nullptr to memcpy in realloc

At the following line:

memcpy( NewData, Data, ( PrevCapacity > NewCapacity ?

There are conditions under which you end up passing nullptr to memcpy, which is undefined behavior (see https://en.cppreference.com/w/cpp/string/byte/memcpy) even when the size of the buffer being copied is zero.

This occurs when constructing a basic resampler, e.g. r8b::CDSPResampler16 resampler(22050, 48000, 1000); causes a nullptr to be passed to memcpy.

I observed this when running this using clang's UndefinedBehaviorSanitizer. I do not know if there are other spots with similar types of undefined behavior, but this was the first that it crashed on.

Voice pitch changes in a streaming application

Hi, thanks for the great library!

I tested the library in a non-streaming application, and everything worked fine (reading from one file, converting sr, writing the resulting buffer to another file).

However, when I switched to a streaming application, I noticed that the library alters the voice pitch - makes the voice pitch high when converting the sampling rate to a higher rate (16KHz->48KHz) and low when converting the sampling rate to a lower rate (48KHz->16KHz).

I looked at everything and cannot find the bug in my code. Do you know by any chance if anything in the library might cause this behavior?

Thank you so much!

CDSPHBUpsampler.inc NEON is limited to ARM64, document that?

Hi,

I am using your library in my Mini Piano Lite Android app. Today I tried updating to the 5.6 version of r8brain. However, I got compile errors that eluded me for a while. It turns out that the CDSPHBUpsampler.inc NEON code uses ARM64 only NEON types, and won't compile on ARMv7a NEON. PFFFT works with ARMv7a NEON, but CDSPHBUpsampler.inc doesn't.

I got it working by removing my check for ARMv7A in the upsampler, but I thought it would be useful to add some info about supported NEON versions in the readme?

Thanks for the great library.

Peter

Why is getLatency() not implemented?

Hi Aleksey,

The comments and explanations in the source code clearly indicate that the resamplers introduce a latency, related to a number of settings, some of which can be tweaked.
Then, why is the virtual getLatency() method not implemented/overridden? It now simply returns 0. This is pretty annoying if you want to report latency to the host.
There are some hints that the processing can "consume" latency, which of course makes no sense. The internal filters cannot "catch up"... This is also demonstrated by the included example, where empty samples are used to "flush" the last output samples from the filters.
My use case is to downsample-process-upsample in the host's callback at high external SRs (above 96 K).

There is internal administration in the resamplers, FIR filter, etc., from which it should be possible to report the current latency... Why not implement the getLatency() method?

Cheers

Crash on exit

corrupted size vs. prev_size while consolidating
1

Probably I am getting crash upon destruction of static filters in r8base.cpp. I am using v5.6.

const correctness in CDSPProcessor::process

Would it be possible to change the signature of CDSPProcessor::process so that the input array is const? Right now I have to const_cast my input arrays: not a big deal, of course, but it seems there is nothing to lose since a non-const array can always be passed as const, and a const array would no longer need to be cast. Is there any downside I'm not aware of?

CMake Capability

I am working on a project right now that utilizes the R8Brain library, and it involves with CMake build. Do you intend to have a CMake build for this library or have any Conan recipes for it? Conan currently only has a version 4.6 recipe, but I would like to use the most updated version (5.2).

Heap Curroption Exception with PFFFT when the global static list of objects is freed

Greetings!

When using PFFFT, the destructor of CFixedBuffer<float> work, defined at line 344 of CDSPRealFFT.h, will throw Invalid address specified to RtlValidateHeap on Windows, compiled with Visual Studio 2019, and free: invalid pointer on Linux compiled with GCC 8.3.0 or Clang 7.0.1.
I didn't test on other platforms.

Here is some code to reproduce the isssue:

#define R8B_PFFFT 1
#include "CDSPResampler.h"

int main(){
    r8b::CDSPResampler24 resampler(1.0, 2.0, 256);
    return 0;
}

gcd algorithm

I saw findGCD mentioned in the changelog so I looked at it out of curiosity. Is there a reason why it's tediously using repeated subtraction instead of using Euclid's algorithm?

e.g. something like:

#include <cmath>

// Computes greatest common divisor of two floating-point numbers.
// Returns zero if both arguments are zero.
template< typename Float >
Float fgcd( Float a, Float b )
{
	// bail out if one of the arguments is a NaN or ±infinity
	if( ! std::isfinite( a ) || ! std::isfinite( b ) )
		return (Float)NAN;

	a = std::fabs( a );
	b = std::fabs( b );

	if( a < b )
		std::swap( a, b );

	while( b ) {
		a = std::fabs( std::remainder( a, b ) );
		std::swap( a, b );
	}

	return a;
}

The number of loop iterations cannot exceed the number of mantissa bits, i.e. 53 for double (although the worst example I've found used 42 iterations). Note that any two finite floating-point numbers have a greatest common divisor unless they're both zero (in which case this returns zero by standard mathematical convention).

Varispeed support

Thanks for your awesome library. I've used it on a previous project and it has worked very well.

I was wondering if I could get your advice on adding varispeed support to your library. The library seems to assume that the resampling ratio is fixed during stream conversion.

I've already implemented a varispeed SRC using Julius O Smith's sinc table algorithm, but as you would guess, the real-time performance is not acceptable.

It might make the question easier if I limit the scope of the problem to output/input ratios near 1.0.

Again, thanks for your contribution to open source.

Changing sample rate after initialisation

Hi,

Thanks for your years of work on this excellent library. I'm currently using it for my network audio project as a resampling stage before and after Opus encode/decode. I'd also like to use it to correct clock drift between the sender and receiver.

Is there a function to change the sample rate ratio after the CDSPResampler has had samples go through it? I don't really need a continuous change. I'll be monitoring the receive buffer and adjusting the ratio when necessary to keep it from underflowing or overflowing.

I did something like this with your library about 6 years ago. I don't think that function exists any more though and it's been so long I can't find my old code.

Thanks,
Sam.

Power of two FFT resampler affects volume level

The power of two FFT resampler increases or decreases the volume level relative to the resampling ratio. Doubling the sample rate doubles the volume, and halving the sample rate halves the volume.

If this is affected by the FFT chosen, then it is specific to PFFFT double and my new vDSP implementation.

It doesn't seem to affect the convolver used by the non-integer ratios, so it doesn't appear to apply to FFT conversions that start and end at the same filter bank size.

Exponent has no digits error

Hello @avaneev

I'm trying to use your library on a Raspberry Pi, but I'm getting the following errors when building:

In file included from r8brain-free-src/CDSPFIRFilter.h:18,
                 from r8brain-free-src/CDSPBlockConvolver.h:18,
                 from r8brain-free-src/CDSPResampler.h:21,
                 from process.cpp:3:
r8brain-free-src/CDSPSincFilterGen.h:330:29: error: exponent has no digits
  330 |                 if( Freq1 < 0x1p-42 )
      |                             ^~~~
r8brain-free-src/CDSPSincFilterGen.h:484:52: error: exponent has no digits
  484 |                 int IsZeroX = ( fabs( fd - 1.0 ) < 0x1p-42 );
      |                                                    ^~~~
r8brain-free-src/CDSPSincFilterGen.h:486:53: error: exponent has no digits
  486 |                 IsZeroX = ( IsZeroX || fabs( fd ) < 0x1p-42 );
      |                                                     ^~~~

I've used your library before and if I switch to that older commit (cb67707) I can build it and there are no problems... However with the current version, I'm running into the above issue.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.