First time here? Check out the FAQ!
x

How do I reset reading through an array without missing the first element?

0 votes
466 views

I am making a simple "sequencer" that reads through an array of VCS controls. I wanted something simpler and more controllable than the AnalogueSequencer which I've achieved with some CapyTalk in a few SoundToGlobalControllers... But I have a problem resetting the sequencer - it keeps missing the first item in the array.  I can see why it's happening but not sure the best way to solve the problem.

I've got a Start button that's supposed to reset the position within the sequence and start it. I'm using a TriggeredSoundToGlobal:

GeneratedEvent: !Gate
Value: 1
Trigger: !Start

which sets !Gate to 1 when the Start button is pushed.

Then I use a SoundToGlobalController to create a clock signal for stepping through the array:

GeneratedEvent: !ArrayGate
Value: !Gate bpm: !BPM

And another STGC to index into the array:

GeneratedEvent: !Position
Value: !ArrayGate nextIndexMod: 48 reset: !Start

Pressing the Start button does reset the !Position array index to 0, but it also starts the !ArrayGate which immediately advances !Position to 1. Which is the problem - when I hit Start, I miss the first element of the array.

 

I've tried using turnOnAfter: to delay the reset signal but that just makes a mess and I've been looking at alignWith: but I can't see how that would help either. I know I must be missing something obvious and simple. Any ideas?

 

asked Aug 23, 2016 in Capytalk & Smalltalk by alan-jackson (Virtuoso) (15,840 points)

1 Answer

+1 vote
 
Best answer

Does this work in your Sound?

(!ArrayGate nextIndex - (!Start sampleAndHold: !ArrayGate nextIndex + 1)) mod: 48

The idea is that !Start will force the expression to 47, and the next !ArrayGate will be zero.

answered Aug 23, 2016 by ssc (Savant) (128,120 points)
selected Aug 23, 2016 by alan-jackson
That works great, thanks!

I'm staring at that and having a hard time working out what's going on. It's sparking off more questions or realisations...

Ok... so when I press the Start button, just while I'm holding it down, sampleAndHold will return

"!ArrayGate nextIndex + 1".

Everything in the brackets evaluates to -1 and "-1 mod: 48" is 47.

So... "!ArrayGate nextIndex" is referenced twice in this expression, but I'm guessing that it's only evaluated once? Or rather that nextIndex doesn't increment the index the second time it's referenced in the expression? That's interesting.

Also how does "nextIndex" know to start again from zero? If you see what I mean. You're not using nextIndexMod.

Imagine nextIndex "returns" 5. (You can probably see me struggling with the smalltalkiness of it all!) Then the sampleAndHold expression is returning 6, so it's effectively

(5 - 6) mod: 48

which is 47. But on the next !ArrayGate trigger why doesn't nextIndex return 6?
Yes, all occurrences of a non-random subexpression within a Capytalk expression will have the same value, so both nextIndex subexpressions will always have the same value.

The sampleAndHold: samples the value of the argument at the moment that !Start goes from false to true and holds that value until the next !Start trigger.

The difference "(!ArrayGate nextIndex - (!Start sampleAndHold: !ArrayGate nextIndex + 1))" is reset to -1 when !Start is triggered and counts up from there on every subsequent !Gate.
I wanted to add a button to toggle looping behaviour reading through the array. I found this really hard! I have done it but I suspect my solution is more complex than it needs to be.

The way I stop the sequencer reading through the array is with a TriggeredSoundToGlobalController:

GeneratedEvent: !Gate
Trigger: (1 - !Loop) * (!Position ge: 47)

So if we're at the end of the array and !Loop is false, then make !Gate false.  Which works OK except that the solution above for the !Start signal is to force the value to the end of the array. Which means if !Loop is disabled (false) then pressing !Start just... well.. stops.

The solution I came up with is this...

The STGC that sets the array position now has this:

value:
(!ArrayGate * (1 - !ClockDisable)) nextIndexMod: 48 reset: !Start


And the !Start button now affects three sounds. A STGC and two TriggeredSTGCs as follows:

TriggeredSTGC "Start 0":

GeneratedEvent: !Gate
Value: 0
Trigger: !Start

----

TriggeredSTGC "Start 1":

GeneratedEvent: !Gate
Value: 1
Trigger: !Start turnOnAfter: 0.01 s for: 0.01 s

----

STGC "ClockDisable":

GeneratedEvent: !ClockDisable
Value: !Start turnOnAfter: 0 s for: ((!BPM bpm s) * 0.9)


So when the !Start button is pushed, it momentarily dips the !Gate signal to 0. This realigns the !ArrayGate signal which is being generated by an STGC:

GeneratedEvent: !ArrayGate
value: !Gate bpm: !BPM

Then 0.01 of a second later it raises the !Gate signal to 1.

It also takes the !ClockDisable signal to 1 for 0.9 of a BPM cycle.

The !ClockDisable signal prevents the array position incrementing on the first tick of !ArrayGate.

I got there in the end!

----

One of the things I tried which I thought would work but didn't was this -

In the Array Position STGC:

GeneratedEvent: !Position
Value: (!ArrayGate nextIndex mod: 48) - (!Start sampleAndHold: ((!ArrayGate nextIndex mod: 48) + 1))

With the idea that when !Start was pressed !Position would go to -1, but the rest of the time !Position would cycle between 0 and 47. But instead it goes a bit mad. What I think was happening was that the two "mod: 48" messages get out of sync with each other... or rather the objects they affect do.
If you decide to pursue this last approach (the one that hadn't worked yet), try doing the arithmetic first and only then taking the modulo of the entire result.  For your example, that would be:
((!ArrayGate nextIndex) - (!Start sampleAndHold: ((!ArrayGate nextIndex) + 1)))  mod: 48
Yes, that's what you initially suggested and that worked until I added the !Loop toggle button.

The problem with doing the mod: 48 last is that when !Start triggers (goes positive) the array position is set to 47, the final position in the array.

The TriggeredSTGC that prevents looping does this:

GeneratedEvent: !Gate
Trigger: (1 - !Loop) * (!Position ge: 47)

It's checking to see if we're at the end of the array and if we are, and looping is disabled, then it stops playback by setting !Gate to 0.  

In other words I need two different states, one that is "before" the first step of the array and another that is at the end of the array.

Doing the mod: 48 at the end means that the result is always between 0 and 47 and there can be only one state. I was trying to find a way that !Start could force the position to -1, ie. before the beginning of the array, rather than the final position of the array.
...