SynthMaker

Denormals in the de-zipper

Archive · 2008 15 old posts

From the original SynthMaker forum (2004–2014), with its members’ names and dates.

infuzion ·

I did a bit more hacking on malc's ASM de-zipper. Without the hop(), this new version is about 5 cycles faster on my C2D, but maybe 1.1 cycle slower on my AMD64, & .2 cycles slower than his original. This new one has more hand-interleaving to take advantage of (expecting) C2D to execute add & compare ops at the same time, executing up to 3 movaps at once, & a trick with the pre-loading the out into xmm6: streamin in;streamout out; float in1=0;float cnt=2048; float step=0;float F1=1; float F16=16;float F0=0; float F2048=2048; movaps xmm6,out; mov eax,ecx;and eax,127;cmp eax,0;jnz endCalcStep; // Calculate cnt movaps xmm0,in1; cmpps xmm0,in,0; movaps xmm2,cnt; movaps xmm1,xmm2; //swap movaps xmm7,F0; cmpps xmm2,F2048,0; orps xmm0,xmm2;

addps xmm1,F1; minps xmm1,F16; andps xmm0,xmm1; movaps xmm2,xmm0; movaps xmm3,xmm0; movaps cnt,xmm0;

// Calculate step movaps xmm1,in; cmpps xmm0,xmm7,0; subps xmm1,xmm6; //out cmpps xmm2,xmm7,6; divps xmm1,F2048; cmpps xmm3,F16,1;

andps xmm2,xmm3; andps xmm2,step; andps xmm0,xmm1;

addps xmm0,xmm2; movaps step,xmm0;

// Calculate out movaps xmm6,xmm0; //outside hop() out cmpps xmm0,xmm7,4; andps xmm0,out;

cmpps xmm6,xmm7,0; andps xmm6,in; movaps xmm1,in; addps xmm6,xmm0; movaps out,xmm6;

// Remember previous in movaps in1,xmm1;

endCalcStep:

// Add step addps xmm6,step; movaps out,xmm6; Could someone please double-check the output values?

Note: I don't think I'll ever do this much hacking inside a hop() again; not worth it. The only thing makes this worth it to me is that I could have this code 64+ times in my VSTi. After removing the obvious extra movaps & the typical quick-fixes (which do make a difference inside a longer hop()), an extra 1 minute max of special hacking inside a hop is more than enough.

Zynewave ·

infuzion wrote: I did a bit more hacking on malc's ASM de-zipper.

Thanks for your efforts. I know malc is still working on the de-zipper. I got an email from him, where he presented the excellent idea that the hop should be kept constant, and only the glide period should be varied in 128 sample steps, to compensate for the different sample rates. That's sufficiently precise.

Here's a part of my reply to him:

My main concern was the factor 2 involved with 88.2/96 kHz double samplerates. While we're at it: Why not adjust the glide periods so that it is closer to a more sensible value such as 50 ms. Looks nicer if you want to write the glide time in the component reference pdf. That results in 2176 for 44.1 (49.3ms) and 2432 for 48.0 (50.7 ms).

I've also done some thinking on the de-zipper code. It struck me that the step calculation can become inaccurate for larger glide periods, due to the limited float precision. This means that the gliding can over/undershoot the target value. So, since the divide is performed in each hop block anyway I figured it could just as well recalc a more precise step value based on the decreasing glide period. It turned out to be a simpler and more efficient implementation, involving fewer compares. I was kicking myself for not having thought of this solution from the beginning:

streamin in; streamin glidePeriodPlus128; streamout out; float cnt = 0; float in1, step;

hop(128) { // Use max(,1) to avoid runaway cnt and division by zero in step calc. cnt = max(cnt - 128 + (in1 != in)&(cnt>0)&(glidePeriodPlus128-cnt), 1); step = (cnt>1)&((in - out) / cnt); // Copy in to out when not stepping, to compensate // for any rounding errors in step additions. out = (step!=0)&out + (step==0)∈

in1 = in; }

out = out + step;

aliasant ·

Infuzion said: Could someone please double-check the output values?

I checked and it seems to output the values correct.

I also checked with a frequency graph on all the different DeZip versions and they all shows up on the low end..... except Zynewaves new thing.

This one looks like something else.

Edit: Looking at Zynewaves new thing I noticed that the glideperiod needs to get up above 1024. Then it starts to sound good.

Zynewave ·

aliasant wrote: Edit: Looking at Zynewaves new thing I noticed that the glideperiod needs to get up above 1024. Then it starts to sound good.

You need to set the glideperiod input to 2048+128 to make it behave like the earlier de-zipper versions.

aliasant ·

Zynewave wrote: aliasant wrote: Edit: Looking at Zynewaves new thing I noticed that the glideperiod needs to get up above 1024. Then it starts to sound good.

You need to set the glideperiod input to 2048+128 to make it behave like the earlier de-zipper versions.

Yes. I see that now. Cool. Experimenting with it. Sending a LF squarewave thru it and other stupid things =)

infuzion ·

Zynewave wrote: It struck me that the step calculation can become inaccurate for larger glide periods, due to the limited float precision. ...I was kicking myself for not having thought of this solution from the beginning:

"limited float precision"... is that another vote for SSE2? 😉 Sometimes it takes a while for me to figure out the best (often simplest) solution. :/

aliasant wrote: Infuzion said: Could someone please double-check the output values?

I checked and it seems to output the values correct. I also checked with a frequency graph on all the different DeZip versions and they all shows up on the low end..... except Zynewaves new thing.

Thanks fro the help aliasant... is malc's/mine act like a filter?

aliasant ·

This is me running a 1kHz Saw thru the dezipper so yes. It acts like a filter of some kind.

Andrew J ·

Well that is what a de-zipper is for! You filter out higher frequencies so that you don't have big/fast changes (i.e. pops).

oddson ·

Here's one way to smooth and still avoid denormals but it won't work on data with a broad range of scale:

streamin x; streamout y;

hop(32){ y = y + ((x<y & -2) + 1) * 0.001; }

It moves in set step sizes and therefore avoids denormal multiplication.

It may not be appropriate for the default dezipper code because of the scale problem but if you work only with normalized parameters it might be right for someone with dozens of dezippers in their projects (and you could pick your own hop and delta values).

The existing timer mechanism could still be used to flip to the exact value after the code has got you into the ballpark.

malc ·

So based on Frits' great idea of using a fixed glide time and the code he presented earlier, here's what IMHO looks to be a good candidate for the dezipper primitive: Download Latest Dezipper

The code is here. If you fancy waving your magic wand over it infuzion please do.

streamin in; streamin glidePeriodPlus128; streamout out;

float cnt=0; float in1=0; float step=0; float F0=0; float F1=1; float F128=128;

// Only calculate step every 128 samples mov eax,ecx; and eax,127; cmp eax,0; jnz endCalcStep;

// Calculate cnt movaps xmm0,cnt; movaps xmm2,xmm0; movaps xmm3,xmm0; subps xmm0,F128;

movaps xmm1,in1; cmpps xmm1,in,4; cmpps xmm2,F0,6; andps xmm1,xmm2;

movaps xmm2,glidePeriodPlus128; subps xmm2,xmm3; andps xmm1,xmm2; addps xmm0,xmm1; maxps xmm0,F1; movaps cnt,xmm0;

// Calculate step movaps xmm3,xmm0; cmpps xmm0,F1,6; movaps xmm1,in; subps xmm1,out; movaps xmm2,xmm1; divps xmm2,xmm3; andps xmm0,xmm2; movaps step,xmm0;

// Calculate out movaps xmm2,xmm0; cmpps xmm0,F0,4; andps xmm0,out; cmpps xmm2,F0,0; andps xmm2,in; addps xmm0,xmm2; movaps out,xmm0;

// Remember previous in movaps xmm0,in; movaps in1,xmm0;

endCalcStep:

// Add step movaps xmm0,out; addps xmm0,step; movaps out,xmm0;

The glidePeriodPlus128 input is calculated in a green module by using sample rate and a fixed (but user defineable) glide period in millisecs.

Zynewave ·

malc wrote: The glidePeriodPlus128 input is calculated in a green module by using sample rate and a fixed (but user defineable) glide period in millisecs.

I've already notified malc by email: There's a small bug in the green module. It should add 128 to the final int output, due to the way that "cnt" is calculated in the code.

infuzion ·

Zynewave wrote: malc wrote: The glidePeriodPlus128 input is calculated in a green module by using sample rate and a fixed (but user defineable) glide period in millisecs.

I've already notified malc by email: There's a small bug in the green module. It should add 128 to the final int output, due to the way that "cnt" is calculated in the code.

Yea, I was wondering where the +128 of the glidePeriodPlus128 went to? 🙂

Might be a day or 2 before I can really study this; I just got a 20% pay cut, so I think I'll job-hunt in the morning rather than SM-hack; depends how early I can wake up.

infuzion ·

malc wrote: The code is here. If you fancy waving your magic wand over it infuzion please do.

OK, I couldn't sleep... so here it is: http://shup.com/Shup/33660/new-dezipper ... _saved.osm

streamin in;streamout out; streamin glidePeriodPlus128; float cnt=0;float in1=0;float step=0; float F0=0;float F1=1;float F128=128; movaps xmm7,out; //hop(128) mov eax,ecx;and eax,127;cmp eax,0;jnz endCalcStep; // Calculate cnt movaps xmm0,cnt; movaps xmm2,xmm0; movaps xmm3,xmm0; subps xmm0,F128; movaps xmm6,in;

movaps xmm1,in1; cmpps xmm1,xmm6,4; movaps xmm4,glidePeriodPlus128; cmpps xmm2,F0,6; subps xmm4,xmm3; andps xmm1,xmm2;

andps xmm1,xmm4; addps xmm0,xmm1; maxps xmm0,F1; movaps cnt,xmm0;

// Calculate step movaps xmm3,xmm0; cmpps xmm0,F1,6; movaps xmm4,xmm6; subps xmm6,xmm7; //in-out movaps xmm2,xmm6; divps xmm2,xmm3; andps xmm0,xmm2; movaps step,xmm0;

// Calculate out movaps xmm2,xmm0; cmpps xmm0,F0,4; andps xmm7,xmm0; cmpps xmm2,F0,0; andps xmm2,xmm4; addps xmm7,xmm2; movaps out,xmm7;

// Remember previous in // movaps xmm4,in; movaps in1,xmm4;

endCalcStep:

// Add step //movaps xmm7,out before hop addps xmm7,step; movaps out,xmm7; I commented out a few commands so you know we don't need them. Considering the hop, I saved an average of 0.0625 cycles!! Without the hop, it would have been about 8 cycle-savings on both my AMD64 & C2D, which is decent. Most of that savings is from pre-loading in & out values in a xmm & using that xmm. Using an xmm for F0 (=0) was slower.

I added the 128 in the green, & did a tad of primitive reduction there.... I don't think using a Int in a Float Divide would matter speed wise for a call-once-on-load routine?

If someone can test output please? 😇 (remember malc's may not have the green +128 just before output)

malc ·

Works well here inf, thanks for looking at it!

I completely forgot to add the 128 after Zynewave pointed it out 😊

Well I think I can make a primitive from this now. If anyone finds any other issues let me know.

Thanks to everyone - top effort!

Zynewave ·

infuzion wrote: If someone can test output please? 😇 (remember malc's may not have the green +128 just before output)

Works fine here. Thanks.

I think this thing is ready for mass production!

Wrote something here? To have a post removed or your name hidden, write to [email protected].

Before your first post

A few rules keep this a friendly place. You agree to them once.

Shown next to what you post, here and in the app. It doesn’t have to be your real name: 3–24 lower-case letters, digits or _ (capitals become lower case, spaces become _).

    Then your post goes up straight away.

    Delete this reply?

    Open it in SynthMaker?

    It goes on the track you’re editing there, in place of that track’s sound, and into My sounds. Your other tracks stay as they are.

    Want to keep that track’s sound? Start a new song in SynthMaker first (Song › New song), then open this.

    Report this reply?

    Only the developer reads reports. The person isn’t told who reported them.

    What’s wrong with it?

    Block this person?

    You won’t see their posts and replies here any more. They aren’t told, and you can unblock them at any time.

    People you’ve blocked

    Their posts and replies are hidden from you. Unblock someone to see them again.

    The community rules

    A few rules keep this a friendly place. Everyone agrees to them once, before their first post.