Clusters¶
Clusters combine pooled memory allocation (via the type system) with field validation and asynchronous dispatch. They provide structured data with lifecycle hooks and callback-driven processing. The Voice cluster is the canonical implementation.
Defining a Cluster¶
Member Definitions¶
Fields are defined via a MEMBERS define. For extensible clusters, members are split for library extension:
define Voice.MEMBERS := Voice.BASE_MEMBERS, Voice.ADD_MEMBERSLibraries extend members using +=:
define Voice.ADD_MEMBERS += Stl.Modulation.MEMBERS
define Voice.ADD_MEMBERS += Stl.LegatoInfo.MEMBERSInitialization Lists¶
Each cluster defines an INIT_LIST with default values matching the member order:
define Voice.INIT_LIST := Voice.BASE_INIT, Voice.ADD_INIT
declare Voice.BASE_INIT[] := (...
-1, ... {input}
-1, ... {runtime}
TRUE, ... {auto_release}
-1, ... {vo_parent}
-1, ... {note}
0, ... {volume}
0, ... {pan}
0 ... {tune}
)The system merges all initialization lists at startup:
literate_macro(Voice.WriteInitList) on Voice.INIT_LISTValidation Macros¶
Each field that requires validation gets a parameterized define:
define Voice.validate.input(#v#) := (in_range(#v#, 0, ivls.input_type.SIZE - 1))
define Voice.validate.note(#v#) := (in_range(#v#, 0, 127))
define Voice.validate.runtime(#v#) := (#v# >= -1)
define Voice.validate.event(#v#) := (BOOL.TRUE)The dispatch system auto-validates before executing callbacks:
macro cluster.Validate(#cluster#, #member#)
if not (#cluster#.validate.#member#(#cluster#[self].#member#))
message('ArgFailure (#cluster#): #cluster#[self].#member# invalid: ' & #cluster#[self].#member#)
self_invalid := TRUE
end if
end macroCLUSTER_TABLE Registration¶
Register clusters with the dispatch system using +=:
define CLUSTER_TABLE += VoiceThis generates: task enumeration, memory pool/type infrastructure via type.Create, dispatch queue (adt.CreateQueue), and lifecycle hooks.
Dispatch System¶
Architecture¶
The _process taskfunc validates fields and executes the cluster callback:
taskfunc #cluster#._process(ref)
declare self := ref
declare self_invalid := FALSE
declare self_auto_delete := TRUE
if check_ref(#cluster#, self)
literate_post_macro(cluster.Validate(#cluster#, #l#)) on #cluster#.MEMBERS
if self_invalid = TRUE
#cluster#.CB.Cancelled()
message("Cluster [#cluster#] fired with invalid arguments! Dumping state:")
cluster.DumpSelf(Voice)
else
#cluster#.CB()
end if
if self_auto_delete = TRUE
#cluster#.DeleteInCB()
#cluster#.delete(self)
end if
end if
end taskfuncControl Variables¶
| Variable | Purpose |
|---|---|
self |
Reference to the current cluster instance |
self_invalid |
Set to TRUE to abort processing |
self_auto_delete |
Set to FALSE to prevent automatic deletion |
Voice Cluster Fields¶
Core / Hierarchy¶
| Field | Default | Description |
|---|---|---|
input |
-1 | Input type (VOICE_ON, VOICE_OFF) |
runtime |
-1 | Runtime reference |
auto_release |
TRUE | Auto-release on parent release |
vo_parent / vo_child |
-1 | Parent/first child voice |
vo_left / vo_right |
-1 | Sibling references |
Flow / MIDI / Sound¶
| Field | Default | Description |
|---|---|---|
flow / stage |
0 / -1 | Current flow and stage |
note / vel / midi_ch |
-1 | MIDI data |
event |
-1 | Kontakt event ID |
volume / pan / tune |
0 | Sound parameters |
dyn_layer / rr |
1 / 0 | Dynamic layer, round robin index |
Lifecycle Hooks¶
macro Voice.Constructor(#ref#)
declare ref := #ref#
__RUN_CB__(VoiceConstructor)
end macro
macro Voice.Destructor(#ref#)
declare ref := #ref#
__RUN_CB__(VoiceDestructor)
end macroDebugging¶
cluster.Dump(Voice, self) // Dump all fields for a reference
cluster.DumpSelf(Voice) // Shorthand within a callback
if check_ref(Voice, vo_ref) // Validate before access
{ ... safe to access ... }
end ifComplete Example¶
{ Define }
define MyCluster.MEMBERS := target, value, callback_id
macro MyCluster.def()
declare MyCluster.INIT[] := (-1, 0, -1)
define MyCluster.validate.target(#v#) := (#v# >= -1)
define MyCluster.validate.value(#v#) := (BOOL.TRUE)
define MyCluster.validate.callback_id(#v#) := (#v# >= 0)
end macro
macro MyCluster.CB()
declare target := MyCluster[self].target
declare value := MyCluster[self].value
{ Process the cluster... }
end macro
{ Register in builder.ksp }
define CLUSTER_TABLE += MyCluster
{ Use }
declare my_ref := MyCluster.new()
MyCluster[my_ref].target := some_target
MyCluster[my_ref].value := 100
MyCluster.push(my_ref)