First time here? Check out the FAQ!
x

Why can't I use an EventVariable as the argument to smooth: ?

0 votes
405 views

I've got the following code in the Left parameter of a Level Sound:

| transition last_transition smoothing |


transition := EventVariable new initialValue: 0.
last_transition := EventVariable new initialValue: 0.
smoothing := EventVariable new initialValue: 0.


(last_transition <+ transition),

(transition <+ ((!Bar mod: 8) of: #(0 0.5 1 0 1 0.5 0 1))),

((transition eq: 0.5)
    true:
        ((last_transition eq: 0)
            true:
                ((transition <+ 1),
                (smoothing <+ 4))
            false:
                ((transition <+ 0),
                (smoothing <+ 4)))
    false:
        smoothing <+ 0),

(transition smooth: smoothing s).

 

In the middle of the code is an array of "transitions": (0 0.5 1 0 1 0.5 0 1), where a "0" means be silent, a "1" means be loud and a "0.5" means fade up or down. The direction of the fade depends on the previous value, so if the previous value was 0 then a "0.5" would mean fade up.

I'm using smooth: to do the fading. I have some logic that sets the length of smoothing time in the EventVariable "smoothing".

But this code doesn't work. It's just silent all the time. If I replace the final "smoothing" with a literal number then it works fine, eg:

 

...

(transition smooth: 1 s).

 

if I replace it with an EventValue it kind of works for a while then stops working:

(transition smooth: !Smoothing s).

 

Am I doing something silly?

 

asked Jan 4, 2019 in Capytalk & Smalltalk by alan-jackson (Virtuoso) (15,840 points)

I've tried an alternative approach using ramp: instead of smooth: but it's getting weirder.

Here's my alternative code:

| transition last_transition fadeDirection|


transition := EventVariable new initialValue: 0.
last_transition := EventVariable new initialValue: 0.
fadeDirection := EventVariable new initialValue: 0.    "0 = down, 1 = up"

(last_transition <+ transition),

(transition <+ ((!Bar mod: 8) of: #(0 0.5 1 0 1 0.5 0 1))),

( (transition eq: 0.5)
    true:
        ( ((last_transition eq: 0)
            true:
                fadeDirection <+ 1
            false:
                ((last_transition eq: 1)
                    true:
                        fadeDirection <+ 0
                    false:        "if the last transition was a fade then invert the fade direction"
                        fadeDirection <+ (1 - fadeDirection))),
            ((fadeDirection - (((transition eq: 0.5) turnOnAfter: 0 s for: 0.001 s) ramp: 2 s))))
    false:
        transition)

What this is trying to do is if the transition is "0.5" then it returns a 2 second ramp. When the transitions are 0 or 1 it's working fine, but when they're 0.5 it sounds like it's ring modulating. I assumed this whole piece of CapyTalk would only get evaluated when the value of !Bar changes. !Bar is the output of a SoundToGlobalController which has this as its Value:

(4 s tick) countTriggersMod: 8

But I'm now thinking the code is evaluating once per milisecond, which migh explain the ring mod like sound.

----

I just tried changing the STGC to a TriggeredSoundToGlobalController, but that makes no difference.

----

OK here's my guess as to what's going on. Because I've included a ramp: in the expression the expression is now being evaluated once per milisecond because the ramp is contantly updating it's output. And so that branch of the "true: false:" tree keeps being re-evaluated which is retriggering the ramp or something odd.

2 Answers

+2 votes
 
Best answer

We recommend you use the newer expression:

!anEventExpression smoothFor: !aDuration s jump: !aTrigger
 

This version (and smoothFor: without the jump:) continually updates the smoothing duration.

Instead of setting the smooth time to zero, you can use the jump argument as a trigger to jump instantly to the new value.

answered Jan 4, 2019 by ssc (Savant) (126,620 points)
selected Jan 29, 2019 by alan-jackson
0 votes

I've got in working in the end using a ramp:, interpolateFrom:to: and eventually remembering to use evaluate: (!!!)

evaluate: is very handy if you're trying to save a previous value in an EventVariable.

Here's the working code:

| transition last_transition level fadeFrom fadeTo |


transition := EventVariable new initialValue: 0.
last_transition := EventVariable new initialValue: 0.
level := EventVariable new initialValue: 0.
fadeFrom := EventVariable new initialValue: 0.
fadeTo := EventVariable new initialValue: 0.


(level <+ (((!Bar hasChangedInLast: 0.001 s) ramp: 2 s) interpolateFrom: fadeFrom to: fadeTo)  ),

((!Bar hasChangedInLast: 0.001 s) evaluate:
    (

        (last_transition <+ transition),

        (transition <+ (!Bar of: #(0 0.5 1 0 1 0.5 0 1))),

        ( (transition eq: 0.5)
            true:
                ( (last_transition eq: 0)
                    true:
                        ((fadeFrom <+ 0),
                        (fadeTo <+ 1))
                    false:
                        ((last_transition eq: 1)
                            true:
                                ((fadeFrom <+ 1),
                                (fadeTo <+ 0))
                            false:        "if the last transition was a fade then invert the fade direction"
                                ((fadeFrom <+ (1 - fadeFrom)),
                                (fadeTo <+ (1 - fadeTo)))
                    )
            )
            false:
                ((fadeFrom <+ transition),
                (fadeTo <+ transition))
        )
    )
),

level.

 

This does the fading up and down I wanted.

I still don't know why the smoothed: way didn't work in my original question. Maybe it had something to do with not using evaluate: ? I might try that out later.

answered Jan 4, 2019 by alan-jackson (Virtuoso) (15,840 points)
...