notes.md : Updates

This commit is contained in:
kevin 2024-12-23 16:36:32 -05:00
parent e095b0ccd8
commit cb401cdb9b

189
notes.md
View File

@ -941,6 +941,15 @@ resolvable without more information.
### TODO: ### TODO:
- Memory allocation of all non-integral types should be the responsibility of the the processor instances
this includes strings. Change the built-in string type to be a const string and
make it the responsibility of the proc instances with 'string' var's to handle
alloc and dealloc of strings.
- Add a proc class flag: 'top-level-only-fl' to indicate that a processor
cannot run as part of a poly. Any processor that calls a global function,
like 'network_apply_preset()' must run a the top level only.
- Eliminate the value() custom proc_t function and replace it by setting a 'delta flag' on the - Eliminate the value() custom proc_t function and replace it by setting a 'delta flag' on the
variables that change. Optionally a linked list of changed variables could be implemented to variables that change. Optionally a linked list of changed variables could be implemented to
avoid having to search for changed variable values - although this list might have to be implemented as a thread safe linked list. avoid having to search for changed variable values - although this list might have to be implemented as a thread safe linked list.
@ -1174,8 +1183,88 @@ These callbacks occur asynchronously on the IO system audio processing thread.
Network Architecture and Theory of Operation
---------------------------------------------
Uniform Presets: A _caw_ network is an ordered set of processors where a given
processor may contain a set of internal networks.
This leads to a heirarchy of networks and processors
that can be depicted like this.
[ diagram goes here.]
This diagram shows a two level network, where the internal
network contains an array of networks.
Networks are executed one processor at a time from top to bottom.
The networks that are members of network arrays, sibling networks,
may execute concurrently. For this reason sibling networks
may not contain any inter-connnections.
There is no way for the top level of a network to be a sibling
network, therefore it's processors are guaranteed to run in sequence
and be the only processor running while they are executing.
Network presets can only be applied between execution cycles (by the
control application) or by a top-level processor. This is the case
because it guarantees that no processors are running when the preset
values are set.
Records
-------
The primary reason to use a 'record' data type is to allow multiple
data values to be transmitted during a single cycle. In effect a
record allows a table of values to be transmitted, one row at a time,
during a single execution cycle. For example let's say that a the
output of a processor is an irregular pulse whose rate might be faster
than the audio frequency. This would require multiple pairs of value
(delta-time,amplitude) to be generated during a given cycle. The
receiving processor would then iterate through the list of pairs and
processes them each.
Another example, would be MIDI values that contain some additional
side information. The MIDI data type already has the feature that it
can generate multiple messages per execution cycle. It's format,
however, is fixed. There is no way to add addtional information, like
score location, to each message. The fields of the record data type,
however, can hold any of the other data types.
The secondary reasone to use the record data type is to simplify
the output and input interfaces to a processor and thereby
decrease the number of connections between processors.
They also make clear that a set of values is synchronized in time.
For example a set of (x,y) coordinates placed in a record
make it clear that the two values belong together in a way
that two input variables may not.
Records are also very efficient. Given that the field index is
computed in advance setting and getting the field variable
is very fast. Setting a record field avoids the overhead
of notifying receiving processors of every new value. The
receiving processor is only notified that the record
as a whole has changed.
Records are also implented in such a way that appending
a additition fields to an existing record is very fast.
The new record effectively inherits the contents of the
existing record by reference. No data is copied.
Optional Variables
-------------------
The current design does not allow for optional variables.
At processor instantiation all defined variables must exist and have a
valid value. In generate this is a good thing because it
means that call to get_var() will always return a value.
midi_out has implemented 'in' and 'rin' as optional
variables. Look there for an example of how to accomplish this.
Presets:
---------------- ----------------
Preset description and application without the presence of 'poly' proc's is very straight forward: Preset description and application without the presence of 'poly' proc's is very straight forward:
@ -1231,7 +1320,7 @@ for the label in the processor instance configation and then in the processor cl
--- ---
The following example shows how the preset processor labels can use underscore notation to address a range of processors. The following example shows how the preset processor labels can use suffix notation to address a range of processors.
``` ```
example_2: example_2:
@ -1251,16 +1340,14 @@ example_2:
} }
presets: { presets: {
a: { g_: { gain:0.1 } }, // Use underscore notation to apply a preset value to g0,g1,g2. a: { g_: { gain:0.1 } }, // Use suffix notation to apply a preset value to g0,g1,g2.
b: { g0_2: { gain:0.2 } }, // Use underscore notation to apply a preset value to g0 and g1. b: { g0_2: { gain:0.2 } }, // Use suffix notation to apply a preset value to g0 and g1.
c: { g2: { gain:0.3 } }, // Apply a preset value to g2 only. c: { g2: { gain:0.3 } }, // Apply a preset value to g2 only.
} }
} }
} }
``` ```
Todo: This should work now.
--- ---
@ -1289,9 +1376,9 @@ example_3: {
presets: presets:
{ {
a:{ lfo:{ hz:1 } }, a:{ lfo:{ gain:0.1 } },
b:{ lfo:{ hz:2 } }, b:{ lfo:{ gain:0.2 } },
c:{ lfo0_1: { hz:3 }, lfo2:{ hz:4 } }, c:{ lfo0_1: { gain:0.3 }, lfo2:{ gain:0.4 } },
} }
} }
@ -1324,7 +1411,28 @@ In the example the outer most presets may therefore address the 'osc_poly' prese
the processors contained by 'osc_poly'. the processors contained by 'osc_poly'.
--- ---
Presets with hetergenous poly networks
---
Dual Presets
---
Dual Presets with poly networks.
---
Dual Presets with heterogenous poly networks
---
Presets with user defined processors
---
Presets with user defined processors in poly networks.
---
Presets with user defined processors containing poly networks.
---
Use 'interface' objects to intercept preset values so that the Use 'interface' objects to intercept preset values so that the
can be processed before being passed on to a the object that can be processed before being passed on to a the object that
they represent. they represent.
@ -1362,32 +1470,53 @@ example_4:
--- ---
Preset Implementation:
How are presets implemented: All presets are resolved to (proc,var,value) tuples when the networks are created.
A given named network preset is therefore a list these tuples.
1. At compile time: Applying the preset is then just a matter of calling
var_set(proc,var,value) for each tuple in the list.
- Each named preset is stored as a list of value-records and label-records This pre-processing approach mostly avoids having to do value parsing
called preset-value-lists. or variable resolution at runtime.
- value-records have the form { var, chIdx, value } and are created from each
preset value that directly references a variable.
- label-records are formed from labeled preset references and have the form
{ preset-value-list-ptr }. Where the list refers to the list associated with the given label.
- The collection of multiple named presets are then stored as a list of Preset dictionaries have the following grammar:
preset-value-lists as part of the network instance in which they were defined. ```
<preset-dict> -> <preset-label> : { <proc-label>: (<proc-preset-label> | <value-dict>) }
2. At runtime the preset is applied by: <value-dict> -> { <var_label>:(<literal> | [ <literal>* ])
- Resolving the preset label to a preset-value-list. ``
- Calling var_set(var,chIdx,value) for all value-records
- Iterating over all records in the list refered to preset-value-list-ptr of the label-records.
The notable characteristic of this approach is that it is very fast. A preset is a named ('<preset-label>') dictionary.
With the exception of resolving the initial preset label to the top level preset-value-list The pairs contained by the dictionary reference processors (<proc-label>).
no search or address resolution is required to apply the preset. The value of each pair is either a dictionary of variable
values (<value-dict>) or a label (<proc-preset-label>).
The <value-dict> is a collection of literal values
which can be directly converted to (proc,var,value) tuples.
In the case where a value is a list of literals the
individual values are used to address successive channels.
As part of variable resolution new variable channels will
be created if a preset references a channel that does
not yet exist on the given variable. This guarantees
that the variable channel will be valid should the preset
be applied.
When the value of a preset pair is a <proc-preset-label>
the label may refer to one of three possible
source of preset variable values.
1. Processor class preset. This is a named <value-dict> defined with the processor class description.
2. Processor instance preset. This is a named <value-dict> defined with the processor instance.
3. Poly processor network preset. If the <proc-label> associated with
this <proc-preset-label> is a 'poly' processor then this label refers
to a network preset defined within the 'poly' instances.
Note that both <proc-label> and <var-label> strings may use 'suffix'
notation. In the case of variables this allows the preset to target
specific 'mult' variable instances or ranges of instances.
When the network is a poly network using suffix notation
with a <proc-label> allows the target to particular
instances or ranges of instances.
Final Notes: Final Notes: