Oversampling in VMD

User avatar
ChR_is
Posts: 106
Joined: Wed Sep 22, 2021 5:48 pm

Re: Oversampling in VMD

Post by ChR_is »

utdgrant wrote: Thu Jan 12, 2023 11:57 am Can't thank you enough for this, Chris. It's exactly what I've been seeking for quite some time, now. What a fantastic, generous gesture.

I'm currently investigating how it can be retrofitted to some of the non-linear Dome Music Technologies audio modules. Of course, any modifications I make to the core library will be shared with the community in the same spirit as the original.
you're welcome! also feel free to reach out with questions or if you need help integration R_OpenLib into your projects. :)
my advice would be to stick to the R_OpenLib conventions/interfaces. when the library is expanded everything will fit into place. therefor, if you used the interfaces as they are, you'll be able to mix and match with other library modules as well ;)
ColinP
Posts: 940
Joined: Mon Aug 03, 2020 7:46 pm

Re: Oversampling in VMD

Post by ColinP »

ChR_is wrote: Thu Jan 12, 2023 3:18 pm that's what i meant by filter 'memory'. each biquad in the stack remembers two states. these are processed past values.
I must confess I missed that reference to filter memory and quickly scanning through the code I interpreted m_biquadCoeffs to be a passive data structure holding just coefficients. Only later did I see the helpful "// stores freq, res, coefficients and past values" comment and delve deeper into what was going on. It should have been obvious to me that some kind of Z memory was in play. In my defence I'm surrounded by water starting at a minimum depth of 1.8 metres that has a temperature of 4 C and this kind of situation adversely affects my ability to think straight.

Your contribution is pretty awesome. I'm very grateful as I looked at oversampling for a couple of weeks last year and found it difficult to find useful resources. I understand the concepts but at an academic rather than practical level. I'm a generalist and knowing a little about a lot of things can be dangerous.

In my research I did manage to come across this excellent introduction to polyphase interpolation though...

https://www.allaboutcircuits.com/techni ... ilter-dsp/
User avatar
ChR_is
Posts: 106
Joined: Wed Sep 22, 2021 5:48 pm

Re: Oversampling in VMD

Post by ChR_is »

ColinP wrote: Thu Jan 12, 2023 4:03 pm
ChR_is wrote: Thu Jan 12, 2023 3:18 pm that's what i meant by filter 'memory'. each biquad in the stack remembers two states. these are processed past values.
I must confess I missed that reference to filter memory and quickly scanning through the code I interpreted m_biquadCoeffs to be a passive data structure holding just coefficients. Only later did I see the helpful "// stores freq, res, coefficients and past values" comment and delve deeper into what was going on. It should have been obvious to me that some kind of Z memory was in play. In my defence I'm surrounded by water starting at a minimum depth of 1.8 metres that has a temperature of 4 C and this kind of situation adversely affects my ability to think straight.

Your contribution is pretty awesome. I'm very grateful as I looked at oversampling for a couple of weeks last year and found it difficult to find useful resources. I understand the concepts but at an academic rather than practical level. I'm a generalist and knowing a little about a lot of things can be dangerous.

In my research I did manage to come across this excellent introduction to polyphase interpolation though...

https://www.allaboutcircuits.com/techni ... ilter-dsp/
it's been quite a while since i did my research on these topics and implemented my oversampling library, so i can't tell you in full detail why i chose not to implement polyphase IIRs. if i remember correctly it was either not worth the effort or didn't work with the filters i was using. but i did implement polyphase FIR filters for oversampling. i will open source these and add them to R_OpenLib as well in the future, but 1.) i need to rewrite my library so they are more user-friendly and 2.) i'll keep them exclusive to R_Ware for economical reasons for now. :twisted:
ColinP
Posts: 940
Joined: Mon Aug 03, 2020 7:46 pm

Re: Oversampling in VMD

Post by ColinP »

ChR_is wrote: Thu Jan 12, 2023 7:44 pm it's been quite a while since i did my research on these topics and implemented my oversampling library, so i can't tell you in full detail why i chose not to implement polyphase IIRs. if i remember correctly it was either not worth the effort or didn't work with the filters i was using. but i did implement polyphase FIR filters for oversampling. i will open source these and add them to R_OpenLib as well in the future, but 1.) i need to rewrite my library so they are more user-friendly and 2.) i'll keep them exclusive to R_Ware for economical reasons for now. :twisted:
Is it even possible to have polyphase interpolation that use IIRs? My (albeit limited) understanding is that polyphase interpolation only works with FIR filters.

