chuck neophyte trying to remove clicks via Envelope patch
Hello chucKsters, I am trying to write a simple program that reassigns four sine oscillator's pitch and volume at each iteration through a while(true) loop. I decided i didn't want the clicks at the beginning of each note attack, so I thought that I could remove them by just adding an envelope to ramp up to the gain value for each event. After trying different values for the Envelope time and rate, I can't seem to figure out how to remove the clicks. I apologize as this is probably something obvious that I am blind to in my myopic state. My intuition is that if I increased the rate of the envelope the ramp up to 1 would be smoother and then my speakers wouldn't click. However I am unsure how to adjust the rate as I don't know what range it would be in. Sorry for my n00bishness Ryan. SinOsc s1 => Envelope e=> dac; SinOsc s2 => e => dac; SinOsc s3 => e=> dac; SinOsc s4 => e=> dac; //.... while( true ) { .1 => float t =>e.time; //reassign gain Std.rand2f(0,1) => float g; g => s1.gain; g => s2.gain; g => s3.gain; g => s4.gain; //reassign pitch Std.mtof(instrumentSet1[Std.rand2(0,6)]) => s1.freq; Std.mtof(instrumentSet1[Std.rand2(0,6)]) => s2.freq; Std.mtof(instrumentSet1[Std.rand2(0,6)]) => s3.freq; Std.mtof(instrumentSet1[Std.rand2(0,6)]) => s4.freq; //Advance time by 1 samp e.keyOn(); 1000::ms => now; e.keyOff(); } -- "Le savant n’étudie pas la nature parce que cela est utile ; il l’étudie parce qu’il y prend plaisir et il y prend plaisir parce qu’elle est belle." - Henri Poincaré
Hi Ryan: Your code looks about right, but you may need to leave time for the envelope to ramp down as well as up. Try these as the last lines of your while (true) loop: e.keyOn(); 1000::ms => now; e.keyOff(); 1000::ms => now; or, to make the wait time equal to the ramp time: e.keyOn(); e.time()::second => now; e.keyOff(); e.time()::second => now; No, I haven't tried it. Let me know... - Rob On 24 Nov 2009, at 17:02, Ryan Wieghard wrote:
Hello chucKsters,
I am trying to write a simple program that reassigns four sine oscillator's pitch and volume at each iteration through a while(true) loop. I decided i didn't want the clicks at the beginning of each note attack, so I thought that I could remove them by just adding an envelope to ramp up to the gain value for each event. After trying different values for the Envelope time and rate, I can't seem to figure out how to remove the clicks. I apologize as this is probably something obvious that I am blind to in my myopic state. My intuition is that if I increased the rate of the envelope the ramp up to 1 would be smoother and then my speakers wouldn't click. However I am unsure how to adjust the rate as I don't know what range it would be in.
Sorry for my n00bishness
Ryan.
SinOsc s1 => Envelope e=> dac; SinOsc s2 => e => dac; SinOsc s3 => e=> dac; SinOsc s4 => e=> dac;
//....
while( true ) { .1 => float t =>e.time;
//reassign gain
Std.rand2f(0,1) => float g; g => s1.gain; g => s2.gain; g => s3.gain; g => s4.gain;
//reassign pitch
Std.mtof(instrumentSet1[Std.rand2(0,6)]) => s1.freq; Std.mtof(instrumentSet1[Std.rand2(0,6)]) => s2.freq; Std.mtof(instrumentSet1[Std.rand2(0,6)]) => s3.freq; Std.mtof(instrumentSet1[Std.rand2(0,6)]) => s4.freq;
//Advance time by 1 samp e.keyOn(); 1000::ms => now; e.keyOff(); }
-- "Le savant n’étudie pas la nature parce que cela est utile ; il l’étudie parce qu’il y prend plaisir et il y prend plaisir parce qu’elle est belle." - Henri Poincaré _______________________________________________ chuck-users mailing list chuck-users@lists.cs.princeton.edu https://lists.cs.princeton.edu/mailman/listinfo/chuck-users
--: Robert Poor e: robert.poor@nbt-ventures.com p: +1 617 818 5115 b: http://blog.nbt-ventures.com --: This message and the information it contains are the proprietary and confidential property of NBT Ventures and may be privileged. If you are not the intended recipient, please do not read, copy, disclose or distribute its contents to any party, and notify the sender immediately. --:
If you'd rather not add a second time advance after the keyOff(),
another approach is to set up two banks of sines which take it in
turns to play. You could arrange things so that the release portion of
one set of sines would happen at the same time as the attack portion
of the other set, which would avoid clicks.
On Wed, Nov 25, 2009 at 2:12 AM, Robert Poor
Hi Ryan:
Your code looks about right, but you may need to leave time for the envelope to ramp down as well as up. Try these as the last lines of your while (true) loop:
e.keyOn(); 1000::ms => now; e.keyOff(); 1000::ms => now;
or, to make the wait time equal to the ramp time:
e.keyOn(); e.time()::second => now; e.keyOff(); e.time()::second => now;
No, I haven't tried it. Let me know...
- Rob
On 24 Nov 2009, at 17:02, Ryan Wieghard wrote:
Hello chucKsters,
I am trying to write a simple program that reassigns four sine oscillator's pitch and volume at each iteration through a while(true) loop. I decided i didn't want the clicks at the beginning of each note attack, so I thought that I could remove them by just adding an envelope to ramp up to the gain value for each event. After trying different values for the Envelope time and rate, I can't seem to figure out how to remove the clicks. I apologize as this is probably something obvious that I am blind to in my myopic state. My intuition is that if I increased the rate of the envelope the ramp up to 1 would be smoother and then my speakers wouldn't click. However I am unsure how to adjust the rate as I don't know what range it would be in.
Sorry for my n00bishness
Ryan.
SinOsc s1 => Envelope e=> dac; SinOsc s2 => e => dac; SinOsc s3 => e=> dac; SinOsc s4 => e=> dac;
//....
while( true ) { .1 => float t =>e.time;
//reassign gain
Std.rand2f(0,1) => float g; g => s1.gain; g => s2.gain; g => s3.gain; g => s4.gain;
//reassign pitch
Std.mtof(instrumentSet1[Std.rand2(0,6)]) => s1.freq; Std.mtof(instrumentSet1[Std.rand2(0,6)]) => s2.freq; Std.mtof(instrumentSet1[Std.rand2(0,6)]) => s3.freq; Std.mtof(instrumentSet1[Std.rand2(0,6)]) => s4.freq;
//Advance time by 1 samp e.keyOn(); 1000::ms => now; e.keyOff(); }
-- "Le savant n’étudie pas la nature parce que cela est utile ; il l’étudie parce qu’il y prend plaisir et il y prend plaisir parce qu’elle est belle." - Henri Poincaré _______________________________________________ chuck-users mailing list chuck-users@lists.cs.princeton.edu https://lists.cs.princeton.edu/mailman/listinfo/chuck-users
--: Robert Poor e: robert.poor@nbt-ventures.com p: +1 617 818 5115 b: http://blog.nbt-ventures.com --: This message and the information it contains are the proprietary and confidential property of NBT Ventures and may be privileged. If you are not the intended recipient, please do not read, copy, disclose or distribute its contents to any party, and notify the sender immediately. --:
_______________________________________________ chuck-users mailing list chuck-users@lists.cs.princeton.edu https://lists.cs.princeton.edu/mailman/listinfo/chuck-users
Ryan, When we just want to change a volume (not necessarily start a "new note" as such) it may be better to instead use Envelope.target( float value ). This will ramp to the new value over the set .duration() without any need for further attention and -barring very short set durations- won't cause clicks. The "rate" your question refers to is set by Envelope.duration( dur ). Something like 50::ms would be my starting point in looking for a good value. You may well be able to get away with a shorter duration. Hope that helps, Kas.
Ryan (& Kas?),
You're just making swells, right? In that case, the only change should
be that you need to provide ramp-down time of e.duration() => now.
Also, make sure you scale the gain so that you keep the overall power
of the signal below 1.0.
One other thing, though, is that you don't need to reconnect the e =>
dac every time--just do this (with my personal favorite trick, the
array of SinOscs)
SinOsc s[4];
Envelope e => Gain g => dac;
1.0 / s.size() => g.gain; // scales gain to 0.25 for 4 SinOscs.
for (int i; i < s.size(); i++) {
s[i] => e;
}
Makes it easy to change the gains, too, with
for (int i; i < s.size(); i ++) {
Std.rand2f(0, 1) => s[i].gain;
}
and then once you expand from 4 to 100 SinOscs you'll only have to
change the one line of code.
Andrew
2009/11/25 Kassen
Ryan,
When we just want to change a volume (not necessarily start a "new note" as such) it may be better to instead use Envelope.target( float value ). This will ramp to the new value over the set .duration() without any need for further attention and -barring very short set durations- won't cause clicks.
The "rate" your question refers to is set by Envelope.duration( dur ). Something like 50::ms would be my starting point in looking for a good value. You may well be able to get away with a shorter duration.
Hope that helps, Kas.
_______________________________________________ chuck-users mailing list chuck-users@lists.cs.princeton.edu https://lists.cs.princeton.edu/mailman/listinfo/chuck-users
Andrew; You're just making swells, right? I was assuming the Envelope was for click-removal and the rest of the tone was desired to be of constant volume. I may well be misunderstanding the question.
In that case, the only change should be that you need to provide ramp-down time of e.duration() => now.
Yes, while it may not be needed here this is a great trick. Any function returning a dur may be chucked to now. This is great as it leads to compact code, it's expressive, and there is only a single thing to change if we need to speed the piece up or down. You can even take it further, this is perfectly legal as well. 10::my_SinOsc.period() => now; //advances time by exactly 10 cycles.
Also, make sure you scale the gain so that you keep the overall power of the signal below 1.0.
Yes, that too is a good trick and a good reason to keep track of the number of UGens connected to the dac. and then once you expand from 4 to 100 SinOscs you'll only have to
change the one line of code.
This too is great advice that could stand repeating. As a rule of thumb I tend to scan my code with my eyes half-closed; anything that looks monotonous or repetitive when looking at it like that is cause for adding a array (or more rarely a function). This keeps code-size down, cuts down on editing time and errors. All good notes on style. Kas.
Kas,
Oh, right, I suppose your intuition on the click removal is a good
one. No one really wants a .1 second swell, after all. My thinking was
that if you don't do the full ramp down/ramp up again you'll have to
add envelopes to each of the pitches or else you'll get
frequency-change clicks, and that just gets into more hairy territory.
I like to hold (in an ideal world) to the whole code, refactor, repeat
cycle.
Good call on the .period() thing--I'm going to try passing time in
multiples of periods to get instantaneous click-less transitions. This
sounds like something Stockhausen would do, making durations in
periods. Sounds algorithmic.
Andrew
2009/11/25 Kassen
Andrew;
You're just making swells, right?
I was assuming the Envelope was for click-removal and the rest of the tone was desired to be of constant volume. I may well be misunderstanding the question.
In that case, the only change should be that you need to provide ramp-down time of e.duration() => now.
Yes, while it may not be needed here this is a great trick. Any function returning a dur may be chucked to now. This is great as it leads to compact code, it's expressive, and there is only a single thing to change if we need to speed the piece up or down. You can even take it further, this is perfectly legal as well.
10::my_SinOsc.period() => now; //advances time by exactly 10 cycles.
Also, make sure you scale the gain so that you keep the overall power of the signal below 1.0.
Yes, that too is a good trick and a good reason to keep track of the number of UGens connected to the dac.
and then once you expand from 4 to 100 SinOscs you'll only have to change the one line of code.
This too is great advice that could stand repeating. As a rule of thumb I tend to scan my code with my eyes half-closed; anything that looks monotonous or repetitive when looking at it like that is cause for adding a array (or more rarely a function). This keeps code-size down, cuts down on editing time and errors.
All good notes on style.
Kas.
_______________________________________________ chuck-users mailing list chuck-users@lists.cs.princeton.edu https://lists.cs.princeton.edu/mailman/listinfo/chuck-users
Andrew, Oh, right, I suppose your intuition on the click removal is a good
one. No one really wants a .1 second swell, after all.
One more or less follows from the other... Instantaneous changes will leave spectral artefacts. In general Envelope can't be praised highly enough for it's uses as a utility module, I feel.
My thinking was that if you don't do the full ramp down/ramp up again you'll have to add envelopes to each of the pitches or else you'll get frequency-change clicks, and that just gets into more hairy territory.
Fortunately in this case we can have cheap gradual frequency changes without clicks as we are dealing with a SinOsc here. SinOsc can track a input, which may be a Envelope (used with a Step set to 1 as it's own input). Set the osc's frequency to 0 and sync to fm modulation.
From there on your Envelope's .target() can set the desired frequency with the .duration() setting the length of the glide. This is very cheap, doesn't require attention once set and generally a good idea, but it only works with the basic osc's as those are the only UGens with a .sync(). The good news is that you can still have arbitrary wave-forms as you can use this with a Phasor and a LiSa set to track.
I could supply a example if needed. For other UGens you are on your own. Instantaneous frequency changes *will* mean a more or less sharp corner in the resultant signal which *will* have a spectral effect. It's hard to say anything about the general case; you'll have to analyse the situation and your needs and come up with cleverness to minimise audible artefacts. I think SC interpolates over the block-size, that's a good solution to minimise artefacts in most cases but I don't think that would suit our more precise control and less clear boundary between code and UGens. Good call on the .period() thing--I'm going to try passing time in
multiples of periods to get instantaneous click-less transitions. This sounds like something Stockhausen would do, making durations in periods. Sounds algorithmic.
It's a good feature; a strongly-timed approach to frequency. Aside from the kind of technique mentioned it's especially convenient for expressing LFO rates as multiples or fractions of musical intervals. The idea is one of my contributions and one I'm still a bit proud of. Pride is a bad emotion, I'd much rather be dis-satisfied and work harder because of it, but this is just so ChucKian that I can't help it. :-) Yours, Kas.
participants (5)
-
Andrew C. Smith
-
Kassen
-
Robert Poor
-
Ryan Wieghard
-
Tomasz Kaye's brain