diff --git a/examples/examples.cfg b/examples/examples.cfg index 3457764..7937ffe 100644 --- a/examples/examples.cfg +++ b/examples/examples.cfg @@ -1,13 +1,13 @@ { - base_dir: "~/src/caw/examples", // Base project directory. See 'sine_file_01' below. - io_dict: "~/src/caw/src/caw/cfg/io.cfg" // Runtime configuration file. - proc_dict: "~/src/caw/src/libcw/flow/proc_dict.cfg", // Processor class definition file. - subnet_dict: "~/src/caw/src/libcw/flow/subnet_dict.cfg", // User written subnet files + base_dir: "~/src/caw/examples/io", // Base project directory. See 'sine_file_01' below. + io_dict: "~/src/caw/src/caw/cfg/io.cfg" // Runtime configuration file. + proc_dict: "~/src/caw/src/libcw/flow/proc_dict.cfg", // Processor class definition file. + udp_dict: "~/src/caw/src/libcw/flow/udp_dict.cfg", // User defined proc files programs: { - rt_sine_00: { + ex_00_rt_sine: { dur_limit_secs:5.0, @@ -21,7 +21,7 @@ } // Demonstrate a simple two processor network. - sine_file_01: { + ex_01_sine_file: { non_real_time_fl:true, dur_limit_secs:5.0, @@ -37,7 +37,7 @@ // Demonstrate a simple signal processing network - mod_sine_02: { + ex_02_mod_sine: { non_real_time_fl:true, dur_limit_secs:5.0, @@ -55,7 +55,7 @@ // Demonstrate applying a preset at initialization time. - presets_03: { + ex_03_presets: { non_real_time_fl:true, dur_limit_secs:5.0, @@ -82,7 +82,7 @@ } // Demonstrate the `print` processor and event programming. - program_04: { + ex_04_program: { non_real_time_fl:true, dur_limit_secs: 10.0, @@ -97,7 +97,7 @@ } // Demonstrate 'mult' inputs. - mult_inputs_05: { + ex_05_mult_inputs: { non_real_time_fl:true, dur_limit_secs: 10.0, @@ -115,7 +115,7 @@ // Demonstrate different forms of the in-stmt - mult_conn_06: { + ex_06_mult_conn: { non_real_time_fl:true, dur_limit_secs: 5.0, @@ -147,7 +147,7 @@ // Demonstrate creating processors with explicit sfx-ids and connecting to them with a single in-stmt. - proc_suffix_07: { + ex_07_proc_suffix: { non_real_time_fl:true, dur_limit_secs: 5.0, @@ -172,7 +172,7 @@ } // Demonstrate instantiating 'mult' variables from the an 'args' statement. - mix_08: { + ex_08_mix: { non_real_time_fl:true, dur_limit_secs:5.0, @@ -193,7 +193,7 @@ // Demonstrate a network with a polyphonic subnet. - simple_poly_09: { + ex_09_homog_poly: { non_real_time_fl:true, dur_limit_secs: 5.0, @@ -207,7 +207,9 @@ osc_poly: { class: poly, - args: { count:3 }, // Create 3 instances of 'network'. + + // Create 3 instances of 'network' and run them in concurrently. + args: { count:3, parallel_fl:true }, network: { procs: { @@ -226,8 +228,54 @@ } } + // Demonstrate a network with poly network with different subnets. + ex_10_hetero_poly: { + + non_real_time_fl:true, + dur_limit_secs: 5.0, - feedback_10: { + network: { + + procs: { + + g_list: { class: list, args: { in:0, list:[ 110f,220f,440f ]}}, + dc_list: { class: list, args: { in:0, list:[ 220f,440f,880f ]}}, + + osc_poly: { + class: poly, + + args: { parallel_fl:true }, + + network: [ + + // network 0 + { + procs: { + lfo: { class: sine_tone, in:{ _.dc:_.dc_list.value_, _.gain:_.g_list.value_ } args: { ch_cnt:1, hz:3 }}, + sh: { class: sample_hold, in:{ in:lfo.out }}, + osc: { class: sine_tone, in:{ hz: sh.out }}, + } + }, + + // network 1 + { + procs: { + oscil: { class: sine_tone, args:{ hz:55 }}, + } + } + ] + } + + // Iterate over the instances of `osc_poly.osc_.out` to create one `audio_merge` + // input for every output from the polyphonic network. + merge: { class: audio_merge, in:{ in0:osc_poly.osc.out, in1:osc_poly.oscil1.out}, args:{ gain:1, out_gain:0.5 }}, + af: { class: audio_file_out, in:{ in:merge.out } args:{ fname:"$/out.wav"} } + } + } + } + + + ex_11_feedback: { non_real_time_fl:true, max_cycle_count: 10, @@ -243,7 +291,7 @@ }, - feedback_11: { + ex_11a_feedback: { non_real_time_fl:true, max_cycle_count: 10, print_network_fl: true, @@ -259,7 +307,7 @@ } }, - subnet_12 : { + ex_12_user_defined_proc : { non_real_time_fl: true, dur_limit_secs: 5, diff --git a/examples/examples.md b/examples/examples.md index 508cafe..0339f93 100644 --- a/examples/examples.md +++ b/examples/examples.md @@ -8,13 +8,13 @@ system parameters that the program needs to compile and run the program. ``` javascript { - base_dir: "~/src/caw/examples", + base_dir: "~/src/caw/examples/io", proc_dict: "~/src/caw/examples/proc_dict.cfg", mode: non_real_time, programs: { - sine_file_01: { + ex_01_sine_file: { dur_limit_secs:5.0, // Run the network for 5 seconds @@ -33,7 +33,7 @@ system parameters that the program needs to compile and run the program. } ``` -![Example 0](svg/00_osc_af.svg "`sine_file_01` processing network") +![Example 0](svg/00_osc_af.svg "`ex_01_sine_file` processing network") When executed this program will write a five second sine signal to an audio file named `~/src/caw/examples/sine_file_01/out.wav`. The output file name @@ -103,7 +103,7 @@ audio_file_out: { ``` The class definitions specify the names, types and default values for -each variable. Since the `sine_tone` instance in `sine_file_00` +each variable. Since the `sine_tone` instance in `ex_01_sine_file` doesn't override any of the the variable default values the generated audio file must be a stereo (`ch_cnt`=2), 440 Hertz signal with an amplitude of 0.8. @@ -124,7 +124,7 @@ Attribute | Description src | This variable must be connected to a source variable or the processor instantiation will fail. no_src | This variable cannot be connected to a source variable (it is write-only, or output only). init | This variable is only read at processer instantiation time, changes during runtime will be ignored. -mult | This variable may be instantiated multiple times. See `mult_input_05` below. +mult | This variable may be instantiated multiple times. See `ex_05_mult_input` below. out | This is a subnet output variable. __caw__ uses types and does it's best at converting between types where the conversion will @@ -161,13 +161,13 @@ ftime | double | Fractional time in seconds or milliseconds. Also notice that the processor class has named presets. During processor instantiaion these presets can be used to set the -initial state of the processor. See `mod_sine_02` below for +initial state of the processor. See `ex_02_mod_sine` below for an example of a class preset used this way. ### Example 02: Modulated Sine Signal -This example is an extended version of `sine_file_01` where a low frequency oscillator (LFO) +This example is an extended version of `ex_01_sine_file` where a low frequency oscillator (LFO) 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 modulate the frequency of an audio frequency `sine_tone` oscillator. @@ -178,7 +178,7 @@ of 440. The LFO output signal is therefore sweeping an amplitude between 330 and 550 which will be treated as frequency values by `osc`. ``` json -mod_sine_02: { +ex_02_mod_sine: { dur_limit_secs:5.0, @@ -194,7 +194,7 @@ mod_sine_02: { } ``` -![Example 2](svg/02_mod_sine.svg "`mod_sine_02` processing network") +![Example 2](svg/02_mod_sine.svg "`ex_02_mod_sine` processing network") The `osc` instance in this example uses a `preset` statement. This will have the effect of applying the class preset `mono` to the `osc` when it is @@ -226,8 +226,8 @@ value from the buffer, and 'mean' is the average of all the values in the buffer 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. -`mod_sine_02` showed the use of a class preset to set the number of -audio channels generated by the audio oscillator. `presets_03` shows +`ex_02_mod_sine` showed the use of a class preset to set the number of +audio channels generated by the audio oscillator. `ex_03_presets` shows how presets can be specified and applied for the entire network. In this example four network presets are specified in the `presets` statement @@ -238,7 +238,7 @@ If this example was run in real-time it would also be possible to apply the the presets while the network was running. ``` JSON -presets_03: { +ex_03_presets: { dur_limit_secs:5.0, preset: "a", @@ -276,7 +276,7 @@ independently. One of the simplest ways to address the individual channels of a processor is by providing a list of values in a preset specification. Several examples of this are shown in the presets contained in then network -`presets` dictionary in `presets_03`. For example the preset +`presets` dictionary in `ex_03_presets`. For example the preset `a.lfo.dc` specifies that the DC offset of first channel of the LFO should be 880 and the second channel should be 770. @@ -285,7 +285,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. Note that if a processor specifies a class preset with a `preset` -statement, as in the `osc` processor in `mod_sine_02`, or sets +statement, as in the `osc` processor in `ex_02_mod_sine`, or sets initial values with an `args` statement, these values will be applied to the processor when it is instantiated, but may be overwritten when the network preset is applied. For example, @@ -319,7 +319,7 @@ TODO: Check that this accurately describes preset interpolation. ### Example 04 : Event Programming ``` -program_04: { +ex_04_program: { dur_limit_secs: 10.0, @@ -333,7 +333,7 @@ program_04: { } ``` -![Example 4](svg/04_program.svg "`program_04` processing network") +![Example 4](svg/04_program.svg "`ex_04_program` processing network") This program demonstrates how __caw__ passes messages between processors. @@ -369,13 +369,13 @@ always print after the value received by `log.in`. ### Example 05: Processors with expandable numbers of inputs -`mult_inputs_05` extends `program_04` by including a __number__ and __add__ processor. +`ex_05_mult_inputs` extends `ex_04_program` 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 then sums the output of _cnt_ and _numb_. ``` -mult_inputs_05: { +ex_05_mult_inputs: { dur_limit_secs: 10.0, @@ -391,7 +391,7 @@ mult_inputs_05: { } ``` -![Example 5](svg/05_mult_inputs.svg "`mult_inputs_05` processing network") +![Example 5](svg/05_mult_inputs.svg "`ex_05_mult_inputs` processing network") The notable new concept introduced by this program is the concept of __mult__ variables. These are variables which can be instantiated @@ -416,7 +416,7 @@ to easily create and connect many `mult` variables in a single connection expression. ``` -mult_conn_06: { +ex_06_mult_conn: { dur_limit_secs: 5.0, @@ -443,7 +443,7 @@ mult_conn_06: { } ``` -![Example 6](svg/06_mult_conn.svg "`mult_conn_06` processing network") +![Example 6](svg/06_mult_conn.svg "`ex_-6_mult_conn` processing network") The audio source for this network is a six channel signal generator, where the frequency is each channel is incremented by an octave. @@ -455,7 +455,7 @@ variables `out0`,`out1` and `out2`. The __audio_split__ class takes a single signal and splits it into multiple signals. The __audio_merge__ class takes multple signals and concatenates them into a single signal. -Each of the three merge processor (merge_a,merge_b,merge_c) in `mult_conn_06` +Each of the three merge processor (merge_a,merge_b,merge_c) in `ex_06_mult_conn` demonstrates three different ways of selecting multiple signals to merge in with a single `in:{...}` statement expression. @@ -520,7 +520,7 @@ An error should be generated. ### Example 07: Processor suffix notiation -As demonstrated in `mult_conn_06` variables are identified by their label +As demonstrated in `ex_-6_mult_conn` variables are identified by their label and an integer suffix id. By default, for non __mult__ variables, the suffix id is set to 0. Using the `in:{...}` statement however variables that have the 'mult' attribute can be instantiated multiple times with each instance having a different suffix id. @@ -529,7 +529,7 @@ Processors instances use a similar naming scheme; they have both a text label and a suffix id. ``` -proc_suffix_07: { +ex_07_proc_suffix: { dur_limit_secs: 5.0, network: { @@ -549,14 +549,14 @@ proc_suffix_07: { ``` -![Example 7](svg/07_proc_suffix.svg "`proc_suffix_07` processing network") +![Example 7](svg/07_proc_suffix.svg "`ex_07_proc_suffix` processing network") In this example three __audio_gain__ processors are instantiated with the same label 'g' and are then differentiated by their suffix id's: 0,1, and 2. The merge processor is then able to connect to them using a single `in:{...}` expression, `in_:g_.out` which iterates over the gain processors suffix id. This expression is very similar to the -`merge_a` connection expression in `mult_conn_06`: `in_:split.out_` +`merge_a` connection expression in `ex_06_mult_conn`: `in_:split.out_` which iterated over the label suffix id's of the `split.out`. In this case the connection is iterating over the label suffix id's of the networks processors rather than over a processors variables. @@ -596,7 +596,7 @@ The slight complication however is that every input also has a gain coefficient associated with it that the user may want to set. ``` -mix_08: { +ex_08_mix: { non_real_time_fl:true, dur_limit_secs:5.0, @@ -614,9 +614,9 @@ mix_08: { } ``` -![Example 8](svg/08_mix.svg "`mix_08` processing network") +![Example 8](svg/08_mix.svg "`ex_08_mix` processing network") -Notice that the `mix` processor instantiates two stereo input channels in the `in:{...}` statement +Notice that the `mix` processor instantiates two stereo input channels in the `in:{...}` statement and then assigns initial gain values to each individual channel. If a scalar value was given instead of a list (e.g. `igain0:0.8`) then the scalar value would be assigned to all channels @@ -625,11 +625,10 @@ list (e.g. `igain0:0.8`) then the scalar value would be assigned to all channels This example introduces the __poly__ construct. In previous examples when the network used multiple copies of the same processor they were manually constructed - each with a unique suffix id. The __poly__ construct allows whole sub-networks to be duplicated -and automatically assigned unique suffix id's. - +and automatically assigned unique suffix id's. ``` -simple_poly_09: { +ex_09_simple_poly: { non_real_time_fl:true, dur_limit_secs: 5.0, @@ -638,7 +637,10 @@ simple_poly_09: { procs: { + // LFO gain parameters - one per poly voice g_list: { class: list, args: { in:0, list:[ 110f,220f,440f ]}}, + + // LFO DC offset parameters - one per poly voice dc_list: { class: list, args: { in:0, list:[ 220f,440f,880f ]}}, osc_poly: { @@ -663,7 +665,9 @@ simple_poly_09: { } ``` -![Example 9](svg/09_simple_poly.svg "`simple_poly_09` processing network") +![Example 9](svg/09_simple_poly.svg "`ex_09_simple_poly` processing network") + +This program instantiates three modulated sine tones each with a different set of parameters. Notice the _lfo_ `in:{...}` statement for the `dc` variable connection. The statement contains three underscores. The first @@ -683,15 +687,83 @@ and desination processors share the same id. This allows the suffix id to be dropped from the source processor and thereby to simplify the syntax for connecting sub-network processors. -Finally note that _poly_ to external connections are simply made +Also note that _poly_ to external connections are simply made by referring to the poly source by name to locate the source processor. This is shown in the `merge` input statement `in:{ in_:osc_poly.osc_.out}`. +The final characteristic to note about the poly construct +is the use of the 'parallel_fl' attribute. When this flag is +set the subnets will run concurrently in separate threads. +Since no connections between subnets is possible, and no other +processors can run, while the subnets are running this is +always safe. -### Example 10: Feedback +### Example 10: Heterogeneous polyphonic subnet + +In the previous example each of the three voices shared the same network +structure. In this example there are two voice with different +networks. ``` -feedback_10: { +ex_10_hetero_poly: { + + non_real_time_fl:true, + dur_limit_secs: 5.0, + + network: { + procs: { + + g_list: { class: list, args: { in:0, list:[ 110f,220f,440f ]}}, + dc_list: { class: list, args: { in:0, list:[ 220f,440f,880f ]}}, + + osc_poly: { + class: poly, + + args: { parallel_fl:true }, + + network: [ + + // network 0 + { + procs: { + lfo: { class: sine_tone, in:{ _.dc:_.dc_list.value_, _.gain:_.g_list.value_ } args: { ch_cnt:1, hz:3 }}, + sh: { class: sample_hold, in:{ in:lfo.out }}, + osc_a: { class: sine_tone, in:{ hz: sh.out }}, + } + }, + + // network 1 + { + procs: { + osc_b: { class: sine_tone, args:{ hz:55 }}, + } + } + ] + } + + // Iterate over the instances of `osc_poly.osc_.out` to create one `audio_merge` + // input for every output from the polyphonic network. + merge: { class: audio_merge, in:{ in0:osc_poly.osc_a.out, in1:osc_poly.osc_b.out}, args:{ gain:1, out_gain:0.5 }}, + af: { class: audio_file_out, in:{ in:merge.out } args:{ fname:"$/out.wav"} } + } + } +} +``` + +Since the structure of the two subnets is different the very compact +`in:{...}` statements used to connect the `merge` processor to +the output of each of the `osc` processors is no longer possible. +However, this example demonstrates how `caw`can be used to +run heterogeneous networks concurrently thereby makeing better +use of available hardware cores. + + + + +### Example 11: Feedback + +``` +ex_11_feedback: { non_real_time_fl:true, max_cycle_count: 10, @@ -705,7 +777,7 @@ feedback_10: { } } ``` -![Example 10](svg/10_feedback.svg "`feedback_10` processing network") +![Example 11](svg/11_feedback.svg "`ex_11_feedback` processing network") This example demonstrates how to achieve a feedback connection using the `out:{...}` statement. Until now all the examples have been @@ -816,7 +888,7 @@ shown in the `mo_out` port. The `mod_osc` user defined processor is instantiated and used just like a built-in processor. ``` -user_defined_subnet_12 : { +user_defined_proc_12 : { non_real_time_fl: true, dur_limit_secs: 5,