But getting solid info on DSP is really hard work. So much of it is hidden behind propiertorial walls and many of the open articles don't contain any code and assume a level of understanding of the maths involved that's beyond your average programmer's skill set. I guess it's understandable as these are valuable "tricks of the trade" but often the final code ends up being pretty simple compared with the complexity that most programmers deal with routinely.

I paused my adventures in oversampling land because I came to the conclusion that the cost benefit ratio ain't that brilliant compared with other ways of avoiding aliasing. Having say four times more CPU use is seriously costly and needs to be weighed carefully.

Also personally the only real examples I've heard of nasty aliasing are in unrealistic situation using exposed synth sounds that would be as boring as hell even without the aliasing. I am probably alone in this opiniion here but in real world scenarios where music is multi-layered and fluid I doubt that any listener would ever think gosh - that track would have been great but is ruined because of the aliasing.

But it's still a very interesting field.
User avatar
utdgrant
Posts: 535
Joined: Wed Apr 07, 2021 8:58 am
Location: Scotland
Contact:

Re: Oversampling in VMD

Post by utdgrant »

Hi Chris,

Just to let you know that the Hardclip example project built straight out of the box for me.

I'm looking forward to some experimentation over the coming weekend. :)

Thanks again,
Grant
______________________
Dome Music Technologies
ColinP
Posts: 940
Joined: Mon Aug 03, 2020 7:46 pm

Re: Oversampling in VMD

Post by ColinP »

Just one little suggestion.

The example process you use is...

Code: Select all

return 0.5 * ( Math.abs( value + 1 ) - Math.abs( value - 1 ) );
...this is an extremely obscure and inefficient way of calculating a simple function...

xfer.png
xfer.png (30.06 KiB) Viewed 1124 times

If you replaced it with something like the following then it would be far easier for people to understand what's going on.

Code: Select all

if( value >= 1)
	return 1;
if( value <= -1 )
	return -1;
return value;
AllanH
Posts: 13
Joined: Sat Nov 26, 2022 5:23 pm

Re: Oversampling in VMD

Post by AllanH »

@Chris - thank you for sharing the library - this is very generous. I see you also took the time to refactor and improve it just a day later! Much appreciated!
User avatar
ChR_is
Posts: 106
Joined: Wed Sep 22, 2021 5:48 pm

Re: Oversampling in VMD

Post by ChR_is »

@ColinP how do you come to the conclusion that my hardclip function is inefficient? it is indeed obscure, but it's not inefficient ;)
in my experience branching (e.g. if/else) is the most expensive operation you can do for multiple reasons. a 1 mult, 3 adds and 2 function calls that will probably be optimized heavily by the JIT are very efficient imho. but my opinion doesn't count when it comes to facts, so i created a small testbench in Processing and here are the results (values in nanoseconds):

Code: Select all

ifs avrg:      2464,1439
Obscure avrg:  126,8041

ifs avrg:      2603,2708
Obscure avrg:  129,8245

ifs avrg:      2439,2907
Obscure avrg:  121,0953

ifs avrg:      2596,2596
Obscure avrg:  127,8232

ifs avrg:      2425,8882
Obscure avrg:  120,8945

ifs avrg:      2613,9049
Obscure avrg:  128,3053

ifs avrg:      2424,9403
Obscure avrg:  120,4341
from these measurements i can conclude that my obscure method is about 20 times as efficient/takes about 1/20 of the time than the ifs.

here's the Processing sketch if you want to try it out on your own system:

Code: Select all

int cntr;
int limit;
double avrgIfs;
double avrgObscure;

void init()
{
  cntr=0;
  avrgIfs=0;
  avrgObscure=0;
}

void setup()
{
  frameRate( 48000 );
  limit = 240000;
  init();
}

