A Sample script for the not-things TimeSeq module.
It is unlikely that the TimeSeq module will be used as a Bernoulli Gate. After all: Bernoulli Gate module already does an excellent job at providing that functionality. The construction of this script will however show another way that TimeSeq can be used in other ways than for plain linear sequences. We’ll start out constructing a simplified version of a Bernoulli Gate, and then move on to a more complete version.
Throughout the script, we’ll use short versions of notations where possible, e.g.:
timing
property will default to a start
timing, we’ll only include the timing
property on actions if it is different from that start
timing value.The basic version of the Bernoulli Gate will accept an input trigger on an input port. Each time a trigger is detected, a 0.5 second gate is sent out on one of two possible output ports. Which of the two possible output ports is receiving that gate is determined by a fixed 50% probability.
To detect incoming triggers, an input-trigger must be defined that will be linked to an internal TimeSeq trigger signal:
"input-triggers": [
{
"id": "determine-output",
"input": 1
}
]
The script snipplet above specifies that when a trigger is detected on input port one, an internal trigger with the id determine-output
should be fired.
In order to actually handle the input-trigger, we need to define a lane that will start when the trigger is fired:
"lanes": [
{
"auto-start": false,
"loop": false,
"start-trigger": "determine-output",
"segments": [
{
"duration": { "millis": 500 },
"actions": [
]
}
]
}
]
The start-trigger
specifies that this lane will start when the previously defined input-trigger is received. Since it is defined as a start-trigger
(and not a restart-trigger
), this lane will only react to the input trigger if the lane isn’t running yet. Otherwise, the trigger will be ignored.
This lane is started based on a trigger, so it should not auto-start
. And to provide the Bernoulli Gate functionality, we also want to stop the lane processing when it reaches the end, so loop
is set to false
.
The first segment of the lane will last for half a second. This is where the gate output signal will be generated that lasts for that duration. The actions of this segment will handle the logic that is required for the Bernoulli Gate.
The first action to perform for a Bernoulli Gate is to determine which of the two outputs will receive the resulting gate signal. This will be done in this first action of the lane:
{
"set-variable": {
"name": "bernoulli-chance",
"value": {
"rand": {
"lower": -5,
"upper": 5
}
}
},
{
"set-value": {
"output": 3,
"value": { "variable": "bernoulli-chance" }
}
}
}
The set-variable action will set a variable with name bernoulli-chance
to a random value between -5V and 5V. We can use that to determine the active output in the next steps. To be able to examine and visualize this chance variable, we’ll also output it on output port 3.
Using the generated bernoulli-chance
variable, we can now set the “gate high” (i.e. 10V) signal on the appropriate output port:
{
"if": {
"gt": [
{ "variable": "bernoulli-chance" },
0
]
},
"set-value": {
"output": 1,
"value": 10
}
}
The if condition will check if the bernoulli-chance
variable is greater than (gt
) 0V and set the first output to 10V if this is the case. This will result in a 50% probability since the variable is between -5V and 5V.
The second output is set using a similar check:
{
"if": {
"lte": [
{ "variable": "bernoulli-chance" },
0
]
},
"set-value": {
"output": 2,
"value": 10
}
}
This action will set the second output to 10V if the variable is less than or equal to 0V. The equal will make sure that an exact 0V random value doesn’t result in no outputs being triggered, which is why an lte
is used instead of an lt
.
Since the segment that contains these actions has a duration of 500 milliseconds, the currently assigned gate will remain active on either the first or the second port for that duration.
Once the previous segment finishes, a new segment will be executed that sets both outputs back to 0V:
{
"duration": { "samples": 1 },
"actions": [
{
"set-value": {
"output": 1,
"value": 0
}
},
{
"set-value": {
"output": 2,
"value": 0
}
}
]
}
This segment has a 1 sample duration (the shortest possible segment duration) since its only purpose is to reset both output ports back to 0V. When this segment finishes, the lane will also finish since it is set to not loop. Both gate outputs will be low again now, and the lane is ready to receive the next input-trigger.
The full script for the basic Bernoulli Gate can be found in bernoulli-basic.json.
The bernoulli-basic.vcv patch shows this script in action:
bernoulli-chance
variable that is used by the script to determine which of the two outputs should receive the generated gate signal.For the full Bernoulli Gate functionality, we need to extend the basic version with two features:
To achieve the controllable probability, we’ll use a voltage from an input port. We’ll define that the voltage on this input port is expected to be between 0V and 10V: 0V means that all gates should go to the first output, 10V means that all gates should go to the second output and any value in between should cause the probability to move between those two extremes, with 5V giving a 50% probability again.
An update of the if conditions on the actions that were previously created in the Send Output Gate section will give us this desired functionality:
{
"if": {
"gt": [
{ "variable": "bernoulli-chance" },
{
"input": 2,
"calc": [
{ "sub": 5 }
]
}
]
},
"set-value": {
"output": 1,
"value": 10
}
}
Instead of comparing the bernoulli-chance
variable (which is between -5V and 5V) with a fixed 0V value (Which resulted in a 50% probability), we now compare it with the current voltage on input port 2 (which is expected to be between 0V and 10V). By subtracting 5V from this input value using a calculation, its range is moved down into the same -5V to 5V range as the bernoulli-chance
variable, so that a simple gt
(greater than) compare is sufficient to determine if the output should be set to 10V.
The same logic applies to the if condition of the second output, but with a lte
(less than or equal) instead of a gt
compare.
To get the output gate to last as long as the input gate signal that started it, some timing changes will have to be done, combined with an additional lane. The logic to implement in the script will be:
reset-output
)reset-output
trigger and that loopsTiming-wise, the first segment from the basic script will become (omitting the existing actions for brevity):
{
"duration": { "samples": 1 },
"actions": [
{
"set-variable": {
"name": "bernoulli-chance",
"value": {
"rand": {
"lower": -5,
"upper": 5
}
}
}
},
{
"set-value": {
"output": 3,
"value": { "variable": "bernoulli-chance" }
}
},
{
"if": {
"gt": [
{ "variable": "bernoulli-chance" },
{
"input": 2,
"calc": [
{ "sub": 5 }
]
}
]
},
"set-value": {
"output": 1,
"value": 10
}
},
{
"if": {
"lte": [
{ "variable": "bernoulli-chance" },
{
"input": 2,
"calc": [
{ "sub": 5 }
]
}
]
},
"set-value": {
"output": 2,
"value": 10
}
},
{
"timing": "end",
"trigger": "reset-output"
}
]
}
Next to the previously explained changes to the if conditions of the set-value actions, the duration of the segment has been changed to 1 sample, and an additional action has been added at the end of the action list. This action has an end
timing (so it will be executed when the segment completes) and will cause an internal trigger to be fired with the reset-output
name.
Once this new version of the segment has completed, the relevant output port will be set to 10V (i.e. a high gate signal), and the reset-output
trigger can be used to start waiting for the input gate to go low again.
To wait for the input gate to go low again, we’ll create a new lane that is started by the previously fired reset-output
trigger and will loop a single 1-sample length segment that checks if the input voltage has gone below 1V (i.e. the gate went low again):
{
"auto-start": false,
"loop": true,
"start-trigger": "reset-output",
"stop-trigger": "stop-reset-output",
"segments": [
{
"duration": { "samples": 1 },
"actions": [
{
"if": {
"lt": [
{ "input": 1 },
1
]
},
"set-value": {
"output": 1,
"value": 0
}
},
{
"if": {
"lt": [
{ "input": 1 },
1
]
},
"set-value": {
"output": 2,
"value": 0
}
},
{
"if": {
"lt": [
{ "input": 1 },
1
]
},
"trigger": "stop-reset-output"
}
]
}
]
}
This lane will not auto-start
since it is expected to start based on the reset-output
internal trigger, as specified by the start-trigger
property. Since the lane has to wait for the input gate to go low again, it will loop
.
The segment in this lane contains three actions that are all using the same if conditional: the voltage on input port 1 has to be below 1V again. When this condition is met, the three actions will be performed:
stop-reset-output
trigger that will stop the lane from running: since we set both outputs to a low gate again, we no longer need to check for a low input gate anymore until a new input gate triggers the first lane.The stop-reset-output
trigger will stop the second lane from looping since it is used in its stop-trigger
property.
The full script for the full Bernoulli Gate can be found in bernoulli-full.
The bernoulli-basic.vcv patch shows this script in action. It adds an additional 8VERT module, on which the probability of the output gate selection can be set using a 0V-10V value.