First time here? Check out the FAQ!
x

initialising an Eventvariable array with a certain set of values

0 votes
644 views
Is there a way to initialise an array of eventvariables to a start value? For example so they are all 1 instead of 0?

I can write to the array, and I can read from it, but on startup it's always just zero, and it would work a lot better if it started as being all ones.

My code is this:

 

| pitchArray |

pitchArray := EventVariable new size: 100.

(!onRow2 true: ((pitchArray @< !pitchWritePtr) <~ (!Enc0 )) false: (nil)),

(pitchArray @< !pitchReadPtr rounded)
asked Jan 14, 2019 in Capytalk & Smalltalk by anders-skibsted (Adept) (1,320 points)
Unfortunately, there is not a way to initialize the values of EventValues at compile time before the Sound starts running. One workaround might be to have a gate that starts your sequencer (like !On). That way you could first initialize your Array and only after it is initialized, then start the sequence playing.
The other approach would be to use a Smalltalk Array containing EventValues (instead of using a Capytalk array). After playing the Sound the first time, you could capture the initial values of all EventValues in a preset and always start the Sound in that preset.
Is it the pitchArray that you want to initialize to 1s? Or the logic (the onRow)? If you are using them as a logic values, you could change the sense of the logic test
!onRow false: (do something real) true: (nil)
It is the pitchArray that I want to initialize to 1s.
But I guess I just have do as you said and write a command that will fill it with 1s as it starts running and then turn on the sequencer after that.
I just have to sync it up with the rest of my sequencer arrays... But I guess I can use a sort of global !On to start all my sequencers, sync them up in a similar fashion
another related question.
Is there a capytalk function that is a value of 1 for for example 5 seconds, and then returns to 0.

I want to write a small initialization function that only runs once for the above code. Something like:

("be one for 2 seconds" true: (((pitchArray @< initWritePtr) <~ 1)) false: (nil)).

and then have initWritePtr be a scaled ramp that runs through my array and makes sure every value is 1
"Is there a capytalk function that is a value of 1 for for example 5 seconds, and then returns to 0?"
!Trigger turnOnFor: !OnDur s
or if you want to delay it
!Trigger turnOnAfter: !Delay s for: !OnDuration s

1 Answer

–1 vote
 
Best answer

This was my first guess at an answer which was wrong as SSC points out in the comments below:


I'm not in front of Kyma right now so I'll need to double check this later. When I tried this before I had to explicitly set all the values eg.

(0 to: pitchArray size) do: [:i| (pitchArray @< i) <~ 1].

There is an EventValue that gives you the amount of time since the Sound has been running. I can't remember it off the top of my head right now but I remember using that:

((!TimeThingICan'tRemember lt: 1) true: ((0 to: pitchArray size) do: [:i| (pitchArray @< i) <~ 1].) false: nil),

 or something like that.
 


Here's a better answer that should work:

| pitchArray initialiseAllExpression |
pitchArray := EventVariable new size: 64.

"construct the expression that initialises all the values of the pitchArray"
initialiseAllExpression := (pitchArray @< 0) <+ 0. 

initialiseAllExpression := (1 to: (64 - 1)) inject: initialiseAllExpression into: [:expr :i| (expr, ((pitchArray @< i) <+ 0))].

((1 turnOnAfter: 0 s for: 0.001 s) evaluate: initialiseAllExpression)

 

The reason why this works, if I remember right, is all down to the "," (the comma). The Sound's parameter field will only evaluate one Capytalk expression, but it can be a compound one. Compound Capytalk expressions are separated by commas. In the "inject:into:" expression, the key part is:

[:expr :i| (expr, ((pitchArray @< i) <+ 0))]

 

which means for each iteration of the loop, take the output of the last iteration (:expr) and construct 

(expr, ((pitchArray @< i) <+ 0))

 

The "expr," is the magic bit that's building up a big compound Capytalk expression with the sub-expressions separated by commas. Like:

((pitchArray @ 0) <+ 0),
((pitchArray @ 1) <+ 0),
((pitchArray @ 2) <+ 0),
...

 


 

Ugh, my first guess was badly wrong in two ways. (silly me for making a guess while not sitting at Kyma, sorry)

First I used "do:".

If you evaluate this code (using Cmd-Y):

| pitchArray |

pitchArray := EventVariable new size: 10.
(0 to: pitchArray size) do: [:i| (pitchArray @< i) <~ 1]. 

you get "(0 to: 10)" because do: answers the receiver. Smalltalk will happily go round the loop 10 times at compile time, but will return (0 to: 10). 

Ah... but what about collect:?

If you evaluate this....

| pitchArray |

pitchArray := EventVariable new size: 10.
(0 to: pitchArray size) collect: [:i| (pitchArray @< i) <~ 1].

you get

(((Var132 @< 0 ) <~ 1 ) ((Var132 @< 1 ) <~ 1 ) ((Var132 @< 2 ) <~ 1 ) ((Var132 @< 3 ) <~ 1 ) ((Var132 @< 4 ) <~ 1 ) ((Var132 @< 5 ) <~ 1 ) ((Var132 @< 6 ) <~ 1 ) ((Var132 @< 7 ) <~ 1 ) ((Var132 @< 8 ) <~ 1 ) ((Var132 @< 9 ) <~ 1 ) ((Var132 @< 10 ) <~ 1 ))

an array of Capytalk expressions NOT separated by commas. Oops. Which is why you need inject:into:.

 

answered Jan 15, 2019 by alan-jackson (Virtuoso) (15,840 points)
selected Jan 17, 2019 by anders-skibsted
This could work if you were initializing a Smalltalk Array rather than a Capytalk array. Unfortunately, the loop iterating over the elements of the array would happen at compile time, and the <~ assignments can only happen during run time.
It may be that a Smalltalk Array of EventValues is the better solution (and save their initial values in presets).
Doesn't the Smalltalk generate a large Capytalk expression with 100 lines in it that will do the assignment at run time?

(I'll have to look how I did this before when I get home!)
You probably built up the expression from a kernel using inject:into:? Something like
0 to: pitchArray size inject: startingKernel into: [:expr  :i | expr  := expr, ((pitchArray @< i) <+ 1)].
You're absolutely right I did. Sorry for heading off in the wrong direction there.

Here's what I did (register is my equivalent of pitchArray and resetValue is the value I'm initialising to.):

|  resetValue register resetAllExpression |


"The reset value for elements of the register"
resetValue := 0.

register := EventVariable new size: 64.

"construct the expression that resets all the values of the register to the reset value"
resetAllExpression := (register @< 0) <+ resetValue.

resetAllExpression := (1 to: (64 - 1)) inject: resetAllExpression into: [:expr :i|
    (expr, ((register @< i) <+ resetValue))].   


((1 turnOnAfter: 0 s for: 0.001 s) evaluate: resetAllExpression),

...other Capytalk here...

I'll amend the answer above to add a corrected version.
Thanks for the detailed explanation, Alan! :)
...