void draw()
{
  double timer;
  double[] valuesIf = new double[480];
  double[] valuesObs = new double[480];
  if( ++cntr > limit )
  {
    System.out.println( String.format(
      "ifs avrg:      %.4f\n"
    + "Obscure avrg:  %.4f\n", avrgIfs/cntr, avrgObscure/cntr
    ) );
    init();
  }
   
  for( int i = 0; i < 480; ++i )
  {    
    double value = 13 * Math.random() - 7;
    valuesIf[i] = value;
    valuesObs[i] = value;
  }
  
  timer = System.nanoTime();
  for( int i = 0; i < 480; ++i )
  {
    if( valuesIf[i] >= 1)
    {
      valuesIf[i] = 1;
    }
    else if( valuesIf[i] <= -1 )
    {
      valuesIf[i] = -1;
    }
  }
  timer = System.nanoTime() - timer;
  avrgIfs += timer;
  
  timer = System.nanoTime();
  for( int i = 0; i < 480; ++i )
  {
    valuesObs[i] = 0.5 * ( Math.abs( valuesObs[i] + 1 ) - Math.abs( valuesObs[i] - 1 ) );
  }
  timer = System.nanoTime() - timer;
  avrgObscure += timer;
}
ColinP
Posts: 940
Joined: Mon Aug 03, 2020 7:46 pm

Re: Oversampling in VMD

Post by ColinP »

Wow, thanks Chris, that's something of an education. I'm very surprised by those metrics.

I'll run through the reasoning that led me to expect totally different results. There might be a bug in the following pseudo machine code but I'm sure you'll get the drift.

I would expect the if version to work something like this...

Code: Select all

	load value
	subtract 1
	branch to A if sign negative
	load 1
	branch to Exit
A:
	load value
	add 1
	branch to B if sign negative
	load value
	branch to Exit
B:
	load -1
Exit:

So if value >= 1 this would take 5 cycles
if value <= -1 this would take 8 cycles
if value < 1 and value > -1 this would take 7 cycles

While for the obscure version I would expect something like the following...

Code: Select all

	load register 1 with value
	add 1 to register 1
	branch to A if register 1 positive
	negate register 1
A:
	load register 2 with value
	subtract 1 from register 2
	branch to B if register 2 positive
	negate register 2
B:
	subtract register 2 from register 1
	multiply register 1 by 0.5
So if value >= 1 then 8
if value <= -1 then 10
if value < 1 and value > -1 then 9

So more cycles and one would also expect a double precision multiplication to take longer to execute that the other "virtual cycles" I'm using in this model.

Trying to analyse why I'm so wrong, I can see that the abs() probably doesn't use a branch these days as it'll be a hardware function in modern FPUs and will probably execute much faster than in the old days.

But I'm still really surprised by those metrics. Instruction caching should make such small amounts of code work smoothly from an instruction fetch POV. I guess I'm underestimating the impact on pipelining of branching. I wonder if the random data you are using makes any difference to how well the branch prediction works. More coherent/montonic data might have different results but I doubt it would turn around that surprising 20:1 result.

Just as a matter of interest what CPU did you run that test on?

Anyway, thanks for opening my eyes.
User avatar
utdgrant
Posts: 535
Joined: Wed Apr 07, 2021 8:58 am
Location: Scotland
Contact:

Re: Oversampling in VMD

Post by utdgrant »

ChR_is wrote: Sat Jan 14, 2023 2:12 pm in my experience branching (e.g. if/else) is the most expensive operation you can do for multiple reasons. a 1 mult, 3 adds and 2 function calls that will probably be optimized heavily by the JIT are very efficient imho. but my opinion doesn't count when it comes to facts, so i created a small testbench in Processing and here are the results (values in nanoseconds):

Code: Select all

ifs avrg:      2464,1439
Obscure avrg:  126,8041
from these measurements i can conclude that my obscure method is about 20 times as efficient/takes about 1/20 of the time than the ifs.
Can't argue with the evidence! :)

That's so unintuitive to someone who started coding in the early '80s on simple 8-bit processors. However, I've kept pace with the technology and I can understand why modern processors with multiple cores, pipelining and look-ahead will choke on branch instructions. I didn't realise it would have such a dramatic impact on performance, though!

Fascinating stuff again, Chris. Thanks for taking the time to develop and run these benchmarks.
______________________
Dome Music Technologies
Post Reply

Return to “Module Designer”