examples/examples.md, examples/proc_dict.cfg, main.cfg : Updates to support examples.md documentation.

This commit is contained in:
kevin 2024-06-01 08:04:23 -04:00
parent 2fb2feb431
commit 5308bce0cd
3 changed files with 207 additions and 60 deletions

View File

@ -4,6 +4,10 @@
### Example 01 - Write a sine signal to an audio file. ### Example 01 - Write a sine signal to an audio file.
__caw__ programs are described using a slightly extended form of JSON.
In this example the program is contained in the dictionary labeled `sine_file_01` and
the preceeding fields (e.g. `base_dir`,`proc_dict`,`subnet_dict`, etc.) contain
system parameters that the program needs to compile and run the program.
``` ```
{ {
@ -13,7 +17,7 @@
programs: { programs: {
example_01: { sine_file_01: {
durLimitSecs:5.0, durLimitSecs:5.0,
@ -30,22 +34,17 @@
``` ```
__caw__ programs are described using a slightly extended form of JSON. When executed this program will write a five second sine signal to an audio file
In this example the program is contained in the dictionary labeled `example_01` and named `~/src/caw/examples/sine_file_01/out.wav`. The output file name
the preceeding fields (e.g. `base_dir`,`proc_dict`,`subnet_dict`, etc.) contain
system parameters that the program needs to compile and run the program.
When run this program will write a five second sine signal to an audio file
named `~/src/caw/examples/example_01/out.wav`. The output file name
is formed by joining the value of the system parameter `base_dir` with is formed by joining the value of the system parameter `base_dir` with
the name of the program `example_01`. the name of the program `sine_file_01`.
Run the program like this: Run the program like this:
``` ```
caw example.cfg example_01 caw example.cfg sine_file_01
``` ```
__caw__ specify and run a network of virtual processors. The network is __caw__ programs specify and run a network of virtual processors. The network is
described in the `procs` dictionary. described in the `procs` dictionary.
The line beginning with `osc: {` defines an instance of a `sine_tone` processor The line beginning with `osc: {` defines an instance of a `sine_tone` processor
@ -104,7 +103,7 @@ audio_file_out: {
Based on the `sine_tone` class all the default values for the signal generator Based on the `sine_tone` class all the default values for the signal generator
are apparent. With this information it is clear that the audio file are apparent. With this information it is clear that the audio file
written by `example_01` contains a stereo (`chCnt`=2), 440 Hertz signal written by `sine_file_01` contains a stereo (`chCnt`=2), 440 Hertz signal
with an amplitude of 0.8. with an amplitude of 0.8.
Note that unless stated otherwise all variables can be either input or output ports for their Note that unless stated otherwise all variables can be either input or output ports for their
@ -122,18 +121,18 @@ Link to proc class desc reference.
### Example 02: Modulated Sine Signal ### Example 02: Modulated Sine Signal
This example is an extended version of `example_01` where a low frequency oscillator This example is an extended version of `sine_file_01` where a low frequency oscillator
is formed using a second `sine_tone` processor and a sample and hold unit. The output is formed using a second `sine_tone` processor and a sample and hold unit. The output
of the sample and hold unit is then used to module the frequency of an audio of the sample and hold unit is then used to modulate the frequency of an audio
frequency `sine_tone` oscillator. frequency `sine_tone` oscillator.
Note that the LFO output by specifies a 3 Hertz sine signal Note that the LFO output is a 3 Hertz sine signal
with a gain of 110 (220 peak to peak amplitude) and an offset with a gain of 110 (220 peak-to-peak amplitude) and an offset
of 110. The signal is therefore sweeping an amplitude of 440. The signal is therefore sweeping an amplitude
between 330 and 550 which will be treated as frequency values by `osc`. between 330 and 550 which will be treated as frequency values by `osc`.
``` ```
example_02: { mod_sine_02: {
durLimitSecs:5.0, durLimitSecs:5.0,
@ -174,22 +173,22 @@ The `sample_hold` class works by maintaining a buffer of the previous `ftime` mi
samples it has received. The output is both the value of the first sample in the buffer (`sh.out`) samples it has received. The output is both the value of the first sample in the buffer (`sh.out`)
or the mean of all the values in the buffer (`sh.mean`). or the mean of all the values in the buffer (`sh.mean`).
TODO: change the name of the 'ftime' sample and hold variable.
### Example 03: Presets ### Example 03: Presets
One of the fundamental features of __caw__ is the ability to build One of the fundamental features of __caw__ is the ability to build
presets which can set the network or a given processor to a particular state. presets which can set the network, or a given processor, to a particular state.
`example_02` showed the use of a class preset on the audio oscillator. `mod_sine_02` showed the use of a class preset on the audio oscillator.
`example_03` shows how presets can be specified and applied for the entire network. `presets_03` shows how presets can be specified and applied for the entire network.
In this example four network presets are specified in the `presets` statement In this example four network presets are specified in the `presets` statement
and the "a" preset is automatically applied once the network is created and the "a" preset is automatically applied once the network is created
but before it starts to execute. but before it starts to execute.
``` ```
example_03: { presets_03: {
durLimitSecs:5.0, durLimitSecs:5.0,
preset: "a", preset: "a",
@ -225,9 +224,9 @@ then be duplicated for each channel if each channel is to be controlled
independently. independently.
One of the simplest ways to address the individual channels of a One of the simplest ways to address the individual channels of a
processor is by providing a list of value in a preset specification. processor is by providing a list of values in a preset specification.
Several examples of this are shown in the presets contained in network Several examples of this are shown in the presets contained in network
`presets` dictionary in `example_03`. For example the preset `presets` dictionary in `presets_03`. For example the preset
`a.lfo.dc` specifies that the DC offset of first channel of the LFO `a.lfo.dc` specifies that the DC offset of first channel of the LFO
should be 880 and the second channel should be 770. should be 880 and the second channel should be 770.
@ -236,7 +235,7 @@ list of values. If only a single value is given (e.g. `b.lfo.dc`) then
the same value is applied to all channels. the same value is applied to all channels.
Note that if a processor specifies a class preset with a `preset` Note that if a processor specifies a class preset with a `preset`
statement, as in the `osc` processor in `example_02`, or sets statement, as in the `osc` processor in `mod_sine_02`, or sets
initial values with an `args` statement, these initial values with an `args` statement, these
values will be applied to the processor when it is instantiated, but values will be applied to the processor when it is instantiated, but
may be overwritten when the network preset is applied. For example, may be overwritten when the network preset is applied. For example,
@ -259,3 +258,129 @@ Notice that the interpolation algorithm attempts to match channels between the p
however if one of the channels does not exist then it uses channel 0. however if one of the channels does not exist then it uses channel 0.
TODO: Check that this accurately describes preset interpolation. TODO: Check that this accurately describes preset interpolation.
### Example 04 : Programming
```
program_04: {
durLimitSecs: 10.0,
network {
procs: {
tmr: { class: timer, args:{ period_ms:1000.0 }},
cnt: { class: counter, in: { trigger:tmr.out }, args:{ min:0, max:3, inc:1, init:0, mode:modulo } },
print: { class: print, in: { in:cnt.out, eol_fl:cnt.out }, args:{ text:["my","count"] }}
}
}
}
```
This program demonstrates how __caw__ passes messages between processors.
In this case a timer is generates a pulse every 1000 milliseconds
which in turn increments a modulo 3 counter the value of which is
printed to the console.
This program should output:
```
: my : 0.000000 : count
info: : Entering runtime.
: my : 1.000000 : count
: my : 2.000000 : count
: my : 0.000000 : count
: my : 1.000000 : count
: my : 2.000000 : count
: my : 0.000000 : count
: my : 1.000000 : count
: my : 2.000000 : count
: my : 0.000000 : count
: my : 1.000000 : count
```
Notice that the __print__ processor has an _eol_fl_ variable. When this
variable receives any input it prints the last value in the _text_ list
and then a new line.
### Example 05: Processors with __mult__ inputs
`mult_inputs_05` extends `program_04` by including a __number__ and __add__ processor.
The __number__ processor acts like a register than can hold a single value.
As used here the __number__ processor simply holds the constant value '3'.
The __add__ processor sums the output of _cnt_ and _numb_.
```
mult_inputs_05: {
durLimitSecs: 10.0,
network {
procs: {
tmr: { class: timer, args:{ period_ms:1000.0 }},
cnt: { class: counter, in: { trigger:tmr.out }, args:{ min:0, max:3, inc:1, init:0, mode:modulo } },
numb: { class: number, args:{ value:3 }},
sum: { class: add, in: { in0:cnt.out, in1:numb.value } },
print: { class: print, in: { in0:cnt.out, in1:sum.out, eol_fl:cnt.out }, args:{ text:["cnt","add","count"] }}
}
}
}
```
The notable new concept introduced by this program is the concept of
__mult__ variables. These are variables which can be instantiated
multiple times by referencing them in the `in:{}` statement and
including an integer suffix. The _in_ variable of both __add__ and
__print__ have this attribute specified in their class descriptions.
In this program both of these processors have two `in` variables:
`in0` and `in1`. In practice they may have as many inputs as the
network designer requires.
### Example 06: Connecting __mult__ inputs
```
mult_conn_06: {
durLimitSecs: 5.0,
network: {
procs: {
osc: { class: sine_tone, args: { chCnt:6, hz:[110,220,440,880,1760, 3520] }},
split: { class: audio_split, in:{ in:osc.out }, args: { select:[ 0,0, 1,1, 2,2 ] } },
// Create merge.in0,in1,in2 by iterating across all outputs of 'split'.
merge_a: { class: audio_merge, in:{ in_:split.out_ } },
af_a: { class: audio_file_out, in:{ in:merge_a.out }, args:{ fname:"$/out_a.wav" }}
// Create merge.in0,in1 and connect them to split.out0 and split.out1
merge_b: { class: audio_merge, in:{ in_:split.out0_2 } },
af_b: { class: audio_file_out, in:{ in:merge_b.out }, args:{ fname:"$/out_b.wav" }}
// Create merge.in0,in1 and connect them both to split.out1
merge_c: { class: audio_merge, in:{ in0_2:split.out1 } },
af_c: { class: audio_file_out, in:{ in:merge_c.out }, args:{ fname:"$/out_c.wav" }}
}
}
}
```
TODO:
- poly_merge and audio_merge are identical except for the default input gain.
Change the default input gain to default to 1 and then manually set the initial
input gain to 0 when poly_merge is used to cross fade.
- If a proc inst label has an integer suffix it should be taken as the label-sfx-id
this would allow for using 'mult' connections to multiple source procs without using a poly.
```
g0 : { class: audio_gain, in:{ in:osc.out0 }, args: { gain:0.5}},
g1 : { class: audio_gain, in:{ in:osc.out1 }, args: { gain:0.25}},
g2 : { class: audio_gain, in:{ in:osc.out2 }, args: { gain:0.125}},
merge: { class: audio_merge, in:{ in_:g_.out } },
```
Note that this will have problems if done inside a poly.

View File

@ -50,7 +50,7 @@
audio_split: { audio_split: {
vars: { vars: {
in: { type:audio, flags:["src"], doc:"Audio input." }, in: { type:audio, flags:["src"], doc:"Audio input." },
select: { type:int, doc:"Give a list of integers where each integer selects an output channel for the associated input channel." } select: { type:cfg, doc:"Give a list of integers where each integer selects an output channel for the associated input channel." }
igain: { type:coeff, value:1.0, doc:"Audio gain for each input channel." } igain: { type:coeff, value:1.0, doc:"Audio gain for each input channel." }
ogain: { type:coeff, value:1.0, doc:"Audio gain for each output channel." } ogain: { type:coeff, value:1.0, doc:"Audio gain for each output channel." }
out: { type:audio, doc:"Audio output." }, out: { type:audio, doc:"Audio output." },
@ -63,6 +63,7 @@
} }
} }
audio_duplicate: { audio_duplicate: {
vars: { vars: {
in: { type:audio, flags:["src"], doc:"Audio input."}, in: { type:audio, flags:["src"], doc:"Audio input."},
@ -74,15 +75,9 @@
audio_merge: { audio_merge: {
vars: { vars: {
in0: { type:audio, flags:["src"], doc:"First audio input." }, in: { type:audio, flags:["src", "mult"], doc:"Audio input channel." },
in1: { type:audio, flags:["src"], doc:"Second audio input." }, gain: { type:coeff, value: 1, flags:["src", "mult"], doc:"Input channel gain." },
in2: { type:audio, flags:["src","src_opt"], doc:"Third audio input." }, out_gain: { type:coeff, value: 1, doc:"Output gain" },
in3: { type:audio, flags:["src","src_opt"], doc:"Fourth audio input." },
in4: { type:audio, flags:["src","src_opt"], doc:"Fifth audio input." },
in5: { type:audio, flags:["src","src_opt"], doc:"Sixth audio input." },
in6: { type:audio, flags:["src","src_opt"], doc:"Seventh audio input." },
in7: { type:audio, flags:["src","src_opt"], doc:"Eigth audio input." },
gain: { type:coeff, value:1.0, doc:"Audio gain for each selected (output) channel." }
out: { type:audio, doc:"Audio output. Channel count is the sum of the input channel count." }, out: { type:audio, doc:"Audio output. Channel count is the sum of the input channel count." },
} }
} }
@ -623,16 +618,6 @@
} }
}, },
poly_merge: {
vars: {
in: { type:audio, flags:["src", "mult"], doc:"Audio input channel." },
gain: { type:coeff, value: 0, flags:["src", "mult"], doc:"Input channel gain." },
out_gain: { type:coeff, value: 1, doc:"Output gain" },
out: { type:audio, doc:"Audio output." },
}
},
print: { print: {
vars: { vars: {
in: { type:all, flags:["mult"], doc: "Value to print." }, in: { type:all, flags:["mult"], doc: "Value to print." },

View File

@ -8,7 +8,7 @@
programs: { programs: {
example_01: { sine_file_01: {
durLimitSecs:5.0, durLimitSecs:5.0,
@ -22,7 +22,7 @@
} }
example_02: { mod_sine_02: {
durLimitSecs:5.0, durLimitSecs:5.0,
@ -37,7 +37,7 @@
} }
} }
example_03: { presets_03: {
durLimitSecs:5.0, durLimitSecs:5.0,
preset: "a", preset: "a",
@ -62,22 +62,59 @@
} }
} }
example_04: { program_04: {
maxCycleCount: 10, durLimitSecs: 10.0,
network { network {
procs: { procs: {
tmr: { class: timer, args:{ period_ms:1000.0 }}, tmr: { class: timer, args:{ period_ms:1000.0 }},
cnt: { class: counter, in: { trigger:tmr.out }, args:{ min:0, max:3, inc:1, init:0, mode:clip, out_type:uint }} cnt: { class: counter, in: { trigger:tmr.out }, args:{ min:0, max:3, inc:1, init:0, mode:modulo } },
numb: { class: number, print: { class: print, in: { in:cnt.out, eol_fl:cnt.out }, args:{ text:["my","count"] }}
add: { class: add, in: { in0:cnt.out, in1:cnt.out }, } }
}
}
log: { class: print, in: { in0:cnt.out, in1:add.out, eol_fl:add.out }, args:{ text:["a","b","c"] }} mult_inputs_05: {
durLimitSecs: 10.0,
network: {
procs: {
tmr: { class: timer, args:{ period_ms:1000.0 }},
cnt: { class: counter, in: { trigger:tmr.out }, args:{ min:0, max:3, inc:1, init:0, mode:modulo } },
numb: { class: number, args:{ value:3 }},
sum: { class: add, in: { in0:cnt.out, in1:numb.value } },
print: { class: print, in: { in0:cnt.out, in1:sum.out, eol_fl:cnt.out }, args:{ text:["cnt","add","count"] }}
} }
} }
} }
mult_conn_06: {
durLimitSecs: 5.0,
network: {
procs: {
osc: { class: sine_tone, args: { chCnt:6, hz:[110,220,440,880,1760, 3520] }},
split: { class: audio_split, in:{ in:osc.out }, args: { select:[ 0,0, 1,1, 2,2 ] } },
// Create merge.in0,in1,in2 by iterating across all outputs of 'split'.
merge_a: { class: audio_merge, in:{ in_:split.out_ } },
af_a: { class: audio_file_out, in:{ in:merge_a.out }, args:{ fname:"$/out_a.wav" }}
// Create merge.in0,in1 and connect them to split.out0 and split.out1
merge_b: { class: audio_merge, in:{ in_:split.out0_2 } },
af_b: { class: audio_file_out, in:{ in:merge_b.out }, args:{ fname:"$/out_b.wav" }}
// Create merge.in0,in1 and connect them both to split.out1
merge_c: { class: audio_merge, in:{ in0_2:split.out1 } },
af_c: { class: audio_file_out, in:{ in:merge_c.out }, args:{ fname:"$/out_c.wav" }}
}
}
}
} }