Open source high-quality pro audio sample rate converter library released from Voxengo
- KVRAF
- Topic Starter
- 4021 posts since 7 Sep, 2002
- KVRian
- 519 posts since 12 Apr, 2010 from The Netherlands
- KVRian
- 509 posts since 1 May, 2006 from lancaster, pa
I have been trying to implement r8brain in an oversampling scenario and I have a question. Using r8brain to downsample would be the obvious choice; however, what would be the recommended method for upsampling? Would zero-stuffing with some other filtering work more efficiently when upsampling? Or, would it be better to use r8brain for this as well?Aleksey Vaneev wrote:No, that's not overkill, I myself use linear-phase oversampling in Voxengo plug-ins that is close to that latency number and quality. You can reduce latency by using a lower ReqAtten and higher ReqTransBand values, it's flexible.
Thanks for the great library.
-
very angry mobster very angry mobster https://www.kvraudio.com/forum/memberlist.php?mode=viewprofile&u=11047
- KVRian
- 611 posts since 15 Dec, 2003 from Melbourne, Australia
I would use r8brain for both the upsampling and downsampling.random_id wrote:I have been trying to implement r8brain in an oversampling scenario and I have a question. Using r8brain to downsample would be the obvious choice; however, what would be the recommended method for upsampling? Would zero-stuffing with some other filtering work more efficiently when upsampling? Or, would it be better to use r8brain for this as well?Aleksey Vaneev wrote:No, that's not overkill, I myself use linear-phase oversampling in Voxengo plug-ins that is close to that latency number and quality. You can reduce latency by using a lower ReqAtten and higher ReqTransBand values, it's flexible.
Thanks for the great library.
- KVRAF
- Topic Starter
- 4021 posts since 7 Sep, 2002
r8brain-free-src can be used for both tasks. Besides, I think it would be hard to find a faster implementation of linear-phase resampling than what r8brain-free-src offers.random_id wrote: I have been trying to implement r8brain in an oversampling scenario and I have a question. Using r8brain to downsample would be the obvious choice; however, what would be the recommended method for upsampling? Would zero-stuffing with some other filtering work more efficiently when upsampling? Or, would it be better to use r8brain for this as well?
If you just need to do 2x,4x,8x,etc upsampling/downsampling you may use objects of the CDSPBlockConvolver class directly. But you'll need to study the CDSPFIRFilterCache::getLPFilter() function to be fluent with filter parameters. For efficient multi-step resampling the transition bands of intermediate resampling steps must be set to larger values.
- KVRAF
- Topic Starter
- 4021 posts since 7 Sep, 2002
After a bit of thought, I think I'll add an additional "resampler" class which will do the "power of 2" resampling exclusively. It will be different to the CDSPResampler class in that it will report the full latency amount without consuming it automatically.
Right now I think CDSPResampler class cannot be used for oversampling inside plug-in's signal path, because CDSPResampler is not working synchronously.
Right now I think CDSPResampler class cannot be used for oversampling inside plug-in's signal path, because CDSPResampler is not working synchronously.
- KVRian
- 519 posts since 12 Apr, 2010 from The Netherlands
That would be a nice addition.Aleksey Vaneev wrote:After a bit of thought, I think I'll add an additional "resampler" class which will do the "power of 2" resampling exclusively. It will be different to the CDSPResampler class in that it will report the full latency amount without consuming it automatically.
Well, I have experimented with this only briefly, but it does seem to work already. However, I couldn't find a way to read out the latency programmatically, so I just measured it manually.Aleksey Vaneev wrote:Right now I think CDSPResampler class cannot be used for oversampling inside plug-in's signal path, because CDSPResampler is not working synchronously.
- KVRAF
- Topic Starter
- 4021 posts since 7 Sep, 2002
Yes, but that's a work-around. Besides, for the overall latency to remain a whole number, downsampling step may require insertion of several samples. So, in general case things are quite complicated if one wants to have consistent results at any setting.Tale wrote:Well, I have experimented with this only briefly, but it does seem to work already. However, I couldn't find a way to read out the latency programmatically, so I just measured it manually.
- KVRian
- 519 posts since 12 Apr, 2010 from The Netherlands
This was for 2x up, 2x down, and measuring the latency each time you change something was a pain, although it did seemed to be consistent.Aleksey Vaneev wrote:Yes, but that's a work-around. Besides, for the overall latency to remain a whole number, downsampling step may require insertion of several samples. So, in general case things are quite complicated if one wants to have consistent results at any setting.
Still, like I already expressed, I would very much welcome the addition of a better solution.
-
- KVRist
- 49 posts since 27 Dec, 2009 from Berlin
this is great! i am currently working on a c# wrapper.
but there is one thing i don't understand. the process function has double* as input and output type for the buffers. so does the process function only work with double[] types and i have to provide the samples always as double? and how does this work together with the resolution of the resampler type that i create?
i ask this because in my engine am using only 32-bit float buffers and it would need an additional step to convert the input/output buffers which would be painful for the performance. also the input to the resampler is often PCM 16 or 24 bit integer data directly from a wave file that i would need in 32-bit float buffer representation after.
so the setup is most of the time:
wavafile not in engine sample rate -> resampler -> 32-bit float buffer in engine sample rate
whats your suggestion for an optimal signal flow?
here is my first take on it:
https://github.com/tebjan/VVVV.Audio/bl ... nverter.cs
but there is one thing i don't understand. the process function has double* as input and output type for the buffers. so does the process function only work with double[] types and i have to provide the samples always as double? and how does this work together with the resolution of the resampler type that i create?
i ask this because in my engine am using only 32-bit float buffers and it would need an additional step to convert the input/output buffers which would be painful for the performance. also the input to the resampler is often PCM 16 or 24 bit integer data directly from a wave file that i would need in 32-bit float buffer representation after.
so the setup is most of the time:
wavafile not in engine sample rate -> resampler -> 32-bit float buffer in engine sample rate
whats your suggestion for an optimal signal flow?
here is my first take on it:
https://github.com/tebjan/VVVV.Audio/bl ... nverter.cs
-
- KVRist
- 49 posts since 27 Dec, 2009 from Berlin
and i have another question, in which cases does the op0 output buffer point to the input buffer? can you generalize this? only when downsampling?
this is an important question since the c# wrapper has to know whether it has to allocate unmanaged memory. in cases when the op0 output buffer does not point to the input buffer it can just pin the input array for the time of the call and no copy is needed.
this is an important question since the c# wrapper has to know whether it has to allocate unmanaged memory. in cases when the op0 output buffer does not point to the input buffer it can just pin the input array for the time of the call and no copy is needed.
-
- KVRist
- 49 posts since 27 Dec, 2009 from Berlin
i am not sure what i am doing wrong... the process method seems to work, but i get very high output values, like 3.15490588487157E+287. have you ever seen this? might be an obvious problem... my input values are double samples in the range [-1..1], this is correct, or not?
-
very angry mobster very angry mobster https://www.kvraudio.com/forum/memberlist.php?mode=viewprofile&u=11047
- KVRian
- 611 posts since 15 Dec, 2003 from Melbourne, Australia
That would be useful.Aleksey Vaneev wrote:After a bit of thought, I think I'll add an additional "resampler" class which will do the "power of 2" resampling exclusively. It will be different to the CDSPResampler class in that it will report the full latency amount without consuming it automatically.
- KVRAF
- Topic Starter
- 4021 posts since 7 Sep, 2002
Yes, you have to provide "double" samples, always.tonfilm wrote:this is great! i am currently working on a c# wrapper.
but there is one thing i don't understand. the process function has double* as input and output type for the buffers. so does the process function only work with double[] types and i have to provide the samples always as double? and how does this work together with the resolution of the resampler type that i create?
Such conversion is not "pain", it takes no more than 0.1% of conversion time.tonfilm wrote:i ask this because in my engine am using only 32-bit float buffers and it would need an additional step to convert the input/output buffers which would be painful for the performance. also the input to the resampler is often PCM 16 or 24 bit integer data directly from a wave file that i would need in 32-bit float buffer representation after.
Moreover, today it's much more practical to work with "double" buffers exclusively, it's an outdated approach, to use 32-bit float. From my tests, "doubles" work visibly faster than "floats", at least Intel C++ Compiler creates a faster code when "double" type is used.
I would not rely on any pointer comparisons. You have to write your code in a way so that a new block of input data is not processed until you consume all output data.tonfilm wrote:and i have another question, in which cases does the op0 output buffer point to the input buffer? can you generalize this? only when downsampling?
this is an important question since the c# wrapper has to know whether it has to allocate unmanaged memory. in cases when the op0 output buffer does not point to the input buffer it can just pin the input array for the time of the call and no copy is needed.
You have bug somewhere definitely.tonfilm wrote:i am not sure what i am doing wrong... the process method seems to work, but i get very high output values, like 3.15490588487157E+287. have you ever seen this? might be an obvious problem... my input values are double samples in the range [-1..1], this is correct, or not?
-
- KVRist
- 49 posts since 27 Dec, 2009 from Berlin
Yes, that was the case, i was just to overworked to see it. i handed over the wrong pointer to the input array, there are subtle details in .NET interop. it works now like a charm.Aleksey Vaneev wrote: I would not rely on any pointer comparisons. You have to write your code in a way so that a new block of input data is not processed until you consume all output data.
You have bug somewhere definitely.tonfilm wrote:i am not sure what i am doing wrong... the process method seems to work, but i get very high output values, like 3.15490588487157E+287. have you ever seen this? might be an obvious problem... my input values are double samples in the range [-1..1], this is correct, or not?
do you think i should host a project on github for the .NET wrapper? maybe others are interested too. current code is here:
https://github.com/tebjan/VVVV.Audio/bl ... nverter.cs
this is very interesting... as i am in an early stage of the engine, i can still switch to double precision as the overall buffer format. from what you write, it sounds like the thing to do... and hello, we live in 2014 soon, 64-bit machines are here to stay. but i fear that 90% of all VST plugins do not support double precision and modular vst routing is one of the core features of the engine. what do you think? maybe i should open a separate discussion thread for that...Aleksey Vaneev wrote:
Moreover, today it's much more practical to work with "double" buffers exclusively, it's an outdated approach, to use 32-bit float. From my tests, "doubles" work visibly faster than "floats", at least Intel C++ Compiler creates a faster code when "double" type is used.