From the original SynthMaker forum (2004–2014), with its members’ names and dates.
MDMA ·
Hi guys,
I recently noticed that by increasing the lenght of a played note with an envelope substantially increases cpu.
On the tests I've made it increased from 6% to 25%, going from a very short hold setting to a full one!
Is this normal?
Thanks in advance!
cyto ·
Without seeing your envelope code, it's hard to tell exactly what's going on, but I have a sneaking suspicion that what you are experiencing is being caused by de-normalized numbers. If you don't know what "de-normals" are, they are very small numbers (<1e-044-ish) that the CPU can't handle using it's normal math operations, so it needs to use a separate processor to handle them. You get them a lot in feedback loops when you are constantly multiplying a number by a value less than 1. I suspect that your envelope code may be using low-pass filters to accomplish some of the slopes and generally, such filters are very prone to sending the CPU into de-normal mode when one of the feedback arms goes on too long. It may be that the "hold" section is not really the culprit, but rather the hold section may be leaving the envelope "open" long enough to let the attack, decay, etc. filters enter into the de-normal state. Would it possible to post even a stripped down version of you envelope code in order to verify this?
-cyto
MDMA ·
Hey Cyto,
I've used an inspector from the forum and no sign of denormals!
You probably the best guy to talk about this seeing I'm using your pitch envelopes!
This schematic manifests the same issue!
Thanks for helping out mate!
trogluddite ·
MDMA wrote: I've used an inspector from the forum and no sign of denormals!
Denormals definitely sounds the most likely cause to me.
They can occur anywhere where there can be small floats - and that includes places inside blocks of code/assembly where they are hidden away.
Once a regular "normalised" number is added to a denormal number, the result will no longer be de-normal, so it's easy for them not to appear at the stream outputs of a code block.
As it is an envelope problem, I would suggest that one of two things is happening...
1) Envelopes are usually calulated by working out the rate of change (how much to go up or down per sample) - at very long times, this will be a very small number - depending how it is calculated, this value could be the source of denormals.
2) Most synths have several envelopes, and also other things that decay away slowly such as the response of filters. But the sound may go silent long before all of these have completely decayed away. The voice is only finally killed once the longest envelope has decayed to nothing, but other things may have already decayed into the "denormal zone" while the note is still "alive" but inaudible.
MDMA ·
trogluddite wrote: Denormals definitely sounds the most likely cause to me.
They can occur anywhere where there can be small floats - and that includes places inside blocks of code/assembly where they are hidden away.
Once a regular "normalised" number is added to a denormal number, the result will no longer be de-normal, so it's easy for them not to appear at the stream outputs of a code block.
Cheers mate, now I know I cant trust that inspector 100%
These bloddy de-normals are such a headacke, specially in cases like that were they are in the code hidden like you've mentioned!
Is there a way to find them inside code blocks? And to get rid of them we have to either redo our code or have a trial and error session with "add noise" and "truncate", right?
Thanks!
edit: never mind that mate, you've answered this question before when you've pm me once!
Hope you dont mind but I'm posting it here for others to see too, here it goes:
The 1.23e-567 thing is not strictly a denormal thing - it is just a way of writing very big and very small numbers without having to use lots of zeros. You can find a good explanation at the Wikipedia article about scientific notation.
Denormals have to do with the way that computers represent numbers - which is quite similar to the 'e' way of writing numbers, but is different because it is binary. Unfortunately, for very very small numbers, the computer's method does not work very well - making the CPU have to work a lot harder.
This is very annoying because thpse numbers are so small that you would never, ever hear them through your soundcard - so we always try to get rid of them.
Imagine an echo plugin that has some feedback - you make a sound, and then the echoes repeat over and over, but each one gets more and more quiet. After some time, they are too quiet to hear - but the CPU is still doing the maths, and some time, they will get so small to be denormals.
Many other things, such as filters, use these kind of 'decaying delays' (but they are much much smaller delays times).
That's why you get denormals only after the sound stops - when there is some sound coming in, it keeps the numbers big.
Trying to remove them by truncating the output is not always a good way to do get rid of denormals. Sometimes the denormals are inside one of the blocks of code - they don't always come to the output where you can see them, but even the ones 'trapped inside' still cause the CPU to go mad.
A better way is to use the "denormal remover" to add a tiny bit of noise at the input. The noise will be too quiet to hear (-120dB is the quietest for even a good soundcard) - but the noise makes the numbers just big enough all the time to stop them from dying away into the "denormal zone".
Usually, just one noise source right at the start of the schematic is enough to kill denormals all the way through to the ouput - so the CPU power is not really too high (much less than denormals anyway!).
There are also ways by adding clever tricks inside code and assembly - but these are much more tricky, as you need good code/assembly skills to know exactly what instructions to use - and it's even harder to know where to put them in the code (especially if it is someone else's code!).
Hope that's helps you solve your problem - and maybe it will make some of the other forum threads about denormals a bit easier to understand (I remember when I first searched for this problem - I didn't understand it at all!).
the Luddite.
MDMA ·
One other question,
How do you usually track them down?
Poly or mono read after modules to check if numbers are still being calculated after sound can no longer be heard? or by checking windows cpu meter and look for high peaks when sound is no longer present?
cyto ·
Hey MDMA.
I can't seem to recreate the problem on my machine, but looking over the code the most likely culprit is this line...
multThru = (factor*multThru);
... in the bottom pitch envelope block (not the one with adjustable slope). Do me a favor and disconnect that module from your schematic and just use the other envelope to see if the problem still is there. If it goes away, then my suspicions are correct and I'll work out a solution for you.
EDIT: better yet, see if this is any improvement. I just added a quick and dirty subnormal remover into the code right there. If you're still getting CPU spikes, we'll need to look elsewhere.
-cyto
trogluddite ·
MDMA wrote: Hope you dont mind but I'm posting it here for others to see too
No problem, I'm glad it's been useful.
MDMA wrote: How do you usually track them down?
Just the way Cyto suggested in his post, really - systematically bypass modules to isolate the one containing the problem. Once you've found the main module, go inside it, and repeat for sub-modulles etc. until you've found the culprit.
It really helps to be organised with mod's to a schematic too, and try not to add/change too much before having a good thorough test and saving it (with a new version number!). Spotting the problem early, and knowing which change caused it really take a lot of the tedium out of bug-splatting. - I often keep a comment box somewhere at the top schematic level where I can keep a few notes about recent changes, or ideas for improvements.
Something else I find handy is to work on code/assembly modules in a separate workspace away from the main project - kind of a "quarantine area" for stuff I know needs looking at. Without the clutter of the main project all over the place, it is much easier to set up test rigs, and I find puts me in a more analytical sort of mood - getting a problem solved rather than getting frustrated with the broken synth that's glaring at me!
A good external code editor like Notepad++ can help too - SM's text editing is rubbish, and assembly has no variable name checking either. With an external editor, you get all the luxuries of a good word-processor - search, replace, multiple views of the same code block, and much faster to work with than a fiddly little module box.
Inside the code, you're looking for any variables that get used in feedback loops - they will usually be the ones that are read by the code before being stored again later in the code. If at some point a "read before write" variable is scaled down by a multiplication or division you have a very likely cause of denormals.
PS) Don't forget that you can always add an extra "streamout Test" to a code block as a test terminal for the stream inspector. Simply adding a quick "Test=????" or "movaps Test,xmm?" sticks a "test probe" into your code so that you can see what's going on inside.
MDMA ·
cyto wrote: Hey MDMA.
I can't seem to recreate the problem on my machine, but looking over the code the most likely culprit is this line...
multThru = (factor*multThru);
... in the bottom pitch envelope block (not the one with adjustable slope). Do me a favor and disconnect that module from your schematic and just use the other envelope to see if the problem still is there. If it goes away, then my suspicions are correct and I'll work out a solution for you.
EDIT: better yet, see if this is any improvement. I just added a quick and dirty subnormal remover into the code right there. If you're still getting CPU spikes, we'll need to look elsewhere.
-cyto
Looking at the synthmaker stream %, I would say this schematic is better, I've run similar tests and on the previous version the stream was reaching 2.1% and its now reaching 1.3% 🙂
Only, it got a bit harder for me to check on hosts like I was doing before in studio one 2 winch has a nice vst individual cpu meter because I've just cleaned my cpu's fan and got back alot of power I was missing, so before the first schematic was 4-6% on studio one and now goes only to 1-2% and the new schematic to 0-1%, preaty much what I get in synthmaker now. The thing is my full plugin was going 8-25% sometimes 35% and now only reaches 6-8%, its good but bad at the same time as I have less precision lol.
Anyway, cyto, thanks a lot mate, that already presents a nice solution and now I need to check what you've done in the code to make it happen so that I can learn how to do it myself and stop pissing you guys off all the time lol.
Trogluddite, thanks a lot for the nice tips mate, they make a lot of scence and I will definatly follow them! 🙂
cyto ·
@MDMA - glad it worked out! 🙂
trogluddite wrote: PS) Don't forget that you can always add an extra "streamout Test" to a code block as a test terminal for the stream inspector. Simply adding a quick "Test=????" or "movaps Test,xmm?" sticks a "test probe" into your code so that you can see what's going on inside.
Funny you should mention this. If you look in the revised schematic I posted above, you can see where instead of just declaring the "multThru" as a variable, I made a streamout of it so I could check its status. After I posted, I was embarrassed that I left it like that, but now I claim that I intentionally left it like that for "teaching purposes" 😉
When I'm working on assembly blocks, I always put 4 extra monoouts so I can always shoot a quick value to them to see what's going on. Very useful for "debugging".
-cyto
MDMA ·
cyto wrote: @MDMA - glad it worked out! 🙂
trogluddite wrote: PS) Don't forget that you can always add an extra "streamout Test" to a code block as a test terminal for the stream inspector. Simply adding a quick "Test=????" or "movaps Test,xmm?" sticks a "test probe" into your code so that you can see what's going on inside.
Funny you should mention this. If you look in the revised schematic I posted above, you can see where instead of just declaring the "multThru" as a variable, I made a streamout of it so I could check its status. After I posted, I was embarrassed that I left it like that, but now I claim that I intentionally left it like that for "teaching purposes" 😉
When I'm working on assembly blocks, I always put 4 extra monoouts so I can always shoot a quick value to them to see what's going on. Very useful for "debugging".
-cyto
Cheers mate, that ended up being very instructive after all 😉
Now I've understood from your code that you (max)ed the "multitru"(value manifesting denormals) with "1e-036"(the denormal remover), this was to allways have big numbers going on to keep the cpu from going to denormal state right?
My question now is:
How did you came up with this number and would this be an aproach to any code block?
If not, what other aproaches can we use?
PS-Thanks alot guys, this is great as I feel it's going to take alot of weight and fustration of my shoulders, I was giving up on finding solutions for these denormals and was very upset with all this but you are great teachers and very nice patiente persons who after teaching in many different ways are finally drilling trough this hard skull of mine and clarifying this for me and for anyone who wants to learn!
Many thanks!
cyto ·
MDMA wrote: My question now is:
How did you came up with this number and would this be an aproach to any code block?
If not, what other aproaches can we use?
The main reason I use that particular number is because the smallest "normalized" value in float 32 is (I think) 1.17549435E-38 (positive or negative). The 1e-036 is a nice round number that is close enough to that value but still allows a little "wiggle room" for another mult or two.
And no, that is not a good all-purpose subnormal removal method. I can get away with it here because I know that the "multThru" value is always going to be positive. In most situations, though, you'll be trying to remove subnormals from oscillating signals that swing between positive and negative numbers. In which case, you need to test for the absolute value of the signal. Something like this may be appropriate...
Some people like to simply add a small value (1e-024ish) to the signal and then immediately subtract that value out again. This method exploits the fact that the insignificant subNormal value gets "wiped out" in the arithmetic. I don't particularly like this method for a number of reasons that I won't get into here. Of course there always is the option of adding small amounts of noise to the signal. This works on the same principal of "wiping out" the insignificant values.
I guess the bottom line is, whatever method you choose really does rely on the context of the situation. When you're working with your own code its somewhat easy to keep track of potential subnormal "traps" and you can put in the appropriate safeguards. Modifying others' code is a little harder and requires some detective work to identify the problem areas. This is something that you always need to keep an eye out for, but the good news all of these frustrating situations like you've experienced always provide you with additional insight so the next time you run across them, you'll be armed to deal with them better.
-cyto
Wrote something here? To have a post removed or your name hidden, write to [email protected].