Open source high-quality pro audio sample rate converter library released from Voxengo
- KVRAF
- Topic Starter
- 4021 posts since 7 Sep, 2002
-
- KVRer
- 4 posts since 15 Apr, 2006 from New York
AUTO-ADMIN: Non-MP3, WAV, OGG, SoundCloud, YouTube, Vimeo, Twitter and Facebook links in this post have been protected automatically. Once the member reaches 5 posts the links will function as normal.
Hello Aleksey,I've been doing well with your wonderful samplerate converter, and getting ready for an upcoming release of our framework that uses r8brain for conversion on Windows. However, I'm hitting a snag when the CDSPResampler is destroyed at shutdown. Specifcally, CSyncObject::aquire() is consistently failing on the call to EnterCriticalSection() due to an access violation.
It is diffult for me to understand why this is failing at shutdown, however I can destroy the resamplers at runtime without any problems (and also no issues on mac, but that is most likely because memory usage is a bit more lax there). Does the following stack trace indicate any clues to you? I'm using v1.5.
Code: Select all (#)
> DeviceTest.exe!r8b::CSyncObject::acquire() Line 599 C++
DeviceTest.exe!r8b::CSyncKeeper::CSyncKeeper(r8b::CSyncObject & aSyncObj) Line 673 C++
DeviceTest.exe!r8b::CDSPFIRFilter::unref() Line 646 C++
DeviceTest.exe!r8b::CDSPBlockConvolver::~CDSPBlockConvolver() Line 109 C++
DeviceTest.exe!r8b::CDSPBlockConvolver::`scalar deleting destructor'(unsigned int) C++
DeviceTest.exe!r8b::CPtrKeeper<r8b::CDSPBlockConvolver *>::~CPtrKeeper<r8b::CDSPBlockConvolver *>() Line 491 C++
DeviceTest.exe!`eh vector destructor iterator'(void * ptr, unsigned int size, int count, void (void *) * pDtor) C++
DeviceTest.exe!r8b::CDSPResampler<r8b::CDSPFracInterpolator<24,673,9> >::~CDSPResampler<r8b::CDSPFracInterpolator<24,673,9> >() C++
DeviceTest.exe!r8b::CDSPResampler24::~CDSPResampler24() C++
DeviceTest.exe!r8b::CDSPResampler24::`scalar deleting destructor'(unsigned int) C++
DeviceTest.exe!std::default_delete<r8b::CDSPResampler24>::operator()(r8b::CDSPResampler24 * _Ptr) Line 1152 C++
DeviceTest.exe!std::unique_ptr<r8b::CDSPResampler24,std::default_delete<r8b::CDSPResampler24> >::_Delete() Line 1445 C++
DeviceTest.exe!std::unique_ptr<r8b::CDSPResampler24,std::default_delete<r8b::CDSPResampler24> >::~unique_ptr<r8b::CDSPResampler24,std::default_delete<r8b::CDSPResampler24> >() Line 1400 C++
DeviceTest.exe!std::unique_ptr<r8b::CDSPResampler24,std::default_delete<r8b::CDSPResampler24> >::`scalar deleting destructor'(unsigned int) C++
DeviceTest.exe!std::allocator<std::unique_ptr<r8b::CDSPResampler24,std::default_delete<r8b::CDSPResampler24> > >::destroy<std::unique_ptr<r8b::CDSPResampler24,std::default_delete<r8b::CDSPResampler24> > >(std::unique_ptr<r8b::CDSPResampler24,std::default_delete<r8b::CDSPResampler24> > * _Ptr) Line 624 C++
DeviceTest.exe!std::allocator_traits<std::allocator<std::unique_ptr<r8b::CDSPResampler24,std::default_delete<r8b::CDSPResampler24> > > >::destroy<std::unique_ptr<r8b::CDSPResampler24,std::default_delete<r8b::CDSPResampler24> > >(std::allocator<std::unique_ptr<r8b::CDSPResampler24,std::default_delete<r8b::CDSPResampler24> > > & _Al, std::unique_ptr<r8b::CDSPResampler24,std::default_delete<r8b::CDSPResampler24> > * _Ptr) Line 758 C++
DeviceTest.exe!std::_Wrap_alloc<std::allocator<std::unique_ptr<r8b::CDSPResampler24,std::default_delete<r8b::CDSPResampler24> > > >::destroy<std::unique_ptr<r8b::CDSPResampler24,std::default_delete<r8b::CDSPResampler24> > >(std::unique_ptr<r8b::CDSPResampler24,std::default_delete<r8b::CDSPResampler24> > * _Ptr) Line 909 C++
DeviceTest.exe!std::_Destroy_range<std::_Wrap_alloc<std::allocator<std::unique_ptr<r8b::CDSPResampler24,std::default_delete<r8b::CDSPResampler24> > > > >(std::unique_ptr<r8b::CDSPResampler24,std::default_delete<r8b::CDSPResampler24> > * _First, std::unique_ptr<r8b::CDSPResampler24,std::default_delete<r8b::CDSPResampler24> > * _Last, std::_Wrap_alloc<std::allocator<std::unique_ptr<r8b::CDSPResampler24,std::default_delete<r8b::CDSPResampler24> > > > & _Al, std::_Nonscalar_ptr_iterator_tag __formal) Line 89 C++
DeviceTest.exe!std::_Destroy_range<std::_Wrap_alloc<std::allocator<std::unique_ptr<r8b::CDSPResampler24,std::default_delete<r8b::CDSPResampler24> > > > >(std::unique_ptr<r8b::CDSPResampler24,std::default_delete<r8b::CDSPResampler24> > * _First, std::unique_ptr<r8b::CDSPResampler24,std::default_delete<r8b::CDSPResampler24> > * _Last, std::_Wrap_alloc<std::allocator<std::unique_ptr<r8b::CDSPResampler24,std::default_delete<r8b::CDSPResampler24> > > > & _Al) Line 80 C++
DeviceTest.exe!std::vector<std::unique_ptr<r8b::CDSPResampler24,std::default_delete<r8b::CDSPResampler24> >,std::allocator<std::unique_ptr<r8b::CDSPResampler24,std::default_delete<r8b::CDSPResampler24> > > >::_Destroy(std::unique_ptr<r8b::CDSPResampler24,std::default_delete<r8b::CDSPResampler24> > * _First, std::unique_ptr<r8b::CDSPResampler24,std::default_delete<r8b::CDSPResampler24> > * _Last) Line 1480 C++
DeviceTest.exe!std::vector<std::unique_ptr<r8b::CDSPResampler24,std::default_delete<r8b::CDSPResampler24> >,std::allocator<std::unique_ptr<r8b::CDSPResampler24,std::default_delete<r8b::CDSPResampler24> > > >::_Tidy() Line 1541 C++
DeviceTest.exe!std::vector<std::unique_ptr<r8b::CDSPResampler24,std::default_delete<r8b::CDSPResampler24> >,std::allocator<std::unique_ptr<r8b::CDSPResampler24,std::default_delete<r8b::CDSPResampler24> > > >::~vector<std::unique_ptr<r8b::CDSPResampler24,std::default_delete<r8b::CDSPResampler24> >,std::allocator<std::unique_ptr<r8b::CDSPResampler24,std::default_delete<r8b::CDSPResampler24> > > >() Line 901 C++
DeviceTest.exe!cinder::audio2::dsp::ConverterImplR8brain::~ConverterImplR8brain() Line 66 C++
-
- KVRer
- 4 posts since 15 Apr, 2006 from New York
Ah, I think I found the reason: the node that is using CDSPResampler is ultimately maintained by a static 'master' context, that is deallocated at app shutdown. This must be happening after the static objects used by r8brain are destroyed in atexit, leading to the bad access.
I can acommodate for this in my design, although it does bring up a question that has come to mind before, which is why there are static objects used by the converters. Are you saving memory by reusing buffers / processors, or is it a performance optimization?
Thanks again,
Rich
I can acommodate for this in my design, although it does bring up a question that has come to mind before, which is why there are static objects used by the converters. Are you saving memory by reusing buffers / processors, or is it a performance optimization?
Thanks again,
Rich
-
- KVRer
- 2 posts since 6 Aug, 2014
r8brain looks like a great library, but I can't get a (very simple) example to work.
Here is my code:
const double input_sample_rate = 48000.0 ;
const double output_sample_rate = 44100.0 ;
const int input_sample_count = 2880 ;
// double in [input_sample_count] ;
// A passed-in single channel sample of a 440Hz sine wave. Approx 50 millisecs in duration.
// Values in the buffer are between -1.0 and 1.0
CDSPResampler24 resampler ( input_sample_rate, output_sample_rate, input_sample_count ) ;
double * out ;
int output_sample_count = resampler.process( in, input_sample_count, out );
output_sample_count is returned as 1346 which looks wrong to me. I'd expect it to be approximately twice that. Also the output of successive converted samples played through a speaker sounds like the right frequency but very jittery, not smooth.
I am instantiating a new resampler local object to handle each small sample. Is that what I am doing wrong? Should I use a single, persistent object on which I call process() for each sample?
Environment: Visual Studio 2012 32-bit C++ native code build
Here is my code:
const double input_sample_rate = 48000.0 ;
const double output_sample_rate = 44100.0 ;
const int input_sample_count = 2880 ;
// double in [input_sample_count] ;
// A passed-in single channel sample of a 440Hz sine wave. Approx 50 millisecs in duration.
// Values in the buffer are between -1.0 and 1.0
CDSPResampler24 resampler ( input_sample_rate, output_sample_rate, input_sample_count ) ;
double * out ;
int output_sample_count = resampler.process( in, input_sample_count, out );
output_sample_count is returned as 1346 which looks wrong to me. I'd expect it to be approximately twice that. Also the output of successive converted samples played through a speaker sounds like the right frequency but very jittery, not smooth.
I am instantiating a new resampler local object to handle each small sample. Is that what I am doing wrong? Should I use a single, persistent object on which I call process() for each sample?
Environment: Visual Studio 2012 32-bit C++ native code build
- KVRian
- 520 posts since 12 Apr, 2010 from The Netherlands
Yeah, you will probably need a persistent resampler object; that is what I do anyway. If you use the resampler to resample unrelated sample bocks (e.g. different IRs), then you will need to clear the resampler object before doing a new block.
- KVRAF
- Topic Starter
- 4021 posts since 7 Sep, 2002
Please follow the example code in the "example.cpp". Basically, you can't resample audio with r8brain-free-src in "one shot", it should be done iteratively.
Using a single persistent resampler object is a more efficient way to use this library (given the input and output sample rates remain same). Just call the "clear()" function before each resampling process.
Using a single persistent resampler object is a more efficient way to use this library (given the input and output sample rates remain same). Just call the "clear()" function before each resampling process.
- KVRAF
- Topic Starter
- 4021 posts since 7 Sep, 2002
This library uses static objects to collect reusable filter objects.reakinator wrote:I can acommodate for this in my design, although it does bring up a question that has come to mind before, which is why there are static objects used by the converters. Are you saving memory by reusing buffers / processors, or is it a performance optimization?
I may rethink this approach. However, it will require to maintain and pass to the resampler a special "cache" object, which is a bit more complicated approach than using static objects.
- KVRian
- 520 posts since 12 Apr, 2010 from The Netherlands
I have recently updated to r69, and the Xcode LLVM compiler complained about two calls to implicitly-deleted default constructor. But I just found that this has been fixed in r70. Thanks!
Not necessarily, I have found that sometimes it is more efficient to create a local (non-persistent) resampler object each call.Aleksey Vaneev wrote:Using a single persistent resampler object is a more efficient way to use this library (given the input and output sample rates remain same).
- KVRian
- 520 posts since 12 Apr, 2010 from The Netherlands
Oops, I forgot to mention the warning I have been getting in VS2013:
Would it be safe to ignore this warning? Note that everything seems to work just fine.
EDIT: I have found what causes the warning, it is this line (#375) in CDSPResampler.h:
If I remove the "const" the warning goes away, i.e.:
Code: Select all
CDSPResampler.h(375) : warning C4373: 'r8b::CDSPResampler<r8b::CDSPFracInterpolator<18,137>>::process': virtual function overrides 'r8b::CDSPProcessor::process', previous versions of the compiler did not override when parameters only differed by const/volatile qualifiers
CDSPProcessor.h(107) : see declaration of 'r8b::CDSPProcessor::process'
CDSPResampler.h(496) : see reference to class template instantiation 'r8b::CDSPResampler<r8b::CDSPFracInterpolator<18,137>>' being compiled
EDIT: I have found what causes the warning, it is this line (#375) in CDSPResampler.h:
Code: Select all
virtual int process( double* const ip0, int l, double*& op0 )
Code: Select all
virtual int process( double* ip0, int l, double*& op0 )
-
- KVRer
- 1 posts since 27 Sep, 2014
Thanks for this library, I am using it in my mobile piano app to handle resampling audio sources that are not the same as my internal sample rate. Performance is quite good, although I am on ARM.
I have a few questions:
- On Android I use short * PCM data. What is recommended in this case for r8brain input: -1.0 <-> 1.0 or (double)SHRT_MIN <-> (double)SHRT_MAX? Both seem to work ok, but I don't know what the input limits are?
- Second is a issue: I get overflowed data back from r8brain. Is this expected? For example, when my audio data is between -1.0 and 1.0 range, I sometimes get values back that are > 1.0, like 1.00123. It's not hard to manually check all output, but I did not expect it to happen. (22050 => 44100)
- I am not an expert at all in resampling and all audio processing stuff. So I was wondering, what is the difference between a CDSPResampler16 and CDSPResampler24? Has the CDSPResampler24 any benefits when using 16 bit pcm data? I assume it's slower?
Thanks again for this library!
I have a few questions:
- On Android I use short * PCM data. What is recommended in this case for r8brain input: -1.0 <-> 1.0 or (double)SHRT_MIN <-> (double)SHRT_MAX? Both seem to work ok, but I don't know what the input limits are?
- Second is a issue: I get overflowed data back from r8brain. Is this expected? For example, when my audio data is between -1.0 and 1.0 range, I sometimes get values back that are > 1.0, like 1.00123. It's not hard to manually check all output, but I did not expect it to happen. (22050 => 44100)
- I am not an expert at all in resampling and all audio processing stuff. So I was wondering, what is the difference between a CDSPResampler16 and CDSPResampler24? Has the CDSPResampler24 any benefits when using 16 bit pcm data? I assume it's slower?
Thanks again for this library!
- KVRAF
- 7950 posts since 12 Feb, 2006 from Helsinki, Finland
I can't answer the other questions, but:
When you resample a signal, you are conceptually reconstructing the actual continuous audio signal, then filtering it (if necessary), and then sampling the result. When up-sampling, some of the samples are taken between the original samples and can be larger than the input samples (because the signal is larger there). When down-sampling, the filtering required to band-limit the signal to the lower bandwidth can also add some further ringing.
The upper limit is something like 3dB higher than the input sample maximum, though in practice you need a specially crafted worst-case signal to observe this much overshoot. All this is perfectly normal though and it also happens with analog signals when they finally come out of your computer (and in some cases, you can actually distort badly designed amplifiers as a result). In practice you might (or might not) want to leave some headroom and then just clip it and accept the distortion if it's not enough.
From the scientific point of view, the whole idea of trying to push digital signals to "full scale" is completely ridiculous, but good luck trying to explain that to users.
This is not just expected, it's also the desired result from any half-decent resampling library. The digital audio signal does not represent a series of steps or points, but a series of scaled and time-shifted cardinal sines (sinc functions). The actual audio signal is the sum of these functions (which are all infinite in time as well .. in theory anyway) and in general the maximum/minimum values for the signal are larger than the maximum/minimum values for the individual samples.Peterdk wrote: - Second is a issue: I get overflowed data back from r8brain. Is this expected? For example, when my audio data is between -1.0 and 1.0 range, I sometimes get values back that are > 1.0, like 1.00123. It's not hard to manually check all output, but I did not expect it to happen. (22050 => 44100)
When you resample a signal, you are conceptually reconstructing the actual continuous audio signal, then filtering it (if necessary), and then sampling the result. When up-sampling, some of the samples are taken between the original samples and can be larger than the input samples (because the signal is larger there). When down-sampling, the filtering required to band-limit the signal to the lower bandwidth can also add some further ringing.
The upper limit is something like 3dB higher than the input sample maximum, though in practice you need a specially crafted worst-case signal to observe this much overshoot. All this is perfectly normal though and it also happens with analog signals when they finally come out of your computer (and in some cases, you can actually distort badly designed amplifiers as a result). In practice you might (or might not) want to leave some headroom and then just clip it and accept the distortion if it's not enough.
From the scientific point of view, the whole idea of trying to push digital signals to "full scale" is completely ridiculous, but good luck trying to explain that to users.
-
- KVRian
- 574 posts since 1 Jan, 2013 from Denmark
Besides mystrans excellent answer, this effect also gave rise to "True-peak" meters/limiters etc., that process an oversampled (4x usually, and thus closer to the continous analog signal) version. The oversampled processing means that intersample-peaks can be avoided. What is important to realize is, that your original signal already contained these peaks, and the resampling shifted your waveform to some extent, so it actually is measureable in the discrete domain.Peterdk wrote:- Second is a issue: I get overflowed data back from r8brain. Is this expected? For example, when my audio data is between -1.0 and 1.0 range, I sometimes get values back that are > 1.0, like 1.00123. It's not hard to manually check all output, but I did not expect it to happen. (22050 => 44100)
- KVRAF
- Topic Starter
- 4021 posts since 7 Sep, 2002
Tale, thanks for the warning - I'll make a fix in a future update.
Peterdk, the library can handle any signal scale. As others have replied already, resampling does produce signal overshoots due to filtering.
CDSPResampler24 is slower than CDSPResampler16, but it performs a full-resolution 24-bit resampling. For "consumer" audio signal resampling (e.g. music playback) CDSPResampler16 should be enough.
Peterdk, the library can handle any signal scale. As others have replied already, resampling does produce signal overshoots due to filtering.
CDSPResampler24 is slower than CDSPResampler16, but it performs a full-resolution 24-bit resampling. For "consumer" audio signal resampling (e.g. music playback) CDSPResampler16 should be enough.
-
- KVRer
- 9 posts since 10 Apr, 2007
Woah this looks great! Nice work man! Quick question though! I assume it has not been tested in Xcode or it would have been mentioned? Any problems I should be aware of when using with Xcode ?
thanks!
thanks!