From the original SynthMaker forum (2004–2014), with its members’ names and dates.
oddson ·
Here's a custom coded ADSR for those who have asked how custom envelopes work.
I'll see about writing it up as a tutorial (before they take away my 'wiki guru' designation).
I'm not the best coder around here so it may not be ideal but hopefully you can get the idea from it.
Off course there's not much point to a custom linear segment ADSR itself. But hopefully those interested can start to see where non-linear or multi segments might fit in.
simpleEnv.osm
I took one liberty with the ADSR concept. Instead of calculating the slope of the release to ensure it ends at the number of samples at the R input I've calculated the slope to give that release time only if the release occurs in the sustain phase. If it occurs early, the release will run shorter (if in the early part of the attack) or longer based on the output value when the release is detected.
I've used a 16 cycle hop -- just to show you can get away with it.
In fact I just cranked it up to 1024 and (with my tin ear on my crappy speakers playing a saw wave with 1-2 second segments) I can't hear any problem with it.
sequencer ·
I think you will be keeping the wiki guru title for a long time to come.
I would also add "all round helpful dude guru" to that, but I guess it probably wouldn't fit in the space allotted. 🙂
rlahalla ·
+1 😀 😀
TheAudiophileDutchman ·
Thanks a lot (herewith you also answered my quite recent request)! 🙂
AngularMomentum ·
sequencer wrote: I think you will be keeping the wiki guru title for a long time to come.
I second that as well
global goon ·
wow, cool... has anyone worked out how to get different curves for the slopes? I don't understand the masking and stuff in this module. 😊
infuzion ·
oddson wrote: I've used a 16 cycle hop -- just to show you can get away with it.
In fact I just cranked it up to 1024 and (with my tin ear on my crappy speakers playing a saw wave with 1-2 second segments) I can't hear any problem with it.
How was the CPU usage for you with the various hops?
cheers
oddson ·
@infuzion - changing the hop on this simple of an envelope won't give dramatic differences. But on my Drawn Envelope the difference is significant.
@global goon - shifrin and sambean collaborated on a multi/curved-segement env but it's likely too complicated for learning from for most people.
As for masking... I admitted in the comments of this that I'm not the best at it. I'll try to explain:
out = out + (stage==1 & Aslope) + (stage==2 & Dslope); // output A & D
out equals out from the previous run through. Then, a different slope value is added for stage 1 or stage 2. If the current stage is neither then this step does not change the value of out.
out = (stage<3 & out) + (stage==3 & S); // ...& S
if the stage is less than the sustain (=3) then out is output from above step. If it is stage 3 then it's the sustain value.
out = (stage<4 & out) + (stage==4 & (out1+Rslope)); // ..& R
For all stages <4 output is out from above, for release it's the saved output plus the release slope. I never figured out when I couldn't just use the current value of out for stage 4 but it didn't work unless i used an explicit assignment to a new variable.
infuzion ·
oddson wrote: For all stages <4 output is out from above, for release it's the saved output plus the release slope. I never figured out when I couldn't just use the current value of out for stage 4 but it didn't work unless i used an explicit assignment to a new variable.
I'm wondering it's how Rslope is calcualted?
BTW, you can reduce cycles by avoiding divides, like instead of:
hopN = 16; // must be same as hop number in main loop
hopA = A/hopN; // number of hops through A,...
hopD = D/hopN; // ...D,...
hopR = R/hopN; // ...R
you can do:
hopN = 1/16; // 1/x, x=hop number in main loop
hopA = A*hopN; // number of hops through A,...
hopD = D*hopN; // ...D,...
hopR = R*hopN; // ...R
saving about 51 cycles or so (each divide is about 35 cycles, as opposed as 7 cycles for multiplies (in SSE)).
But I think you can avoid using hopA/D/R alltogether if you increment cntr by the hop # (in this case 16) rather than 1. Might have to increment at the end of loop, rather than beginning. No time to test right now, if someone wants to give it a go please.
oddson ·
...saving about 51 cycles or so ...
OK... but it's in stage(0) so that's only 51 cycles per note that's played. I'm fairly carefull inside a hop or at sample rate but I don't worry about one time stuff.
If you want to be picky 1/16 should be 0.0625. 😉
oddson ·
infuzion wrote: ...I'm wondering it's how Rslope is calcualted?
Rslope = -1*S / hopR ;
The slope in the release section is the sustain value divided by the length of the release in units equal to the hop length (negated).
It's the amount the output is reduced by for each run through the code.
So if the release is in the sustain phase it should hit zero (approximately) after the number of samples designated in the release value.
It's the same calculation as all the other slope values. But if the release occurs near the top of the attack the release section will run longer than the designated release time. I'm not sure if that's how ADSRs normally work or not but it makes more sence to me and it's a whole lot easier to calculate.
infuzion ·
How about eliminating 2 variables, 1 add & 1 compare+assign in the hop loop?
BTW, in programming it's bad form to use a reserved word, so I changed stage to stg.
streamin A;streamin D;streamin S;streamin R;
streamin holdStage;
streamout out;
streamboolout releaseOff;
float cntr; // counter
float Aslope,Dslope,Rslope; // slope of phases
float hopA,hopAD; // ADSR adjusted for hop length
float stg; // stages:A=1,D=2,S=3,R=4
stage(0){
hopA = A; // number of hops through A,(end of 1st stage)...
Aslope = 1 / (A*0.0625); // output change per hop in A: 1/(A*(1/hop-amount))
hopAD = A+D; // end of 2nd ADSR stage
Dslope = (-1+S) / (D*0.0625);
Rslope = -1*S / (R*0.0625);
}
hop(16) //hopN value - but varible can't be used here
{
stg = 1 + ((cntr>hopA) & 1) + ((cntr>(hopAD)) & 1); // stage for ADS
stg = (holdStage==3 & stg) + (holdStage==4 & 4); // ... and R
out = out + (stg==1&Aslope)+(stg==2&Dslope); // output A & D
out = (stg<3 & out) + (stg==3 & S) + (stg==4 & (out+Rslope)); // ..& R
releaseOff = (stg==4 & out<=Rslope);
cntr = cntr + 16; // hop counter
}
Saved about 15 ASM commands so I guess about 50 cycles in the main loop?
I put the Aslope assignment after the hopA assign, so when I ASM it, it will be easier to minimize the movaps.
oddson ·
I'll have to defer to you as you're way ahead of me in this stuff. I was just making a custom envelope to demonstrate how it's done because it was asked in a couple of places.
I'm not real strong on coding and have no clue about assembly.
Maybe one day I'll be able to read asm stuff but it's all greek to me now. 🙂
infuzion ·
oddson wrote: I'm not real strong on coding and have no clue about assembly.
Hey man, the last programming class I took was COBAL, with printouts on a huge dot-matrix printer. I'm totally self-taught past that, so if I can do it, so can you 😉
I did learn how to do evelopes from your example; I couldn't grasp it from before.
SSE ASM isn't too hard once you get used to polish notation.
http://en.wikipedia.org/wiki/Polish_notation
infuzion ·
update:
[old link]
& idea
[old link]
infuzion ·
version 1.6:
* hop changed to 32; doesn't seem to matter if it is that large, & now we have 1/2 the computations. Might want to lower it back down to 4,8,16 if you'll always have steeper slopes.
* clipped range to 0..1; I was getting slight pops sometimes
Wrote something here? To have a post removed or your name hidden, write to [email protected].