diff --git a/Makefile.am b/Makefile.am index ac568fd..82f0f8d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -4,8 +4,8 @@ libcwSRC = libcwHDR += src/libcw/cwCommon.h src/libcw/cwCommonImpl.h src/libcw/cwMem.h src/libcw/cwLog.h src/libcw/cwUtility.h libcwSRC += src/libcw/cwCommonImpl.cpp src/libcw/cwMem.cpp src/libcw/cwLog.cpp src/libcw/cwUtility.cpp -libcwHDR += src/libcw/cwString.h src/libcw/cwMath.h src/libcw/cwVectOps.h src/libcw/cwMtx.h src/libcw/cwVariant.h -libcwSRC += src/libcw/cwString.cpp src/libcw/cwMath.cpp src/libcw/cwMtx.cpp src/libcw/cwVariant.cpp +libcwHDR += src/libcw/cwString.h src/libcw/cwMath.h src/libcw/cwVectOps.h src/libcw/cwMtx.h src/libcw/cwVariant.h +libcwSRC += src/libcw/cwString.cpp src/libcw/cwMath.cpp src/libcw/cwVectOps.cpp src/libcw/cwMtx.cpp src/libcw/cwVariant.cpp libcwHDR += src/libcw/cwB23Tree.h libcwSRC += src/libcw/cwB23Tree.cpp @@ -34,8 +34,8 @@ libcwSRC += src/libcw/cwAudioFile.cpp src/libcw/cwMidiFile.cpp libcwHDR += src/libcw/cwAudioFileOps.h src/libcw/cwAudioTransforms.h src/libcw/cwDspTransforms.h src/libcw/cwAudioFileProc.h src/libcw/cwPvAudioFileProc.h libcwSRC += src/libcw/cwAudioFileOps.cpp src/libcw/cwAudioTransforms.cpp src/libcw/cwDspTransforms.cpp src/libcw/cwAudioFileProc.cpp src/libcw/cwPvAudioFileProc.cpp -libcwHDR += src/libcw/cwFlow.h src/libcw/cwFlowDecl.h src/libcw/cwFlowTypes.h src/libcw/cwFlowProc.h src/libcw/cwFlowCross.h -libcwSRC += src/libcw/cwFlow.cpp src/libcw/cwFlowTypes.cpp src/libcw/cwFlowProc.cpp src/libcw/cwFlowCross.cpp +libcwHDR += src/libcw/cwFlow.h src/libcw/cwFlowDecl.h src/libcw/cwFlowTypes.h src/libcw/cwFlowNet.h src/libcw/cwFlowProc.h src/libcw/cwFlowCross.h src/libcw/cwFlowTest.h +libcwSRC += src/libcw/cwFlow.cpp src/libcw/cwFlowTypes.cpp src/libcw/cwFlowNet.cpp src/libcw/cwFlowProc.cpp src/libcw/cwFlowCross.cpp src/libcw/cwFlowTest.cpp if cwWEBSOCK libcwHDR += src/libcw/cwWebSock.h src/libcw/cwWebSockSvr.h @@ -46,8 +46,8 @@ libcwSRC += src/libcw/cwUi.cpp src/libcw/cwUiTest.cpp endif -libcwHDR += src/libcw/cwKeyboard.h -libcwSRC += src/libcw/cwKeyboard.cpp +libcwHDR += src/libcw/cwKeyboard.h src/libcw/cwTest.h +libcwSRC += src/libcw/cwKeyboard.cpp src/libcw/cwTest.cpp libcwHDR += src/libcw/cwSerialPortDecls.h src/libcw/cwSerialPort.h src/libcw/cwSerialPortSrv.h libcwSRC += src/libcw/cwSerialPort.cpp src/libcw/cwSerialPortSrv.cpp diff --git a/README.md b/README.md index 2a8f27c..f4a0647 100644 --- a/README.md +++ b/README.md @@ -432,7 +432,7 @@ struct in_addr { export LD_LIBRARY_PATH=~/sdk/libwebsockets/build/out/lib -*** Raspberry Pi Build Notes: +### Raspberry Pi Build Notes: cd sdk mkdir libwebsockets @@ -444,10 +444,10 @@ struct in_addr { -*** Flow Notes: +### Flow Notes: -- When a variable has a variant with a numberic channel should the 'all' channel variant be removed? +- When a variable has a variant with a numeric channel should the 'all' channel variant be removed? - Check for duplicate 'vid'-'chIdx' pairs in var_regster(). (The concatenation of 'vid' and 'chIdx' should be unique @@ -469,16 +469,21 @@ specific types, to pass through. For example a 'selector' (n inputs, 1 output) o DONE: Add a version of var_register() that both registers and returns the value of the variable. -*** Flow Instance Creation: +### Flow Instance Creation: -1. Create all vars from the class description and initially set their -value to the default value given in the class. chIdx=kAnyChIdx. +0. Parse the 'in' list and create any 'mult' variables whose +'in-var' contains an integer or underscore suffix. See +"'in' List Syntax and Semantics" below. + +1. Create all vars from the class description, that were not +already instantiated during 'in' list processing, and set their +initial value to the default value given in the class. chIdx=kAnyChIdx. Note that all vars must be included in the class description. -2. Apply the preset record from the class description according to the -label given in the instance definition. +2. Apply the preset records from the class description according to the +'presets' list given in the instance definition. If the variable values are given as a scalar then the existing variable is simply given a new value. @@ -489,17 +494,23 @@ index of the value in the list. This is referred to as 'channelizing' the variable because the variable will then be represented by multiple physical variable records - one for each channel. This means that all variables will have their initial record, with the chIdx set to 'any', -and then they may also have further variable records will for each explicit +and then they may also have further variable records for each explicit channel number. The complete list of channelized variable record is kept, in channel order, using the 'ch_link' links with the base of the list on the 'any' record. -3. Apply the variable values defined in the instance 'args' record. -This application is treated similarly to the 'class' -preset. If the variable value is presented in a list then -the value is assigned to a specific channel if the channel -already exists then the value is simply replaced, if the -channel does not exist then the variable is 'channelized'. +3. Apply the variable values defined in a instance 'args' record. +The 'args' record may have multiple sets of args. +If the preset instance includes an 'argsLabel' value then this record +is selected to be applied. If No 'argsLabel' is given then +the record named 'default' is selected. If there is no record +named 'default' then no record is applied. + +The application of the args record proceeds exactly the same as +applying a 'class' preset. If the variable value is presented in a +list then the value is assigned to a specific channel. If the channel +already exists then the value is simply replaced. If the channel does +not exist then the variable is 'channelized'. 4. The varaibles listed in the 'in' list of the instance cfg. are connected to their source variables. @@ -518,7 +529,646 @@ before registering the variable. 6. The internal variable id map is created to implement fast access to registered variables. +7. The + +# Notes on 'poly' and 'mult': + +The 'in' statement is formed by a list of _Connect Expressions_ : + +`:` + +There are three forms of connect expressions: + +1. Simple Connect Expression: Both the input and source labels +identify vars in the input and source instance. + +2. Manual Mult Connect Expression: The input identifer ends with an +integer. This expression indicates that an input var will be +instantiated and connected to the source var. The integer indicates +the suffix (sfx) id of the input var. e.g. `in0:osc.out`,`in1:filt.out`. + +3. PolyMult Connect Expression: The source identifier has an +underscore suffix. This form indicates that there will one instance of +this var for each poly instance that the source var instances is +contained by. e.g. `in:osc_.out` If `osc` is contained by an order 3 +poly then statement will create and connect three instances of `in` - +`in0:osc0.out`,`in1:osc1.out` and `in2:osc2.out`. + +Notes: +- For an input variable to be used in either of the 'Manual' or 'PolyMult' +forms the var class desc must have the 'mult' attribute. + +- If any var has an integer suffix then this is converted to it's sfx id. + +- If the input var of a poly mult expression has an integer suffix then this is taken to be the +base sfx id for that poly connection. Other connections in the same statement will be +incremented from that base value. e.g `in3:osc_.out` becomes +`in3:osc0.out`,`in4:osc1.out` and `in5:osc2.out`. + +- The first and last poly source instance can be indicated by specifying a +begin poly index and count before and after the source index underscore: +e.g. `in:osc3_3.out` becomes: `in0:osc3.out`,`in1:osc4.out` and `in2:osc5.out`. + +- A similar scheme can be used to indicate particular source instance vars: +`in:osc.out1_2` becomes `in0:osc.out1`,`in1:osc.out2` + +- It is a compile time error to have more than one input variable with the same sfx id. + +'in' List Syntax and Semantics: +=============================== + +Syntax: +------- +The 'in' list has the follow syntax: +`in: { in-stmt* }` +`in-stmt` -> `in_expr`":" `src_expr` +`in-expr` -> `in-proc-id`".`in-var-id` +`src-expr` -> `src-proc-id`"."`src-var-id` +`in-var-id` -> `var-id` +`src-proc-id` -> `var-id` +`src-var-id` -> `var-id` +`var-id` -> `label` { `label-sfx` } +`label-sfx` -> { `pri-int`} {{"_"} `sec-int` } +`pri-int` -> int +`sec-int` -> int + + +Semantics: +---------- + +### `in-proc-id` + +- The `in-proc-id` is only used when the in-stmt +is iterating over the in-proc sfx-id. +This precludes iterating over the in-var, as discussed below. + +In this case the only useful option is to set the 'var-id` to `_` +as the in-proc is taken as the the proc which the +in-stmt belongs to. + +The iterating source and/or var sfx-id are then set +to the current proc sfx-id + source `pri-int`. + + +### `in-var-id` + +- The `label` part of the `in-var-id` must match to a +var description in the input proc class description. + +- If no `label-sfx` is given then no special action +need by taken at var creation time. This var will be +created by default and later connected to the source inst/var. + + +- (0) If the "_" is given: + + This is an "iterating" in-stmt where multiple + input vars will be created and connected. + + + If no `pri-int` is given then the `pri-int` defaults to 0. + + + If the `pri-int` is given then it indicates that + an instance of this var should be created where + the `pri-int` becomes the sfx-id of the var instance. + + + If `sec-int` is given then it gives the + count of input vars which will be created. The + sfx-id of each new input var begins with `pri-int` + and increments for each new var. + + + (1) If no `sec-int` is given then the `sec-int` is implied by the count + of source variables indicated in the `src-expr`. + + +- If "_" is not given: + + No `sec-int` can exist without a "_". + + + If a `pri-int` is given then a single + input var is created and the `pri-int` + gives the sfx-id. This single input + var is then connected to a single src var. + + + If no `pri-int` is given + then the default var is created + with kBaseSfxId and is connected + to a single source var. + + +### `src-proc-id` + +- The `label` part of the `src-proc-id` must match to a +previously created proc instance in the current network. + +- If a `label-sfx` is given then the `pri-int` gives +the sfx-id of the first proc inst to connect to. +If no `pri-int` is given then the first sfx-id +defaults to 0. + + +- If "_" is given: + + This is an "iterating" src-proc and therefore + the in-var must also be iterating. See (0) + + + If a `sec-int` is given then this gives the count of + connections across multiple proc instances with + sfx-id's beginnign with `pri-int`. Note that if + `sec-int` is given then the `in-var-id` must be + iterating and NOT specify an iteration count, + as in (1) above. + + + If no `sec-int` is given then the + `sec-int` defaults to the count of + available proc instances with the given `label` + following the source proc inst `pri-int`. + + + +- If "_" is not given then this is not an + iterating proc inst. + + + If the input var is iterating + then it must specify the iteration count or + the `src-var-id` must be iterating. + + + If the `pri-int` is given then it specifies + the sfx-id of the src-proc + + + If the `pri-int` is not given + + - If the src-net is the same as the in-var net then + the sfx-id of the in-var proc is used as the src-proc sfx-id + + +### `src-var-id` + +- The `label` part of the `in-var-id` must match to a +var description in the source proc class descriptions. + +- If a `label-sfx` is given then the `pri-int` gives +the sfx-id of the first source var to connect to +on the source proc instance. If no `pri-int` is +given then the first sfx-id defaults to 0. + +- If a "_" is given: + + This is an "iterating" + source var and therefore the input var + must specifiy an iterating connection and + the source proc inst must not specify an iterating + connection. See (0) above. + + + If a `sec-int` is given then this gives the count of + connections across multiple source vars with + sfx-id's beginnign with `pri-int`. Note that if + `sec-int` is given then the `in-var-id` must be + iterating and NOT specify an iteration count, + as in (1) above. + + + + If `sec-int` is not given + then the `sec-int` defaults to the count of + available source vars with the given `label` + following the source var `pri-int`. + +- If "_" is not given then this is not an + iterating source var. If the input var is iterating + then it must specify the iteration count or + the `src-proc-id` must be iterating. + + +### Notes: + +- If the `in-var-id` is iterating but neither `src-proc-id` +or `src-var-id` are iterating then the `in-var-id` must +specify the iteration count and the connection will be +made to exactly one source var on the source proc inst. + +- If `in-var-id` is iterating then the iterations count +must come from exactly one place: + + the input var `sec-int` + + the source proc `sec-int` + + the source var `sec-int` + +This means that only one literal iter count can be +given per `in-stmt`. It is a syntax error if +more than one literal iter counts are given. + +- Use cases + + connect one input to one source + + connect multiple inputs to the same var on multiple procs + + connect multiple inputs to multiple vars on one proc + + connect multiple inputs to one var on one proc + + +### in-stmt Examples: + +`in:sproc.svar` Connect the local variable `in` to the source variable `sproc.svar`. + +`in0:sproc.svar` Create variables `in0` and connect to `sproc.svar`. + +`in_2:sproc.svar` Create variables `in0` and `in1` and connect both new variables to `sproc.svar`. + +`in_:sproc.svar0_2` Create variables `in0` and `in1` and connect them to `sproc.svar0` and `sproc.svar1`. + +`in3_3:sproc.svar` Create variables `in3`,`in4` and `in5` and connect them all to `sproc.svar`. + +`in_:sproc.svar1_2` Create variables `in0`,`in1` and connect them to `sproc.svar1` and `sproc.svar2`. + +`in1_2:sproc.svar3_` Create variables `in1`,`in2` and connect them to `sproc.svar3` and `sproc.svar4`. + +`in_:sproc.svar_` Create vars `in0 ... n-1` where `n` is count of vars on `sproc` with the label `svar`. +`n` is called the 'mult-count' of `svar`. The new variables `in0 ... n-1` are also connected to `sproc.svar0 ... n-1`. + +`in_:sproc_.svar` Create vars `in0 ... n` where `n` is determineed by the count of procs named `sproc`. +`n` is called the 'poly-count' of `sproc`. The new variables `in0 ... n-1` are also connected to `sproc.svar0 ... n-1` + +If an underscore precedes the in-var then this implies that the connection is being +made from a poly context. + +`foo : { ... in:{ _.in:sproc.svar_ } ... } ` This example shows an excerpt from the network +definition of proc `foo` which is assumed to be a poly proc (there are multiple procs named 'foo' in this network). +This connection iterates across the procs `foo:0 ... foo:n-1` connecting the +the local variable 'in' to `sproc.svar0 ... n-1`. Where `n` is the poly count of `foo`. + +`foo : { ... in:{ 1_3.in:sproc.svar_ } ... }` Connect `foo:1-in:0` to `sproc:svar0` and `foo:2-in:0` to `sproc:svar1`. + +`foo : { ... in:{ 1_3.in:sproc_.svar } ... }` Connect `foo:1-in:0` to `sproc0:svar0` and `foo:2-in:0` to `sproc1:svar`. + +#### in-stmt Anti-Examples + +`in_:sproc_.svar_` This is illegal because there is no way to determine how many `in` variables should be created. + +`in:sproc.svar_` This is illegal because it suggests that multiple sources should be connected to a single input variable. + +`in:sproc_.svar` This is illegal because it suggests that multiple sources should be connected to a single input variable. + +`_.in_:sproc.svar` This is illegal because it suggests simultaneously iterating across both the local proc and var. +This would be possible if there was a way to separate how the two separate iterations should be distributed +to the source. To make this legal would require an additional special character to show how to apply the poly +iteration and/or var iteration to the source. (e.g. `_.in_:sproc*.svar_`) + +### out-stmt Examples: + +`out:iproc.ivar` Connect the local source variable `out` to the input variable `iproc:ivar`. + +`out:iproc.ivar_` Connect the local source variable `out` to the input variables `iproc:ivar0` and `iproc:ivar1`. + +`out_:iproc.ivar_` Connect the local souce variables `out0 ... out n-1` to the input variables `iproc:ivar0 ... iproc:ivar n-1` +where `n` is the mult count of the `out`. + +`out_:iproc_.ivar` Connect the local souce variables `out0 ... out n-1` to the input variables `iproc0:ivar ... iproc n-1:ivar` +where `n` is the mult count of the `out`. + + +`_.out:iproc.ivar_` Connect the local source variables `foo0:out`, `foo n-1:out` to the input variables `iproc:ivar0`, `iproc:ivar n-1`. +where `n` is the poly count of `foo`. + + + +Var Updates and Preset Application +================================== + +Variable addresses are formed from the following parameters: +`()*,var_label,var_label_sfx_id, ch_idx` + +In the cases of poly procs (procs with public internal networks) it +may not always be possible to know the `` without +asking for it at runtime. For example for the cross-fader control the +application must ask for the `` current or next +poly channel depending on which one it is targetting. + +It is notable that any proc with an internal network has +to solve this problem. The problem is especially acute +for proc's which change the 'current' poly channel at runtime. + +The alternative is to include the concept of special values +in the address (e.g. kInvalidIdx means the application isn't +sure and the network should decide how to resolve the address) +The problem with this is that the information +to make that decision may require more information than +just a simple 'special value' can encode. It also means +complicating the var set/get pipeline with 'escape' routines. + +There are at least two known use cases which need to address +this issue: +1. The cross-fader: The application may wish to address + updates to the current or next poly channel but this + channel can't be determined until runtime. + +- The application asks for the current or next `proc_label_sfx_id` + at runtime depending on what its interested in doing, + and sets the update address accordingly. + + +- Two interface objects are setup as sources for the `xfade_ctl` + object. The address of each of these objects can be + determined prior to runtime. The application then simply addresses + the object corresponding to method (direct vs deferred) it requires. + This solution is particularly appealing because it means that + presets may be completely resolved to their potential + target procs (there would be up to 'poly-count' potential targets) + prior to runtime. + + As it stands now the problem with this approach is that it + does not allow for the message to be resolved to its final + destination. If the message is addressed to a proxy proc + then that proxy must mimic all the vars on the object which + it is providing an interface for. (This is actually possible + and may be a viable solution???) + + One solution to this is to create a data type which is an + address/value packet. The packet would then be directed + to a router which would in turn use the value to forward + the packet to the next destination. Each router that the + packet passed through would strip off a value and + pass along the message. This is sensible since the 'value' + associated with a router is in fact another address. + +2. The polyphonic sampler: + +- How can voices be addressed once they are started? + + A given note is started - how do we later address that note to turn it off? + Answer: MIDI pitch and channel - only one note may be sounding on a given MIDI pitch and channel at any one time. + + - Extending ths idea to the xfader: There are two channels: current and deferred, + but which are redirected to point to 2 of the 3 physical channels .... this would + require the idea of 'redirected' networks, i.e. networks whose proc lists were + really pointers to the physical procs. + - sd_poly maintains the physical networks as it is currently implemnted. + - xfade_ctl maintains the redirected networks - requests for proc/var addresses + on the redirected networks will naturally resolve to physical networks. + + - Required modifications: + + variable getters and setters must use a variable args scheme specify the var address: + `(proc-name,proc-sfx-id)*, var-name,var-sfx-id` + Example: `xfad_ctl,0,pva,1,wnd_len,0,0` + - The first 0 is known because there is only one `xfad_ctl`. + - The 1 indicates the 'deferred' channel. + - The second 0 is known because there is only one `wnd_len` per `pva`. + - The third 0 indicates the channel index of the var. + + + the address resolver must then recognize how to follow internal networks + + + Networks must be maintained as lists of pointers to procs + rather than a linked list of physical pointers. + + + `xfade_ctl` must be instantiated after `sd_poly` and be able + to access the internal network built by `sd_poly`. + + +Generalizing the Addressing +--------------------------- +Change the set/get interface to include a list of (proc-label,proc-sfx-id) +to determine the address of the var. + +Note that this still requires knowing the final address in advance. +In general a router will not know how to resolve a msg to the +next destination without having a final address. +In otherwords setting 'proc-sfx-id' to kInvalidId is not +resolvable without more information. + + + + +### TODO: + +- Check for illegal variable names in class descriptions. (no periods, trailing digits, or trailing underscores) +- Check for unknown fields where the syntax clearly specifies only certain options. + +- Class presets cannot address 'mult' variables (maybe this is ok since 'mult' variables are generally connected to a source.). + +- Documentation w/ examples. + + Write the rules for each implementing member function. + +- value() should return a special return-code value to indicate that the +value should not be updated and distinguish it from an error code - which should stop the system. + +- flow classes and variable should have a consistent naming style: camelCase or snake_case. + +- String assignment is allocating memory: + See: `rc_t _val_set( value_t* val, const char* v ) cwFlowTypes.cpp line:464.` + + +- Variable attributes should be meaningful. e.g. src,src_opt,mult,init, .... + Should we check for 'src' or 'mult' attribute on var's? + (In other words: Enforce var attributes.) + +- Reduce runtime overhead for var get/set operations. + +- Implement matrix types. + +- Should the `object_t` be used in place of `value_t`? + +- Allow min/max limits on numeric variables. + +- log: + + should print the values for all channels - right now it is only + printing the values for kAnyChIdx + + log should print values for abuf (mean,max), fbuf (mean,max) mag, mbuf + + + +- DONE: Compile presets: at load time the presets should be resolved + to the proc and vars to which they will be assigned. + + +- DONE: (We are not removing the kAnyChIdx) +Should the var's with multiple channels remove the 'kAnyChIdx'? +This may be a good idea because 'kAnyChIdx' will in general not be used +if a var has been channelized - and yet it is possible for another +var to connect to it as a source ... which doesn't provoke an error +but would almost certainly not do what the user expects. +Note that the kAnyChIdx provides an easy way to set all of the channels +of a variable to the same value. + + +- DONE: verifiy that all proc variables values have a valid type - (i.e. (type & typeMask) != 0) + when the proc instance create is complete. This checks that both the type is assigned and + a valid value has been assigned - since the type is assigned the first time a value is set. + +- DONE: 'poly' should be implemented as a proc-inst with an internal network - but the +elements of the network should be visible outside of it. + +- DONE: 'sub' should be implemented as proc-inst with an internal network, but the +elements of the network should not be visible outside of it. Instead it should +include the idea of input and output ports which act as proxies to the physical +ports of the internal elements. + +- DONE: 'poly' and 'sub' should be arbitrarily nestable. + +- DONE: Allow multiple types on an input. + For example 'adder' should have a single input + which can by any numeric type. + + +- DONE: Make a standard way to turn on output printing from any port on any instance +This might be a better approach to logging than having a 'printer' object. +Add proc instance field: `log:{ var_label_0:0, var_label_1:0 } ` + +Next: + +- Complete subnets: + + Subnets should have presets written in terms of the subnet vars rather than the network vars + or the value application needs to follow the internal variable src_var back to the proxy var. + + + DONE: write a paragraph in the flow_doc.md about overall approach taken to subnet implementation. + + + DONE: subnet var desc's should be the same as non+subnet vars but also include the 'proxy' field. + In particular they should get default values. + If a var desc is part of a subnet then it must have a proxy. + The output variables of var desc's must have the 'out' attribute + + + + DONE: improve the subnet creating code by using consistent naming + use proxy or wrap but not both + + + DONE: improve code comments on subnet creation + +- Audio inputs should be able to be initialized with a channel count and srate without actually connecting an input. + This will allow feedback connections to be attached to them at a later stage of the network + instantiation. +- Remove the multiple 'args' thing and and 'argsLabel'. 'args' should be a simple set of arg's. +- Implement subnet preset application. +- Implement the var attributes and attribute checking. +- Implement dynamic loading of procs. +- Implement a debug mode to aid in building networks and subnets (or is logging good enough) +- Implement multi-field messages. +- Look more closely at the way of identify an in-stmt src-net or a out-stmt in-net. +It's not clear there is a difference between specifying `_` and the default behaviour. +Is there a way to tell it to search the entire network from the root? Isn't that +what '_' is supposed to do. + +- DONE: Implement feedback + +- DONE: Implement the ability to set backward connections - from late to early proc's. + This can be done by implementing the same process as 'in_stmt' but in a separate + 'out_stmt'. The difficulty is that it prevents doing some checks until the network + is completely specified. For example if audio inputs can accept connections from + later proc's then they will not have all of their inputs when they are instantiated. + One way around this is to instantiate them with an initial set of inputs but then + allow those inputs to be replaced by a later connection. + +BUGS: +- The counter modulo mode is not working as expected. + + + +Host Environments: +------------------ +- CLI, no GUI, no I/O, non-real-time only. +- CLI, no GUI, w/ I/O and real-time +- GUI, with configurable control panels + + +- DONE: Implement 'preset' proc. This will involve implementing the 'cfg' datatype. + +- DONE: Finish the 'poly' frawework. We are making 'mult' var's, but do any of the procs explicitly deal with them? + +- DONE: Turn on variable 'broadcast'. Why was it turned off? ... maybe multiple updates? + +- DONE: There is no way for a proc in a poly context to use it's poly channel number to +select a mult variable. For example in an osc in a poly has no way to select +the frequency of the osc by conneting to a non-poly proc - like a list. +Consider: +1. Use a difference 'in' statememt (e.g. 'poly-in' but the + same syntax used for connecting 'mult' variables.) +2. Include the proc name in the 'in' var to indicate a poly index is being iterated + e.g. `lfo: { class:sine_tone, in:{ osc_.dc:list.value_ } }` + +- DONE: Fix up the coding language - change the use of `instance_t` to `proc_t` and `inst` to `proc`, change use of `ctx` in cwFlowProc + + + +Prior to executing the custom constructor the values are assigned to the +variables as follows: +1. Default value as defined by the class are applied when the variable is created. +2. The proc instance 'preset' class preset is applied. +3. The proc instance 'args' values are applied. + +During this stage of processing preset values may be given for +variables that do not yet exist. This will commonly occur when a +variable has multiple channels that will not be created until the +custom constructor is run. For these cases the variable will be +pre-emptively created and the preset value will be applied. + +This approach has the advantage of communicating network information +to the proc constructor from the network configuration - thereby +allowing the network programmer to influence the configuration of the +proc. instance. + + +DONE: After the network is fully instantiated the network and class presets +are compiled. At this point all preset values must be resolvable to +an actual proc variable. A warning is issued for presets with values +that cannot be resolved and they are disabled. The primary reason +that a preset might not be resolvable is by targetting a variable +channel that does not exist. + + + +- How much of the proc initialization implementation can use the preset compile/apply code? +- DONE: All cfg to value conversion should go through `cfg_to_value()`. + + +Names: +ixon - +hoot +caw, screech, warble, coo, peep, hoot, gobble, quack, honk, whistle, tweet, cheep, chirrup, trill, squawk, seet, +cluck,cackle,clack +cock-a-dooodle-doo +song,tune,aria + + +caw by example: +0. DONE: Add log object. + DONE: Add initial network preset selection system parameter. + +1. sine->af + in-stmt + +2. sine->af with network preset + topics:preset, class info + +3. number,timer,counter,list,log + topics: log, data types, system parameters + +4. sine->delay->mixer->af + --------> + topics: mult vars, system parameters + +5. topic: modulate sine with sine + +6. topic: modulate sine with sine and timed preset change. + +7. topic: iterating input stmt 0 - connect multiple inputs to a single source + +8. topic: iterating input stmt 1 - multiple inputs to multiple sources + +9. topic: iterating input stmt 2 - two ranges + +12. topic: poly + +13. topic: poly w/iterating input stmt + +14. topic: poly w/ xfade ctl and presets + +15. topic: msg feedback + +16. topic: audio feedback + +17. topic: subnets + +18. topic: subnet with presets + +19. topic: presets w/ sfx id's + + + + + + + + + + diff --git a/cwAudioBuf.cpp b/cwAudioBuf.cpp index e8dfdee..905925f 100644 --- a/cwAudioBuf.cpp +++ b/cwAudioBuf.cpp @@ -1,6 +1,7 @@ #include "cwCommon.h" #include "cwLog.h" #include "cwCommonImpl.h" +#include "cwTest.h" #include "cwMem.h" #include "cwTime.h" #include "cwTextBuf.h" diff --git a/cwAudioDevice.cpp b/cwAudioDevice.cpp index b46e81d..a1c40f5 100644 --- a/cwAudioDevice.cpp +++ b/cwAudioDevice.cpp @@ -1,6 +1,7 @@ #include "cwCommon.h" #include "cwLog.h" #include "cwCommonImpl.h" +#include "cwTest.h" #include "cwMem.h" #include "cwTime.h" #include "cwAudioDevice.h" diff --git a/cwAudioDeviceAlsa.cpp b/cwAudioDeviceAlsa.cpp index c866342..cf070ee 100644 --- a/cwAudioDeviceAlsa.cpp +++ b/cwAudioDeviceAlsa.cpp @@ -1,6 +1,7 @@ #include "cwCommon.h" #include "cwLog.h" #include "cwCommonImpl.h" +#include "cwTest.h" #include "cwMem.h" #include "cwTime.h" #include "cwTextBuf.h" diff --git a/cwAudioDeviceFile.cpp b/cwAudioDeviceFile.cpp index c64f7d4..24a0dc5 100644 --- a/cwAudioDeviceFile.cpp +++ b/cwAudioDeviceFile.cpp @@ -1,6 +1,7 @@ #include "cwCommon.h" #include "cwLog.h" #include "cwCommonImpl.h" +#include "cwTest.h" #include "cwMem.h" #include "cwText.h" #include "cwFile.h" diff --git a/cwAudioDeviceTest.cpp b/cwAudioDeviceTest.cpp index cdc4dda..1c18429 100644 --- a/cwAudioDeviceTest.cpp +++ b/cwAudioDeviceTest.cpp @@ -1,6 +1,7 @@ #include "cwCommon.h" #include "cwLog.h" #include "cwCommonImpl.h" +#include "cwTest.h" #include "cwMem.h" #include "cwTime.h" #include "cwTextBuf.h" diff --git a/cwAudioFile.cpp b/cwAudioFile.cpp index 02b6724..a303524 100644 --- a/cwAudioFile.cpp +++ b/cwAudioFile.cpp @@ -1,6 +1,7 @@ #include "cwCommon.h" #include "cwLog.h" #include "cwCommonImpl.h" +#include "cwTest.h" #include "cwMem.h" #include "cwFile.h" #include "cwObject.h" diff --git a/cwAudioFileOps.cpp b/cwAudioFileOps.cpp index 03e71bd..93994f3 100644 --- a/cwAudioFileOps.cpp +++ b/cwAudioFileOps.cpp @@ -1,6 +1,7 @@ #include "cwCommon.h" #include "cwLog.h" #include "cwCommonImpl.h" +#include "cwTest.h" #include "cwMem.h" #include "cwFile.h" #include "cwText.h" diff --git a/cwAudioFileProc.cpp b/cwAudioFileProc.cpp index 13c9c1b..88782cd 100644 --- a/cwAudioFileProc.cpp +++ b/cwAudioFileProc.cpp @@ -1,6 +1,7 @@ #include "cwCommon.h" #include "cwLog.h" #include "cwCommonImpl.h" +#include "cwTest.h" #include "cwMem.h" #include "cwFile.h" #include "cwText.h" diff --git a/cwAudioTransforms.cpp b/cwAudioTransforms.cpp index d36022a..d85a253 100644 --- a/cwAudioTransforms.cpp +++ b/cwAudioTransforms.cpp @@ -1,6 +1,7 @@ #include "cwCommon.h" #include "cwLog.h" #include "cwCommonImpl.h" +#include "cwTest.h" #include "cwMem.h" #include "cwFile.h" #include "cwText.h" diff --git a/cwB23Tree.cpp b/cwB23Tree.cpp index d028cf9..70e3bf9 100644 --- a/cwB23Tree.cpp +++ b/cwB23Tree.cpp @@ -1,6 +1,7 @@ #include "cwCommon.h" #include "cwLog.h" #include "cwCommonImpl.h" +#include "cwTest.h" #include "cwMem.h" #include "cwObject.h" #include "cwB23Tree.h" diff --git a/cwCommonImpl.cpp b/cwCommonImpl.cpp index b7aed68..4db910d 100644 --- a/cwCommonImpl.cpp +++ b/cwCommonImpl.cpp @@ -13,16 +13,32 @@ namespace cw nanosleep(ts,NULL); } + const idLabelPair_t* _idToSlot( const idLabelPair_t* array, unsigned id, unsigned eolId ) + { + const idLabelPair_t* p = array; + for(; p->id != eolId; ++p) + if( p->id == id ) + break; + + return p; + } + +} + + + +const char* cw::idToLabelNull( const idLabelPair_t* array, unsigned id, unsigned eolId ) +{ + const idLabelPair_t* p = _idToSlot(array,id,eolId); + + return p->id == eolId ? nullptr : p->label; } const char* cw::idToLabel( const idLabelPair_t* array, unsigned id, unsigned eolId ) { - const idLabelPair_t* p = array; - for(; p->id != eolId; ++p) - if( p->id == id ) - return p->label; + const idLabelPair_t* p = _idToSlot(array,id,eolId); - return nullptr; + return p->label; } unsigned cw::labelToId( const idLabelPair_t* array, const char* label, unsigned eolId ) @@ -31,16 +47,12 @@ unsigned cw::labelToId( const idLabelPair_t* array, const char* label, unsigned if( label != nullptr ) for(; p->id != eolId; ++p) - if( std::strcmp(label,p->label) == 0 ) + if( p->label != nullptr && std::strcmp(label,p->label) == 0 ) return p->id; return eolId; } - - - - void cw::sleepSec( unsigned secs ) { struct timespec ts; diff --git a/cwCommonImpl.h b/cwCommonImpl.h index 7b59c59..9d0d4db 100644 --- a/cwCommonImpl.h +++ b/cwCommonImpl.h @@ -138,8 +138,8 @@ namespace cw { -#define cwAssert(C) while(1){ if(!(C)){ cwLogFatal(kAssertFailRC,"Assert failed on condition:%s",#C ); } break; } - +#define cwAssert(C) while(1){ if(!(C)) { cwLogFatal(kAssertFailRC,"Assert failed on condition:%s",#C ); } break; } +#define cwRuntimeCheck(C) while(1){ if(!(C)) { rc=cwLogError(kAssertFailRC,"Runtime error check failed on condition:%s",#C); goto errLabel; } break; } @@ -160,6 +160,9 @@ namespace cw } idLabelPair_t; // Return nullptr if id is not found. + const char* idToLabelNull( const idLabelPair_t* array, unsigned id, unsigned eolId ); + + // Returns label in 'eolId' slot if id is not found. const char* idToLabel( const idLabelPair_t* array, unsigned id, unsigned eolId ); // Returns eolId if the id is not found. diff --git a/cwCsv.cpp b/cwCsv.cpp index 3db4498..e42d9e1 100644 --- a/cwCsv.cpp +++ b/cwCsv.cpp @@ -1,6 +1,7 @@ #include "cwCommon.h" #include "cwLog.h" #include "cwCommonImpl.h" +#include "cwTest.h" #include "cwMem.h" #include "cwText.h" #include "cwFile.h" diff --git a/cwDataSets.cpp b/cwDataSets.cpp index 127327b..8365665 100644 --- a/cwDataSets.cpp +++ b/cwDataSets.cpp @@ -1,6 +1,7 @@ #include "cwCommon.h" #include "cwLog.h" #include "cwCommonImpl.h" +#include "cwTest.h" #include "cwMem.h" #include "cwObject.h" #include "cwFile.h" diff --git a/cwDnsSd.cpp b/cwDnsSd.cpp index cbff1d5..31525c5 100644 --- a/cwDnsSd.cpp +++ b/cwDnsSd.cpp @@ -1,6 +1,7 @@ #include "cwCommon.h" #include "cwLog.h" #include "cwCommonImpl.h" +#include "cwTest.h" #include "cwMem.h" #include "cwTime.h" diff --git a/cwDsp.cpp b/cwDsp.cpp index 7962a69..4f4d513 100644 --- a/cwDsp.cpp +++ b/cwDsp.cpp @@ -1,6 +1,7 @@ #include "cwCommon.h" #include "cwLog.h" #include "cwCommonImpl.h" +#include "cwTest.h" #include "cwMem.h" #include "cwUtility.h" #include "cwMath.h" diff --git a/cwDspTransforms.cpp b/cwDspTransforms.cpp index 801fea8..29b1841 100644 --- a/cwDspTransforms.cpp +++ b/cwDspTransforms.cpp @@ -1,6 +1,7 @@ #include "cwCommon.h" #include "cwLog.h" #include "cwCommonImpl.h" +#include "cwTest.h" #include "cwMem.h" #include "cwFile.h" #include "cwText.h" @@ -21,9 +22,9 @@ namespace cw { namespace compressor { - void _ms_to_samples( obj_t*p, real_t ms, unsigned& outRef ) + void _ms_to_samples( obj_t*p, ftime_t ms, unsigned& outRef ) { - outRef = std::max((real_t)1,(real_t)floor(ms * p->srate / 1000.0)); + outRef = std::max(1u,(unsigned)floor(ms * p->srate / 1000.0)); } } } @@ -33,7 +34,7 @@ namespace cw // compressor // -cw::rc_t cw::dsp::compressor::create( obj_t*& p, real_t srate, unsigned procSmpCnt, real_t inGain, real_t rmsWndMaxMs, real_t rmsWndMs, real_t threshDb, real_t ratio_num, real_t atkMs, real_t rlsMs, real_t outGain, bool bypassFl ) +cw::rc_t cw::dsp::compressor::create( obj_t*& p, srate_t srate, unsigned procSmpCnt, coeff_t inGain, ftime_t rmsWndMaxMs, ftime_t rmsWndMs, coeff_t threshDb, coeff_t ratio_num, ftime_t atkMs, ftime_t rlsMs, coeff_t outGain, bool bypassFl ) { p = mem::allocZ(); @@ -105,8 +106,8 @@ cw::rc_t cw::dsp::compressor::exec( obj_t* p, const sample_t* x, sample_t* y, un p->rmsWnd[ p->rmsWndIdx ] = vop::rms(xx, n); // calc and store signal RMS p->rmsWndIdx = (p->rmsWndIdx + 1) % p->rmsWndCnt; // advance the RMS storage buffer - real_t rmsLin = vop::mean(p->rmsWnd,p->rmsWndCnt); // calc avg RMS - real_t rmsDb = std::max(-100.0,20 * log10(std::max((real_t)0.00001,rmsLin))); // convert avg RMS to dB + coeff_t rmsLin = vop::mean(p->rmsWnd,p->rmsWndCnt); // calc avg RMS + coeff_t rmsDb = std::max(-100.0,20 * log10(std::max((coeff_t)0.00001,rmsLin))); // convert avg RMS to dB rmsDb += 100.0; // if the compressor is bypassed @@ -147,17 +148,17 @@ cw::rc_t cw::dsp::compressor::exec( obj_t* p, const sample_t* x, sample_t* y, un } -void cw::dsp::compressor::set_attack_ms( obj_t* p, real_t ms ) +void cw::dsp::compressor::set_attack_ms( obj_t* p, ftime_t ms ) { _ms_to_samples(p,ms,p->atkSmp); } -void cw::dsp::compressor::set_release_ms( obj_t* p, real_t ms ) +void cw::dsp::compressor::set_release_ms( obj_t* p, ftime_t ms ) { _ms_to_samples(p,ms,p->rlsSmp); } -void cw::dsp::compressor::set_rms_wnd_ms( obj_t* p, real_t ms ) +void cw::dsp::compressor::set_rms_wnd_ms( obj_t* p, ftime_t ms ) { p->rmsWndCnt = std::max((unsigned)1,(unsigned)floor(ms * p->srate / (1000.0 * p->procSmpCnt))); @@ -170,7 +171,7 @@ void cw::dsp::compressor::set_rms_wnd_ms( obj_t* p, real_t ms ) // Limiter // -cw::rc_t cw::dsp::limiter::create( obj_t*& p, real_t srate, unsigned procSmpCnt, real_t thresh, real_t igain, real_t ogain, bool bypassFl ) +cw::rc_t cw::dsp::limiter::create( obj_t*& p, srate_t srate, unsigned procSmpCnt, coeff_t thresh, coeff_t igain, coeff_t ogain, bool bypassFl ) { p = mem::allocZ(); @@ -196,7 +197,7 @@ cw::rc_t cw::dsp::limiter::exec( obj_t* p, const sample_t* x, sample_t* y, unsig } else { - real_t T = p->thresh * p->ogain; + coeff_t T = p->thresh * p->ogain; for(unsigned i=0; i(); @@ -255,12 +256,12 @@ cw::rc_t cw::dsp::dc_filter::exec( obj_t* p, const sample_t* x, sample_t* y, uns if( p->bypassFl ) vop::copy(y,x,n); else - vop::filter(y,n,x,n,p->b0, p->b, p->a, p->d, 1 ); + vop::filter(y,n,x,n,p->b0, p->b, p->a, p->d, 1 ); return kOkRC; } -cw::rc_t cw::dsp::dc_filter::set( obj_t* p, real_t gain, bool bypassFl ) +cw::rc_t cw::dsp::dc_filter::set( obj_t* p, coeff_t gain, bool bypassFl ) { p->gain = gain; p->bypassFl = bypassFl; @@ -272,7 +273,7 @@ cw::rc_t cw::dsp::dc_filter::set( obj_t* p, real_t gain, bool bypassFl ) // Recorder // -cw::rc_t cw::dsp::recorder::create( obj_t*& pRef, real_t srate, real_t max_secs, unsigned chN ) +cw::rc_t cw::dsp::recorder::create( obj_t*& pRef, srate_t srate, ftime_t max_secs, unsigned chN ) { obj_t* p = mem::allocZ(); p->srate = srate; @@ -383,7 +384,7 @@ namespace cw { } } -cw::rc_t cw::dsp::audio_meter::create( obj_t*& p, real_t srate, real_t maxWndMs, real_t wndMs, real_t peakThreshDb ) +cw::rc_t cw::dsp::audio_meter::create( obj_t*& p, srate_t srate, ftime_t maxWndMs, ftime_t wndMs, coeff_t peakThreshDb ) { rc_t rc = kOkRC; @@ -475,7 +476,7 @@ void cw::dsp::audio_meter::reset( obj_t* p ) p->clipCnt = 0; } -void cw::dsp::audio_meter::set_window_ms( obj_t* p, real_t wndMs ) +void cw::dsp::audio_meter::set_window_ms( obj_t* p, ftime_t wndMs ) { unsigned wndSmpN = (unsigned)((wndMs * p->srate)/1000.0); diff --git a/cwDspTransforms.h b/cwDspTransforms.h index 0d361bc..4248c0e 100644 --- a/cwDspTransforms.h +++ b/cwDspTransforms.h @@ -14,36 +14,36 @@ namespace cw typedef struct { - real_t srate; // system sample rate + srate_t srate; // system sample rate unsigned procSmpCnt; // samples per exec cycle - real_t inGain; // input gain - real_t threshDb; // threshold in dB (max:100 min:0) - real_t ratio_num; // numerator of the ratio + coeff_t inGain; // input gain + coeff_t threshDb; // threshold in dB (max:100 min:0) + coeff_t ratio_num; // numerator of the ratio unsigned atkSmp; // time to reduce the signal by 10.0 db unsigned rlsSmp; // time to increase the signal by 10.0 db - real_t outGain; // makeup gain + coeff_t outGain; // makeup gain bool bypassFl; // bypass enable sample_t* rmsWnd; // rmsWnd[rmsWndAllocCnt] unsigned rmsWndAllocCnt; // unsigned rmsWndCnt; // current RMS window size (rmsWndCnt must be <= rmsWndAllocCnt) unsigned rmsWndIdx; // next RMS window input index unsigned state; // env. state - real_t rmsDb; // current incoming signal RMS (max:100 min:0) - real_t gain; // current compressor gain - real_t timeConstDb; // the atk/rls will incr/decr by 'timeConstDb' per atkMs/rlsMs. - real_t pkDb; // - real_t accumDb; // + coeff_t rmsDb; // current incoming signal RMS (max:100 min:0) + coeff_t gain; // current compressor gain + coeff_t timeConstDb; // the atk/rls will incr/decr by 'timeConstDb' per atkMs/rlsMs. + coeff_t pkDb; // + coeff_t accumDb; // } obj_t; - rc_t create( obj_t*& p, real_t srate, unsigned procSmpCnt, real_t inGain, real_t rmsWndMaxMs, real_t rmsWndMs, real_t threshDb, real_t ratio, real_t atkMs, real_t rlsMs, real_t outGain, bool bypassFl ); + rc_t create( obj_t*& p, srate_t srate, unsigned procSmpCnt, coeff_t inGain, ftime_t rmsWndMaxMs, ftime_t rmsWndMs, coeff_t threshDb, coeff_t ratio, ftime_t atkMs, ftime_t rlsMs, coeff_t outGain, bool bypassFl ); rc_t destroy( obj_t*& pp ); rc_t exec( obj_t* p, const sample_t* x, sample_t* y, unsigned n ); - void set_attack_ms( obj_t* p, real_t ms ); - void set_release_ms( obj_t* p, real_t ms ); - void set_thresh_db( obj_t* p, real_t thresh ); - void set_rms_wnd_ms( obj_t* p, real_t ms ); + void set_attack_ms( obj_t* p, ftime_t ms ); + void set_release_ms( obj_t* p, ftime_t ms ); + void set_thresh_db( obj_t* p, coeff_t thresh ); + void set_rms_wnd_ms( obj_t* p, ftime_t ms ); } namespace limiter @@ -51,13 +51,13 @@ namespace cw typedef struct { unsigned procSmpCnt; - real_t igain; // applied before thresholding - real_t thresh; // linear (0.0-1.0) threshold. - real_t ogain; // applied after thresholding + coeff_t igain; // applied before thresholding + coeff_t thresh; // linear (0.0-1.0) threshold. + coeff_t ogain; // applied after thresholding bool bypassFl; } obj_t; - rc_t create( obj_t*& p, real_t srate, unsigned procSmpCnt, real_t thresh, real_t igain, real_t ogain, bool bypassFl ); + rc_t create( obj_t*& p, srate_t srate, unsigned procSmpCnt, coeff_t thresh, coeff_t igain, coeff_t ogain, bool bypassFl ); rc_t destroy( obj_t*& pp ); rc_t exec( obj_t* p, const sample_t* x, sample_t* y, unsigned n ); } @@ -66,32 +66,32 @@ namespace cw { typedef struct { - real_t d[2]; // - real_t b[1]; // - real_t a[1]; // a[dn] feedback coeff's - real_t b0; // feedforward coeff 0 + coeff_t d[2]; // + coeff_t b[1]; // + coeff_t a[1]; // a[dn] feedback coeff's + coeff_t b0; // feedforward coeff 0 bool bypassFl; - real_t gain; + coeff_t gain; } obj_t; - rc_t create( obj_t*& p, real_t srate, unsigned procSmpCnt, real_t gain, bool bypassFl ); + rc_t create( obj_t*& p, srate_t srate, unsigned procSmpCnt, coeff_t gain, bool bypassFl ); rc_t destroy( obj_t*& pp ); rc_t exec( obj_t* p, const sample_t* x, sample_t* y, unsigned n ); - rc_t set( obj_t* p, real_t gain, bool bypassFl ); + rc_t set( obj_t* p, coeff_t gain, bool bypassFl ); } namespace recorder { typedef struct { - real_t srate; // + srate_t srate; // unsigned maxFrameN; // unsigned chN; // channel count unsigned frameIdx; // next frame to write sample_t* buf; // [ [maxFrameN] [maxFrameN] ] } obj_t; // ch0 ch1 - rc_t create( obj_t*& pRef, real_t srate, real_t max_secs, unsigned chN ); + rc_t create( obj_t*& pRef, srate_t srate, ftime_t max_secs, unsigned chN ); rc_t destroy( obj_t*& pRef); rc_t exec( obj_t* p, const sample_t* buf, unsigned chN, unsigned frameN ); @@ -107,10 +107,10 @@ namespace cw unsigned maxWndSmpN; unsigned wndSmpN; sample_t* wndV; - real_t srate; - real_t peakThreshDb; - real_t outLin; - real_t outDb; + srate_t srate; + coeff_t peakThreshDb; + coeff_t outLin; + coeff_t outDb; bool peakFl; bool clipFl; unsigned peakCnt; @@ -118,11 +118,11 @@ namespace cw unsigned wi; } obj_t; - rc_t create( obj_t*& p, real_t srate, real_t maxWndMs, real_t wndMs, real_t peakThreshDb ); + rc_t create( obj_t*& p, srate_t srate, ftime_t maxWndMs, ftime_t wndMs, coeff_t peakThreshDb ); rc_t destroy( obj_t*& pp ); rc_t exec( obj_t* p, const sample_t* x, unsigned n ); void reset( obj_t* p ); - void set_window_ms( obj_t* p, real_t wndMs ); + void set_window_ms( obj_t* p, ftime_t wndMs ); } } diff --git a/cwDspTypes.h b/cwDspTypes.h index 9a03f69..e8cc1ab 100644 --- a/cwDspTypes.h +++ b/cwDspTypes.h @@ -5,10 +5,11 @@ namespace cw { namespace dsp { - typedef float real_t; typedef float sample_t; - typedef float fd_real_t; + typedef float fd_sample_t; // Frequency domain sample - type used by magnitude,phase,real,imag. part of spectral values typedef float srate_t; + typedef float coeff_t; // values that are directly applied to signals of sample_t. + typedef double ftime_t; // any time value expressed as a floating point value - could be seconds, milliseconds, etc } } #endif diff --git a/cwDynRefTbl.cpp b/cwDynRefTbl.cpp index 407c80d..a263c84 100644 --- a/cwDynRefTbl.cpp +++ b/cwDynRefTbl.cpp @@ -1,6 +1,7 @@ #include "cwCommon.h" #include "cwLog.h" #include "cwCommonImpl.h" +#include "cwTest.h" #include "cwMem.h" #include "cwText.h" #include "cwObject.h" diff --git a/cwEuCon.cpp b/cwEuCon.cpp index f7b482f..55b19ce 100644 --- a/cwEuCon.cpp +++ b/cwEuCon.cpp @@ -2,6 +2,7 @@ #include "cwLog.h" #include "cwCommonImpl.h" +#include "cwTest.h" #include "cwMem.h" #include "cwTime.h" diff --git a/cwFile.cpp b/cwFile.cpp index e8a35bd..dcb845a 100644 --- a/cwFile.cpp +++ b/cwFile.cpp @@ -1,6 +1,8 @@ #include "cwCommon.h" #include "cwLog.h" #include "cwCommonImpl.h" +#include "cwTest.h" +#include "cwObject.h" #include "cwMem.h" #include "cwFileSys.h" #include "cwText.h" diff --git a/cwFileSys.cpp b/cwFileSys.cpp index 32264cc..9574f50 100644 --- a/cwFileSys.cpp +++ b/cwFileSys.cpp @@ -1,8 +1,12 @@ #include "cwCommon.h" #include "cwLog.h" +#include "cwCommonImpl.h" + +#include "cwTest.h" +#include "cwObject.h" #include "cwFileSys.h" -#include "cwCommonImpl.h" + #include "cwMem.h" #include "cwString.h" #include "cwText.h" @@ -75,7 +79,7 @@ bool cw::filesys::isDir( const char* dir0 ) { // if the dir does not exist if( errno == ENOENT ) - return false; + goto errLabel; cwLogSysError( kOpFailRC, errno, "'stat' failed on '%s'",cwStringNullGuard(dir)); goto errLabel; @@ -106,7 +110,7 @@ bool cw::filesys::isFile( const char* fn0 ) // if the file does not exist if( errno == ENOENT ) - return false; + goto errLabel; cwLogSysError( kOpFailRC, errno, "'stat' failed on '%s'.",cwStringNullGuard(fn)); goto errLabel; @@ -137,7 +141,7 @@ bool cw::filesys::isLink( const char* fn0 ) { // if the file does not exist if( errno == ENOENT ) - return false; + goto errLabel; cwLogSysError( kOpFailRC, errno, "'stat' failed on '%s'.",cwStringNullGuard(fn)); goto errLabel; @@ -893,3 +897,33 @@ cw::rc_t cw::filesys::makeDir( const char* dirStr ) return kOkRC; } + + +cw::rc_t cw::filesys::test( const test::test_args_t& args ) +{ + rc_t rc = kOkRC; + + filesys::pathPart_t* pp = filesys::pathParts(__FILE__); + + cwLogPrint("dir:%s",pp->dirStr); + cwLogPrint("fn: %s",pp->fnStr); + cwLogPrint("ext:%s",pp->extStr); + + char* fn = filesys::makeFn( pp->dirStr, pp->fnStr, pp->extStr, nullptr ); + + cwLogPrint("fn: %s",fn); + + mem::release(pp); + mem::release(fn); + + + const char myPath[] = "~/src/foo"; + + char* expPath = filesys::expandPath(myPath); + + cwLogPrint("%s %s",myPath,expPath); + + mem::release(expPath); + + return rc; +} diff --git a/cwFileSys.h b/cwFileSys.h index ae2924c..d55af8c 100644 --- a/cwFileSys.h +++ b/cwFileSys.h @@ -90,6 +90,8 @@ namespace cw char* makeVersionedDirectory(const char* recordDir, const char* recordFolder ); rc_t makeDir( const char* dirStr ); + + rc_t test( const test::test_args_t& args ); } } diff --git a/cwFlow.cpp b/cwFlow.cpp index 8ce5c62..600bbfe 100644 --- a/cwFlow.cpp +++ b/cwFlow.cpp @@ -1,16 +1,22 @@ #include "cwCommon.h" #include "cwLog.h" #include "cwCommonImpl.h" +#include "cwTest.h" #include "cwMem.h" #include "cwText.h" +#include "cwNumericConvert.h" #include "cwObject.h" + #include "cwAudioFile.h" #include "cwVectOps.h" #include "cwMtx.h" -#include "cwDspTypes.h" // real_t, sample_t +#include "cwDspTypes.h" // coeff_t, sample_t, srate_t ... +#include "cwTime.h" +#include "cwMidiDecls.h" #include "cwFlowDecl.h" #include "cwFlow.h" #include "cwFlowTypes.h" +#include "cwFlowNet.h" #include "cwFlowProc.h" namespace cw @@ -24,10 +30,14 @@ namespace cw } library_t; library_t g_library[] = { + { "subnet", &subnet::members }, + { "poly", &poly::members }, + { "midi_in", &midi_in::members }, + { "midi_out", &midi_out::members }, { "audio_in", &audio_in::members }, { "audio_out", &audio_out::members }, - { "audioFileIn", &audioFileIn::members }, - { "audioFileOut", &audioFileOut::members }, + { "audio_file_in", &audio_file_in::members }, + { "audio_file_out", &audio_file_out::members }, { "audio_gain", &audio_gain::members }, { "audio_split", &audio_split::members }, { "audio_duplicate", &audio_duplicate::members }, @@ -44,6 +54,16 @@ namespace cw { "balance", &balance::members }, { "audio_meter", &audio_meter::members }, { "audio_marker", &audio_marker::members }, + { "xfade_ctl", &xfade_ctl::members }, + { "poly_merge", &poly_merge::members }, + { "sample_hold", &sample_hold::members }, + { "number", &number::members }, + { "timer", &timer::members }, + { "counter", &counter::members }, + { "list", &list::members }, + { "add", &add::members }, + { "preset", &preset::members }, + { "print", &print::members }, { nullptr, nullptr } }; @@ -85,6 +105,161 @@ namespace cw } return rc; } + + rc_t _parse_subnet_var_proxy_string( const char* proxyStr, var_desc_t* var_desc ) + { + rc_t rc = kOkRC; + + const char* period; + + // find the separating period + if((period = firstMatchChar(proxyStr,'.')) == nullptr ) + { + rc = cwLogError(kSyntaxErrorRC,"The separating '.' could not be found in the proxy string '%s'.",cwStringNullGuard(proxyStr)); + goto errLabel; + } + + // validate the length of the proc inst label + if( period-proxyStr == 0 ) + { + rc = cwLogError(kSyntaxErrorRC,"No proxy proc instance was found in the proxy string '%s'.",cwStringNullGuard(proxyStr)); + goto errLabel; + } + + // validate the length of the var label + if( textLength(period+1) == 0 ) + { + rc = cwLogError(kSyntaxErrorRC,"No proxy var was found in the proxy string '%s'.",cwStringNullGuard(proxyStr)); + goto errLabel; + } + + + var_desc->proxyProcLabel = mem::duplStr(proxyStr,period-proxyStr); + var_desc->proxyVarLabel = mem::duplStr(period+1); + + errLabel: + return rc; + } + + + rc_t _parse_class_var_attribute_flags(const object_t* var_flags_obj, unsigned& flags_ref) + { + rc_t rc = kOkRC; + unsigned result_flags = 0; + + flags_ref = 0; + + if( !var_flags_obj->is_list() ) + { + rc = cwLogError(kSyntaxErrorRC,"The variable description 'flags' field must be a list."); + goto errLabel; + } + + for(unsigned i=0; ichild_count(); ++i) + { + const object_t* flag_obj = var_flags_obj->child_ele(i); + const char* flag_label = nullptr; + unsigned flag = 0; + + // validate the flag syntax + if( flag_obj == nullptr || !flag_obj->is_string() || flag_obj->value(flag_label)!=kOkRC ) + { + rc = cwLogError(kSyntaxErrorRC,"Invalid variable description flag syntax on flag index %i.",i); + goto errLabel; + } + + // parse the flag + if((flag = var_desc_attr_label_to_flag(flag_label)) == kInvalidVarDescFl ) + { + rc = cwLogError(kInvalidArgRC,"The variable description flag ('%s') at flag index %i is not valid.",cwStringNullGuard(flag_label),i); + goto errLabel; + } + + result_flags |= flag; + } + + flags_ref = result_flags; + errLabel: + return rc; + + } + + rc_t _parse_class_var_cfg(flow_t* p, class_desc_t* class_desc, const object_t* var_desc_pair, var_desc_t*& var_desc_ref ) + { + rc_t rc = kOkRC; + const object_t* var_flags_obj = nullptr; + const char* var_value_type_str = nullptr; + const char* var_label = nullptr; + const char* proxy_string = nullptr; + var_desc_t* vd = nullptr; + + var_desc_ref = nullptr; + + if(var_desc_pair==nullptr || !var_desc_pair->is_pair() || var_desc_pair->pair_label()==nullptr || (var_label = var_desc_pair->pair_label())==nullptr || var_desc_pair->pair_value()==nullptr || !var_desc_pair->pair_value()->is_dict() ) + { + rc = cwLogError(kSyntaxErrorRC,"An invalid variable description syntax was encountered."); + goto errLabel; + } + + // Allocate the var. desc record + if((vd = var_desc_create( var_label, var_desc_pair->pair_value())) == nullptr ) + { + rc = cwLogError(kObjAllocFailRC,"Variable description allocation failed."); + goto errLabel; + } + + + // get the variable description + if((rc = vd->cfg->getv("doc", vd->docText)) != kOkRC ) + { + rc = cwLogError(rc,"Parsing failed on class:%s variable: '%s'.", class_desc->label, vd->label ); + goto errLabel; + } + + // get the variable description + if((rc = vd->cfg->getv_opt("flags", var_flags_obj, + "type", var_value_type_str, + "value", vd->val_cfg, + "proxy", proxy_string )) != kOkRC ) + { + rc = cwLogError(rc,"Parsing optional fields failed."); + goto errLabel; + } + + // convert the type string to a numeric type flag + if( var_value_type_str != nullptr ) + if( (vd->type = value_type_label_to_flag( var_value_type_str )) == kInvalidTId ) + { + rc = cwLogError(kSyntaxErrorRC,"Invalid variable description type flag: '%s' was encountered.", var_value_type_str ); + goto errLabel; + } + + // parse the proxy string into it's two parts: . + if( proxy_string != nullptr ) + { + if((rc = _parse_subnet_var_proxy_string( proxy_string, vd )) != kOkRC ) + goto errLabel; + } + + // parse the var desc attribute flags + if( var_flags_obj != nullptr ) + { + vd->flags = 0; + if((rc = _parse_class_var_attribute_flags(var_flags_obj, vd->flags)) != kOkRC ) + goto errLabel; + } + + var_desc_ref = vd; + + errLabel: + if( rc != kOkRC ) + { + rc = cwLogError(rc,"A variable description create failed on class desc:'%s' var:'%s'.",cwStringNullGuard(class_desc->label),cwStringNullGuard(var_label)); + var_desc_destroy(vd); + } + + return rc; + } rc_t _parse_class_cfg(flow_t* p, const object_t* classCfg) { @@ -109,7 +284,8 @@ namespace cw // get the variable description if((rc = cd->cfg->getv_opt("vars", varD, - "presets", presetD )) != kOkRC ) + "presets", presetD, + "poly_limit_cnt", cd->polyLimitN)) != kOkRC ) { rc = cwLogError(rc,"Parsing failed while parsing class desc:'%s'", cwStringNullGuard(cd->label) ); goto errLabel; @@ -136,7 +312,7 @@ namespace cw goto errLabel; } - preset_t* preset = mem::allocZ< preset_t >(); + class_preset_t* preset = mem::allocZ< class_preset_t >(); preset->label = pair->pair_label(); preset->cfg = pair->pair_value(); @@ -157,63 +333,38 @@ namespace cw // get the class member functions if((cd->members = _find_library_record(cd->label)) == nullptr ) { - rc = cwLogError(kSyntaxErrorRC,"The '%s' class member function record could not be found..", cd->label ); + rc = cwLogError(kSyntaxErrorRC,"The '%s' class member function record could not be found.", cd->label ); goto errLabel; } // for each class value description for(unsigned j=0; jchild_count(); ++j) { - const object_t* var_obj = varD->child_ele(j); - const object_t* var_flags_obj = nullptr; - const char* type_str = nullptr; - unsigned type_flag = 0; - bool srcVarFl = false; - bool srcOptFl = false; - var_desc_t* vd = mem::allocZ(); - - vd->label = var_obj->pair_label(); - vd->cfg = var_obj->pair_value(); - - // get the variable description - if((rc = vd->cfg->getv("type", type_str, - "doc", vd->docText)) != kOkRC ) + const object_t* var_obj = varD->child_ele(j); + var_desc_t* vd = nullptr; + + if((rc = _parse_class_var_cfg(p, cd, var_obj, vd )) != kOkRC ) { - rc = cwLogError(rc,"Parsing failed on class:%s variable: '%s'.", cd->label, vd->label ); + rc = cwLogError(rc,"Variable description created failed on the class desc '%s' on the variable description at index '%i'.",cwStringNullGuard(cd->label),j); goto errLabel; } - // convert the type string to a numeric type flag - if( (type_flag = value_type_label_to_flag( type_str )) == kInvalidTId ) + if( vd->type == kInvalidTFl ) { - rc = cwLogError(rc,"Invalid type flag: '%s' class:'%s' value:'%s'.", type_str, cd->label, vd->label ); - goto errLabel; - } - - // get the variable description - if((rc = vd->cfg->getv_opt("flags", var_flags_obj, - "value",vd->val_cfg)) != kOkRC ) - { - rc = cwLogError(rc,"Parsing optional fields failed on class:%s variable: '%s'.", cd->label, vd->label ); + rc = cwLogError(rc,"The variable description '%s' in class description '%s' does not have a valid 'type' field.",cwStringNullGuard(vd->label),cwStringNullGuard(cd->label)); goto errLabel; } - // check for 'src' flag - if((rc = _is_var_flag_set( var_flags_obj, "src", cd->label, vd->label, srcVarFl )) != kOkRC ) - goto errLabel; - - // check for 'src_opt' flag - if((rc = _is_var_flag_set( var_flags_obj, "src_opt", cd->label, vd->label, srcOptFl )) != kOkRC ) - goto errLabel; - - vd->type |= type_flag; - - if( srcVarFl ) - vd->flags |= kSrcVarFl; - - if( srcOptFl ) - vd->flags |= kSrcOptVarFl; + if( vd->proxyProcLabel != nullptr || vd->proxyVarLabel != nullptr ) + { + cwLogWarning("The 'proxy' field in the variable description '%s' on class description '%s' will be ignored because the variable is not part of a subnet definition.",cwStringNullGuard(vd->label),cwStringNullGuard(cd->label)); + } + if( cwIsFlag(vd->flags,kSubnetOutVarDescFl ) ) + { + cwLogWarning("The 'out' flag in the variable description '%s' on class description '%s' will be ignored because the variable is not part of a subnet definition.",cwStringNullGuard(vd->label),cwStringNullGuard(cd->label)); + } + vd->link = cd->varDescL; cd->varDescL = vd; } @@ -225,931 +376,257 @@ namespace cw return rc; } - void _connect_vars( variable_t* src_var, variable_t* in_var ) + rc_t _find_subnet_proc_class_desc( flow_t* p, const object_t* subnetProcD, const char* procInstLabel, const class_desc_t*& class_desc_ref ) { - // connect in_var into src_var's outgoing var chain - in_var->connect_link = src_var->connect_link; - src_var->connect_link = in_var; + rc_t rc = kOkRC; + const object_t* procInstD = nullptr; + const object_t* classStr = nullptr; + const char* class_label = nullptr; - assert( src_var->value != nullptr ); - - in_var->value = src_var->value; - in_var->src_var = src_var; - } - - rc_t _setup_input( flow_t* p, instance_t* in_inst, const char* in_var_label, const char* src_label_arg ) - { - rc_t rc = kOkRC; - unsigned src_charN = textLength(src_label_arg); - variable_t* src_var = nullptr; - instance_t* src_inst = nullptr; - variable_t* in_var = nullptr; - - char sbuf[ src_charN+1 ]; + class_desc_ref = nullptr; - // copy the id into the buf - strncpy(sbuf,src_label_arg,src_charN+1); - - // advance suffix to the '.' - char* suffix = sbuf; - while( *suffix && *suffix != '.') - ++suffix; - - // if a '.' suffix was found - if( *suffix ) + // find the proc inst dict in the subnet + if((procInstD = subnetProcD->find_child(procInstLabel)) == nullptr ) { - *suffix = 0; - ++suffix; - } - - // locate source instance - if((rc = instance_find(p, sbuf, src_inst )) != kOkRC ) - { - rc = cwLogError(kSyntaxErrorRC,"The source instance '%s' was not found.", cwStringNullGuard(sbuf) ); + rc = cwLogError(kSyntaxErrorRC,"The proc instance '%s' from the proxy var list could not be foud in the subnet.",cwStringNullGuard(procInstLabel)); goto errLabel; } - // locate source value - if((rc = var_find( src_inst, suffix, kAnyChIdx, src_var)) != kOkRC ) + // find the proc class label of the proc inst + if((classStr = procInstD->find_child("class")) == nullptr || (rc = classStr->value(class_label))!=kOkRC) { - rc = cwLogError(rc,"The source var '%s' was not found on the source instance '%s'.", cwStringNullGuard(suffix), cwStringNullGuard(sbuf)); + rc = cwLogError(kSyntaxErrorRC,"The 'class' field could not be found in the '%s' proc instance record.", cwStringNullGuard(procInstLabel)); goto errLabel; } - // locate input value - if((rc = var_find( in_inst, in_var_label, kAnyChIdx, in_var )) != kOkRC ) + // find the associated class desc record + if((class_desc_ref = class_desc_find(p,class_label)) == nullptr) { - rc = cwLogError(rc,"The input value '%s' was not found on the instance '%s'.", cwStringNullGuard(in_var_label), cwStringNullGuard(in_inst->label)); - goto errLabel; - } - - // verify that the src_value type is included in the in_value type flags - if( cwIsNotFlag(in_var->varDesc->type, src_var->varDesc->type) ) - { - rc = cwLogError(kSyntaxErrorRC,"The type flags don't match on input:%s %s source:%s %s .", in_inst->label, in_var_label, src_inst->label, suffix); - goto errLabel; - } - - if( src_var->value == nullptr ) - { - rc = cwLogError(kSyntaxErrorRC,"The source value is null on the connection input:%s %s source:%s %s .", in_inst->label, in_var_label, src_inst->label, suffix); + rc = cwLogError(kEleNotFoundRC,"The class desc record '%s' for the proc instance '%s' could not be found.",class_label,cwStringNullGuard(procInstLabel)); goto errLabel; } - _connect_vars( src_var, in_var ); - - //cwLogInfo("'%s:%s' connected to source '%s:%s' %p.", in_inst->label, in_var_label, src_inst->label, suffix, in_var->value ); - errLabel: + return rc; } - void _destroy_inst( instance_t* inst ) + rc_t _create_subnet_var_desc( flow_t* p, class_desc_t* subnetClassDesc, const object_t* subnetProcD, const object_t* varDescPair, var_desc_t*& vd_ref ) { - if( inst == nullptr ) - return; + rc_t rc = kOkRC; + const class_desc_t* proxy_class_desc = nullptr; + const var_desc_t* proxy_var_desc = nullptr; + var_desc_t* var_desc = nullptr; - if( inst->class_desc->members->destroy != nullptr && inst->userPtr != nullptr ) - inst->class_desc->members->destroy( inst ); + vd_ref = nullptr; - // destroy the instance variables - variable_t* var0 = inst->varL; - variable_t* var1 = nullptr; - while( var0 != nullptr ) + // parse the variable descripiton and create a var_desc_t record + if((rc = _parse_class_var_cfg(p, subnetClassDesc, varDescPair, var_desc )) != kOkRC ) { - var1 = var0->var_link; - _var_destroy(var0); - var0 = var1; - } - - - mem::release(inst->varMapA); - mem::release(inst); - } - - rc_t _var_map_id_to_index( instance_t* inst, unsigned vid, unsigned chIdx, unsigned& idxRef ); - - rc_t _create_instance_var_map( instance_t* inst ) - { - rc_t rc = kOkRC; - unsigned max_vid = kInvalidId; - unsigned max_chIdx = 0; - variable_t* var = inst->varL; - //variable_t* v0 = nullptr; - - // determine the max variable vid and max channel index value among all variables - for(; var!=nullptr; var = var->var_link ) - { - if( var->vid != kInvalidId ) - { - if( max_vid == kInvalidId || var->vid > max_vid ) - max_vid = var->vid; - - if( var->chIdx != kAnyChIdx && (var->chIdx+1) > max_chIdx ) - max_chIdx = (var->chIdx + 1); - - } - } - - // If there are any variables - if( max_vid != kInvalidId ) - { - // create the variable map array - inst->varMapChN = max_chIdx + 1; - inst->varMapIdN = max_vid + 1; - inst->varMapN = inst->varMapIdN * inst->varMapChN; - inst->varMapA = mem::allocZ( inst->varMapN ); - - // assign each variable to a location in the map - for(variable_t* var=inst->varL; var!=nullptr; var=var->var_link) - if( var->vid != kInvalidId ) - { - unsigned idx = kInvalidIdx; - - if((rc = _var_map_id_to_index( inst, var->vid, var->chIdx, idx )) != kOkRC ) - goto errLabel; - - - // verify that there are not multiple variables per map position - if( inst->varMapA[ idx ] != nullptr ) - { - variable_t* v0 = inst->varMapA[idx]; - rc = cwLogError(kInvalidStateRC,"The variable '%s' id:%i ch:%i and '%s' id:%i ch:%i share the same variable map position on instance: %s. This is usually cased by duplicate variable id's.", - v0->label,v0->vid,v0->chIdx, var->label,var->vid,var->chIdx,inst->label); - - goto errLabel; - } - - // assign this variable to a map position - inst->varMapA[ idx ] = var; - - if( var->chIdx != kAnyChIdx && var->value == nullptr ) - { - rc = cwLogError(kInvalidStateRC,"The value of the variable '%s' ch:%i on instance:'%s' has not been set.",var->label,var->chIdx,inst->label); - goto errLabel; - } - - } - - } - - errLabel: - return rc; - - } - - void _complete_input_connections( instance_t* inst ) - { - for(variable_t* var=inst->varL; var!=nullptr; var=var->var_link) - if(var->chIdx == kAnyChIdx && is_connected_to_external_proc(var) ) - { - - variable_t* base_src_var = var->src_var; - - // since 'var' is on the 'any' channel the 'src' var must also be on the 'any' channel - assert( base_src_var->chIdx == kAnyChIdx ); - - //printf("%s %s\n",inst->label,var->label); - - // for each var channel in the input var - for(variable_t* in_var = var->ch_link; in_var != nullptr; in_var=in_var->ch_link) - { - // locate the matching channel on the 'src' var - variable_t* svar = base_src_var; - for(; svar!=nullptr; svar=svar->ch_link) - if( svar->chIdx == in_var->chIdx ) - break; - - // connect the src->input var - _connect_vars( svar==nullptr ? base_src_var : svar, in_var); - } - } - } - - rc_t _call_value_func_on_all_variables( instance_t* inst ) - { - rc_t rc = kOkRC; - rc_t rc1 = kOkRC; - - for(unsigned i=0; ivarMapN; ++i) - if( inst->varMapA[i] != nullptr && inst->varMapA[i]->vid != kInvalidId ) - { - variable_t* var = inst->varMapA[i]; - - if((rc = var->inst->class_desc->members->value( var->inst, var )) != kOkRC ) - rc1 = cwLogError(rc,"The proc instance '%s' reported an invalid valid on variable:%s chIdx:%i.", var->inst->label, var->label, var->chIdx ); - } - - return rc1; - } - - rc_t _var_channelize( instance_t* inst, const char* preset_label, const char* type_src_label, const char* value_label, const object_t* value ) - { - rc_t rc = kOkRC; - - variable_t* dummy = nullptr; - - // verify that a valid value exists - if( value == nullptr ) - { - rc = cwLogError(kSyntaxErrorRC,"Unexpected missig value on %s preset '%s' instance '%s' variable '%s'.", type_src_label, preset_label, inst->label, cwStringNullGuard(value_label) ); goto errLabel; } - // if a list of values was given - if( value->is_list() ) + if( var_desc->type != 0 ) { - for(unsigned chIdx=0; chIdxchild_count(); ++chIdx) - if((rc = var_channelize( inst, value_label, chIdx, value->child_ele(chIdx), kInvalidId, dummy )) != kOkRC ) - goto errLabel; - } - else // otherwise a single value was given - { - if((rc = var_channelize( inst, value_label, kAnyChIdx, value, kInvalidId, dummy )) != kOkRC ) - goto errLabel; + cwLogWarning("The 'type' field int the variable description '%s' on the class description '%s' will be ignored because the variable is proxied.",cwStringNullGuard(subnetClassDesc->label),cwStringNullGuard(var_desc->label)); } - errLabel: - return rc; - } - - rc_t _preset_channelize_vars( instance_t* inst, const char* type_src_label, const char* preset_label, const object_t* preset_cfg ) - { - rc_t rc = kOkRC; - - //cwLogInfo("Channelizing '%s' preset %i vars for '%s'.",type_src_label, preset_cfg==nullptr ? 0 : preset_cfg->child_count(), inst->label ); + // verify that a proxy-proc-label and proxy-var-label were specified in the variable descripiton + if( var_desc->proxyProcLabel == nullptr || var_desc->proxyVarLabel == nullptr ) + { + rc = cwLogError(kSyntaxErrorRC,"The subnet variable description '%s' in the subnet '%s' must have a valid 'proxy' field.",cwStringNullGuard(var_desc->label),cwStringNullGuard(subnetClassDesc->label)); + goto errLabel; + } - // validate the syntax of the preset record - if( !preset_cfg->is_dict() ) + // locate the class desc associated with proxy proc + if((rc = _find_subnet_proc_class_desc( p, subnetProcD, var_desc->proxyProcLabel, proxy_class_desc )) != kOkRC ) { - rc = cwLogError(kSyntaxErrorRC,"The preset record '%s' on %s '%s' is not a dictionary.", preset_label, type_src_label, inst->class_desc->label ); goto errLabel; } - - // for each preset variable - for(unsigned i=0; ichild_count(); ++i) + // locate the var desc associated with the proxy proc var + if((proxy_var_desc = var_desc_find( proxy_class_desc, var_desc->proxyVarLabel)) == nullptr ) { - const object_t* value = preset_cfg->child_ele(i)->pair_value(); - const char* value_label = preset_cfg->child_ele(i)->pair_label(); - if((rc = _var_channelize( inst, preset_label, type_src_label, value_label, value )) != kOkRC ) - goto errLabel; - - + rc = cwLogError(kEleNotFoundRC,"The subnet proxied variable desc '%s.%s' could not be found in subnet '%s'.",cwStringNullGuard(var_desc->proxyProcLabel),cwStringNullGuard(var_desc->proxyVarLabel),cwStringNullGuard(subnetClassDesc->label)); + goto errLabel; } + // get the subnet var_desc type from the proxied var_desc + var_desc->type = proxy_var_desc->type; + + // augment the subnet var_desc flags from the proxied var_desc + var_desc->flags |= proxy_var_desc->flags; + + // if no default value was given to the subnet var desc then get it from the proxied var desc + if( var_desc->val_cfg == nullptr ) + var_desc->val_cfg = proxy_var_desc->val_cfg; + + vd_ref = var_desc; + errLabel: + if( rc != kOkRC ) - rc = cwLogError(rc,"Apply %s preset failed on instance:%s class:%s preset:%s.", type_src_label, inst->label, inst->class_desc->label, preset_label ); - - return rc; - } - - - template< typename T > - T _interp_dual_value( T v0, T v1, double coeff ) - { - T y; - if( v0 == v1 ) - y = v0; - else - y = (T)(v0 + (v1-v0)*coeff ); - - //printf("%f %f -> %f\n",(double)v0,(double)v1,(double)y); - return y; - } - - rc_t _set_var_from_dual_preset_scalar_scalar( instance_t* inst, const char* var_label, const object_t* scalar_0, const object_t* scalar_1, double coeff, unsigned chIdx ) - { - rc_t rc = kOkRC; - object_t interped_value; - variable_t* dummy = nullptr; - - // one of the input values must exist - if( scalar_0==nullptr && scalar_1==nullptr ) - { - rc = cwLogError(kInvalidArgRC,"The numeric types of both operands of a dual value are null."); - goto errLabel; + { + rc = cwLogError(rc,"The creation of proxy var '%s' failed.",var_desc==nullptr ? "" : cwStringNullGuard(var_desc->label)); + var_desc_destroy(var_desc); } - - // It's possible that one or the other input value does not exist - if( scalar_0 == nullptr ) - scalar_0 = scalar_1; - else - { - if( scalar_1 == nullptr ) - scalar_1 = scalar_0; - } - - // verify that the input values are the same type - if( scalar_0->type->id != scalar_1->type->id ) - { - rc = cwLogError(kInvalidArgRC,"The numeric types of both operands of a dual value preset must match. (%s != %s).",cwStringNullGuard(scalar_0->type->label),cwStringNullGuard(scalar_1->type->label)); - goto errLabel; - } - - printf("%s:%s :",inst->label,var_label); - switch( scalar_0->type->id ) - { - case kInt32TId: - interped_value.set_value( _interp_dual_value(scalar_0->u.i32,scalar_1->u.i32,coeff) ); - break; - case kUInt32TId: - interped_value.set_value( _interp_dual_value(scalar_0->u.u32,scalar_1->u.u32,coeff) ); - break; - case kInt64TId: - assert(0); - //interped_value.set_value( _interp_dual_value(scalar_0->u.i64,scalar_1->u.i64,coeff) ); - break; - case kUInt64TId: - assert(0); - //interped_value.set_value( _interp_dual_value(scalar_0->u.u64,scalar_1->u.u64,coeff) ); - break; - case kFloatTId: - interped_value.set_value( _interp_dual_value(scalar_0->u.f,scalar_1->u.f,coeff) ); - break; - case kDoubleTId: - interped_value.set_value( _interp_dual_value(scalar_0->u.d,scalar_1->u.d,coeff) ); - break; - - default: - rc = cwLogError(kInvalidStateRC,"Preset dual values of type '%s' cannot be interpolated.",cwStringNullGuard(scalar_0->type->label)); - goto errLabel; - } - - - if((rc = var_channelize( inst, var_label, chIdx, &interped_value, kInvalidId, dummy )) != kOkRC ) - { - rc = cwLogError(kInvalidArgRC,"Dual value preset application failed."); - goto errLabel; - } - - errLabel: - return rc; - } - - rc_t _set_var_from_dual_preset_list_list( instance_t* inst, const char* var_label, const object_t* list_0, const object_t* list_1, double coeff ) - { - rc_t rc = kOkRC; - - if( list_0->child_count() != list_1->child_count() ) - return cwLogError(kInvalidArgRC,"If two lists are to be applied as a dual preset they must be the same length."); - - for(unsigned chIdx=0; chIdxchild_count(); ++chIdx) - if((rc = _set_var_from_dual_preset_scalar_scalar(inst,var_label,list_0->child_ele(chIdx),list_1->child_ele(chIdx),coeff,chIdx)) != kOkRC ) - goto errLabel; - - errLabel: - return rc; - } - - rc_t _set_var_from_dual_preset_scalar_list( instance_t* inst, const char* var_label, const object_t* scalar, const object_t* list, double coeff ) - { - rc_t rc = kOkRC; - for(unsigned chIdx=0; chIdxchild_count(); ++chIdx) - if((rc = _set_var_from_dual_preset_scalar_scalar(inst,var_label,scalar,list->child_ele(chIdx),coeff,chIdx)) != kOkRC ) - goto errLabel; - - errLabel: - return rc; - } - - rc_t _set_var_from_dual_preset_list_scalar( instance_t* inst, const char* var_label, const object_t* list, const object_t* scalar, double coeff ) - { - rc_t rc = kOkRC; - for(unsigned chIdx=0; chIdxchild_count(); ++chIdx) - if((rc = _set_var_from_dual_preset_scalar_scalar(inst,var_label,list->child_ele(chIdx),scalar,coeff,chIdx)) != kOkRC ) - goto errLabel; - - errLabel: return rc; } - rc_t _set_var_from_dual_preset_scalar_scalar( instance_t* inst, const char* var_label, const object_t* scalar_0, const object_t* scalar_1, double coeff ) - { - return _set_var_from_dual_preset_scalar_scalar(inst,var_label,scalar_0,scalar_1,coeff,kAnyChIdx); - } - - - rc_t _is_legal_dual_value( const object_t* value ) + rc_t _parse_subnet_vars( flow_t* p, class_desc_t* class_desc, const object_t* subnetProcD, const object_t* varD ) { rc_t rc = kOkRC; - - if( value->is_list() ) - { - if( value->child_count() == 0 ) - { - rc = cwLogError(kInvalidArgRC,"Empty lists values cannot be applied as part of a dual value preset."); - goto errLabel; - } - } - else - { - switch( value->type->id ) - { - case kInt32TId: - case kUInt32TId: - case kInt64TId: - case kUInt64TId: - case kFloatTId: - case kDoubleTId: - break; - default: - rc = cwLogError(kInvalidArgRC,"Objects of type '%s' cannot be applied as part of a dual value preset.",cwStringNullGuard(value->type->label)); - } - } + unsigned varN = 0; + if( !varD->is_dict() ) + { + rc = cwLogError(kSyntaxErrorRC,"The proxy variable dictionary is invalid."); + goto errLabel; + } + + varN = varD->child_count(); + + // Fill the class_Desc.varDescL list from the subnet 'vars' dictioanry + for(unsigned i=0; ichild_ele(i); + var_desc_t* var_desc = nullptr; + + if((rc = _create_subnet_var_desc( p, class_desc, subnetProcD, child_pair, var_desc )) != kOkRC ) + goto errLabel; + + + var_desc->link = class_desc->varDescL; + class_desc->varDescL = var_desc; + + //printf("Wrapper var-desc created: %i of %i : %s:%s proxy:%s:%s flags:%i.\n", i, varN, class_desc->label, var_desc->label, var_desc->proxyProcLabel,var_desc->proxyVarLabel,var_desc->flags); + + } + errLabel: return rc; - } - - rc_t _set_var_from_dual_preset( instance_t* inst, const char* var_label, const object_t* value_0, const object_t* value_1, double coeff ) + + rc_t _create_subnet_class_desc( flow_t* p, const object_t* class_obj, class_desc_t* class_desc ) + { + rc_t rc = kOkRC; + const object_t* varD = nullptr; + const object_t* subnetD = nullptr; + const object_t* subnetProcD = nullptr; + const object_t* subnetPresetD = nullptr; + const char* subnetProcDescLabel = nullptr; + + // Validate the subnet proc desc label and value + if( class_obj==nullptr || !class_obj->is_pair() || class_obj->pair_value()==nullptr || !class_obj->pair_value()->is_dict() || (subnetProcDescLabel = class_obj->pair_label()) == nullptr ) + { + rc = cwLogError(kInvalidArgRC,"An invalid subnet description '%s' was encountered.",cwStringNullGuard(subnetProcDescLabel)); + goto errLabel; + } + + // verify that another subnet with the same name does not already exist + if( class_desc_find(p,subnetProcDescLabel) != nullptr ) + { + rc = cwLogError(kInvalidStateRC,"A subnet named '%s' already exists.",subnetProcDescLabel); + goto errLabel; + } + + class_desc->cfg = class_obj->pair_value(); + class_desc->label = class_obj->pair_label(); + + // get the 'subnet' members record + if((class_desc->members = _find_library_record("subnet")) == nullptr ) + { + rc = cwLogError(kSyntaxErrorRC,"The 'subnet' class member function record could not be found." ); + goto errLabel; + } + + // get the variable description + if((rc = class_desc->cfg->getv_opt("vars", varD, + "network", subnetD)) != kOkRC ) + { + rc = cwLogError(rc,"Parse failed while parsing subnet desc:'%s'", cwStringNullGuard(class_desc->label) ); + goto errLabel; + } + + // get the subnet proc and preset dictionaries + if((rc = subnetD->getv("procs", subnetProcD, + "presets", subnetPresetD)) != kOkRC ) + { + rc = cwLogError(rc,"Parse failed on the 'network' element."); + goto errLabel; + } + + // fill class_desc.varDescL from the subnet vars dictionary + if((rc = _parse_subnet_vars( p, class_desc, subnetProcD, varD )) != kOkRC ) + { + rc = cwLogError(rc,"Subnet 'vars' processing failed."); + goto errLabel; + } + + + errLabel: + if(rc != kOkRC ) + rc = cwLogError(rc,"'proc' class description creation failed for the subnet '%s'. ",cwStringNullGuard(subnetProcDescLabel)); + + return rc; + } + + + rc_t _parse_subnet_cfg(flow_t* p, const object_t* subnetCfg) { rc_t rc = kOkRC; - - // dual values must be either numeric scalars or lists - if((rc = _is_legal_dual_value(value_0)) != kOkRC || (rc = _is_legal_dual_value(value_1)) != kOkRC) - goto errLabel; + + if( !subnetCfg->is_dict() ) + return cwLogError(kSyntaxErrorRC,"The subnet class description dictionary does not have dictionary syntax."); - - // if both values are lists then they must be the same length - if( value_0->is_list() && value_1->is_list() ) + unsigned subnetDescN = subnetCfg->child_count(); + p->subnetDescA = mem::allocZ( subnetDescN ); + + // for each subnet description + for(unsigned i=0; iis_list() ) + const object_t* subnet_obj = subnetCfg->child_ele(i); + + if((rc = _create_subnet_class_desc(p, subnet_obj, p->subnetDescA + i )) != kOkRC ) { - rc = _set_var_from_dual_preset_list_scalar( inst, var_label, value_0, value_1, coeff ); + rc = cwLogError(rc,"Subnet class description create failed on the subnet at index:%i.",i); goto errLabel; } - else - { - // if value_1 is a list and value_0 is a scalar - if( value_1->is_list() ) - { - rc = _set_var_from_dual_preset_scalar_list( inst, var_label, value_0, value_1, coeff ); - goto errLabel; - } - else // both values are scalars - { - rc = _set_var_from_dual_preset_scalar_scalar( inst, var_label, value_0, value_1, coeff ); - goto errLabel; - } - } + + // We have to update the size of the subnet class array + // as we go because we may want be able to search p->subnetDescA[] + // aand to do that we must now the current length. + p->subnetDescN += 1; } + assert( subnetDescN == p->subnetDescN ); errLabel: - return rc; - } - - rc_t _multi_preset_channelize_vars( instance_t* inst, const char* type_src_label, const char** presetLabelA, const object_t** preset_cfgA, unsigned presetN, double coeff ) - { - rc_t rc = kOkRC; - const char* preset_label_0 = ""; - const char* preset_label_1 = ""; - - //cwLogInfo("Channelizing '%s' preset %i vars for '%s'.",type_src_label, preset_cfg==nullptr ? 0 : preset_cfg->child_count(), inst->label ); - - if( presetN < 2 ) - { - rc = cwLogError(kInvalidArgRC,"There must be at least 2 presets selected to interpolate between preset variable dictionaries."); - goto errLabel; - } - - if( presetN > 2 ) - { - cwLogWarning("More than two presets dictionaries were specified for interpolation. Only the first two will be used."); - goto errLabel; - } - - preset_label_0 = presetLabelA[0]; - preset_label_1 = presetLabelA[1]; - - // validate each of the preset records is a dict - for(unsigned i=0; iis_dict() ) - { - rc = cwLogError(kSyntaxErrorRC,"The preset record '%s' on %s '%s' is not a dictionary.", presetLabelA[i], type_src_label, inst->class_desc->label ); - goto errLabel; - } - - - // for each preset variable in the first preset var dict - for(unsigned i=0; ichild_count(); ++i) - { - const char* var_label = preset_cfgA[0]->child_ele(i)->pair_label(); - const object_t* value_0 = preset_cfgA[0]->child_ele(i)->pair_value(); - - const object_t* value_1 = preset_cfgA[1]->find_child(var_label); - - if( value_0 == nullptr && value_1 == nullptr ) - { - rc = cwLogError(kSyntaxErrorRC,"Unexpected missig values on %s preset '%s' instance '%s' variable '%s'.", type_src_label, presetLabelA[0], inst->label, cwStringNullGuard(var_label) ); - goto errLabel; - } - - if( value_0 == nullptr ) - { - cwLogWarning("The preset variable '%s' was not found for the preset: '%s'. Falling back to single value assign.",cwStringNullGuard(var_label),cwStringNullGuard(presetLabelA[0])); - - rc = _var_channelize( inst, preset_label_1, "dual class", var_label, value_1 ); - goto errLabel; - } - - if( value_1 == nullptr ) - { - cwLogWarning("The preset variable '%s' was not found for the preset: '%s'. Falling back to single value assign.",cwStringNullGuard(var_label),cwStringNullGuard(presetLabelA[1])); - - rc = _var_channelize( inst, preset_label_0, "dual class", var_label, value_0 ); - goto errLabel; - } - - - if((rc = _set_var_from_dual_preset( inst, var_label, value_0, value_1, coeff )) != kOkRC ) - { - rc = cwLogError(rc,"Multi preset application failed on variable:%s.",cwStringNullGuard(var_label)); - goto errLabel; - } - } - - errLabel: if( rc != kOkRC ) - rc = cwLogError(rc,"Apply %s multi-preset failed on instance:%s class:%s presetA:%s presetB:%s.", type_src_label, inst->label, inst->class_desc->label, preset_label_0, preset_label_1 ); - + { + rc = cwLogError(rc,"Subnet processing failed."); + + } + return rc; + } - - rc_t _class_multi_preset_channelize_vars(instance_t* inst, const char** class_preset_labelA, unsigned presetN, double coeff ) + void _release_class_desc_array( class_desc_t*& classDescA, unsigned classDescN ) { - rc_t rc = kOkRC; - const object_t* presetCfgA[ presetN ]; - const char* presetLabelA[ presetN ]; - unsigned presetCfgN = 0; - - for(unsigned i=0; iclass_desc, class_preset_labelA[i])) == nullptr ) - { - rc = cwLogError(kInvalidIdRC,"The preset '%s' could not be found for the instance '%s'.", class_preset_labelA[i], inst->label); - goto errLabel; - } - - if( pr->cfg == nullptr ) - { - rc = cwLogError(kInvalidIdRC,"The value of preset '%s' was empty in instance '%s'.", class_preset_labelA[i], inst->label); - goto errLabel; - } - - presetCfgA[ presetCfgN] = pr->cfg; - presetLabelA[presetCfgN] = class_preset_labelA[i]; - presetCfgN++; - } + class_desc_t* cd = classDescA + i; + class_desc_destroy(cd); } - // dispatch based on the count of presets located - switch( presetCfgN ) - { - case 0: - rc = cwLogError(kInvalidArgRC,"No valid class preset records were found while attempting apply a multi-preset."); - break; - - case 1: - // only one valid preset was located - apply it directly - rc = _preset_channelize_vars( inst, "class", presetLabelA[0], presetCfgA[0]); - break; - - default: - // more than one preset was located - apply it's interpolated values - rc = _multi_preset_channelize_vars( inst, "class", presetLabelA, presetCfgA, presetCfgN, coeff); - } - - - errLabel: - return rc; - + mem::release(classDescA); } - rc_t _class_preset_channelize_vars( instance_t* inst, const char* preset_label ) - { - rc_t rc = kOkRC; - const preset_t* pr; - - if( preset_label == nullptr ) - return kOkRC; - - // locate the requestd preset record - if((pr = class_preset_find(inst->class_desc, preset_label)) == nullptr ) - { - rc = cwLogError(kInvalidIdRC,"The preset '%s' could not be found for the instance '%s'.", preset_label, inst->label); - goto errLabel; - } - - rc = _preset_channelize_vars( inst, "class", preset_label, pr->cfg); - - errLabel: - return rc; - } - - - rc_t _class_apply_presets( instance_t* inst, const object_t* preset_labels ) - { - rc_t rc = kOkRC; - const char* s = nullptr; - - // if preset_labels is a string - if( preset_labels->is_string() && preset_labels->value(s)==kOkRC ) - return _class_preset_channelize_vars(inst,s); - - // if the preset_labels is not a list - if( !preset_labels->is_list() ) - rc = cwLogError(kSyntaxErrorRC,"The preset list on instance '%s' is neither a list nor a string.",inst->label); - else - { - // preset_labels is a list. - - // for each label listed in the preset label list - for(unsigned i=0; ichild_count(); ++i) - { - const object_t* label_obj = preset_labels->child_ele(i); - - // verify that the label is a strng - if( !label_obj->is_string() || label_obj->value(s) != kOkRC ) - { - rc = cwLogError(kSyntaxErrorRC,"The preset list does not contain string on instance '%s'.",inst->label); - goto errLabel; - } - - // apply a preset label - if((rc = _class_preset_channelize_vars( inst, s)) != kOkRC ) - goto errLabel; - } - } - - errLabel: - return rc; - } - - - - - rc_t _inst_args_channelize_vars( instance_t* inst, const char* arg_label, const object_t* arg_cfg ) - { - rc_t rc = kOkRC; - - if( arg_cfg == nullptr ) - return rc; - - return _preset_channelize_vars( inst, "instance", arg_label, arg_cfg ); - - } - - typedef struct inst_parse_vars_str - { - const char* inst_label; - const char* inst_clas_label; - const object_t* in_dict; - const char* arg_label; - const object_t* preset_labels; - const object_t* arg_cfg; - } inst_parse_vars_t; - - rc_t _parse_instance_cfg( flow_t* p, const object_t* inst_cfg, inst_parse_vars_t& pvars ) - { - rc_t rc = kOkRC; - const object_t* arg_dict = nullptr; - - // validate the syntax of the inst_cfg pair - if( inst_cfg == nullptr || !inst_cfg->is_pair() || inst_cfg->pair_label()==nullptr || inst_cfg->pair_value()==nullptr ) - { - rc = cwLogError(kSyntaxErrorRC,"The instance cfg. is not a valid pair. No instance label could be parsed."); - goto errLabel; - } - - pvars.inst_label = inst_cfg->pair_label(); - - // verify that the instance label is unique - if( instance_find(p,pvars.inst_label) != nullptr ) - { - rc = cwLogError(kSyntaxErrorRC,"The instance label '%s' has already been used.",pvars.inst_label); - goto errLabel; - } - - // get the instance class label - if((rc = inst_cfg->pair_value()->getv("class",pvars.inst_clas_label)) != kOkRC ) - { - rc = cwLogError(kSyntaxErrorRC,"The instance cfg. %s is missing: 'type'.",pvars.inst_label); - goto errLabel; - } - - // parse the optional args - if((rc = inst_cfg->pair_value()->getv_opt("args", arg_dict, - "in", pvars.in_dict, - "argLabel", pvars.arg_label, - "preset", pvars.preset_labels)) != kOkRC ) - { - rc = cwLogError(kSyntaxErrorRC,"The instance cfg. '%s' missing: 'type'.",pvars.inst_label); - goto errLabel; - } - - // if an argument dict was given in the instanec cfg - if( arg_dict != nullptr ) - { - bool rptErrFl = true; - - // verify the arg. dict is actually a dict. - if( !arg_dict->is_dict() ) - { - cwLogError(kSyntaxErrorRC,"The instance argument dictionary on instance '%s' is not a dictionary.",pvars.inst_label); - goto errLabel; - } - - // if no label was given then try 'default' - if( pvars.arg_label == nullptr) - { - pvars.arg_label = "default"; - rptErrFl = false; - } - - // locate the specified argument record - if((pvars.arg_cfg = arg_dict->find_child(pvars.arg_label)) == nullptr ) - { - - // if an explicit arg. label was given but it was not found - if( rptErrFl ) - { - rc = cwLogError(kSyntaxErrorRC,"The argument cfg. '%s' was not found on instance cfg. '%s'.",pvars.arg_label,pvars.inst_label); - goto errLabel; - } - - // no explicit arg. label was given - make arg_dict the instance arg cfg. - pvars.arg_cfg = arg_dict; - pvars.arg_label = nullptr; - } - } - - errLabel: - if( rc != kOkRC ) - rc = cwLogError(kSyntaxErrorRC,"Configuration parsing failed on instance: '%s'.", cwStringNullGuard(pvars.inst_label) ); - - return rc; - } - - rc_t _create_instance( flow_t* p, const object_t* inst_cfg ) - { - rc_t rc = kOkRC; - inst_parse_vars_t pvars = {}; - instance_t* inst = nullptr; - class_desc_t* class_desc = nullptr; - - // parse the instance configuration - if((rc = _parse_instance_cfg( p, inst_cfg, pvars )) != kOkRC ) - goto errLabel; - - // locate the class desc - if(( class_desc = class_desc_find(p,pvars.inst_clas_label)) == nullptr ) - { - rc = cwLogError(kSyntaxErrorRC,"The flow class '%s' was not found.",cwStringNullGuard(pvars.inst_clas_label)); - goto errLabel; - } - - // instantiate the instance - inst = mem::allocZ(); - - inst->ctx = p; - inst->label = pvars.inst_label; - inst->inst_cfg = inst_cfg; - inst->arg_label = pvars.arg_label; - inst->arg_cfg = pvars.arg_cfg; - inst->class_desc = class_desc; - - // Instantiate all the variables in the class description - for(var_desc_t* vd=class_desc->varDescL; vd!=nullptr; vd=vd->link) - { - variable_t* var = nullptr; - if((rc = var_create( inst, vd->label, kInvalidId, kAnyChIdx, vd->val_cfg, var )) != kOkRC ) - goto errLabel; - } - - // All the variables that can be used by this instance have now been created - // and the chIdx of each variable is set to 'any'. - - // If a 'preset' field was included in the instance cfg then apply the specified class preset - if( pvars.preset_labels != nullptr ) - if((rc = _class_apply_presets(inst, pvars.preset_labels )) != kOkRC ) - goto errLabel; - - // All the class presets values have now been set and those variables - // that were expressed with a list have numeric channel indexes assigned. - - // Apply the instance preset values. - if( pvars.arg_cfg != nullptr ) - if((rc = _inst_args_channelize_vars( inst, pvars.arg_label, pvars.arg_cfg )) != kOkRC ) - goto errLabel; - - // All the instance arg values have now been set and those variables - // that were expressed with a list have numeric channel indexes assigned. - - - // TODO: Should the 'all' variable be removed for variables that have numeric channel indexes? - - // connect the variable lists in the instance 'in' dictionary - if( pvars.in_dict != nullptr ) - { - if( !pvars.in_dict->is_dict() ) - { - cwLogError(kSyntaxErrorRC,"The 'in' dict in instance '%s' is not a valid dictionary.",inst->label); - goto errLabel; - } - - // for each input variable in the 'in' set - for(unsigned i=0; ichild_count(); ++i) - { - const object_t* in_pair = pvars.in_dict->child_ele(i); - const char* in_var_label = in_pair->pair_label(); - const char* src_label = nullptr; - const var_desc_t* vd = nullptr; - - // locate the var desc of the associated variable - if((vd = var_desc_find( class_desc, in_var_label)) == nullptr ) - { - cwLogError(kSyntaxErrorRC,"The value description for the 'in' value '%s' was not found on instance '%s'. Maybe '%s' is not marked as a 'src' attribute in the class variable descripiton.",in_var_label,inst->label,in_var_label); - goto errLabel; - } - - // Note that all variable's found by the above call to var_desc_find() should be 'src' variables. - //assert( cwIsFlag(vd->flags,kSrcVarFl) ); - - // if this value is a 'src' value then it must be setup prior to the instance being instantiated - //if( cwIsFlag(vd->flags,kSrcVarFl) ) - //{ - in_pair->pair_value()->value(src_label); - - // locate the pointer to the referenced output abuf and store it in inst->srcABuf[i] - if((rc = _setup_input( p, inst, in_var_label, src_label )) != kOkRC ) - { - rc = cwLogError(kSyntaxErrorRC,"The 'in' variable at index %i is not valid on instance '%s'.", i, inst->label ); - goto errLabel; - } - //} - } - } - - // Complete the instantiation - - // Call the custom instance create() function. - if((rc = class_desc->members->create( inst )) != kOkRC ) - { - rc = cwLogError(kInvalidArgRC,"Instantiation failed on instance '%s'.", inst->label ); - goto errLabel; - } - - // Create the instance->varMap[] lookup array - if((rc =_create_instance_var_map( inst )) != kOkRC ) - goto errLabel; - - // - _complete_input_connections(inst); - - // call the 'value()' function to inform the instance of the current value of all of it's variables. - if((rc = _call_value_func_on_all_variables( inst )) != kOkRC ) - goto errLabel; - - // insert an instance in the network - if( p->network_tail == nullptr ) - { - p->network_head = inst; - p->network_tail = inst; - } - else - { - p->network_tail->link = inst; - p->network_tail = inst; - } - - - errLabel: - if( rc != kOkRC ) - _destroy_inst(inst); - - return rc; - } - - rc_t _destroy( flow_t* p) { rc_t rc = kOkRC; @@ -1157,388 +634,16 @@ namespace cw if( p == nullptr ) return rc; - instance_t* i0=p->network_head; - instance_t* i1=nullptr; - - // destroy the instances - while(i0!=nullptr) - { - i1 = i0->link; - _destroy_inst(i0); - i0 = i1; - } - - // release the class records - for(unsigned i=0; iclassDescN; ++i) - { - class_desc_t* cd = p->classDescA + i; - - // release the var desc list - var_desc_t* vd0 = cd->varDescL; - var_desc_t* vd1 = nullptr; - while( vd0 != nullptr ) - { - vd1 = vd0->link; - mem::release(vd0); - vd0 = vd1; - } - - // release the preset list - preset_t* pr0 = cd->presetL; - preset_t* pr1 = nullptr; - while( pr0 != nullptr ) - { - pr1 = pr0->link; - mem::release(pr0); - pr0 = pr1; - } - } - - mem::release(p->classDescA); + network_destroy(p->net); + + _release_class_desc_array(p->classDescA,p->classDescN); + _release_class_desc_array(p->subnetDescA,p->subnetDescN); + mem::release(p); return rc; } - const object_t* _find_network_preset( flow_t* p, const char* presetLabel ) - { - const object_t* preset_value = nullptr; - - if( p->presetCfg != nullptr ) - { - rc_t rc; - - if((rc = p->presetCfg->getv_opt( presetLabel, preset_value )) != kOkRC ) - cwLogError(rc,"Search for network preset named '%s' failed.", cwStringNullGuard(presetLabel)); - } - - return preset_value; - - } - - rc_t _exec_cycle( flow_t* p ) - { - rc_t rc = kOkRC; - - for(instance_t* inst = p->network_head; inst!=nullptr; inst=inst->link) - { - if((rc = inst->class_desc->members->exec(inst)) != kOkRC ) - { - break; - } - } - - return rc; - } - - rc_t _get_variable( flow_t* p, const char* inst_label, const char* var_label, unsigned chIdx, instance_t*& instPtrRef, variable_t*& varPtrRef ) - { - rc_t rc = kOkRC; - instance_t* inst = nullptr; - variable_t* var = nullptr; - - varPtrRef = nullptr; - instPtrRef = nullptr; - - // locate the proc instance - if((inst = instance_find(p,inst_label)) == nullptr ) - { - rc = cwLogError(kInvalidIdRC,"Unknown proc instance label '%s'.", cwStringNullGuard(inst_label)); - goto errLabel; - } - - // locate the variable - if((rc = var_find( inst, var_label, chIdx, var)) != kOkRC ) - { - rc = cwLogError(kInvalidArgRC,"The variable '%s' could not be found on the proc instance '%s'.",cwStringNullGuard(var_label),cwStringNullGuard(inst_label)); - goto errLabel; - } - - instPtrRef = inst; - varPtrRef = var; - - errLabel: - return rc; - } - - template< typename T > - rc_t _set_variable_value( flow_t* p, const char* inst_label, const char* var_label, unsigned chIdx, T value ) - { - rc_t rc = kOkRC; - instance_t* inst = nullptr; - variable_t* var = nullptr; - - // get the variable - if((rc = _get_variable(p,inst_label,var_label,chIdx,inst,var)) != kOkRC ) - goto errLabel; - - // set the variable value - if((rc = var_set( inst, var->vid, chIdx, value )) != kOkRC ) - { - rc = cwLogError(kOpFailRC,"The variable set failed on instance:'%s' variable:'%s'.",cwStringNullGuard(inst_label),cwStringNullGuard(var_label)); - goto errLabel; - } - - errLabel: - return rc; - } - - template< typename T > - rc_t _get_variable_value( flow_t* p, const char* inst_label, const char* var_label, unsigned chIdx, T& valueRef ) - { - rc_t rc = kOkRC; - instance_t* inst = nullptr; - variable_t* var = nullptr; - - // get the variable - if((rc = _get_variable(p,inst_label,var_label,chIdx,inst,var)) != kOkRC ) - goto errLabel; - - // get the variable value - if((rc = var_get( inst, var->vid, chIdx, valueRef )) != kOkRC ) - { - rc = cwLogError(kOpFailRC,"The variable get failed on instance:'%s' variable:'%s'.",cwStringNullGuard(inst_label),cwStringNullGuard(var_label)); - goto errLabel; - } - - errLabel: - return rc; - } - - unsigned _select_ranked_ele_by_rank_prob( const preset_order_t* presetA, const bool* selV , unsigned presetN ) - { - - // get a count of the candidate presets - unsigned rankN = selV==nullptr ? presetN : std::count_if(selV,selV+presetN,[](const bool& x){ return x; }); - - if( rankN == 0 ) - { - cwLogWarning("All preset candidates have been eliminated."); - return kInvalidIdx; - } - - unsigned rankV[ rankN ]; - unsigned idxMapA[ rankN ]; - - // fill rankV[] with candidates 'order' value - for(unsigned i=0,j=0; i 1 ); - - unsigned threshV[ rankN ]; - unsigned uniqueRankV[ rankN ]; - unsigned uniqueRankN = 0; - unsigned sel_idx = rankN - 1; // - - // for each possible rank value - for(unsigned i=0; ichild_count(); ++i) - { - const object_t* inst_pair; - if((inst_pair = net_preset_pair->child_ele(i)) != nullptr && inst_pair->is_pair() && textIsEqual(inst_pair->pair_label(),instance_label) ) - { - - preset_val_ref = inst_pair->pair_value(); - - goto errLabel; - } - } - - rc = cwLogError(kInvalidArgRC,"The preset instance label '%s' was not found.",cwStringNullGuard(preset_label)); - - errLabel: - return rc; - } } } @@ -1549,80 +654,114 @@ void cw::flow::print_abuf( const abuf_t* abuf ) void cw::flow::print_external_device( const external_device_t* dev ) { - printf("Dev: %10s id:%3i type:%3i fl:0x%x : ", cwStringNullGuard(dev->label),dev->ioDevId,dev->typeId,dev->flags); + printf("Dev: %10s type:%3i fl:0x%x : ", cwStringNullGuard(dev->devLabel),dev->typeId,dev->flags); if( dev->typeId == kAudioDevTypeId ) print_abuf(dev->u.a.abuf); printf("\n"); } + cw::rc_t cw::flow::create( handle_t& hRef, - const object_t& classCfg, - const object_t& networkCfg, + const object_t* classCfg, + const object_t* flowCfg, + const object_t* subnetCfg, + const char* proj_dir, external_device_t* deviceA, unsigned deviceN ) { rc_t rc = kOkRC; - const object_t* network = nullptr; + const object_t* networkCfg = nullptr; bool printClassDictFl = false; bool printNetworkFl = false; + variable_t* proxyVarL = nullptr; + unsigned maxCycleCount = kInvalidCnt; if(( rc = destroy(hRef)) != kOkRC ) return rc; flow_t* p = mem::allocZ(); - p->networkCfg = &networkCfg; // TODO: duplicate cfg? + p->flowCfg = flowCfg; // TODO: duplicate cfg? p->deviceA = deviceA; p->deviceN = deviceN; // parse the class description array - if((rc = _parse_class_cfg(p,&classCfg)) != kOkRC ) + if((rc = _parse_class_cfg(p,classCfg)) != kOkRC ) { rc = cwLogError(kSyntaxErrorRC,"Error parsing the class description list."); goto errLabel; } + // parse the subnet descriptions + if( subnetCfg != nullptr ) + if((rc = _parse_subnet_cfg(p,subnetCfg)) != kOkRC ) + { + rc = cwLogError(kSyntaxErrorRC,"Error parsing the subnet list."); + goto errLabel; + } + // parse the main audio file processor cfg record - if((rc = networkCfg.getv("framesPerCycle", p->framesPerCycle, - "multiPriPresetProbFl", p->multiPriPresetProbFl, - "multiSecPresetProbFl", p->multiSecPresetProbFl, - "multiPresetInterpFl", p->multiPresetInterpFl, - "network", network)) != kOkRC ) + if((rc = flowCfg->getv("network", networkCfg)) != kOkRC ) { rc = cwLogError(kSyntaxErrorRC,"Error parsing the required flow configuration parameters."); goto errLabel; } + p->framesPerCycle = kDefaultFramesPerCycle; + p->sample_rate = kDefaultSampleRate; + p->maxCycleCount = kInvalidCnt; + p->proj_dir = proj_dir; + // parse the optional args - if((rc = networkCfg.getv_opt("maxCycleCount", p->maxCycleCount, - "printClassDictFl", printClassDictFl, - "printNetworkFl", printNetworkFl, - "presets", p->presetCfg)) != kOkRC ) + if((rc = flowCfg->getv_opt("framesPerCycle", p->framesPerCycle, + "sample_rate", p->sample_rate, + "maxCycleCount", maxCycleCount, + "preset", p->init_net_preset_label, + "multiPriPresetProbFl", p->multiPriPresetProbFl, + "multiSecPresetProbFl", p->multiSecPresetProbFl, + "multiPresetInterpFl", p->multiPresetInterpFl, + "printClassDictFl", printClassDictFl, + "printNetworkFl", printNetworkFl)) != kOkRC ) { rc = cwLogError(kSyntaxErrorRC,"Error parsing the optional flow configuration parameters."); goto errLabel; } + if( maxCycleCount != kInvalidCnt ) + p->maxCycleCount = maxCycleCount; + + for(unsigned i=0; iframeN != p->framesPerCycle ) + cwLogWarning("The audio frame count (%i) for audio device '%s' does not match the Flow framesPerCycle (%i).",deviceA[i].u.a.abuf->frameN,p->framesPerCycle); + } + // print the class dict if( printClassDictFl ) class_dict_print( p ); - // build the network - for(unsigned i=0; ichild_count(); ++i) + // instantiate the network + if((rc = network_create(p,networkCfg,p->net,proxyVarL)) != kOkRC ) { - const object_t* inst_cfg = network->child_ele(i); - - // create the instance - if( (rc= _create_instance( p, inst_cfg ) ) != kOkRC ) - { - rc = cwLogError(rc,"The instantiation at proc index %i is invalid.",i); - goto errLabel; - - } + rc = cwLogError(rc,"Network creation failed."); + goto errLabel; } if( printNetworkFl ) - network_print(p); + network_print(p->net); + + if( p->init_net_preset_label != nullptr ) + network_apply_preset( p->net, p->init_net_preset_label ); + + p->isInRuntimeFl = true; + cwLogInfo("Entering runtime."); hRef.set(p); @@ -1672,7 +811,7 @@ unsigned cw::flow::preset_cfg_flags( handle_t h ) cw::rc_t cw::flow::exec_cycle( handle_t h ) { - return _exec_cycle(_handleToPtr(h)); + return exec_cycle(_handleToPtr(h)->net); } cw::rc_t cw::flow::exec( handle_t h ) @@ -1680,441 +819,75 @@ cw::rc_t cw::flow::exec( handle_t h ) rc_t rc = kOkRC; flow_t* p = _handleToPtr(h); - while( true ) + for(; (p->maxCycleCount==kInvalidCnt || (p->maxCycleCount!=kInvalidCnt && p->cycleIndex < p->maxCycleCount)) && rc == kOkRC; p->cycleIndex++ ) { - rc = _exec_cycle(p); + rc = exec_cycle(p->net); + // kEofRC indicates that the network asked to terminate if( rc == kEofRC ) { rc = kOkRC; break; } - p->cycleIndex += 1; - if( p->maxCycleCount > 0 && p->cycleIndex >= p->maxCycleCount ) - break; } + if( p->maxCycleCount != kInvalidCnt && p->cycleIndex >= p->maxCycleCount ) + cwLogInfo("'maxCycleCnt' reached: %i. Shutting down flow.",p->maxCycleCount); + return rc; } + cw::rc_t cw::flow::apply_preset( handle_t h, const char* presetLabel ) { - rc_t rc = kOkRC; flow_t* p = _handleToPtr(h); - const object_t* net_preset_value; - const object_t* preset_pair; - - // locate the cfg of the requested preset - if((net_preset_value = _find_network_preset(p, presetLabel )) == nullptr ) - { - rc = cwLogError(kInvalidIdRC,"The network preset '%s' could not be found.", presetLabel ); - goto errLabel; - } - - // for each instance in the preset - for(unsigned i=0; ichild_count(); ++i) - { - // get the instance label/value pair - if((preset_pair = net_preset_value->child_ele(i)) != nullptr && preset_pair->is_pair() ) - { - const char* inst_label = preset_pair->pair_label(); - const object_t* preset_value_cfg = preset_pair->pair_value(); - instance_t* inst; - - // locate the instance - if((inst = instance_find(p,inst_label)) == nullptr ) - { - rc = cwLogError(kInvalidIdRC,"The network instance '%s' refered to in network preset '%s' could not be found.",inst_label,presetLabel); - goto errLabel; - } - - // if the preset value is a string then look it up in the class dictionary - if( preset_value_cfg->is_string() ) - { - const char* class_preset_label; - preset_value_cfg->value(class_preset_label); - _class_preset_channelize_vars(inst, class_preset_label ); - } - else - { - // if the preset value is a dict then apply it directly - if( preset_value_cfg->is_dict() ) - { - if((rc = _preset_channelize_vars( inst, "network", presetLabel, preset_value_cfg )) != kOkRC ) - { - rc = cwLogError(rc,"The preset '%s' application failed on instance '%s'.", presetLabel, inst_label ); - goto errLabel; - } - - } - else - { - rc = cwLogError(kSyntaxErrorRC,"The network preset '%s' instance '%s' does not have a string or dictionary value.", presetLabel, inst_label ); - goto errLabel; - } - } - } - else - { - rc = cwLogError(kSyntaxErrorRC,"The network preset '%s' is malformed.",presetLabel); - goto errLabel; - } - } - - cwLogInfo("Activated preset:%s",presetLabel); - errLabel: - return rc; + return network_apply_preset(p->net,presetLabel); } cw::rc_t cw::flow::apply_dual_preset( handle_t h, const char* presetLabel_0, const char* presetLabel_1, double coeff ) { - rc_t rc = kOkRC; flow_t* p = _handleToPtr(h); - const object_t* net_preset_value_0; - - cwLogInfo("*** Applying dual: %s %s : %f",presetLabel_0, presetLabel_1, coeff ); - - // locate the cfg of the requested preset - if((net_preset_value_0 = _find_network_preset(p, presetLabel_0 )) == nullptr ) - { - rc = cwLogError(kInvalidIdRC,"The network preset '%s' could not be found.", presetLabel_0 ); - goto errLabel; - } - - // for each instance in the preset - for(unsigned i=0; ichild_count(); ++i) - { - const object_t* preset_pair_0 = net_preset_value_0->child_ele(i); - const char* inst_label = preset_pair_0->pair_label(); - const object_t* preset_value_cfg_0 = preset_pair_0->pair_value(); - instance_t* inst = nullptr; - const object_t* preset_value_cfg_1 = nullptr; - const int two = 2; - const char* class_preset_labelA[two]; - - // get the instance label/value pair - if((preset_pair_0 = net_preset_value_0->child_ele(i)) == nullptr || !preset_pair_0->is_pair() ) - { - rc = cwLogError(kSyntaxErrorRC,"An invalid preset value pair was encountered in '%s'.",presetLabel_0); - goto errLabel; - } - - // verify that the preset value is a string or dict - if( preset_pair_0->pair_value()==nullptr || (!preset_value_cfg_0->is_dict() && !preset_value_cfg_0->is_string() )) - { - rc = cwLogError(kSyntaxErrorRC,"The preset value pair for instance '%s' in '%s' is not a 'dict' or 'string'.",inst_label,presetLabel_0); - goto errLabel; - } - - // locate the instance associated with the primary and secondary preset - if((inst = instance_find(p,inst_label)) == nullptr ) - { - rc = cwLogError(kInvalidIdRC,"The network instance '%s' refered to in network preset '%s' could not be found.",cwStringNullGuard(inst_label),cwStringNullGuard(presetLabel_0)); - goto errLabel; - } - - // locate the second instance/preset value pair - if((rc = _find_network_preset_instance_pair( p, presetLabel_1, inst_label, preset_value_cfg_1 )) != kOkRC ) - { - rc = cwLogError(kInvalidIdRC,"The second network instance '%s' refered to in network preset '%s' could not be found.",inst_label,presetLabel_1); - goto errLabel; - } - - // TODO: We require that the instance presets both be of the same type: string or dict. - // There's no good reason for this, as string's resolve to class dict presets anyway. - // Fix this! - if( !(preset_value_cfg_0->is_dict() == preset_value_cfg_1->is_dict() && preset_value_cfg_0->is_string() == preset_value_cfg_1->is_string()) ) - { - rc = cwLogError(kInvalidIdRC,"The value type (string or dict) of dual network presets must match. (%s != %s)",preset_value_cfg_0->type->label,preset_value_cfg_1->type->label); - goto errLabel; - } - - preset_value_cfg_0->value(class_preset_labelA[0]); - preset_value_cfg_1->value(class_preset_labelA[1]); - - - // if the preset value is a string then look it up in the class dictionary - if( preset_value_cfg_0->is_string() ) - { - rc = _class_multi_preset_channelize_vars(inst, class_preset_labelA, two, coeff ); - } - else - { - assert( preset_value_cfg_1->is_dict() ); - - const object_t* preset_value_cfgA[] = { preset_value_cfg_0, preset_value_cfg_1}; - - if((rc = _multi_preset_channelize_vars( inst, "network", class_preset_labelA, preset_value_cfgA, two, coeff )) != kOkRC ) - { - rc = cwLogError(rc,"The dual preset '%s':'%s' application failed on instance '%s'.", cwStringNullGuard(class_preset_labelA[0]), cwStringNullGuard(class_preset_labelA[1]), inst_label ); - goto errLabel; - } - } - } - - - errLabel: - - if( rc != kOkRC ) - rc = cwLogError(rc,"The dual preset '%s':'%s' application failed.", cwStringNullGuard(presetLabel_0), cwStringNullGuard(presetLabel_1) ); - - return rc; + return network_apply_dual_preset(p->net,presetLabel_0, presetLabel_1, coeff ); } cw::rc_t cw::flow::apply_preset( handle_t h, const multi_preset_selector_t& mps ) { - rc_t rc = kOkRC; - const char* label0 = nullptr; - const char* label1 = nullptr; - bool priProbFl = cwIsFlag(mps.flags, kPriPresetProbFl ); - bool secProbFl = cwIsFlag(mps.flags, kSecPresetProbFl ); - bool interpFl = cwIsFlag(mps.flags, kInterpPresetFl ); - bool allFl = cwIsFlag(mps.flags, kAllowAllPresetFl); - bool dryPrioFl = cwIsFlag(mps.flags, kDryPriorityPresetFl); - bool drySelFl = cwIsFlag(mps.flags, kDrySelectedPresetFl); - - printf("preset flags: pri:%i sec:%i interp:%i all:%i dry-(prio:%i sel:%i) n:%i\n",priProbFl,secProbFl,interpFl,allFl,dryPrioFl,drySelFl,mps.presetN); - - // verify that the set of candidate presets is not empty - if( mps.presetN == 0 ) - { - cwLogError(kInvalidArgRC,"A multi-preset application was requested but no presets were provided."); - goto errLabel; - } - - // if only a single candidate preset exists or needs to be selected - if( interpFl==false || mps.presetN==1 ) - { - // if only a single candidate preset is available or pri. probablity is not enabled - if( mps.presetN == 1 || priProbFl==false ) - label0 = mps.presetA[0].preset_label; - else - { - if( priProbFl ) - label0 = _select_ranked_ele_label_by_rank_prob( mps.presetA, nullptr, mps.presetN ); - else - label0 = mps.presetA[0].preset_label; - } - } - else // interpolation has been selected and at least 2 presets exist - { - unsigned pri_sel_idx = 0; - - // select the primary preset - if( priProbFl ) - pri_sel_idx = _select_ranked_ele_by_rank_prob( mps.presetA, nullptr, mps.presetN ); - else - { - // select all presets assigned to order == 1 - bool selA[ mps.presetN ]; - for(unsigned i=0; inet,mps); } -/* -cw::rc_t cw::flow::apply_preset( handle_t h, const multi_preset_selector_t& multi_preset_sel ) -{ - rc_t rc = kOkRC; - const char* label0 = nullptr; - const char* label1 = nullptr; - const char* prob_label = nullptr; - bool multiPriPresetProbFl = cwIsFlag(multi_preset_sel.flags, kPriPresetProbFl ); - bool multiSecPresetProbFl = cwIsFlag(multi_preset_sel.flags, kSecPresetProbFl ); - bool multiPresetInterpFl = cwIsFlag(multi_preset_sel.flags, kInterpPresetFl ); - - // verify that the set of presets to select from is not empty - if( multi_preset_sel.presetN == 0 ) - { - cwLogError(kInvalidArgRC,"A multi-preset application was requested but no presets were provided."); - goto errLabel; - } - - // if probabistic selection was requested and is possible - if( multiPresetProbFl && multi_preset_sel.presetN > 1 ) - { - auto presetA = multi_preset_sel.presetA; - auto presetN = multi_preset_sel.presetN; - - // if we are interpolating then the base preset is always the first one in presetA[] - // so do not include it as a candidate for probabilistic selection - if( multiPresetInterpFl ) - { - presetA += 1; - presetN -= 1; - - // if only one preset remains in the list then prob. selection is not possible - if( presetN == 1 ) - prob_label = presetA[0].preset_label; - } - - // select a preset based using the ranked-prob. algorithm. - if( prob_label == nullptr ) - { - unsigned prob_sel_idx; - - if((prob_sel_idx = _select_ranked_ele_by_rank_prob( presetA, presetN )) == kInvalidIdx ) - rc = cwLogWarning("The multi-preset select function failed. Selecting preset 0."); - else - { - prob_label = presetA[prob_sel_idx].preset_label; - - cwLogInfo("Multi-preset prob. select:%s : %i from %i", - cwStringNullGuard(prob_label), - prob_sel_idx, - multi_preset_sel.presetN ); - - } - } - } - - // prob_label now holds a probablistically selected preset label - // or null if prob. sel. was not requested or failed - - switch( multi_preset_sel.presetN ) - { - case 0: - assert(0); // we avoided this case at the top of the function - break; - - case 1: - // if there is only one preset to select from - label0 = multi_preset_sel.presetA[0].preset_label; - break; - - default: - // There are at least two presets ... - // ... and prob. select was not requested or failed - if( prob_label == nullptr ) - { - label0 = multi_preset_sel.presetA[0].preset_label; - label1 = multiPresetInterpFl ? multi_preset_sel.presetA[1].preset_label : nullptr; - } - else // ... and a prob. selection exists - { - // if we need two presets - if( multiPresetInterpFl ) - { - label0 = multi_preset_sel.presetA[0].preset_label; - label1 = prob_label; - } - else // otherwise we need only one - { - label0 = prob_label; - label1 = nullptr; - } - } - } - - if( label0 == nullptr ) - { - rc = cwLogError(kInvalidStateRC,"The selected multi-preset label is empty."); - goto errLabel; - } - - if( label1 == nullptr ) - { - rc = apply_preset( h, label0 ); - } - else - { - double coeff = _calc_multi_preset_dual_coeff(multi_preset_sel); - rc = apply_dual_preset( h, label0, label1, coeff ); - } - -errLabel: - return rc; -} -*/ cw::rc_t cw::flow::set_variable_value( handle_t h, const char* inst_label, const char* var_label, unsigned chIdx, bool value ) -{ return _set_variable_value( _handleToPtr(h), inst_label, var_label, chIdx, value ); } +{ return set_variable_value( _handleToPtr(h)->net, inst_label, var_label, chIdx, value ); } cw::rc_t cw::flow::set_variable_value( handle_t h, const char* inst_label, const char* var_label, unsigned chIdx, int value ) -{ return _set_variable_value( _handleToPtr(h), inst_label, var_label, chIdx, value ); } +{ return set_variable_value( _handleToPtr(h)->net, inst_label, var_label, chIdx, value ); } cw::rc_t cw::flow::set_variable_value( handle_t h, const char* inst_label, const char* var_label, unsigned chIdx, unsigned value ) -{ return _set_variable_value( _handleToPtr(h), inst_label, var_label, chIdx, value ); } +{ return set_variable_value( _handleToPtr(h)->net, inst_label, var_label, chIdx, value ); } cw::rc_t cw::flow::set_variable_value( handle_t h, const char* inst_label, const char* var_label, unsigned chIdx, float value ) -{ return _set_variable_value( _handleToPtr(h), inst_label, var_label, chIdx, value ); } +{ return set_variable_value( _handleToPtr(h)->net, inst_label, var_label, chIdx, value ); } cw::rc_t cw::flow::set_variable_value( handle_t h, const char* inst_label, const char* var_label, unsigned chIdx, double value ) -{ return _set_variable_value( _handleToPtr(h), inst_label, var_label, chIdx, value ); } +{ return set_variable_value( _handleToPtr(h)->net, inst_label, var_label, chIdx, value ); } cw::rc_t cw::flow::get_variable_value( handle_t h, const char* inst_label, const char* var_label, unsigned chIdx, bool& valueRef ) -{ return _get_variable_value( _handleToPtr(h), inst_label, var_label, chIdx, valueRef ); } +{ return get_variable_value( _handleToPtr(h)->net, inst_label, var_label, chIdx, valueRef ); } cw::rc_t cw::flow::get_variable_value( handle_t h, const char* inst_label, const char* var_label, unsigned chIdx, int& valueRef ) -{ return _get_variable_value( _handleToPtr(h), inst_label, var_label, chIdx, valueRef ); } +{ return get_variable_value( _handleToPtr(h)->net, inst_label, var_label, chIdx, valueRef ); } cw::rc_t cw::flow::get_variable_value( handle_t h, const char* inst_label, const char* var_label, unsigned chIdx, unsigned& valueRef ) -{ return _get_variable_value( _handleToPtr(h), inst_label, var_label, chIdx, valueRef ); } +{ return get_variable_value( _handleToPtr(h)->net, inst_label, var_label, chIdx, valueRef ); } cw::rc_t cw::flow::get_variable_value( handle_t h, const char* inst_label, const char* var_label, unsigned chIdx, float& valueRef ) -{ return _get_variable_value( _handleToPtr(h), inst_label, var_label, chIdx, valueRef ); } +{ return get_variable_value( _handleToPtr(h)->net, inst_label, var_label, chIdx, valueRef ); } cw::rc_t cw::flow::get_variable_value( handle_t h, const char* inst_label, const char* var_label, unsigned chIdx, double& valueRef ) -{ return _get_variable_value( _handleToPtr(h), inst_label, var_label, chIdx, valueRef ); } +{ return get_variable_value( _handleToPtr(h)->net, inst_label, var_label, chIdx, valueRef ); } @@ -2131,56 +904,10 @@ void cw::flow::print_network( handle_t h ) for(unsigned i=0; ideviceN; ++i) print_external_device( p->deviceA + i ); - network_print(p); -} - - -cw::rc_t cw::flow::test( const object_t* cfg ) -{ - rc_t rc = kOkRC; - handle_t flowH; - - object_t* class_cfg = nullptr; - const char* flow_proc_fname; - - if((rc = cfg->getv("flow_proc_fname",flow_proc_fname)) != kOkRC ) - { - rc = cwLogError(rc,"The name of the flow_proc_dict file could not be parsed."); - goto errLabel; - } - - if((rc = objectFromFile(flow_proc_fname,class_cfg)) != kOkRC ) - { - rc = cwLogError(rc,"The flow proc dict could not be read from '%s'.",cwStringNullGuard(flow_proc_fname)); - goto errLabel; - } - - // create the flow object - if((rc = create( flowH, *class_cfg, *cfg)) != kOkRC ) - { - rc = cwLogError(rc,"Flow object create failed."); - goto errLabel; - } - - //print_network(flowH); - - // run the network - if((rc = exec( flowH )) != kOkRC ) - rc = cwLogError(rc,"Execution failed."); - - - // destroy the flow object - if((rc = destroy(flowH)) != kOkRC ) - { - rc = cwLogError(rc,"Close the flow object."); - goto errLabel; - } - - errLabel: - if( class_cfg != nullptr ) - class_cfg->free(); - return rc; + network_print(p->net); } + + diff --git a/cwFlow.h b/cwFlow.h index d45df47..1105163 100644 --- a/cwFlow.h +++ b/cwFlow.h @@ -8,54 +8,19 @@ namespace cw typedef handle handle_t; - enum - { - kAudioDevTypeId, - kMidiDevTypeId, - kSerialDevTypeId, - kSocketDevTypeId - }; - - enum - { - kInFl = 0x01, - kOutFl = 0x02 - }; - - - struct abuf_str; - - typedef struct audio_dev_cfg_str - { - struct abuf_str* abuf; // Buffer to receive incoming or send outgoing audio for this device - // The audio_in/audio_out proc's locate and use these buffers. - } audio_dev_cfg_t; - - // Generate external device record - typedef struct external_device_str - { - const char* label; // IO framework device label - unsigned ioDevId; // IO framework device id - unsigned typeId; // see ???DevTypeId above - unsigned flags; // see ???Fl above - - union - { - audio_dev_cfg_t a; // audio devices include this additional record - } u; - - } external_device_t; void print_abuf( const struct abuf_str* abuf ); void print_external_device( const external_device_t* dev ); - rc_t create( handle_t& hRef, - const object_t& classCfg, - const object_t& networkCfg, - external_device_t* deviceA = nullptr, - unsigned deviceN = 0); + rc_t create( handle_t& hRef, + const object_t* classCfg, + const object_t* networkCfg, + const object_t* subnetCfg = nullptr, + const char* projDir = nullptr, + external_device_t* deviceA = nullptr, + unsigned deviceN = 0); rc_t destroy( handle_t& hRef ); @@ -65,11 +30,11 @@ namespace cw rc_t exec_cycle( handle_t h ); // Run the network to completion. - rc_t exec( handle_t h ); + rc_t exec( handle_t h ); rc_t apply_preset( handle_t h, const char* presetLabel ); - rc_t apply_preset( handle_t h, const multi_preset_selector_t& multi_preset_sel ); rc_t apply_dual_preset( handle_t h, const char* presetLabel_0, const char* presetLabel_1, double coeff ); + rc_t apply_preset( handle_t h, const multi_preset_selector_t& multi_preset_sel ); rc_t set_variable_value( handle_t h, const char* inst_label, const char* var_label, unsigned chIdx, bool value ); @@ -87,7 +52,6 @@ namespace cw void print_class_list( handle_t h ); void print_network( handle_t h ); - rc_t test( const object_t* cfg ); diff --git a/cwFlowCross.cpp b/cwFlowCross.cpp index 2839273..de5c646 100644 --- a/cwFlowCross.cpp +++ b/cwFlowCross.cpp @@ -1,6 +1,7 @@ #include "cwCommon.h" #include "cwLog.h" #include "cwCommonImpl.h" +#include "cwTest.h" #include "cwMem.h" #include "cwText.h" #include "cwObject.h" @@ -9,6 +10,8 @@ #include "cwMtx.h" #include "cwDspTypes.h" // real_t, sample_t #include "cwDspTransforms.h" +#include "cwTime.h" +#include "cwMidiDecls.h" #include "cwFlowDecl.h" #include "cwFlow.h" #include "cwFlowTypes.h" @@ -28,14 +31,15 @@ namespace cw kFadeOutStateId, }; + // Each duplicated network is represented by a flow_netword_t record in flow_cross_t.netA[]. typedef struct flow_network_str { dsp::recorder::obj_t* recorder; - flow::external_device_t* deviceA; + flow::external_device_t* deviceA; // deviceA[ deviceN ] - cloned exteranl device array unsigned deviceN; - flow::handle_t flowH; - + flow::handle_t flowH; + unsigned stateId; // inactive, fade-in, fade-out double fadeGain; // 0 0->1 1->0 unsigned fadeSmpN; // @@ -47,7 +51,7 @@ namespace cw typedef struct flow_cross_str { - unsigned cur_idx; + unsigned cur_idx; // index of the network currently receiving parameter updates double srate; unsigned netN; @@ -106,8 +110,21 @@ namespace cw memcpy(devA,srcDevA,devN * sizeof(flow::external_device_t)); for(unsigned i=0; istateId = net_idx == 0 ? kActiveStateId : kInactiveStateId; net->net_idx = net_idx; - if((rc = flow::create( net->flowH, classCfg, networkCfg, net->deviceA, deviceN )) == kOkRC ) + if((rc = flow::create( net->flowH, &classCfg, &networkCfg, nullptr, nullptr, net->deviceA, deviceN )) == kOkRC ) net->deviceN = deviceN; else { @@ -203,9 +220,20 @@ namespace cw if( net->stateId == kFadeOutStateId && ef == 0.0 ) net->stateId = kInactiveStateId; - } + // Copy audio from the actual external audio device to a cloned audio device + void _update_midi_input( flow_cross_t* p, flow_network_t* net, unsigned devIdx ) + { + flow::midi_dev_cfg_t& src = p->deviceA[devIdx].u.m; // src MIDI device + flow::midi_dev_cfg_t& dst = net->deviceA[devIdx].u.m; // dst MIDI device clone + + // redirect the MIDI msg list array to the clones + dst.msgArray = src.msgArray; + dst.msgCnt = src.msgCnt; + } + + // Copy audio from the actual external audio device to a cloned audio device void _update_audio_input( flow_cross_t* p, flow_network_t* net, unsigned devIdx ) { flow::abuf_t* src = p->deviceA[devIdx].u.a.abuf; @@ -216,6 +244,7 @@ namespace cw //_fade_audio( src, dst, net ); } + void _zero_audio_output( flow_cross_t* p, flow_network_t* net, unsigned devIdx ) { flow::abuf_t* dst = net->deviceA[devIdx].u.a.abuf; @@ -378,13 +407,27 @@ cw::rc_t cw::flow_cross::exec_cycle( handle_t h ) { flow_network_t* net = p->netA + i; - // We generally don't want to fade the input because the state - // of the network delay lines would then be invalid when the - // network is eventually made active again for(unsigned j=0; jdeviceN; ++j) - if( p->deviceA[j].typeId == flow::kAudioDevTypeId && cwIsFlag(p->deviceA[j].flags, flow::kInFl ) ) - _update_audio_input( p, p->netA + i, j ); + if( cwIsFlag(p->deviceA[j].flags, flow::kInFl ) ) + { + switch( p->deviceA[j].typeId) + { + case flow::kAudioDevTypeId: + // We generally don't want to fade the input because the state + // of the network delay lines would then be invalid when the + // network is eventually made active again + + // copy audio from the actual audio device to the cloned audio devices + _update_audio_input( p, p->netA + i, j ); + break; + case flow::kMidiDevTypeId: + // update the cloned MIDI devices from the master device + _update_midi_input( p, p->netA + i, j ); + + } + } + // zero the audio device output buffers because we are about to sum into them for(unsigned j=0; jdeviceN; ++j) if( p->deviceA[j].typeId == flow::kAudioDevTypeId && cwIsFlag(p->deviceA[j].flags, flow::kOutFl ) ) diff --git a/cwFlowDecl.h b/cwFlowDecl.h index ee503fa..2c29322 100644 --- a/cwFlowDecl.h +++ b/cwFlowDecl.h @@ -5,6 +5,67 @@ namespace cw { namespace flow { + + enum + { + kAudioDevTypeId, + kMidiDevTypeId, + kSerialDevTypeId, + kSocketDevTypeId + }; + + enum + { + kInFl = 0x01, + kOutFl = 0x02 + }; + + + struct abuf_str; + + typedef struct audio_dev_cfg_str + { + struct abuf_str* abuf; // Buffer to receive incoming or send outgoing audio for this device + // The audio_in/audio_out proc's locate and use these buffers. + } audio_dev_cfg_t; + + struct external_device_str; + + typedef rc_t (*send_midi_triple_func_t)( struct external_device_str* dev, uint8_t ch, uint8_t status, uint8_t d0, uint8_t d1 ); + + typedef struct midi_dev_cfg_str + { + // msgArray[] contains the current msgs for all devices NOT just the device that this record is embedded in. + // We do this so that the order of messages as they arrived is maintained. Otherwise, to achieve this ordering, + // the messages for all devices would need to be collected and sorted by time. + const midi::ch_msg_t* msgArray; + unsigned msgCnt; + + unsigned maxMsgCnt; // max possible value of msgCnt + send_midi_triple_func_t sendTripleFunc; + } midi_dev_cfg_t; + + // Generate external device record + typedef struct external_device_str + { + void* reserved; + const char* devLabel; // IO framework device label + const char* portLabel; // IO framework MIDI port label (only used by MIDI devices) + unsigned typeId; // see ???DevTypeId above + unsigned flags; // see ???Fl above + + unsigned ioDevIdx; // IO framework device index + unsigned ioPortIdx; // IO framework MIDI port index (only used by MIDI devices) + + union + { + audio_dev_cfg_t a; // audio devices use this record + midi_dev_cfg_t m; // MIDI " " " " + } u; + + } external_device_t; + + enum { kPriPresetProbFl = 0x01, kSecPresetProbFl = 0x02, diff --git a/cwFlowNet.cpp b/cwFlowNet.cpp new file mode 100644 index 0000000..0d5fa1b --- /dev/null +++ b/cwFlowNet.cpp @@ -0,0 +1,3591 @@ +#include "cwCommon.h" +#include "cwLog.h" +#include "cwCommonImpl.h" +#include "cwTest.h" +#include "cwMem.h" +#include "cwText.h" +#include "cwNumericConvert.h" +#include "cwObject.h" + +#include "cwAudioFile.h" +#include "cwVectOps.h" +#include "cwMtx.h" +#include "cwDspTypes.h" // real_t, sample_t +#include "cwTime.h" +#include "cwMidiDecls.h" +#include "cwFlowDecl.h" +#include "cwFlowTypes.h" +#include "cwFlowNet.h" +#include "cwFlowProc.h" + +namespace cw +{ + namespace flow + { + + + void _preset_value_destroy( preset_value_t* v ) + { + mem::release(v); + } + + void _network_preset_destroy(network_preset_t& network_preset) + { + if( network_preset.tid == kPresetVListTId ) + { + preset_value_t* pv = network_preset.u.vlist.value_head; + while( pv != nullptr ) + { + preset_value_t* pv0 = pv->link; + _preset_value_destroy(pv); + pv = pv0; + } + + network_preset.u.vlist.value_head = nullptr; + network_preset.u.vlist.value_tail = nullptr; + } + + } + + void _network_preset_array_destroy( network_t& net ) + { + for(unsigned i=0; ilabel, cwStringNullGuard(var_label) ); + goto errLabel; + } + else + { + bool is_var_cfg_type_fl = (vd = var_desc_find( proc->class_desc, var_label ))!=nullptr && cwIsFlag(vd->type,kCfgTFl); + bool is_list_fl = value->is_list(); + bool is_list_of_list_fl = is_list_fl && value->child_count() > 0 && value->child_ele(0)->is_container(); + bool parse_list_fl = (is_list_fl && !is_var_cfg_type_fl) || (is_list_of_list_fl && is_var_cfg_type_fl); + + // if a list of values was given and the var type is not a 'cfg' type or if a list of lists was given + if( parse_list_fl ) + { + // then each value in the list is assigned to the associated channel + for(unsigned chIdx=0; chIdxchild_count(); ++chIdx) + if((rc = var_channelize( proc, var_label, kBaseSfxId, chIdx, value->child_ele(chIdx), kInvalidId, dummy )) != kOkRC ) + goto errLabel; + } + else // otherwise a single value was given + { + if((rc = var_channelize( proc, var_label, kBaseSfxId, kAnyChIdx, value, kInvalidId, dummy )) != kOkRC ) + goto errLabel; + } + } + + errLabel: + return rc; + } + + rc_t _preset_channelize_vars( proc_t* proc, const char* type_src_label, const char* preset_label, const object_t* preset_cfg ) + { + rc_t rc = kOkRC; + + //cwLogInfo("Channelizing '%s' preset %i vars for '%s'.",type_src_label, preset_cfg==nullptr ? 0 : preset_cfg->child_count(), proc->label ); + + // validate the syntax of the preset record + if( !preset_cfg->is_dict() ) + { + rc = cwLogError(kSyntaxErrorRC,"The preset record '%s' on %s '%s' is not a dictionary.", preset_label, type_src_label, proc->class_desc->label ); + goto errLabel; + } + + + // for each preset variable + for(unsigned i=0; ichild_count(); ++i) + { + const object_t* value = preset_cfg->child_ele(i)->pair_value(); + const char* var_label = preset_cfg->child_ele(i)->pair_label(); + + //cwLogInfo("variable:%s",var_label); + + if((rc = _var_channelize( proc, preset_label, type_src_label, var_label, value )) != kOkRC ) + goto errLabel; + + + } + + errLabel: + if( rc != kOkRC ) + rc = cwLogError(rc,"Apply %s preset failed on proc instance:%s class:%s preset:%s.", type_src_label, proc->label, proc->class_desc->label, preset_label ); + + return rc; + } + +#ifdef NOT_DEF + + template< typename T > + T _interp_dual_value( T v0, T v1, double coeff ) + { + T y; + if( v0 == v1 ) + y = v0; + else + y = (T)(v0 + (v1-v0)*coeff ); + + //printf("%f %f -> %f\n",(double)v0,(double)v1,(double)y); + return y; + } + + rc_t _set_var_from_dual_preset_scalar_scalar( proc_t* proc, const char* var_label, const object_t* scalar_0, const object_t* scalar_1, double coeff, unsigned chIdx ) + { + rc_t rc = kOkRC; + object_t interped_value; + variable_t* dummy = nullptr; + + // one of the input values must exist + if( scalar_0==nullptr && scalar_1==nullptr ) + { + rc = cwLogError(kInvalidArgRC,"The numeric types of both operands of a dual value are null."); + goto errLabel; + } + + // It's possible that one or the other input value does not exist + if( scalar_0 == nullptr ) + scalar_0 = scalar_1; + else + { + if( scalar_1 == nullptr ) + scalar_1 = scalar_0; + } + + // verify that the input values are the same type + if( scalar_0->type->id != scalar_1->type->id ) + { + rc = cwLogError(kInvalidArgRC,"The numeric types of both operands of a dual value preset must match. (%s != %s).",cwStringNullGuard(scalar_0->type->label),cwStringNullGuard(scalar_1->type->label)); + goto errLabel; + } + + printf("%s:%s :",proc->label,var_label); + + switch( scalar_0->type->id ) + { + case kInt32TId: + interped_value.set_value( _interp_dual_value(scalar_0->u.i32,scalar_1->u.i32,coeff) ); + break; + case kUInt32TId: + interped_value.set_value( _interp_dual_value(scalar_0->u.u32,scalar_1->u.u32,coeff) ); + break; + case kInt64TId: + assert(0); + //interped_value.set_value( _interp_dual_value(scalar_0->u.i64,scalar_1->u.i64,coeff) ); + break; + case kUInt64TId: + assert(0); + //interped_value.set_value( _interp_dual_value(scalar_0->u.u64,scalar_1->u.u64,coeff) ); + break; + case kFloatTId: + interped_value.set_value( _interp_dual_value(scalar_0->u.f,scalar_1->u.f,coeff) ); + break; + case kDoubleTId: + interped_value.set_value( _interp_dual_value(scalar_0->u.d,scalar_1->u.d,coeff) ); + break; + + default: + rc = cwLogError(kInvalidStateRC,"Preset dual values of type '%s' cannot be interpolated.",cwStringNullGuard(scalar_0->type->label)); + goto errLabel; + } + + + if((rc = var_channelize( proc, var_label, kBaseSfxId, chIdx, &interped_value, kInvalidId, dummy )) != kOkRC ) + { + rc = cwLogError(kInvalidArgRC,"Dual value preset application failed."); + goto errLabel; + } + + errLabel: + return rc; + } + + rc_t _set_var_from_dual_preset_list_list( proc_t* proc, const char* var_label, const object_t* list_0, const object_t* list_1, double coeff ) + { + rc_t rc = kOkRC; + + if( list_0->child_count() != list_1->child_count() ) + return cwLogError(kInvalidArgRC,"If two lists are to be applied as a dual preset they must be the same length."); + + for(unsigned chIdx=0; chIdxchild_count(); ++chIdx) + if((rc = _set_var_from_dual_preset_scalar_scalar(proc,var_label,list_0->child_ele(chIdx),list_1->child_ele(chIdx),coeff,chIdx)) != kOkRC ) + goto errLabel; + + errLabel: + return rc; + } + + rc_t _set_var_from_dual_preset_scalar_list( proc_t* proc, const char* var_label, const object_t* scalar, const object_t* list, double coeff ) + { + rc_t rc = kOkRC; + for(unsigned chIdx=0; chIdxchild_count(); ++chIdx) + if((rc = _set_var_from_dual_preset_scalar_scalar(proc,var_label,scalar,list->child_ele(chIdx),coeff,chIdx)) != kOkRC ) + goto errLabel; + + errLabel: + return rc; + } + + rc_t _set_var_from_dual_preset_list_scalar( proc_t* proc, const char* var_label, const object_t* list, const object_t* scalar, double coeff ) + { + rc_t rc = kOkRC; + for(unsigned chIdx=0; chIdxchild_count(); ++chIdx) + if((rc = _set_var_from_dual_preset_scalar_scalar(proc,var_label,list->child_ele(chIdx),scalar,coeff,chIdx)) != kOkRC ) + goto errLabel; + + errLabel: + return rc; + } + + rc_t _set_var_from_dual_preset_scalar_scalar( proc_t* proc, const char* var_label, const object_t* scalar_0, const object_t* scalar_1, double coeff ) + { + return _set_var_from_dual_preset_scalar_scalar(proc,var_label,scalar_0,scalar_1,coeff,kAnyChIdx); + } + + + rc_t _is_legal_dual_value( const object_t* value ) + { + rc_t rc = kOkRC; + + if( value->is_list() ) + { + if( value->child_count() == 0 ) + { + rc = cwLogError(kInvalidArgRC,"Empty lists values cannot be applied as part of a dual value preset."); + goto errLabel; + } + + } + else + { + switch( value->type->id ) + { + case kInt32TId: + case kUInt32TId: + case kInt64TId: + case kUInt64TId: + case kFloatTId: + case kDoubleTId: + break; + default: + rc = cwLogError(kInvalidArgRC,"Objects of type '%s' cannot be applied as part of a dual value preset.",cwStringNullGuard(value->type->label)); + } + } + + errLabel: + return rc; + + } + + rc_t _set_var_from_dual_preset( proc_t* proc, const char* var_label, const object_t* value_0, const object_t* value_1, double coeff ) + { + rc_t rc = kOkRC; + + // dual values must be either numeric scalars or lists + if((rc = _is_legal_dual_value(value_0)) != kOkRC || (rc = _is_legal_dual_value(value_1)) != kOkRC) + goto errLabel; + + + // if both values are lists then they must be the same length + if( value_0->is_list() && value_1->is_list() ) + { + rc = _set_var_from_dual_preset_list_list( proc, var_label, value_0, value_1, coeff ); + goto errLabel; + } + else + { + // if value_0 is a list and value_1 is a scalar + if( value_0->is_list() ) + { + rc = _set_var_from_dual_preset_list_scalar( proc, var_label, value_0, value_1, coeff ); + goto errLabel; + } + else + { + // if value_1 is a list and value_0 is a scalar + if( value_1->is_list() ) + { + rc = _set_var_from_dual_preset_scalar_list( proc, var_label, value_0, value_1, coeff ); + goto errLabel; + } + else // both values are scalars + { + rc = _set_var_from_dual_preset_scalar_scalar( proc, var_label, value_0, value_1, coeff ); + goto errLabel; + } + } + } + + errLabel: + return rc; + } + + + rc_t _multi_preset_channelize_vars( proc_t* proc, const char* type_src_label, const char** presetLabelA, const object_t** preset_cfgA, unsigned presetN, double coeff ) + { + rc_t rc = kOkRC; + + const char* preset_label_0 = ""; + const char* preset_label_1 = ""; + + //cwLogInfo("Channelizing '%s' preset %i vars for '%s'.",type_src_label, preset_cfg==nullptr ? 0 : preset_cfg->child_count(), proc->label ); + + if( presetN < 2 ) + { + rc = cwLogError(kInvalidArgRC,"There must be at least 2 presets selected to interpolate between preset variable dictionaries."); + goto errLabel; + } + + if( presetN > 2 ) + { + cwLogWarning("More than two presets dictionaries were specified for interpolation. Only the first two will be used."); + goto errLabel; + } + + preset_label_0 = presetLabelA[0]; + preset_label_1 = presetLabelA[1]; + + // validate each of the preset records is a dict + for(unsigned i=0; iis_dict() ) + { + rc = cwLogError(kSyntaxErrorRC,"The preset record '%s' on %s '%s' is not a dictionary.", presetLabelA[i], type_src_label, proc->class_desc->label ); + goto errLabel; + } + + + // for each preset variable in the first preset var dict + for(unsigned i=0; ichild_count(); ++i) + { + const char* var_label = preset_cfgA[0]->child_ele(i)->pair_label(); + const object_t* value_0 = preset_cfgA[0]->child_ele(i)->pair_value(); + + const object_t* value_1 = preset_cfgA[1]->find_child(var_label); + + if( value_0 == nullptr && value_1 == nullptr ) + { + rc = cwLogError(kSyntaxErrorRC,"Unexpected missig values on %s preset '%s' proc instance '%s' variable '%s'.", type_src_label, presetLabelA[0], proc->label, cwStringNullGuard(var_label) ); + goto errLabel; + } + + if( value_0 == nullptr ) + { + cwLogWarning("The preset variable '%s' was not found for the preset: '%s'. Falling back to single value assign.",cwStringNullGuard(var_label),cwStringNullGuard(presetLabelA[0])); + + rc = _var_channelize( proc, preset_label_1, "dual class", var_label, value_1 ); + goto errLabel; + } + + if( value_1 == nullptr ) + { + cwLogWarning("The preset variable '%s' was not found for the preset: '%s'. Falling back to single value assign.",cwStringNullGuard(var_label),cwStringNullGuard(presetLabelA[1])); + + rc = _var_channelize( proc, preset_label_0, "dual class", var_label, value_0 ); + goto errLabel; + } + + + if((rc = _set_var_from_dual_preset( proc, var_label, value_0, value_1, coeff )) != kOkRC ) + { + rc = cwLogError(rc,"Multi preset application failed on variable:%s.",cwStringNullGuard(var_label)); + goto errLabel; + } + } + + errLabel: + if( rc != kOkRC ) + rc = cwLogError(rc,"Apply %s multi-preset failed on proc instance:%s class:%s presetA:%s presetB:%s.", type_src_label, proc->label, proc->class_desc->label, preset_label_0, preset_label_1 ); + + return rc; + } + + + rc_t _class_multi_preset_channelize_vars(proc_t* proc, const char** class_preset_labelA, unsigned presetN, double coeff ) + { + rc_t rc = kOkRC; + const object_t* presetCfgA[ presetN ]; + const char* presetLabelA[ presetN ]; + unsigned presetCfgN = 0; + + for(unsigned i=0; iclass_desc, class_preset_labelA[i])) == nullptr ) + { + rc = cwLogError(kInvalidIdRC,"The preset '%s' could not be found for the proc instance '%s'.", class_preset_labelA[i], proc->label); + goto errLabel; + } + + if( pr->cfg == nullptr ) + { + rc = cwLogError(kInvalidIdRC,"The value of preset '%s' was empty in proc instance '%s'.", class_preset_labelA[i], proc->label); + goto errLabel; + } + + presetCfgA[ presetCfgN] = pr->cfg; + presetLabelA[presetCfgN] = class_preset_labelA[i]; + presetCfgN++; + } + } + + // dispatch based on the count of presets located + switch( presetCfgN ) + { + case 0: + rc = cwLogError(kInvalidArgRC,"No valid class preset records were found while attempting apply a multi-preset."); + break; + + case 1: + // only one valid preset was located - apply it directly + rc = _preset_channelize_vars( proc, "class", presetLabelA[0], presetCfgA[0]); + break; + + default: + // more than one preset was located - apply it's interpolated values + rc = _multi_preset_channelize_vars( proc, "class", presetLabelA, presetCfgA, presetCfgN, coeff); + } + + + errLabel: + return rc; + + } +#endif + + rc_t _class_preset_channelize_vars( proc_t* proc, const char* preset_label ) + { + rc_t rc = kOkRC; + const class_preset_t* pr; + + if( preset_label == nullptr ) + return kOkRC; + + // locate the requestd preset record + if((pr = class_preset_find(proc->class_desc, preset_label)) == nullptr ) + { + rc = cwLogError(kInvalidIdRC,"The preset '%s' could not be found for the proc instance '%s'.", preset_label, proc->label); + goto errLabel; + } + + rc = _preset_channelize_vars( proc, "class", preset_label, pr->cfg); + + errLabel: + return rc; + } + + + rc_t _class_apply_presets( proc_t* proc, const object_t* preset_labels ) + { + rc_t rc = kOkRC; + const char* s = nullptr; + + // if preset_labels is a string + if( preset_labels->is_string() && preset_labels->value(s)==kOkRC ) + return _class_preset_channelize_vars(proc,s); + + // if the preset_labels is not a list + if( !preset_labels->is_list() ) + rc = cwLogError(kSyntaxErrorRC,"The preset list on proc instance '%s' is neither a list nor a string.",proc->label); + else + { + // preset_labels is a list. + + // for each label listed in the preset label list + for(unsigned i=0; ichild_count(); ++i) + { + const object_t* label_obj = preset_labels->child_ele(i); + + // verify that the label is a strng + if( !label_obj->is_string() || label_obj->value(s) != kOkRC ) + { + rc = cwLogError(kSyntaxErrorRC,"The preset list does not contain string on proc instance '%s'.",proc->label); + goto errLabel; + } + + // apply a preset label + if((rc = _class_preset_channelize_vars( proc, s)) != kOkRC ) + goto errLabel; + } + } + + errLabel: + return rc; + } + + unsigned _select_ranked_ele_by_rank_prob( const preset_order_t* presetA, const bool* selV , unsigned presetN ) + { + + // get a count of the candidate presets + unsigned rankN = selV==nullptr ? presetN : std::count_if(selV,selV+presetN,[](const bool& x){ return x; }); + + if( rankN == 0 ) + { + cwLogWarning("All preset candidates have been eliminated."); + return kInvalidIdx; + } + + unsigned rankV[ rankN ]; + unsigned idxMapA[ rankN ]; + + // fill rankV[] with candidates 'order' value + for(unsigned i=0,j=0; i 1 ); + + unsigned threshV[ rankN ]; + unsigned uniqueRankV[ rankN ]; + unsigned uniqueRankN = 0; + unsigned sel_idx = rankN - 1; // + + // for each possible rank value + for(unsigned i=0; ichild_count(); ++i) + { + const object_t* proc_pair; + if((proc_pair = net_preset_pair->child_ele(i)) != nullptr && proc_pair->is_pair() && textIsEqual(proc_pair->pair_label(),proc_label) ) + { + + preset_val_ref = proc_pair->pair_value(); + + goto errLabel; + } + } + + rc = cwLogError(kInvalidArgRC,"The preset proc instance label '%s' was not found.",cwStringNullGuard(preset_label)); + + errLabel: + return rc; + } +#endif + + + //======================================================================================================= + // + // network creation + // + + typedef enum { + kLocalProcTypeId = 0x01, + kLocalVarTypeId = 0x02, + kRemoteProcTypeId = 0x04, + kRemoteVarTypeId = 0x08 + } io_ele_type_id_t; + + typedef struct io_ele_str + { + io_ele_type_id_t typeId; // See k???TypeId above + char* label; // label of in or src id + unsigned base_sfx_id; // Literal base_sfx_id or kInvalidId if the base_sfx_id was not given or 'is_iter_fl' is false + unsigned sfx_id; // 'sfx_id' is only used by _io_stmt_connect_vars() + unsigned sfx_id_count; // Literal sfx_id_count or kInvalidCnt if not given + unsigned is_iter_fl; // This id included an '_' (underscore) + } io_ele_t; + + + typedef struct io_stmt_str + { + io_ele_t in_proc_ele; // in-proc element + io_ele_t in_var_ele; // in-var element + io_ele_t src_proc_ele; // src-proc element + io_ele_t src_var_ele; // src-var element + + io_ele_t* local_proc_ele; + io_ele_t* local_var_ele; + + char* remote_net_label; + network_t* remote_net; + io_ele_t* remote_proc_ele; + io_ele_t* remote_var_ele; + + bool local_proc_iter_fl; // Is the local proc iterating (this is a poly iteration rather than a var iterator) + + const io_ele_t* iter_cnt_ctl_ele; // Pointer to the ele which is controlling the iteration count (or null if in-var is non-iterating) + unsigned iter_cnt; // Count of iterations or 0 if in-var is non-iterating. + + + // in_stmt only fields + var_desc_t* local_var_desc; // Pointer to the in-var var_desc. + bool in_create_fl; // True if the in_var needs to be created with an sfx_id, false create the var by the default process (w/o sfx_id) + + + } io_stmt_t; + + typedef struct proc_inst_parse_statestr + { + char* proc_label; // + unsigned proc_label_sfx_id; // + const char* proc_clas_label; // + const object_t* preset_labels; // + const object_t* arg_cfg; // + const object_t* log_labels; // + + const object_t* in_dict_cfg; // cfg. node to the in-list + io_stmt_t* iStmtA; + unsigned iStmtN; + + const object_t* out_dict_cfg; // cfg. node to the out-list + io_stmt_t* oStmtA; + unsigned oStmtN; + + } proc_inst_parse_state_t; + + bool _is_non_null_pair( const object_t* cfg ) + { return cfg != nullptr && cfg->is_pair() && cfg->pair_label()!=nullptr && cfg->pair_value()!=nullptr; } + + // Get the count of digits at the end of a string. + unsigned _digit_suffix_char_count( const char* s ) + { + unsigned digitN = 0; + unsigned sn = textLength(s); + if( sn==0 ) + return 0; + + const char* s0 = s + (textLength(s)-1); + + // go backward from the last char until the begin-of-string or a non-digit is found + for(; s0>=s; --s0) + { + if(!isdigit(*s0) ) + break; + ++digitN; + } + + return digitN; + } + + rc_t _io_stmt_parse_proc_var_string( char* str, const char*& in_proc_label, const char*& in_var_label ) + { + rc_t rc = kOkRC; + char* period = nullptr; + + if((period = firstMatchChar(str,'.')) == nullptr ) + { + in_proc_label = nullptr; + in_var_label = str; + } + else + { + *period = '\0'; + in_proc_label = str; + in_var_label = period + 1; + } + + return rc; + } + + rc_t _io_stmt_parse_net_proc_var_string( char* str, char*& src_net_label, const char*& src_proc_label, const char*& src_var_label ) + { + rc_t rc = kOkRC; + char* period0 = nullptr; + char* period1 = nullptr; + + // locate the separator period on the src proc/var id + if((period0 = firstMatchChar(str,'.')) == nullptr ) + { + cwLogError(kSyntaxErrorRC,"No period separator was found in the src net/proc/var for the src specifier:%s.",str); + goto errLabel; + } + + *period0 = 0; + + if((period1 = firstMatchChar(period0+1,'.')) != nullptr ) + { + *period1 = 0; + src_var_label = period1 + 1; // Set a pointer to the src var label + src_proc_label = period0 + 1; + src_net_label = mem::duplStr(str); + } + else + { + src_var_label = period0 + 1; + src_proc_label = str; + src_net_label = nullptr; + } + + if( textLength(src_var_label) == 0 ) + rc = cwLogError(kSyntaxErrorRC,"The 'src-var' label has length 0."); + + if( textLength(src_proc_label) == 0 ) + rc = cwLogError(kSyntaxErrorRC,"The 'src-proc' label has length 0."); + + + errLabel: + return rc; + } + + + rc_t _io_stmt_parse_ele( const char* id_str, io_ele_t& r, bool inProcFl=false ) + { + rc_t rc = kOkRC; + unsigned bufN; + + r.base_sfx_id = kInvalidId; + r.sfx_id_count = kInvalidCnt; + + if((bufN = textLength(id_str)) == 0 ) + { + if( !inProcFl ) + rc = cwLogError(kSyntaxErrorRC,"A blank id string was encountered."); + goto errLabel; + } + else + { + char* underscore = nullptr; + char* digit = nullptr; + int offs = inProcFl ? 1 : 0; + char buf[ bufN+(1+offs) ]; + + // in-proc's don't have a leading label - add one here to please the parser + if(inProcFl) + buf[0] = 'x'; + + // copy the id string into a non-const scratch buffer + textCopy(buf+offs,bufN+1,id_str); + + // locate the last underscore + if((underscore = lastMatchChar(buf,'_')) != nullptr ) + { + *underscore = 0; // terminate the string prior to the underscore + + for(digit = underscore + 1; *digit; digit++) + if( !isdigit(*digit) ) + break; + + // if the underscore was followed by a number + // or if the underscore was the last char + // in the string - then digit will point to + // the terminating zero - otherwise the + // underscore did not indicate an iterating id + if( *digit != 0 ) + { + *underscore = '_'; // replace the underscore - its part of the label + underscore = nullptr; + } + else + { + r.is_iter_fl = true; + + // if there is a number following the underscore then this is the secInt + if( textLength(underscore + 1) ) + { + // a literal iteration count was given - parse it into an integer + if((rc = string_to_number(underscore + 1,r.sfx_id_count)) != kOkRC ) + { + rc = cwLogError(rc,"Unable to parse the secondary integer in the id label '%s'.",cwStringNullGuard(id_str)); + goto errLabel; + } + } + } + } + + // verify that some content remains in the id string + if( textLength(buf) == 0 ) + { + rc = cwLogError(kSyntaxErrorRC,"Unable to parse the id string '%s'.",cwStringNullGuard(id_str)); + goto errLabel; + } + + // go backward from the last char until the begin-of-string or a non-digit is found + for(digit=buf + textLength(buf)-1; digit>buf; --digit) + if(!isdigit(*digit) ) + { + ++digit; // advance to the first digit in the number + break; + } + + // if a digit was found then this is the 'priInt' + if( digit>buf && textLength(digit) ) + { + assert( buf <= digit-1 && digit-1 <= buf + bufN ); + + // a literal base-sfx-id was given - parse it into an integer + if((rc = string_to_number(digit,r.base_sfx_id)) != kOkRC ) + { + rc = cwLogError(rc,"Unable to parse the primary integer in the id '%s'.",cwStringNullGuard(id_str)); + goto errLabel; + } + + *digit = 0; // zero terminate the label + + } + + // verify that some content remains in the id string + if( textLength(buf) == 0 ) + { + rc = cwLogError(kSyntaxErrorRC,"Unexpected invalid id '%s'.",cwStringNullGuard(id_str)); + goto errLabel; + + } + else + { + // store the label + if( !inProcFl ) + r.label = mem::duplStr(buf); + } + } + + + errLabel: + return rc; + } + + + // Recursively search the tree of networks rooted on 'net' for the + // network named 'net_proc_label'. + network_t* _io_stmt_find_labeled_network( network_t& net, const char* net_proc_label ) + { + network_t* labeled_net = nullptr; + + // for each proc instance in the network + for(unsigned i=0; iinternal_net != nullptr ) + { + // if the name of the network matches the key ... + if( textIsEqual(proc->label,net_proc_label) ) + labeled_net = proc->internal_net; // .. we are done + else + { + // ... otherwise recurse + labeled_net = _io_stmt_find_labeled_network(*proc->internal_net,net_proc_label); + } + } + + } + return labeled_net; + } + + + + // If the local-var is iterating then the count of iterations must be controlled by exactly one + // of the 3 parts of the io-stmt: local-var,remote_proc, or remote_var. This function determines + // which element is used to determine the iteration count. + rc_t _io_stmt_determine_iter_count_ctl_ele(network_t& net, + proc_t* proc, + const io_ele_t& localVar, + const io_ele_t& remoteProc, + const io_ele_t& remoteVar, + const char* localLabel, + const char* remoteLabel, + const io_ele_t*& result_ref ) + { + assert( localVar.is_iter_fl ); + rc_t rc = kOkRC; + + result_ref = nullptr; + + const io_ele_t* iter_cnt_ctl_ele = nullptr; + + // if the local-var gives a literal count - then it determines the count + if( localVar.sfx_id_count != kInvalidCnt ) + { + // if the local-var gives a literal count then the remote-proc cannot give one + if( remoteProc.sfx_id_count != kInvalidCnt ) + { + rc = cwLogError(kSyntaxErrorRC,"The %s-var provided a literal iteration count therefore the %s-proc cannot.",localLabel,remoteLabel); + goto errLabel; + } + + // if the local-var gives a literal count then the remote-var cannot give one + if( remoteVar.sfx_id_count != kInvalidCnt ) + { + rc = cwLogError(kSyntaxErrorRC,"The %s-var provided a literal iteration count therefore the %s-var cannot.",localLabel,remoteLabel); + goto errLabel; + } + + iter_cnt_ctl_ele = &localVar; + + } + else // the remote-proc or remote-var must control the iter count + { + // if the remote-proc gives a literal count - then it determines the count + if( remoteProc.sfx_id_count != kInvalidCnt ) + { + // then the remote-var cannot give a literal count + if( remoteVar.sfx_id_count != kInvalidCnt ) + { + rc = cwLogError(kSyntaxErrorRC,"The %s-proc provided a literal iteration count therefore the %s-var cannot.",remoteLabel,remoteLabel); + goto errLabel; + } + + iter_cnt_ctl_ele = &remoteProc; + + } + else + { + // if the remote-var gives a literal count - then it determines the count + if( remoteVar.sfx_id_count != kInvalidCnt ) + { + iter_cnt_ctl_ele = &remoteVar; + } + else // no literal count was given - we need to get the implied count + { + // if the remote-proc is iterating then it will provide the count + if( remoteProc.is_iter_fl ) + { + // the remote-var cannot be iterating if the remote-proc is iterating + if( remoteVar.is_iter_fl ) + { + rc = cwLogError(kSyntaxErrorRC,"The %s-proc is iterating therefore the %s-var cannot.",remoteLabel,remoteLabel); + goto errLabel; + } + + iter_cnt_ctl_ele = &remoteProc; + } + else // the remote-proc isn't iterating check the remote-var + { + if( remoteVar.is_iter_fl ) + { + iter_cnt_ctl_ele = &remoteVar; + } + else // no iteration count control was found + { + rc = cwLogError(kSyntaxErrorRC,"No iteration count control was specified."); + goto errLabel; + } + } + } + } + } + + errLabel: + + if( rc == kOkRC ) + result_ref = iter_cnt_ctl_ele; + + return rc; + } + + rc_t _io_stmt_calc_proc_ele_count(network_t& net, const io_ele_t& proc_ele, const char* in_or_src_label, unsigned& cnt_ref) + { + rc_t rc = kOkRC; + cnt_ref = 0; + + // if a literal proc sfx_id was given then use it otherwise use the default base-sfx-id (0) + unsigned sfx_id = proc_ele.base_sfx_id==kInvalidCnt ? kBaseSfxId : proc_ele.base_sfx_id; + unsigned n; + for(n=0; proc_find(net, proc_ele.label, sfx_id ) != nullptr; ++n ) + sfx_id += 1; + + if( n == 0 ) + { + rc = cwLogError(kSyntaxErrorRC,"The %s-proc '%s:%i' was not found.",in_or_src_label,cwStringNullGuard(proc_ele.label),sfx_id); + goto errLabel; + } + + cnt_ref = n; + errLabel: + return rc; + } + + rc_t _io_stmt_calc_var_ele_count(network_t& net, const io_ele_t& proc_ele, const io_ele_t& var_ele, const char* in_or_src_label, unsigned& cnt_ref) + { + rc_t rc = kOkRC; + proc_t* proc = nullptr; + unsigned proc_sfx_id = proc_ele.base_sfx_id==kInvalidCnt ? kBaseSfxId : proc_ele.base_sfx_id; + + cnt_ref = 0; + + // locate the proc which owns this var + if((proc = proc_find(net,proc_ele.label,proc_sfx_id)) == nullptr ) + { + cwLogError(kSyntaxErrorRC,"The %s-proc inst instance '%s:%i' could not be found.",in_or_src_label,cwStringNullGuard(proc_ele.label),proc_sfx_id); + goto errLabel; + } + else + { + // if a starting var sfx_id was given by the id then use it otherwise use the default base-sfx-id (0) + unsigned sfx_id = var_ele.base_sfx_id==kInvalidCnt ? kBaseSfxId : var_ele.base_sfx_id; + unsigned n; + for(n=0; var_exists(proc,var_ele.label, sfx_id, kAnyChIdx ); ++n ) + sfx_id += 1; + + + if( n == 0 ) + { + cwLogError(kSyntaxErrorRC,"The %s-var '%s:%i' was not found.",in_or_src_label,cwStringNullGuard(var_ele.label),sfx_id); + goto errLabel; + } + + cnt_ref = n; + + } + + errLabel: + return rc; + } + + rc_t _io_stmt_determine_iter_count( network_t& net,proc_t* proc, const char* local_label, const char* remote_label, io_stmt_t& io_stmt ) + { + rc_t rc = kOkRC; + + // it has already been determined that this an iterating io-stmt + // and a iteration count control element has been identified. + assert( io_stmt.local_var_ele->is_iter_fl ); + assert( io_stmt.iter_cnt_ctl_ele != nullptr ); + + switch( io_stmt.iter_cnt_ctl_ele->typeId ) + { + case kLocalVarTypeId: + + assert( io_stmt.iter_cnt_ctl_ele->sfx_id_count != kInvalidCnt ); + + if((io_stmt.iter_cnt = io_stmt.iter_cnt_ctl_ele->sfx_id_count) == 0 ) + rc = cwLogError(rc,"The literal %s-var iteration count on '%s:%i' must be greater than zero.", local_label, cwStringNullGuard(io_stmt.iter_cnt_ctl_ele->label),io_stmt.iter_cnt_ctl_ele->base_sfx_id); + break; + + case kRemoteProcTypeId: + if((rc = _io_stmt_calc_proc_ele_count( *io_stmt.remote_net, *io_stmt.remote_proc_ele, remote_label, io_stmt.iter_cnt )) != kOkRC ) + rc = cwLogError(rc,"Unable to determine the %s-stmt iteration count based on the iteration control %s-proc '%s'.",local_label,remote_label,cwStringNullGuard(io_stmt.remote_proc_ele->label)); + break; + + case kRemoteVarTypeId: + if((rc = _io_stmt_calc_var_ele_count( *io_stmt.remote_net, *io_stmt.remote_proc_ele, *io_stmt.remote_var_ele, remote_label, io_stmt.iter_cnt )) != kOkRC ) + rc = cwLogError(rc,"Unable to determine the %s-stmt iteration count based on the iteration control %s-var '%s'.",local_label,remote_label,cwStringNullGuard(io_stmt.remote_var_ele->label)); + + break; + + default: + rc = cwLogError(kInvalidStateRC,"An unknown %s-stmt element type was encountered.",local_label); + } + + return rc; + } + + void _io_stmt_destroy( io_stmt_t& s ) + { + if( s.local_proc_ele != nullptr ) + mem::release(s.local_proc_ele->label); + + if( s.local_var_ele != nullptr ) + mem::release(s.local_var_ele->label); + + mem::release(s.remote_net_label); + + if( s.remote_proc_ele ) + mem::release(s.remote_proc_ele->label); + + if( s.remote_var_ele ) + mem::release(s.remote_var_ele->label); + } + + void _io_stmt_array_destroy( io_stmt_t*& io_stmtA, unsigned io_stmtN ) + { + if( io_stmtA != nullptr ) + { + for(unsigned i=0; iis_dict() ) + { + cwLogError(kSyntaxErrorRC,"The '%s' dict in proc instance '%s' is not a valid dictionary.",io_label,proc->label); + goto errLabel; + } + + if( io_dict_cfg->child_count() == 0 ) + goto errLabel; + + stmtAllocN = io_dict_cfg->child_count(); + stmtA = mem::allocZ(stmtAllocN); + + // for each input variable in the 'in' set + for(unsigned i=0; ichild_ele(i); + const char* s = nullptr; + + // validate the stmt pair syntax + if( io_stmt_pair==nullptr + || !io_stmt_pair->is_pair() + || textLength(io_stmt_pair->pair_label())==0 + || io_stmt_pair->pair_value()==nullptr + || !io_stmt_pair->pair_value()->is_string() + || (io_stmt_pair->pair_value()->value(s)) != kOkRC + || textLength(s)==0 ) + { + rc = cwLogError(kSyntaxErrorRC,"A syntax error was encoutered while attempting to parse the %s-stmt on the proc %s:%i.",io_label,cwStringNullGuard(proc->label),proc->label_sfx_id); + goto errLabel; + } + + stmtN += 1; + } + + ioArray_Ref = stmtA; + ioArrayN_Ref = stmtN; + + errLabel: + if( rc != kOkRC ) + _io_stmt_array_destroy(stmtA,stmtN); + + return rc; + + } + + // Set 'in_stmt.src_net' based on 'in_stmt.src_net_label' + rc_t _io_stmt_locate_remote_net(network_t& net,proc_t* proc, io_stmt_t& io_stmt) + { + rc_t rc = kOkRC; + network_t* remote_net = nullptr; + + io_stmt.remote_net = nullptr; + + if( io_stmt.remote_net_label == nullptr ) + remote_net = &net; + else + { + if( textIsEqual(io_stmt.remote_net_label,"_") ) + remote_net = &proc->ctx->net; + else + { + if((remote_net = _io_stmt_find_labeled_network(proc->ctx->net,io_stmt.remote_net_label)) == nullptr ) + { + rc = cwLogError(kSyntaxErrorRC,"The source net '%s' was not found.",cwStringNullGuard(io_stmt.remote_net_label)); + goto errLabel; + } + } + } + errLabel: + io_stmt.remote_net = remote_net; + + if( io_stmt.remote_net == nullptr ) + rc = cwLogError(kSyntaxErrorRC,"No remote net was found."); + + return rc; + } + + + rc_t _io_stmt_create( network_t& net, + proc_t* proc, + io_stmt_t& io_stmt, + const char* local_proc_var_str, + const char* remote_net_proc_var_str, + const char* local_label, + const char* remote_label) + { + rc_t rc = kOkRC; + + unsigned local_char_cnt = textLength(local_proc_var_str); + unsigned remote_char_cnt = textLength(remote_net_proc_var_str); + unsigned str_char_cnt = std::max( local_char_cnt, remote_char_cnt ); + + //const char* remote_net_label = nullptr; + const char* remote_proc_label = nullptr; + const char* remote_var_label = nullptr; + const char* local_proc_label = nullptr; + const char* local_var_label = nullptr; + + char str[ str_char_cnt+1 ]; + + io_stmt.remote_proc_ele->typeId = kRemoteProcTypeId; + io_stmt.remote_var_ele->typeId = kRemoteVarTypeId; + io_stmt.local_proc_ele->typeId = kLocalProcTypeId; + io_stmt.local_var_ele->typeId = kLocalVarTypeId; + + // + // Parse the remote net/proc/var + // + + // put the remote net/proc/var string into a non-const scratch buffer + textCopy(str, remote_char_cnt+1, remote_net_proc_var_str ); + + // parse the src part into it's 3 parts + if((rc = _io_stmt_parse_net_proc_var_string(str, io_stmt.remote_net_label, remote_proc_label, remote_var_label )) != kOkRC ) + { + cwLogError(rc,"Unable to parse the '%s' part of an 'io-stmt'.",remote_label); + goto errLabel; + } + + // parse the rem-proc + if((rc = _io_stmt_parse_ele( remote_proc_label, *io_stmt.remote_proc_ele )) != kOkRC ) + { + rc = cwLogError(rc,"Unable to parse the %s-proc from '%s'.",remote_label,cwStringNullGuard(str)); + goto errLabel; + } + + // parse the remote-var + if((rc = _io_stmt_parse_ele( remote_var_label, *io_stmt.remote_var_ele )) != kOkRC ) + { + rc = cwLogError(rc,"Unable to parse the %s-var from '%s'.",remote_label,cwStringNullGuard(str)); + goto errLabel; + } + + + // + // Parse the local proc/var + // + + textCopy(str, local_char_cnt+1, local_proc_var_str ); + + // parse the 'local' part into it's 2 parts + if((rc = _io_stmt_parse_proc_var_string(str, local_proc_label, local_var_label )) != kOkRC ) + { + cwLogError(rc,"Unable to parse the '%s' part of an 'io-stmt'.",local_label); + goto errLabel; + } + + // parse the local-proc + if((rc = _io_stmt_parse_ele( local_proc_label, *io_stmt.local_proc_ele, true )) != kOkRC ) + { + rc = cwLogError(rc,"Unable to parse the %s-proc from '%s'.",local_label,cwStringNullGuard(str)); + goto errLabel; + } + + // parse the local-var + if((rc = _io_stmt_parse_ele( local_var_label, *io_stmt.local_var_ele )) != kOkRC ) + { + rc = cwLogError(rc,"Unable to parse the %s-var from '%s'.",local_label,cwStringNullGuard(str)); + goto errLabel; + } + + + // get the var class desc. for the local-var (only used by in-stmt) + if(( io_stmt.local_var_desc = var_desc_find(proc->class_desc,io_stmt.local_var_ele->label)) == nullptr ) + { + rc = cwLogError(kEleNotFoundRC,"Unable to locate the var class desc for the %s-var from '%s'.",local_label,cwStringNullGuard(io_stmt.local_var_ele->label)); + goto errLabel; + } + + // get the remote net + if((rc = _io_stmt_locate_remote_net(net,proc,io_stmt)) != kOkRC ) + { + rc = cwLogError(rc,"Unable to locate the %s-net '%s'.",remote_label, cwStringNullGuard(io_stmt.remote_net_label)); + goto errLabel; + } + + + // verify that both the local-proc and local-var are not iterating + if( io_stmt.local_proc_ele->is_iter_fl && io_stmt.local_var_ele->is_iter_fl ) + { + rc = cwLogError(kSyntaxErrorRC,"Both the '%s' proc and '%s' var cannot be iterating. See:'%s'",local_label,local_label,cwStringNullGuard(local_proc_var_str)); + goto errLabel; + } + + // if the in-var has an sfx_id, or is iterating, then the var needs to be created (the dflt creation process assumes an sfx-id of 0) + if( io_stmt.local_var_ele->base_sfx_id != kInvalidId || io_stmt.local_var_ele->is_iter_fl ) + { + io_stmt.in_create_fl = true; + if( io_stmt.local_var_ele->base_sfx_id == kInvalidId ) + io_stmt.local_var_ele->base_sfx_id = kBaseSfxId; + } + + // if the remote-proc is not iterating and the remote-proc was not given a literal sfx-id and the remote is on the same net as the proc ... + if( io_stmt.remote_proc_ele->is_iter_fl==false && io_stmt.remote_proc_ele->base_sfx_id==kInvalidId && io_stmt.remote_net==&net) + io_stmt.remote_proc_ele->base_sfx_id = proc->label_sfx_id; // ... then the remote proc takes this proc's sfx id + // (This results in poly proc's connecting to other poly procs with the same sfx-id by default). + + // if this is not an iterating in-stmt ... + if( !io_stmt.local_var_ele->is_iter_fl ) + { + io_stmt.iter_cnt = 1; // ... then it must be a simple 1:1 connection (Note if in-proc is iterating then it this must also be true) + } + else + { + // if the in-stmt is iterating then determine the in-stmt element which controls the iteration count + if((rc = _io_stmt_determine_iter_count_ctl_ele(net,proc, + *io_stmt.local_var_ele, + *io_stmt.remote_proc_ele, + *io_stmt.remote_var_ele, + local_label,remote_label, + io_stmt.iter_cnt_ctl_ele)) != kOkRC ) + { + rc = cwLogError(rc,"Unable to determine the iter count control ele."); + goto errLabel; + } + + // if the local-stmt is iterating then determine the iteration count + if((rc = _io_stmt_determine_iter_count(net,proc,local_label,remote_label,io_stmt)) != kOkRC ) + { + cwLogError(rc,"Unable to determine the %s-stmt iteration count.",local_label); + goto errLabel; + } + } + + + errLabel: + if( rc != kOkRC ) + _io_stmt_destroy(io_stmt); + + return rc; + + } + + rc_t _io_stmt_connect_vars(network_t& net, + proc_t* proc, + const char* local_label, + const char* remote_label, + const io_stmt_t* ioStmtA, + unsigned ioStmtN) + { + rc_t rc = kOkRC; + + // for each io-stmt + for(unsigned i=0; ilabel; + const char* local_var_label = io_stmt.local_var_ele->label; + const char* remote_proc_label = io_stmt.remote_proc_ele->label; + const char* remote_var_label = io_stmt.remote_var_ele->label; + + unsigned local_var_sfx_id = kInvalidId; + unsigned remote_proc_sfx_id = kInvalidId; + unsigned remote_var_sfx_id = kInvalidId; + + // if a literal in-var sfx id was not given ... + if( io_stmt.local_var_ele->base_sfx_id == kInvalidId ) + local_var_sfx_id = kBaseSfxId; // ... then use the default sfx-id + else + local_var_sfx_id = io_stmt.local_var_ele->base_sfx_id; + + // if a literal src-proc sfx id was not given ... + if( io_stmt.remote_proc_ele->base_sfx_id == kInvalidId ) + remote_proc_sfx_id = kBaseSfxId; // ... then use the sfx_id of the in-var proc + else + remote_proc_sfx_id = io_stmt.remote_proc_ele->base_sfx_id; // ... otherwise use the given literal + + // if a literal src-var sfx id was not given ... + if( io_stmt.remote_var_ele->base_sfx_id == kInvalidId ) + remote_var_sfx_id = kBaseSfxId; // ... then use the base-sfx-id + else + remote_var_sfx_id = io_stmt.remote_var_ele->base_sfx_id; // ... otherwise use the given literal + + // When the in-proc is iterating then we incr by the in-proc sfx-id (in this case j will never exceed 0) + // otherwise increment by j - the current iteration count + unsigned iter_incr = io_stmt.local_proc_ele->is_iter_fl ? proc->label_sfx_id : j; + + // both in-var and in-proc cannot be iterating + assert( !(io_stmt.local_var_ele->is_iter_fl && io_stmt.local_proc_ele->is_iter_fl) ); + + // if the in-var is iterating then incr. the in-var sfx-id + if( io_stmt.local_var_ele->is_iter_fl ) + local_var_sfx_id += iter_incr; + + // if this is an iterating src-proc then iter the src-proc-sfx-id + if( io_stmt.remote_proc_ele->is_iter_fl ) + remote_proc_sfx_id += iter_incr; + + // if this is an iterating src-var then iter the src-var-sfx-id + if( io_stmt.remote_var_ele->is_iter_fl ) + remote_var_sfx_id += iter_incr; + + // locate local var + if((rc = var_find( proc, local_var_label, local_var_sfx_id, kAnyChIdx, local_var )) != kOkRC ) + { + rc = cwLogError(rc,"The %s-var '%s:%i' was not found.", local_label, io_stmt.local_var_ele->label, io_stmt.local_var_ele->base_sfx_id + j); + goto errLabel; + } + + // locate remote proc instance + if((remote_proc = proc_find(*remote_net, remote_proc_label, remote_proc_sfx_id )) == nullptr ) + { + rc = cwLogError(kSyntaxErrorRC,"The %s-proc '%s:%i' was not found.", remote_label, io_stmt.remote_proc_ele->label, remote_proc_sfx_id ); + goto errLabel; + } + + // locate remote variable + if((rc = var_find( remote_proc, remote_var_label, remote_var_sfx_id, kAnyChIdx, remote_var)) != kOkRC ) + { + rc = cwLogError(rc,"The %s-var '%s:i' was not found.", remote_label, io_stmt.remote_var_ele->label, remote_var_sfx_id); + goto errLabel; + } + + // verify that the remote_value type is included in the local_value type flags + if( cwIsNotFlag(local_var->varDesc->type, remote_var->varDesc->type) ) + { + rc = cwLogError(kSyntaxErrorRC,"The type flags don't match on %s:%s:%i %s:%s:%i.%s:%i .", local_label,local_var_label, local_var_sfx_id, remote_label, remote_proc_label, remote_proc_sfx_id, remote_var_label, remote_var_sfx_id); + goto errLabel; + } + + // verify that the source exists + if( remote_var->value == nullptr ) + { + rc = cwLogError(kSyntaxErrorRC,"The %s value is null on the connection %s::%s:%i %s:%s:%i.%s:%i .", remote_label, local_label, local_var_label, local_var_sfx_id, remote_label, remote_proc_label, remote_proc_sfx_id, remote_var_label, remote_var_sfx_id); + goto errLabel; + } + + // if this is an 'in-stmt' ... + if( io_stmt.local_proc_ele == &io_stmt.in_proc_ele ) + var_connect( remote_var, local_var ); + else + { + // Disconnect any source that was previously connected to the 'in' var + // (we do this for feedback connections (out-stmts), but not for in-stmts) + var_disconnect( remote_var ); + var_connect( local_var, remote_var ); // ... otherwise it is an out-stmt + } + } + } + + errLabel: + if( rc != kOkRC ) + rc = cwLogError(rc,"Connection failed on proc '%s:%i'.",proc->label,proc->label_sfx_id); + return rc; + } + + + // Find the proxy var associated with the proxied var 'procLabel:varLabel' + const variable_t* _in_stmt_find_proxy_var( const char* procLabel, const char* varLabel, const variable_t* proxyVarL ) + { + for(const variable_t* proxyVar=proxyVarL; proxyVar!=nullptr; proxyVar=proxyVar->var_link) + if( textIsEqual(proxyVar->varDesc->proxyProcLabel,procLabel) && textIsEqual(proxyVar->varDesc->proxyVarLabel,varLabel) ) + return proxyVar; + return nullptr; + } + + rc_t _in_stmt_parse_in_list( network_t& net, proc_t* proc, variable_t* proxyVarL, proc_inst_parse_state_t& pstate ) + { + rc_t rc = kOkRC; + const char* local_label = "in"; + const char* remote_label = "src"; + + if((rc = _io_stmt_array_parse( net, proc, "in", pstate.in_dict_cfg, pstate.iStmtA, pstate.iStmtN)) != kOkRC ) + goto errLabel; + + + for(unsigned i=0; ichild_ele(i)->pair_label(); + + in_stmt->local_proc_ele = &in_stmt->in_proc_ele; + in_stmt->local_var_ele = &in_stmt->in_var_ele; + in_stmt->remote_proc_ele = &in_stmt->src_proc_ele; + in_stmt->remote_var_ele = &in_stmt->src_var_ele; + + + // The validity of all the data elements in this statement was confirmed previously in _io_stmt_array_parse() + pstate.in_dict_cfg->child_ele(i)->pair_value()->value(src_net_proc_var_str); + + assert( src_net_proc_var_str != nullptr ); + + // create the io_stmt record + if((rc = _io_stmt_create( net, proc, *in_stmt, in_proc_var_str, src_net_proc_var_str, local_label, remote_label )) != kOkRC ) + { + rc = cwLogError(rc,"in-stmt create failed on '%s':%s on proc %s:%i", in_proc_var_str, src_net_proc_var_str, proc->label, proc->label_sfx_id ); + goto errLabel; + } + + // if the in-var has an sfx_id, or is iterating, then the var needs to be created (the dflt creation process assumes an sfx-id of 0) + if( in_stmt->in_var_ele.base_sfx_id != kInvalidId || in_stmt->in_var_ele.is_iter_fl ) + { + in_stmt->in_create_fl = true; + if( in_stmt->in_var_ele.base_sfx_id == kInvalidId ) + in_stmt->in_var_ele.base_sfx_id = kBaseSfxId; + } + + // create the var + if( in_stmt->in_create_fl ) + { + const variable_t* proxy_var; + + // a variable cannot be in the 'in' list if it is a proxied variable - because it will + // be connected to a proxy var. + if((proxy_var = _in_stmt_find_proxy_var(proc->label, in_stmt->in_var_ele.label, proxyVarL)) != nullptr ) + { + rc = cwLogError(kSyntaxErrorRC,"The variable:'%s' cannot be used as the in-var of an 'in' statement if it is a subnet variable: '%s'.",cwStringNullGuard(in_stmt->in_var_ele.label),cwStringNullGuard(proxy_var->label)); + goto errLabel; + } + + + for(unsigned i=0; iiter_cnt; ++i) + { + variable_t* dum = nullptr; + + if((rc = var_create( proc, + in_stmt->local_var_desc->label, + in_stmt->in_var_ele.base_sfx_id + i, + kInvalidId, + kAnyChIdx, + in_stmt->local_var_desc->val_cfg, + kInvalidTFl, + dum )) != kOkRC ) + { + rc = cwLogError(rc,"in-stmt var create failed on '%s:%s'.",cwStringNullGuard(in_proc_var_str),cwStringNullGuard(src_net_proc_var_str)); + goto errLabel; + } + } + } + } + + errLabel: + return rc; + } + + rc_t _out_stmt_processing(network_t& net, proc_t* proc, proc_inst_parse_state_t& pstate) + { + rc_t rc = kOkRC; + + const char* local_label = "src"; + const char* remote_label = "in"; + + // parse the out-stmt list + if((rc = _io_stmt_array_parse( net, proc, "out", pstate.out_dict_cfg, pstate.oStmtA, pstate.oStmtN)) != kOkRC ) + goto errLabel; + + // for each out-stmt + for(unsigned i=0; ichild_ele(i)->pair_label(); + + out_stmt->local_proc_ele = &out_stmt->src_proc_ele; + out_stmt->local_var_ele = &out_stmt->src_var_ele; + out_stmt->remote_proc_ele = &out_stmt->in_proc_ele; + out_stmt->remote_var_ele = &out_stmt->in_var_ele; + + // The validity of all the data elements in this statement was confirmed previously in _io_stmt_array_parse() + pstate.out_dict_cfg->child_ele(i)->pair_value()->value(in_net_proc_var_str); + + assert( in_net_proc_var_str != nullptr ); + + // create the io_stmt record + if((rc = _io_stmt_create( net, proc, *out_stmt, src_proc_var_str, in_net_proc_var_str, local_label, remote_label )) != kOkRC ) + { + rc = cwLogError(rc,"out-stmt create failed on '%s':%s on proc %s:%i", src_proc_var_str, in_net_proc_var_str, proc->label, proc->label_sfx_id ); + goto errLabel; + } + } + + // create the connections + if((rc = _io_stmt_connect_vars(net, proc, local_label, remote_label, pstate.oStmtA, pstate.oStmtN)) != kOkRC ) + goto errLabel; + + errLabel: + return rc; + + } + + + // This function is used to create the variables on subnet procs + // which are represented by interface variables on the subnet proxy (wrapper) proc. + // 'proc' is a proc on the subnet's internal proc list + // 'wrap_varL' is a list of all the variables on the wrapper proc. + // These wrapper variables mirror variables on the internal subnet proc's. + // This function finds the variables in wrap_varL that mirror + // variables in 'proc' and instantiates them. + rc_t _subnet_create_proxied_vars( proc_t* proc, variable_t* wrap_varL ) + { + rc_t rc = kOkRC; + + // for each proxy var + for(variable_t* wrap_var=wrap_varL; wrap_var!=nullptr; wrap_var=wrap_var->var_link ) + { + // if this proxy var is on this internal proc (proc->label) + if( textIsEqual(wrap_var->varDesc->proxyProcLabel,proc->label) ) + { + + variable_t* var; + + // create the proxied var + if((rc = var_create( proc, wrap_var->varDesc->proxyVarLabel, wrap_var->label_sfx_id, kInvalidId, wrap_var->chIdx, nullptr, kInvalidTFl, var )) != kOkRC ) + { + rc = cwLogError(rc,"Subnet variable creation failed for %s:%s on wrapper variable:%s:%s.",cwStringNullGuard(wrap_var->varDesc->proxyProcLabel),cwStringNullGuard(wrap_var->varDesc->proxyVarLabel),cwStringNullGuard(wrap_var->proc->label),cwStringNullGuard(wrap_var->label)); + goto errLabel; + } + + //printf("Proxy matched: %s %s %s : flags:%i.\n",proc->label, wrap_var->varDesc->proxyVarLabel, wrap_var->label,wrap_var->varDesc->flags ); + + var->flags |= kProxiedVarFl; + + if( cwIsFlag(wrap_var->varDesc->flags,kSubnetOutVarDescFl) ) + var->flags |= kProxiedOutVarFl; + } + } + + errLabel: + return rc; + } + + variable_t* _subnet_find_proxy_var( variable_t* wrap_varL, variable_t* var ) + { + for(variable_t* wrap_var=wrap_varL; wrap_var!=nullptr; wrap_var=wrap_var->var_link) + if( textIsEqual(wrap_var->varDesc->proxyProcLabel,var->proc->label) && textIsEqual(wrap_var->varDesc->proxyVarLabel,var->label) && (wrap_var->label_sfx_id==var->label_sfx_id) ) + return wrap_var; + + return nullptr; + } + + rc_t _subnet_connect_proxy_vars( proc_t* proc, variable_t* wrap_varL ) + { + rc_t rc = kOkRC; + for(variable_t* var=proc->varL; var!=nullptr; var=var->var_link) + { + if( cwIsFlag(var->flags,kProxiedVarFl) ) + { + variable_t* wrap_var; + if((wrap_var = _subnet_find_proxy_var(wrap_varL,var)) == nullptr ) + { + rc = cwLogError(kEleNotFoundRC,"The wrapped variable '%s:%i' not found on '%s:%i'.",var->label,var->label_sfx_id,proc->label,proc->label_sfx_id); + goto errLabel; + } + + if( cwIsFlag(var->flags,kProxiedOutVarFl) ) + { + //printf("Proxy connection: %i %s:%i-%s:%i -> %s:%i-%s:%i\n",var->flags, + // var->proc->label,var->proc->label_sfx_id,var->label,var->label_sfx_id, + // wrap_var->proc->label,wrap_var->proc->label_sfx_id,wrap_var->label,wrap_var->label_sfx_id ); + + var_connect(var,wrap_var); + } + else + { + //printf("Proxy connection: %i %s:%i-%s:%i -> %s:%i-%s:%i\n",var->flags, + // wrap_var->proc->label,wrap_var->proc->label_sfx_id,wrap_var->label,wrap_var->label_sfx_id, + // var->proc->label,var->proc->label_sfx_id,var->label,var->label_sfx_id ); + + var_connect(wrap_var,var); + } + } + } + + errLabel: + return rc; + } + + // Check if the var named 'label' already exists in 'proc->varL'. + bool _is_var_proc_already_created( proc_t* proc, const char* var_label, const proc_inst_parse_state_t& pstate ) + { + for(unsigned i=0; ivarL; var!=nullptr; var=var->var_link) + if( textIsEqual(var->label,var_label) ) + return true; + + return false; + } + + rc_t _var_map_id_to_index( proc_t* proc, unsigned vid, unsigned chIdx, unsigned& idxRef ); + + rc_t _proc_create_var_map( proc_t* proc ) + { + rc_t rc = kOkRC; + unsigned max_vid = kInvalidId; + unsigned max_chIdx = 0; + variable_t* var = proc->varL; + //variable_t* v0 = nullptr; + + // determine the max variable vid and max channel index value among all variables + for(; var!=nullptr; var = var->var_link ) + { + if( var->vid != kInvalidId ) + { + if( max_vid == kInvalidId || var->vid > max_vid ) + max_vid = var->vid; + + if( var->chIdx != kAnyChIdx && (var->chIdx+1) > max_chIdx ) + max_chIdx = (var->chIdx + 1); + + } + } + + // If there are any variables + if( max_vid != kInvalidId ) + { + // create the variable map array + proc->varMapChN = max_chIdx + 1; + proc->varMapIdN = max_vid + 1; + proc->varMapN = proc->varMapIdN * proc->varMapChN; + proc->varMapA = mem::allocZ( proc->varMapN ); + + // assign each variable to a location in the map + for(variable_t* var=proc->varL; var!=nullptr; var=var->var_link) + if( var->vid != kInvalidId ) + { + unsigned idx = kInvalidIdx; + + if((rc = _var_map_id_to_index( proc, var->vid, var->chIdx, idx )) != kOkRC ) + goto errLabel; + + + // verify that there are not multiple variables per map position + if( proc->varMapA[ idx ] != nullptr ) + { + variable_t* v0 = proc->varMapA[idx]; + rc = cwLogError(kInvalidStateRC,"The variable '%s' id:%i ch:%i and '%s' id:%i ch:%i share the same variable map position on proc instance: %s. This is usually cased by duplicate variable id's.", + v0->label,v0->vid,v0->chIdx, var->label,var->vid,var->chIdx,proc->label); + + goto errLabel; + } + + // assign this variable to a map position + proc->varMapA[ idx ] = var; + + if( var->chIdx != kAnyChIdx && var->value == nullptr ) + { + rc = cwLogError(kInvalidStateRC,"The value of the variable '%s' ch:%i on proc instance:'%s' has not been set.",var->label,var->chIdx,proc->label); + goto errLabel; + } + + } + + } + + errLabel: + return rc; + + } + + /* + void _complete_input_connections( proc_t* proc ) + { + for(variable_t* var=proc->varL; var!=nullptr; var=var->var_link) + if(var->chIdx == kAnyChIdx && is_connected_to_source_proc(var) ) + { + + variable_t* base_src_var = var->src_var; + + // since 'var' is on the 'any' channel the 'src' var must also be on the 'any' channel + assert( base_src_var->chIdx == kAnyChIdx ); + + //printf("%s %s\n",proc->label,var->label); + + // for each var channel in the input var + for(variable_t* in_var = var->ch_link; in_var != nullptr; in_var=in_var->ch_link) + { + // locate the matching channel on the 'src' var + variable_t* svar = base_src_var; + for(; svar!=nullptr; svar=svar->ch_link) + if( svar->chIdx == in_var->chIdx ) + break; + + // connect the src->input var + _connect_vars( svar==nullptr ? base_src_var : svar, in_var); + } + } + } + */ + + + + rc_t _proc_set_log_flags(proc_t* proc, const object_t* log_labels) + { + rc_t rc = kOkRC; + + if( log_labels == nullptr ) + return rc; + + if( !log_labels->is_dict() ) + { + rc = cwLogError(kSyntaxErrorRC,"The log spec on '%s:%i' is not a dictionary.",cwStringNullGuard(proc->label),proc->label_sfx_id); + goto errLabel; + } + + for(unsigned i=0; ichild_count(); ++i) + { + const object_t* pair; + unsigned sfx_id; + + if((pair = log_labels->child_ele(i)) == nullptr || pair->pair_label()==nullptr || pair->pair_value()==nullptr || (rc=pair->pair_value()->value(sfx_id))!=kOkRC ) + { + rc = cwLogError(kSyntaxErrorRC,"Syntax error on log var identifier."); + goto errLabel; + } + + if((rc = var_set_flags( proc, kAnyChIdx, pair->pair_label(), sfx_id, kLogVarFl )) != kOkRC ) + { + rc = cwLogError(rc,"Unable to set var flags on '%s:%i' var:'%s:%i'.",cwStringNullGuard(proc->label),proc->label_sfx_id,pair->pair_label(),sfx_id); + goto errLabel; + } + } + + errLabel: + return rc; + } + + rc_t _proc_call_value_func_on_all_variables( proc_t* proc ) + { + rc_t rc = kOkRC; + rc_t rc1 = kOkRC; + + for(unsigned i=0; ivarMapN; ++i) + if( proc->varMapA[i] != nullptr && proc->varMapA[i]->vid != kInvalidId ) + { + variable_t* var = proc->varMapA[i]; + + if((rc = var_call_custom_value_func( var )) != kOkRC ) + rc1 = cwLogError(rc,"The proc inst instance '%s:%i' reported an invalid valid on variable:%s chIdx:%i.", var->proc->label, var->proc->label_sfx_id, var->label, var->chIdx ); + } + + return rc1; + } + + // Set pstate.proc_label and pstate.label_sfx_id + rc_t _proc_parse_inst_label( const char* proc_label_str, unsigned system_sfx_id, proc_inst_parse_state_t& pstate ) + { + rc_t rc = kOkRC; + unsigned digitCharN = 0; + unsigned sfx_id = kInvalidId; + unsigned sN = textLength(proc_label_str); + char s[sN+1]; + + if( sN == 0 ) + { + rc = cwLogError(kSyntaxErrorRC,"A blank proc-instance label was encountered."); + goto errLabel; + } + + textCopy(s,sN+1,proc_label_str,sN); + + // if this label has no digit suffix + if((digitCharN = _digit_suffix_char_count( s )) > 0) + { + if( digitCharN == sN ) + { + rc = cwLogError(kSyntaxErrorRC,"A proc-instance label ('%s') was encountered that appears to be a number rather than identifier.",s); + goto errLabel; + } + else + { + if( string_to_number(s + sN-digitCharN,sfx_id) != kOkRC ) + { + rc = cwLogError(kOpFailRC,"A proc-instance numeric suffix (%s) could not be converted into an integer.",s); + goto errLabel; + } + + s[sN-digitCharN] = '\0'; + } + } + + // if the parsed sfx-id did not exist + if( sfx_id == kInvalidId ) + { + sfx_id = system_sfx_id==kInvalidId ? kBaseSfxId : system_sfx_id; + } + + // be sure the parsed sfx-id does not conflict with the system provided sfx-id + if( system_sfx_id != kInvalidId && sfx_id != system_sfx_id ) + { + rc = cwLogError(kInvalidStateRC,"The proc instance '%s' numeric suffix id (%i) conflicts with the system provided sfx id (%i).",cwStringNullGuard(proc_label_str),pstate.proc_label_sfx_id,system_sfx_id); + goto errLabel; + } + + pstate.proc_label = mem::duplStr(s); + pstate.proc_label_sfx_id = sfx_id; + + errLabel: + return rc; + + } + + rc_t _proc_parse_cfg( network_t& net, const object_t* proc_inst_cfg, unsigned system_sfx_id, proc_inst_parse_state_t& pstate ) + { + rc_t rc = kOkRC; + const object_t* arg_dict = nullptr; + //unsigned sfx_id; + + // validate the syntax of the proc_inst_cfg pair + if( !_is_non_null_pair(proc_inst_cfg)) + { + rc = cwLogError(kSyntaxErrorRC,"The proc instance cfg. is not a valid pair."); + goto errLabel; + } + + pstate.proc_label_sfx_id = kInvalidId; + + // extract the proc instance label and (sfx-id suffix) + if((rc = _proc_parse_inst_label( proc_inst_cfg->pair_label(), system_sfx_id, pstate )) != kOkRC ) + { + rc = cwLogError(kSyntaxErrorRC,"Parsing failed on the label and sfx-id for '%s'.",cwStringNullGuard(proc_inst_cfg->pair_label())); + goto errLabel; + } + + // verify that the proc instance label is unique + if( proc_find(net,pstate.proc_label,pstate.proc_label_sfx_id) != nullptr ) + { + rc = cwLogError(kSyntaxErrorRC,"The proc instance label '%s:%i' has already been used.",pstate.proc_label,pstate.proc_label_sfx_id); + goto errLabel; + } + + // get the proc instance class label + if((rc = proc_inst_cfg->pair_value()->getv("class",pstate.proc_clas_label)) != kOkRC ) + { + rc = cwLogError(kSyntaxErrorRC,"The proc instance cfg. %s:%i is missing: 'type'.",pstate.proc_label,pstate.proc_label_sfx_id); + goto errLabel; + } + + // parse the optional args + if((rc = proc_inst_cfg->pair_value()->getv_opt("args", arg_dict, + "in", pstate.in_dict_cfg, + "out", pstate.out_dict_cfg, + "preset", pstate.preset_labels, + "log", pstate.log_labels )) != kOkRC ) + { + rc = cwLogError(kSyntaxErrorRC,"The proc instance cfg. '%s:%i' missing: 'type'.",pstate.proc_label,pstate.proc_label_sfx_id); + goto errLabel; + } + + // if an argument dict was given in the proc instance cfg + if( arg_dict != nullptr ) + { + //bool rptErrFl = true; + + // verify the arg. dict is actually a dict. + if( !arg_dict->is_dict() ) + { + cwLogError(kSyntaxErrorRC,"The proc instance argument dictionary on proc instance '%s:%i' is not a dictionary.",pstate.proc_label,pstate.proc_label_sfx_id); + goto errLabel; + } + + + pstate.arg_cfg = arg_dict; + + } + + errLabel: + if( rc != kOkRC ) + rc = cwLogError(kSyntaxErrorRC,"Configuration parsing failed on proc instance: '%s:%i'.", cwStringNullGuard(pstate.proc_label),pstate.proc_label_sfx_id); + + return rc; + } + + + + void _pstate_destroy( proc_inst_parse_state_t pstate ) + { + _io_stmt_array_destroy(pstate.iStmtA,pstate.iStmtN); + _io_stmt_array_destroy(pstate.oStmtA,pstate.oStmtN); + + mem::release(pstate.proc_label); + } + + // Count of proc instances which exist in the network with a given class. + unsigned _poly_copy_count( const network_t& net, const char* proc_clas_label ) + { + unsigned n = 0; + + for(unsigned i=0; iclass_desc->label,proc_clas_label) ) + ++n; + return n; + } + + rc_t _proc_create( flow_t* p, + const object_t* proc_inst_cfg, + unsigned sfx_id, + network_t& net, + variable_t* proxyVarL, + proc_t*& proc_ref ) + { + rc_t rc = kOkRC; + proc_inst_parse_state_t pstate = {}; + proc_t* proc = nullptr; + class_desc_t* class_desc = nullptr; + + proc_ref = nullptr; + + // parse the proc instance configuration + if((rc = _proc_parse_cfg( net, proc_inst_cfg, sfx_id, pstate )) != kOkRC ) + goto errLabel; + + // locate the proc class desc + if(( class_desc = class_desc_find(p,pstate.proc_clas_label)) == nullptr ) + { + rc = cwLogError(kSyntaxErrorRC,"The flow class '%s' was not found.",cwStringNullGuard(pstate.proc_clas_label)); + goto errLabel; + } + + // if the poly proc instance count has been exceeded for this proc inst class ... + if(class_desc->polyLimitN > 0 && _poly_copy_count(net,pstate.proc_clas_label) >= class_desc->polyLimitN ) + { + // ... then silently skip this instantiation + cwLogDebug("The poly class copy count has been exceeded for '%s' - skipping instantiation of sfx_id:%i.",pstate.proc_label,pstate.proc_label_sfx_id); + goto errLabel; + } + + // instantiate the proc instance + proc = mem::allocZ(); + + proc->ctx = p; + proc->label = mem::duplStr(pstate.proc_label); + proc->label_sfx_id = pstate.proc_label_sfx_id; + proc->proc_cfg = proc_inst_cfg->pair_value(); + proc->arg_cfg = pstate.arg_cfg; + proc->class_desc = class_desc; + proc->net = &net; + + // parse the in-list ,fill in pstate.in_array, and create var instances for var's referenced by in-list + if((rc = _in_stmt_parse_in_list( net, proc, proxyVarL, pstate )) != kOkRC ) + { + rc = cwLogError(rc,"in-list parse failed on proc inst instance '%s:%i'.",cwStringNullGuard(proc->label),pstate.proc_label_sfx_id); + goto errLabel; + } + + // if this is a subnet wrapper proc then create the vars that are connected to the proxy vars + if((rc = _subnet_create_proxied_vars( proc, proxyVarL )) != kOkRC ) + { + rc = cwLogError(rc,"Proxy vars create failed on proc inst instance '%s:%i'.",cwStringNullGuard(proc->label),pstate.proc_label_sfx_id); + goto errLabel; + } + + // Instantiate all the variables in the class description - that were not already created in _in_stmt_parse_in_list() + // or _subnet_create_proxied_vars(). + for(var_desc_t* vd=class_desc->varDescL; vd!=nullptr; vd=vd->link) + if( !_is_var_proc_already_created( proc, vd->label, pstate ) && cwIsNotFlag(vd->type,kRuntimeTFl) ) + { + variable_t* var = nullptr; + if((rc = var_create( proc, vd->label, kBaseSfxId, kInvalidId, kAnyChIdx, vd->val_cfg, kInvalidTFl, var )) != kOkRC ) + goto errLabel; + } + + // All the variables that can be used by this proc instance have now been created + // and the chIdx of each variable is set to 'any'. + + // If a 'preset' field was included in the class cfg then apply the specified class preset + // Note that the 'preset' field can be a string or list of strings. + if( pstate.preset_labels != nullptr ) + if((rc = _class_apply_presets(proc, pstate.preset_labels )) != kOkRC ) + goto errLabel; + + // The requested class presets values have now been set and those variables + // that were expressed with a list have numeric channel indexes assigned. + + // Apply the proc inst instance 'args:{}' values. + if( pstate.arg_cfg != nullptr ) + { + if((rc = _preset_channelize_vars( proc, "proc instance", "args", pstate.arg_cfg )) != kOkRC ) + goto errLabel; + } + + // All the proc instance arg values have now been set and those variables + // that were expressed with a list have numeric channel indexes assigned. + + // Connect the var's that are referenced in the in-stmt to their respective sources + if((rc = _io_stmt_connect_vars(net, proc, "in", "src", pstate.iStmtA, pstate.iStmtN)) != kOkRC ) + { + rc = cwLogError(rc,"Input connection processing failed."); + goto errLabel; + } + + // Connect the proxied vars in this subnet proc to their respective proxy vars. + if((rc = _subnet_connect_proxy_vars( proc, proxyVarL )) != kOkRC ) + { + rc = cwLogError(rc,"Proxy connection processing failed."); + goto errLabel; + } + + + // Complete the instantiation of the proc inst instance by calling the custom proc instance creation function. + + // Call the custom proc instance create() function. + if((rc = class_desc->members->create( proc )) != kOkRC ) + { + rc = cwLogError(kInvalidArgRC,"Custom instantiation failed." ); + goto errLabel; + } + + // Create the proc instance->varMap[] lookup array + if((rc =_proc_create_var_map( proc )) != kOkRC ) + { + rc = cwLogError(rc,"Variable map creation failed."); + goto errLabel; + } + + // create the feedback connections + _out_stmt_processing( net, proc, pstate ); + + // the custom creation function may have added channels to in-list vars fix up those connections here. + //_complete_input_connections(proc); + + // set the log flags again so that vars created by the proc instance can be included in the log output + if((rc = _proc_set_log_flags(proc,pstate.log_labels)) != kOkRC ) + goto errLabel; + + // call the 'value()' function to inform the proc instance of the current value of all of it's variables. + if((rc = _proc_call_value_func_on_all_variables( proc )) != kOkRC ) + goto errLabel; + + // validate the proc's state. + if((rc = proc_validate(proc)) != kOkRC ) + { + rc = cwLogError(rc,"proc inst instance validation failed."); + goto errLabel; + } + + proc_ref = proc; + + errLabel: + if( rc != kOkRC ) + { + rc = cwLogError(rc,"Proc instantiation failed on '%s:%i'.",cwStringNullGuard(pstate.proc_label),pstate.proc_label_sfx_id); + proc_destroy(proc); + } + + _pstate_destroy(pstate); + + return rc; + } + + // + // Network preset pair table + // + + rc_t _network_preset_pair_count( const network_t& net, unsigned& count_ref ) + { + rc_t rc = kOkRC; + + count_ref = 0; + unsigned n = 0; + for(unsigned i=0; ivarL; var!=nullptr; var=var->var_link) + if( var->chIdx == kAnyChIdx ) + { + unsigned varChCnt = 0; + if((rc = var_channel_count(var,varChCnt)) != kOkRC ) + { + rc = cwLogError(rc,"The network preset pair count operation failed."); + goto errLabel; + } + + n += varChCnt + 1; // Add 1 for the kAnyCh + } + } + + count_ref = n; + errLabel: + + return rc; + } + + rc_t _network_preset_pair_fill_table( const network_t& net, network_preset_pair_t* nppA, unsigned nppN) + { + rc_t rc = kOkRC; + unsigned j = 0; + for(unsigned i=0; ivarL; var!=nullptr; var=var->var_link) + if( var->chIdx == kAnyChIdx ) + { + unsigned varChCnt = 0; + if((rc = var_channel_count(var,varChCnt)) != kOkRC ) + goto errLabel; + + unsigned k=0; + for(const variable_t* ch_var=var; ch_var!=nullptr; ch_var = ch_var->ch_link, ++j,++k ) + { + if( j >= nppN ) + { + rc = cwLogError(kInvalidStateRC,"Unexpected end of preset-pair table was encountered."); + goto errLabel; + } + + nppA[j].proc = proc; + nppA[j].var = ch_var; + nppA[j].chIdx = var->chIdx;; + nppA[j].chN = varChCnt;; + } + + if( k != varChCnt+1 ) + { + rc = cwLogError(kInvalidStateRC,"An inconsistent var channel count was encountered on '%s:%i'-'%s:%i'.", + cwStringNullGuard(proc->label),proc->label_sfx_id, + cwStringNullGuard(var->label),var->label_sfx_id); + goto errLabel; + } + + } + } + + if( j != nppN ) + rc = cwLogError(kInvalidStateRC,"The expected count of entries in the preset_pair table (%i) does not match the actual count (%i).",nppN,j); + + errLabel: + if( rc != kOkRC ) + rc = cwLogError(rc,"Preset pair table fill failed."); + return rc; + } + + rc_t _network_preset_pair_create_table( network_t& net ) + { + rc_t rc = kOkRC; + unsigned pair_count = 0; + + // get the total count of variables in this network + if((rc = _network_preset_pair_count(net, pair_count )) != kOkRC ) + goto errLabel; + + // allocate the preset pair table + net.preset_pairA = mem::allocZ(pair_count); + net.preset_pairN = pair_count; + + // fill the preset pair table + if((rc= _network_preset_pair_fill_table(net, net.preset_pairA, net.preset_pairN)) != kOkRC ) + goto errLabel; + + + errLabel: + if( rc != kOkRC ) + { + rc = cwLogError(rc,"Network preset pair table create failed."); + mem::release(net.preset_pairA); + net.preset_pairN = 0; + } + + return rc; + } + + unsigned _network_preset_pair_find_index( const network_t& net, const variable_t* var ) + { + for(unsigned i=0; iproc == npp->proc && var == npp->var ) + { + assert( var->chIdx == npp->var->chIdx ); + return i; + } + } + + return kInvalidIdx; + } + + // + // Preset processing + // + + rc_t _parse_network_proc_label( const network_t& net, const network_preset_t& network_preset, const char* proc_label, io_ele_t& proc_id ) + { + rc_t rc = kOkRC; + + // parse the proc label + if((rc= _io_stmt_parse_ele( proc_label, proc_id )) != kOkRC ) + { + rc = cwLogError(kSyntaxErrorRC,"Parse failed on the proc label '%s' of preset '%s'.",cwStringNullGuard(proc_label),cwStringNullGuard(network_preset.label)); + goto errLabel; + } + + // set the proc_id base sfx id + if( proc_id.base_sfx_id == kInvalidId ) + proc_id.base_sfx_id = kBaseSfxId; + + // if not iterating + if( !proc_id.is_iter_fl ) + { + proc_id.sfx_id_count = 1; + } + else + { + if( proc_id.sfx_id_count == kInvalidCnt ) + proc_id.sfx_id_count = proc_mult_count(net, proc_id.label ); + } + + errLabel: + return rc; + } + + rc_t _parse_network_proc_var_label( network_t& net, const char* network_preset_label, const object_t* var_pair, const char* proc_label, unsigned proc_label_sfx_id, io_ele_t& var_id ) + { + rc_t rc = kOkRC; + const char* var_label = nullptr; + + if( var_pair==nullptr || !var_pair->is_pair() || (var_label=var_pair->pair_label())==nullptr || var_pair->pair_value() == nullptr ) + { + rc = cwLogError(kSyntaxErrorRC,"A syntax error was encountered on a preset pair value on preset '%s:%s'.",cwStringNullGuard(network_preset_label),cwStringNullGuard(proc_label)); + goto errLabel; + } + + // + if((rc= _io_stmt_parse_ele( var_label, var_id )) != kOkRC ) + { + rc = cwLogError(rc,"Parse failed on the var label of preset '%s:%s:%s'.",cwStringNullGuard(network_preset_label),cwStringNullGuard(proc_label),cwStringNullGuard(var_label)); + goto errLabel; + } + + // set the var_id base sfx id + if( var_id.base_sfx_id == kInvalidId ) + var_id.base_sfx_id = kBaseSfxId; + + // if not iterating + if( !var_id.is_iter_fl ) + { + var_id.sfx_id_count = 1; + } + else + { + + proc_t* proc; + + // find the var proc + if((proc = proc_find(net,proc_label,proc_label_sfx_id)) == nullptr ) + { + rc = cwLogError(kEleNotFoundRC,"The proc '%s' could not be found for network preset '%s'.",cwStringNullGuard(proc_label),cwStringNullGuard(network_preset_label)); + goto errLabel; + } + + if( var_id.sfx_id_count == kInvalidCnt ) + var_id.sfx_id_count = var_mult_count(proc, var_id.label ); + } + + errLabel: + return rc; + + } + + rc_t _network_preset_get_class_preset( network_t& net, const char* network_preset_label, const char* proc_label, const object_t* class_preset_label_cfg, const object_t*& class_preset_cfg_ref ) + { + rc_t rc = kOkRC; + const char* class_preset_label = nullptr;; + const class_preset_t* class_preset = nullptr; + //class_desc_t* class_desc = nullptr; + proc_t* proc = nullptr; + + class_preset_cfg_ref = nullptr; + + // get the label of the proc class preset + if((rc = class_preset_label_cfg->value(class_preset_label)) != kOkRC ) + { + rc = cwLogError(rc,"The preset label could not be parsed on the preset:'%s:%s'",cwStringNullGuard(network_preset_label),cwStringNullGuard(proc_label)); + goto errLabel; + } + + // locate the proc this preset will be applied to + if((proc = proc_find(net, proc_label, kBaseSfxId )) == nullptr ) + { + rc = cwLogError(rc,"The proc '%s' could not be found for the preset:'%s'",cwStringNullGuard(proc_label),cwStringNullGuard(network_preset_label)); + goto errLabel; + } + + // get the preset record for this proc/preset_label + if((class_preset = class_preset_find( proc->class_desc, class_preset_label )) == nullptr ) + { + rc = cwLogError(rc,"The class description for proc '%s' could not be found for the preset:'%s'",cwStringNullGuard(proc_label),cwStringNullGuard(network_preset_label)); + goto errLabel; + } + + class_preset_cfg_ref = class_preset->cfg; + + errLabel: + return rc; + } + + rc_t _network_preset_create_channel_value( network_t& net, network_preset_t& network_preset, proc_t* proc, variable_t* var, unsigned chN, const object_t* value_cfg ) + { + rc_t rc = kOkRC; + unsigned pairTblIdx = kInvalidIdx; + preset_value_t* preset_value = mem::allocZ(); + + // cfg to value + if((rc = cfg_to_value( value_cfg, preset_value->value )) != kOkRC ) + { + rc = cwLogError(rc,"The preset cfg to value conversion failed on '%s:%i'-'%s:%i'.",cwStringNullGuard(var->label),var->label_sfx_id,cwStringNullGuard(proc->label),proc->label_sfx_id); + goto errLabel; + } + + // locate the the + if((pairTblIdx = _network_preset_pair_find_index(net, var )) == kInvalidIdx ) + { + rc = cwLogError(rc,"The preset pair record could not be found for '%s:%i'-'%s:%i'.",cwStringNullGuard(var->label),var->label_sfx_id,cwStringNullGuard(proc->label),proc->label_sfx_id); + goto errLabel; + } + + preset_value->proc = proc; + preset_value->var = var; + preset_value->pairTblIdx = pairTblIdx; + + if( network_preset.u.vlist.value_head == nullptr ) + network_preset.u.vlist.value_head = preset_value; + else + network_preset.u.vlist.value_tail->link = preset_value; + + network_preset.u.vlist.value_tail = preset_value; + + errLabel: + if(rc != kOkRC ) + _preset_value_destroy(preset_value); + + return rc; + } + + rc_t _network_preset_find_or_create_variable( proc_t* proc, const char* var_label, unsigned var_sfx_id, unsigned chIdx, const object_t* value_cfg, bool allow_create_fl, variable_t*& var_ref ) + { + rc_t rc = kOkRC; + variable_t* var = nullptr; + var_ref = nullptr; + + // if the var was not found on 'chIdx' + if((rc = var_find(proc, var_label, var_sfx_id, chIdx, var )) != kOkRC ) + { + + if( !allow_create_fl ) + { + rc = cwLogError(kEleNotFoundRC,"The preset variable '%s:%i' ch:%i could not be found on proc: '%s:%i'.", + cwStringNullGuard(var_label),var_sfx_id,chIdx, cwStringNullGuard(proc->label),proc->label_sfx_id ); + goto errLabel; + } + else + { + //variable_t* base_var = nullptr; + // get the base var + if((rc = var_find(proc, var_label, kBaseSfxId, kAnyChIdx, var)) != kOkRC ) + { + rc = cwLogError(rc,"The base variable '%s:%i' ch:%i could not be found to pre-emptively create the variable '%s:%i' ch:%i on proc: '%s:%i'.", + cwStringNullGuard(var_label),kBaseSfxId,kAnyChIdx, + cwStringNullGuard(var_label),var_sfx_id,chIdx, + cwStringNullGuard(proc->label),proc->label_sfx_id); + goto errLabel; + } + + // create the variable + if((rc = var_create( proc, var_label, var_sfx_id, kInvalidId, chIdx, value_cfg, kInvalidTFl, var )) != kOkRC ) + { + rc = cwLogError(rc,"Pre-emptive variable creation failed for '%s:%i' ch:%i on proc:'%s:%i'.", + cwStringNullGuard(var_label),var_sfx_id,chIdx, + cwStringNullGuard(proc->label),proc->label_sfx_id); + goto errLabel; + } + } + } + + var_ref = var; + + errLabel: + return rc; + } + + + rc_t _network_preset_create_value( network_t& net, + network_preset_t& network_preset, + const char* proc_label, + unsigned proc_sfx_id, + const char* var_label, + unsigned var_sfx_id, + const object_t* value_cfg ) + { + rc_t rc = kOkRC; + var_desc_t* var_desc = nullptr; + proc_t* proc = nullptr; + + + // locate the proc this preset will be applied to + if((proc = proc_find(net, proc_label, proc_sfx_id )) == nullptr ) + { + rc = cwLogError(kOpFailRC,"The proc '%s:%i' does not exist.",cwStringNullGuard(proc_label),proc_sfx_id); + goto errLabel; + } + else + { + bool is_var_cfg_type_fl = (var_desc = var_desc_find( proc->class_desc, var_label ))!=nullptr && cwIsFlag(var_desc->type,kCfgTFl); + bool is_list_fl = value_cfg->is_list(); + bool is_list_of_list_fl = is_list_fl && value_cfg->child_count() > 0 && value_cfg->child_ele(0)->is_list(); + bool parse_list_fl = (is_list_fl && !is_var_cfg_type_fl) || (is_list_of_list_fl && is_var_cfg_type_fl); + + // Case 1: By default we assume a single variable instance on channel 'kAnyChIdx' .... + unsigned valueN = 1; + unsigned chIdx = kAnyChIdx; + const object_t* vobj = value_cfg; + + // Case 2: ... however if a list of preset values was given and the var type is not a 'cfg' type or if a list of lists was given + // then we are going to iterate through a list of preset values each on a successive channel index + if( parse_list_fl ) + { + chIdx = 0; + valueN = value_cfg->child_count(); + vobj = value_cfg->child_ele(0); + } + + // Iterate over each channel + for(unsigned i = 0; ichild_ele(i); + } + } + //printf("%s %s:%i-%s:%i\n",network_preset.label,proc_label,proc_sfx_id,var_label,var_sfx_id); + } + errLabel: + + return rc; + } + + rc_t _network_preset_parse_dual_label(network_t& net, const object_t* list_cfg, unsigned idx, const char* pri_sec_label, const char* network_preset_label, const network_preset_t*& vlist_ref ) + { + rc_t rc = kOkRC; + const char* preset_label = nullptr; + //const preset_value_list_t* vlist = nullptr; + + vlist_ref = nullptr; + + if( !list_cfg->child_ele(idx)->is_string() ) + { + rc = cwLogError(kSyntaxErrorRC,"The dual preset list %s preset is not a string on network preset:'%s'.",pri_sec_label,cwStringNullGuard(network_preset_label)); + goto errLabel; + } + + if((rc = list_cfg->child_ele(idx)->value(preset_label)) != kOkRC ) + { + rc = cwLogError(kOpFailRC,"The dual preset %s preset could not be parsed on network preset:'%s'.",pri_sec_label,cwStringNullGuard(network_preset_label)); + goto errLabel; + } + + if((vlist_ref = network_preset_from_label(net, preset_label )) == nullptr ) + { + rc = cwLogError(kEleNotFoundRC,"The dual preset %s preset could not be found on network preset:'%s'.",pri_sec_label,cwStringNullGuard(network_preset_label)); + goto errLabel; + } + + errLabel: + return rc; + } + + rc_t _network_preset_parse_dual(flow_t* p, network_t& net, const object_t* dual_list_cfg, network_preset_t& network_preset ) + { + rc_t rc = kOkRC; + + + if( dual_list_cfg==nullptr || !dual_list_cfg->is_list() ) + { + rc = cwLogError(kSyntaxErrorRC,"The dual preset specification is not a list on network preset:'%s'.",cwStringNullGuard(network_preset.label)); + goto errLabel; + } + + if( dual_list_cfg->child_count() != 3 ) + { + rc = cwLogError(kSyntaxErrorRC,"The dual preset list does not have 3 elements on network preset:'%s'.",cwStringNullGuard(network_preset.label)); + goto errLabel; + } + + if((rc = _network_preset_parse_dual_label(net,dual_list_cfg, 0, "primary", network_preset.label, network_preset.u.dual.pri )) != kOkRC ) + goto errLabel; + + + if((rc = _network_preset_parse_dual_label(net,dual_list_cfg, 1, "secondary", network_preset.label, network_preset.u.dual.sec )) != kOkRC ) + goto errLabel; + + + if((rc = dual_list_cfg->child_ele(2)->value(network_preset.u.dual.coeff)) != kOkRC ) + { + rc = cwLogError(kSyntaxErrorRC,"The dual preset coeff could not be parsed on network preset:'%s'.",cwStringNullGuard(network_preset.label)); + goto errLabel; + } + + network_preset.tid = kPresetDualTId; + + + errLabel: + return rc; + } + + rc_t _network_preset_parse_value_list( flow_t* p, network_t& net, const object_t* network_preset_dict_cfg, network_preset_t& network_preset ) + { + rc_t rc = kOkRC; + unsigned procN = 0; + + if( network_preset_dict_cfg==nullptr || !network_preset_dict_cfg->is_dict() ) + { + rc = cwLogError(kSyntaxErrorRC,"The proc preset dictionary is not a dictionary in network preset:'%s'.",cwStringNullGuard(network_preset.label)); + goto errLabel; + } + + network_preset.tid = kPresetVListTId; + + procN = network_preset_dict_cfg->child_count(); + + // for each proc in the network preset + for(unsigned i=0; ichild_ele(i); + const char* proc_label = nullptr; + io_ele_t proc_id = {}; + unsigned varN = 0; + + // validate the process preset syntax + if( proc_preset_pair==nullptr || !proc_preset_pair->is_pair() || (proc_label=proc_preset_pair->pair_label())==nullptr || proc_preset_pair->pair_value()==nullptr ) + { + rc = cwLogError(kSyntaxErrorRC,"A syntax error was encountered on a preset pair on preset '%s'.",cwStringNullGuard(network_preset.label)); + goto errLabel; + } + + // parse the proc label + if((rc= _parse_network_proc_label(net, network_preset, proc_label, proc_id )) != kOkRC ) + goto errLabel; + + // if the preset refers to a proc class preset + if( proc_preset_pair->pair_value()->is_string() ) + { + // get the referenced preset cfg from the class desc + if((rc = _network_preset_get_class_preset( net, network_preset.label, proc_id.label, proc_preset_pair->pair_value(), var_dict )) != kOkRC ) + goto errLabel; + } + else // the preset is a dictionary of var/value pairs + { + // if preset is not a dictionary + if( !proc_preset_pair->pair_value()->is_dict() ) + { + rc = cwLogError(kSyntaxErrorRC,"The preset value dictionary for '%s:%s' is not valid.",cwStringNullGuard(network_preset.label),cwStringNullGuard(proc_id.label)); + goto errLabel; + } + + var_dict = proc_preset_pair->pair_value(); + } + + // var_dict now refers to a dictionary of var/value pairs for a single proc + + varN = var_dict->child_count(); + + // for each proc/sf_id (the proc label may refer to multiple proc instances) + for(unsigned j=0; jchild_ele(k); + unsigned proc_label_sfx_id = proc_id.base_sfx_id + j; + + // parse the preset var label + if((rc = _parse_network_proc_var_label(net, network_preset.label, var_pair, proc_id.label, proc_label_sfx_id, var_id )) != kOkRC ) + goto errLabel; + + // create a preset for each var:sfx_id pair (the var label may refer to multiple var instances) + for(unsigned m=0; mpair_value() )) != kOkRC ) + goto errLabel; + + mem::release(var_id.label); + } + } + + mem::release(proc_id.label); + } + + errLabel: + return rc; + } + + + rc_t _network_preset_parse_top_level_dict( flow_t* p, network_t& net, const object_t* preset_cfg ) + { + rc_t rc = kOkRC; + unsigned presetAllocN = 0; + + if( preset_cfg == nullptr ) + return rc; + + if( !preset_cfg->is_dict() ) + { + rc = cwLogError(kSyntaxErrorRC,"The network preset list is not a dictionary."); + goto errLabel; + } + + presetAllocN = preset_cfg->child_count(); + net.presetA = mem::allocZ(presetAllocN); + net.presetN = 0; + + // parse each preset_label pair + for(unsigned i=0; ichild_ele(i); + network_preset_t& network_preset = net.presetA[i]; + + // validate the network preset pair + if( preset_pair_cfg==nullptr || !preset_pair_cfg->is_pair() || (network_preset.label = preset_pair_cfg->pair_label())==nullptr || preset_pair_cfg->pair_value()==nullptr ) + { + rc = cwLogError(kSyntaxErrorRC,"Invalid syntax encountered on a network preset."); + goto errLabel; + } + + switch( preset_pair_cfg->pair_value()->type_id() ) + { + case kDictTId: // 'value-list' preset + if((rc = _network_preset_parse_value_list(p, net, preset_pair_cfg->pair_value(), network_preset)) != kOkRC ) + { + rc = cwLogError(kSyntaxErrorRC,"Network value-list preset parse failed on preset:'%s'.",cwStringNullGuard(network_preset.label)); + goto errLabel; + } + break; + + case kListTId: // dual preset + if((rc = _network_preset_parse_dual(p, net, preset_pair_cfg->pair_value(), network_preset)) != kOkRC ) + { + rc = cwLogError(kSyntaxErrorRC,"Network dual preset parse failed on preset:'%s'.",cwStringNullGuard(network_preset.label)); + goto errLabel; + } + break; + + default: + rc = cwLogError(kAssertFailRC,"Unknown preset type on network preset: '%s'.",cwStringNullGuard(network_preset.label)); + goto errLabel; + } + + + net.presetN += 1; + } + + errLabel: + if(rc != kOkRC ) + { + _network_preset_array_destroy(net); + } + + return rc; + } + + + template + rc_t _preset_set_var_from_dual_interp_1( variable_t* var, T0 v0, T1 v1, double coeff ) + { + return var_set(var, (T0)(v0 + (v1-v0)*coeff )); + } + + template< typename T > + rc_t _preset_set_var_from_dual_interp_0( variable_t* var, T v0, const value_t* v1, double coeff ) + { + rc_t rc = kOkRC; + switch( v1->tflag & kTypeMask ) + { + case kUIntTFl: + rc = _preset_set_var_from_dual_interp_1(var,v0,v1->u.u,coeff); + break; + + case kIntTFl: + rc = _preset_set_var_from_dual_interp_1(var,v0,v1->u.i,coeff); + break; + + case kFloatTFl: + rc = _preset_set_var_from_dual_interp_1(var,v0,v1->u.f,coeff); + break; + + case kDoubleTFl: + rc = _preset_set_var_from_dual_interp_1(var,v0,v1->u.d,coeff); + break; + + default: + rc = cwLogError(kInvalidDataTypeRC,"The second operand of a set by interpolation had a non-numeric data type."); + } + return rc; + } + + + + rc_t _preset_set_var_from_dual( const preset_value_t* preset_val, const value_t* value_1, double coeff ) + { + rc_t rc = kOkRC; + unsigned legalTypeMask = kUIntTFl | kIntTFl | kFloatTFl | kDoubleTFl; + + if( value_1 == nullptr || (preset_val->value.tflag & legalTypeMask)==0 ) + rc = var_set( preset_val->var, &preset_val->value ); + else + { + if( (value_1->tflag & legalTypeMask) == 0 ) + { + rc = cwLogError(kInvalidDataTypeRC,"The type of value-1 (0x%x) is not a scalar number.",value_1->tflag); + goto errLabel; + } + + switch( preset_val->value.tflag & legalTypeMask ) + { + case kUIntTFl: + rc = _preset_set_var_from_dual_interp_0( preset_val->var, preset_val->value.u.u, value_1, coeff ); + break; + + case kIntTFl: + rc = _preset_set_var_from_dual_interp_0( preset_val->var, preset_val->value.u.i, value_1, coeff ); + break; + + case kFloatTFl: + rc = _preset_set_var_from_dual_interp_0( preset_val->var, preset_val->value.u.f, value_1, coeff ); + break; + + case kDoubleTFl: + rc = _preset_set_var_from_dual_interp_0( preset_val->var, preset_val->value.u.d, value_1, coeff ); + break; + + default: + rc = cwLogError(kInvalidDataTypeRC,"The first operand of a set by interpolation had a non-numeric data type."); + + } + } + + errLabel: + if(rc != kOkRC ) + rc = cwLogError(rc,"Set variable from dual preset failed on '%s:%i'-'%s:%i' ch:0.", + cwStringNullGuard(preset_val->proc->label),preset_val->proc->label_sfx_id, + cwStringNullGuard(preset_val->var->label),preset_val->var->label_sfx_id, + preset_val->var->chIdx); + return rc; + } + + + rc_t _network_apply_preset( network_t& net, const preset_value_list_t* vlist, unsigned proc_label_sfx_id ) + { + rc_t rc = kOkRC; + const preset_value_t* preset_value = nullptr; + + for(preset_value=vlist->value_head; preset_value!=nullptr; preset_value=preset_value->link) + { + if( proc_label_sfx_id==kInvalidId || preset_value->proc->label_sfx_id == proc_label_sfx_id ) + if((rc = var_set( preset_value->var, &preset_value->value )) != kOkRC ) + { + rc = cwLogError(rc,"Preset value apply failed on '%s:%i'-'%s:%i'.", + cwStringNullGuard(preset_value->proc->label),preset_value->proc->label_sfx_id, + cwStringNullGuard(preset_value->var->label),preset_value->var->label_sfx_id); + goto errLabel; + } + } + + errLabel: + return rc; + } + + rc_t _network_apply_dual_preset( network_t& net, const network_preset_t* net_ps0, const network_preset_t* net_ps1, double coeff, unsigned proc_label_sfx_id ) + { + rc_t rc = kOkRC; + + // clear the value field of the preset-pair array + for(unsigned i=0; iu.vlist.value_head; pv1!=nullptr; pv1=pv1->link) + if( proc_label_sfx_id == kInvalidId || pv1->proc->label_sfx_id == proc_label_sfx_id ) + { + if( pv1->var->chIdx != kAnyChIdx ) + net.preset_pairA[ pv1->pairTblIdx ].value = &pv1->value; + else + { + for(unsigned i=0; ipairTblIdx ].chN; ++i) + { + net.preset_pairA[ pv1->pairTblIdx+i ].value = &pv1->value; + assert( textIsEqual(net.preset_pairA[ pv1->pairTblIdx+i ].var->label,pv1->var->label) && net.preset_pairA[ pv1->pairTblIdx+i ].var->label_sfx_id == pv1->var->label_sfx_id ); + } + } + } + + // + for(const preset_value_t* pv0=net_ps0->u.vlist.value_head; pv0!=nullptr; pv0=pv0->link) + if( proc_label_sfx_id == kInvalidId || pv0->proc->label_sfx_id == proc_label_sfx_id ) + { + if( pv0->var->chIdx != kAnyChIdx ) + { + rc = _preset_set_var_from_dual( pv0, net.preset_pairA[ pv0->pairTblIdx ].value, coeff ); + } + else + { + for(unsigned i=0; ipairTblIdx ].chN; ++i) + { + if((rc = _preset_set_var_from_dual( pv0, net.preset_pairA[ pv0->pairTblIdx+i ].value, coeff )) != kOkRC ) + goto errLabel; + + assert( textIsEqual(net.preset_pairA[ pv0->pairTblIdx+i ].var->label,pv0->var->label) && net.preset_pairA[ pv0->pairTblIdx+i ].var->label_sfx_id == pv0->var->label_sfx_id ); + } + } + + } + + errLabel: + return rc; + } + + + } +} + +cw::rc_t cw::flow::network_create( flow_t* p, + const object_t* networkCfg, + network_t& net, + variable_t* proxyVarL, + unsigned polyCnt, + network_order_id_t orderId ) +{ + rc_t rc = kOkRC; + + // default to kNetFirstPolyOrderId + unsigned outerN = polyCnt; + unsigned innerN = 1; + + if((rc = networkCfg->getv("procs",net.procsCfg)) != kOkRC ) + { + rc = cwLogError(rc,"Failed on parsing required network cfg. elements."); + goto errLabel; + } + + if((rc = networkCfg->getv_opt("presets",net.presetsCfg)) != kOkRC ) + { + rc = cwLogError(rc,"Failed on parsing optional network cfg. elements."); + goto errLabel; + } + + + if( orderId == kProcFirstPolyOrderId ) + { + outerN = 1; + innerN = polyCnt; + } + + net.proc_arrayAllocN = polyCnt * net.procsCfg->child_count(); + net.proc_array = mem::allocZ(net.proc_arrayAllocN); + net.proc_arrayN = 0; + + for(unsigned i=0; ichild_count(); ++j) + { + const object_t* proc_cfg = net.procsCfg->child_ele(j); + + for(unsigned k=0; k 1 ) + sfx_id = orderId == kNetFirstPolyOrderId ? i : k; + + assert(net.proc_arrayN < net.proc_arrayAllocN ); + + // create the proc inst instance + if( (rc= _proc_create( p, proc_cfg, sfx_id, net, proxyVarL, net.proc_array[net.proc_arrayN] ) ) != kOkRC ) + { + //rc = cwLogError(rc,"The instantiation at proc index %i is invalid.",net.proc_arrayN); + goto errLabel; + } + + net.proc_arrayN += 1; + } + } + } + + net.poly_cnt = polyCnt; + + + if((rc = _network_preset_pair_create_table(net)) != kOkRC ) + goto errLabel; + + _network_preset_parse_top_level_dict(p, net, net.presetsCfg ); + +errLabel: + if( rc != kOkRC ) + _network_destroy(net); + + return rc; +} + +cw::rc_t cw::flow::network_destroy( network_t& net ) +{ + return _network_destroy(net); +} + +const cw::object_t* cw::flow::find_network_preset( const network_t& net, const char* presetLabel ) +{ + const object_t* preset_value = nullptr; + + if( net.presetsCfg != nullptr ) + { + rc_t rc; + + if((rc = net.presetsCfg->getv_opt( presetLabel, preset_value )) != kOkRC ) + cwLogError(rc,"Search for network preset named '%s' failed.", cwStringNullGuard(presetLabel)); + } + + return preset_value; +} + +cw::rc_t cw::flow::exec_cycle( network_t& net ) +{ + rc_t rc = kOkRC; + + for(unsigned i=0; iclass_desc->members->exec(net.proc_array[i])) != kOkRC ) + { + rc = cwLogError(rc,"Execution failed on the proc:%s:%i.",cwStringNullGuard(net.proc_array[i]->label),net.proc_array[i]->label_sfx_id); + break; + } + } + + return rc; +} + +cw::rc_t cw::flow::get_variable( network_t& net, const char* proc_label, const char* var_label, unsigned chIdx, proc_t*& procPtrRef, variable_t*& varPtrRef ) +{ + rc_t rc = kOkRC; + proc_t* proc = nullptr; + variable_t* var = nullptr; + + varPtrRef = nullptr; + procPtrRef = nullptr; + + // locate the proc inst instance + if((proc = proc_find(net,proc_label,kBaseSfxId)) == nullptr ) + { + rc = cwLogError(kInvalidIdRC,"Unknown proc inst instance label '%s'.", cwStringNullGuard(proc_label)); + goto errLabel; + } + + // locate the variable + if((rc = var_find( proc, var_label, kBaseSfxId, chIdx, var)) != kOkRC ) + { + rc = cwLogError(kInvalidArgRC,"The variable '%s' could not be found on the proc inst instance '%s'.",cwStringNullGuard(var_label),cwStringNullGuard(proc_label)); + goto errLabel; + } + + procPtrRef = proc; + varPtrRef = var; + +errLabel: + return rc; +} + +cw::rc_t cw::flow::network_apply_preset( network_t& net, const char* preset_label, unsigned proc_label_sfx_id ) +{ + rc_t rc = kOkRC; + const network_preset_t* network_preset = nullptr; + + if((network_preset = network_preset_from_label(net, preset_label )) == nullptr ) + { + rc = cwLogError(kInvalidIdRC,"The network preset '%s' could not be found.", cwStringNullGuard(preset_label) ); + goto errLabel; + } + + switch( network_preset->tid ) + { + case kPresetVListTId: + if((rc = _network_apply_preset( net, &network_preset->u.vlist, proc_label_sfx_id )) != kOkRC ) + goto errLabel; + break; + + case kPresetDualTId: + if((rc = _network_apply_dual_preset(net, network_preset->u.dual.pri, network_preset->u.dual.sec, network_preset->u.dual.coeff, proc_label_sfx_id )) != kOkRC ) + goto errLabel; + break; + + default: + rc = cwLogError(kAssertFailRC,"Unknown preset type."); + break; + } + + cwLogInfo("Activated preset:%s",preset_label); + +errLabel: + if(rc != kOkRC ) + rc = cwLogError(rc,"The network application '%s' failed.", cwStringNullGuard(preset_label) ); + + return rc; + +} + + +cw::rc_t cw::flow::network_apply_dual_preset( network_t& net, const char* preset_label_0, const char* preset_label_1, double coeff, unsigned proc_label_sfx_id ) +{ + rc_t rc = kOkRC; + const network_preset_t* net_ps0 = nullptr; + const network_preset_t* net_ps1 = nullptr; + + if((net_ps0 = network_preset_from_label(net, preset_label_0 )) == nullptr ) + { + rc = cwLogError(kInvalidIdRC,"The network preset '%s' could not be found.", preset_label_0 ); + goto errLabel; + } + + if((net_ps1 = network_preset_from_label(net, preset_label_1 )) == nullptr ) + { + rc = cwLogError(kInvalidIdRC,"The network preset '%s' could not be found.", preset_label_1 ); + goto errLabel; + } + + if((rc = _network_apply_dual_preset(net, net_ps0, net_ps1, coeff, proc_label_sfx_id )) != kOkRC ) + goto errLabel; + +errLabel: + if( rc != kOkRC ) + rc = cwLogError(rc,"Apply dual-preset failed."); + + return rc; +} + + +#ifdef NOT_DEF +cw::rc_t cw::flow::network_apply_preset( network_t& net, const char* presetLabel, unsigned proc_label_sfx_id ) +{ + rc_t rc = kOkRC; + const object_t* net_preset_value; + const object_t* preset_pair; + + // locate the cfg of the requested preset + if((net_preset_value = find_network_preset(net, presetLabel )) == nullptr ) + { + rc = cwLogError(kInvalidIdRC,"The network preset '%s' could not be found.", presetLabel ); + goto errLabel; + } + + // for each proc instance in the preset + for(unsigned i=0; ichild_count(); ++i) + { + // get the proc instance label/value pair + if((preset_pair = net_preset_value->child_ele(i)) != nullptr && preset_pair->is_pair() ) + { + const char* proc_label = preset_pair->pair_label(); + const object_t* preset_value_cfg = preset_pair->pair_value(); + proc_t* proc; + + // locate the proc instance + if((proc = proc_find(net,proc_label,proc_label_sfx_id)) == nullptr ) + { + rc = cwLogError(kInvalidIdRC,"The network proc instance '%s' refered to in network preset '%s' could not be found.",proc_label,presetLabel); + goto errLabel; + } + + // if the preset value is a string then look it up in the class preset dictionary + if( preset_value_cfg->is_string() ) + { + const char* class_preset_label; + preset_value_cfg->value(class_preset_label); + _class_preset_channelize_vars(proc, class_preset_label ); + } + else + { + // if the preset value is a dict then apply it directly + if( preset_value_cfg->is_dict() ) + { + if((rc = _preset_channelize_vars( proc, "network", presetLabel, preset_value_cfg )) != kOkRC ) + { + rc = cwLogError(rc,"The preset '%s' application failed on proc instance '%s'.", presetLabel, proc_label ); + goto errLabel; + } + + } + else + { + rc = cwLogError(kSyntaxErrorRC,"The network preset '%s' proc instance '%s' does not have a string or dictionary value.", presetLabel, proc_label ); + goto errLabel; + } + } + } + else + { + rc = cwLogError(kSyntaxErrorRC,"The network preset '%s' is malformed.",presetLabel); + goto errLabel; + } + } + + cwLogInfo("Activated preset:%s",presetLabel); +errLabel: + return rc; +} + +cw::rc_t cw::flow::network_apply_dual_preset( network_t& net, const char* presetLabel_0, const char* presetLabel_1, double coeff ) +{ + rc_t rc = kOkRC; + + const object_t* net_preset_value_0; + + cwLogInfo("*** Applying dual: %s %s : %f",presetLabel_0, presetLabel_1, coeff ); + + // locate the cfg of the requested preset + if((net_preset_value_0 = find_network_preset(net, presetLabel_0 )) == nullptr ) + { + rc = cwLogError(kInvalidIdRC,"The network preset '%s' could not be found.", presetLabel_0 ); + goto errLabel; + } + + // for each proc instance in the preset + for(unsigned i=0; ichild_count(); ++i) + { + const object_t* preset_pair_0 = net_preset_value_0->child_ele(i); + const char* proc_label = preset_pair_0->pair_label(); + const object_t* preset_value_cfg_0 = preset_pair_0->pair_value(); + proc_t* proc = nullptr; + const object_t* preset_value_cfg_1 = nullptr; + const int two = 2; + const char* class_preset_labelA[two]; + + // get the proc instance label/value pair + if((preset_pair_0 = net_preset_value_0->child_ele(i)) == nullptr || !preset_pair_0->is_pair() ) + { + rc = cwLogError(kSyntaxErrorRC,"An invalid preset value pair was encountered in '%s'.",presetLabel_0); + goto errLabel; + } + + // verify that the preset value is a string or dict + if( preset_pair_0->pair_value()==nullptr || (!preset_value_cfg_0->is_dict() && !preset_value_cfg_0->is_string() )) + { + rc = cwLogError(kSyntaxErrorRC,"The preset value pair for proc instance '%s' in '%s' is not a 'dict' or 'string'.",proc_label,presetLabel_0); + goto errLabel; + } + + // locate the proc instance associated with the primary and secondary preset + if((proc = proc_find(net,proc_label,kBaseSfxId)) == nullptr ) + { + rc = cwLogError(kInvalidIdRC,"The network proc instance '%s' refered to in network preset '%s' could not be found.",cwStringNullGuard(proc_label),cwStringNullGuard(presetLabel_0)); + goto errLabel; + } + + // locate the second proc instance/preset value pair + if((rc = _find_network_preset_proc_pair(net, presetLabel_1, proc_label, preset_value_cfg_1 )) != kOkRC ) + { + rc = cwLogError(kInvalidIdRC,"The second network proc instance '%s' refered to in network preset '%s' could not be found.",proc_label,presetLabel_1); + goto errLabel; + } + + // TODO: We require that the proc instance presets both be of the same type: string or dict. + // There's no good reason for this, as string's resolve to class dict presets anyway. + // Fix this! + if( !(preset_value_cfg_0->is_dict() == preset_value_cfg_1->is_dict() && preset_value_cfg_0->is_string() == preset_value_cfg_1->is_string()) ) + { + rc = cwLogError(kInvalidIdRC,"The value type (string or dict) of dual network presets must match. (%s != %s)",preset_value_cfg_0->type->label,preset_value_cfg_1->type->label); + goto errLabel; + } + + preset_value_cfg_0->value(class_preset_labelA[0]); + preset_value_cfg_1->value(class_preset_labelA[1]); + + + // if the preset value is a string then look it up in the class dictionary + if( preset_value_cfg_0->is_string() ) + { + rc = _class_multi_preset_channelize_vars(proc, class_preset_labelA, two, coeff ); + } + else + { + assert( preset_value_cfg_1->is_dict() ); + + const object_t* preset_value_cfgA[] = { preset_value_cfg_0, preset_value_cfg_1}; + + if((rc = _multi_preset_channelize_vars( proc, "network", class_preset_labelA, preset_value_cfgA, two, coeff )) != kOkRC ) + { + rc = cwLogError(rc,"The dual preset '%s':'%s' application failed on proc instance '%s'.", cwStringNullGuard(class_preset_labelA[0]), cwStringNullGuard(class_preset_labelA[1]), proc_label ); + goto errLabel; + } + } + } + + +errLabel: + + if( rc != kOkRC ) + rc = cwLogError(rc,"The dual preset '%s':'%s' application failed.", cwStringNullGuard(presetLabel_0), cwStringNullGuard(presetLabel_1) ); + + return rc; +} + +#endif + + +cw::rc_t cw::flow::network_apply_preset( network_t& net, const multi_preset_selector_t& mps, unsigned proc_label_sfx_id ) +{ + rc_t rc = kOkRC; + const char* label0 = nullptr; + const char* label1 = nullptr; + bool priProbFl = cwIsFlag(mps.flags, kPriPresetProbFl ); + bool secProbFl = cwIsFlag(mps.flags, kSecPresetProbFl ); + bool interpFl = cwIsFlag(mps.flags, kInterpPresetFl ); + + //printf("preset flags: pri:%i sec:%i interp:%i\n",priProbFl,secProbFl,interpFl); + + // verify that the set of candidate presets is not empty + if( mps.presetN == 0 ) + { + cwLogError(kInvalidArgRC,"A multi-preset application was requested but no presets were provided."); + goto errLabel; + } + + // if only a single candidate preset exists or needs to be selected + if( interpFl==false || mps.presetN==1 ) + { + // if only a single candidate preset is available or pri. probablity is not enabled + if( mps.presetN == 1 || priProbFl==false ) + label0 = mps.presetA[0].preset_label; + else + { + if( priProbFl ) + label0 = _select_ranked_ele_label_by_rank_prob( mps.presetA, nullptr, mps.presetN ); + else + label0 = mps.presetA[0].preset_label; + } + } + else // interpolation has been selected and at least 2 presets exist + { + unsigned pri_sel_idx = 0; + + // select the primary preset + if( priProbFl ) + pri_sel_idx = _select_ranked_ele_by_rank_prob( mps.presetA, nullptr, mps.presetN ); + else + { + // select all presets assigned to order == 1 + bool selA[ mps.presetN ]; + for(unsigned i=0; i + rc_t set_variable_value( network_t& net, const char* inst_label, const char* var_label, unsigned chIdx, T value ) + { + rc_t rc = kOkRC; + proc_t* inst = nullptr; + variable_t* var = nullptr; + + // get the variable + if((rc = get_variable(net,inst_label,var_label,chIdx,inst,var)) != kOkRC ) + goto errLabel; + + // set the variable value + if((rc = var_set( inst, var->vid, chIdx, value )) != kOkRC ) + { + rc = cwLogError(kOpFailRC,"The variable set failed on instance:'%s' variable:'%s'.",cwStringNullGuard(inst_label),cwStringNullGuard(var_label)); + goto errLabel; + } + + errLabel: + return rc; + } + + template< typename T > + rc_t get_variable_value( network_t& net, const char* inst_label, const char* var_label, unsigned chIdx, T& valueRef ) + { + rc_t rc = kOkRC; + proc_t* inst = nullptr; + variable_t* var = nullptr; + + // get the variable + if((rc = get_variable(net,inst_label,var_label,chIdx,inst,var)) != kOkRC ) + goto errLabel; + + // get the variable value + if((rc = var_get( inst, var->vid, chIdx, valueRef )) != kOkRC ) + { + rc = cwLogError(kOpFailRC,"The variable get failed on instance:'%s' variable:'%s'.",cwStringNullGuard(inst_label),cwStringNullGuard(var_label)); + goto errLabel; + } + + errLabel: + return rc; + } + + // 'proc_label_sfx_id' is the proc label_sfx_id to be used to identify all proc's which will + // be updated by the preset application. This is used to identify the set of procs to be updated + // for 'poly' networks. + // If 'proc_label_sfx_id' is set to 'kInvalidId' then the preset will be applied to all proc's. + rc_t network_apply_preset( network_t& net, const char* presetLabel, unsigned proc_label_sfx_id=kInvalidId ); + rc_t network_apply_dual_preset( network_t& net, const char* presetLabel_0, const char* presetLabel_1, double coeff, unsigned proc_label_sfx_id=kInvalidId ); + rc_t network_apply_preset( network_t& net, const multi_preset_selector_t& mps, unsigned proc_label_sfx_id=kInvalidId ); + + + } +} + + +#endif diff --git a/cwFlowProc.cpp b/cwFlowProc.cpp index 0e25254..6cd654c 100644 --- a/cwFlowProc.cpp +++ b/cwFlowProc.cpp @@ -1,16 +1,24 @@ #include "cwCommon.h" #include "cwLog.h" #include "cwCommonImpl.h" +#include "cwTest.h" #include "cwMem.h" +#include "cwText.h" #include "cwObject.h" #include "cwAudioFile.h" #include "cwVectOps.h" #include "cwMtx.h" -#include "cwDspTypes.h" // real_t, sample_t +#include "cwDspTypes.h" // srate_t, sample_t, coeff_t, ... + +#include "cwTime.h" +#include "cwMidiDecls.h" + + #include "cwFlowDecl.h" #include "cwFlow.h" #include "cwFlowTypes.h" +#include "cwFlowNet.h" #include "cwFlowProc.h" #include "cwFile.h" @@ -24,6 +32,39 @@ namespace cw namespace flow { + + template< typename inst_t > + rc_t std_destroy( proc_t* proc ) + { + inst_t* p = (inst_t*)proc->userPtr; + rc_t rc = _destroy(proc,p); + mem::release(proc->userPtr); + return rc; + } + + template< typename inst_t > + rc_t std_create( proc_t* proc ) + { + rc_t rc = kOkRC; + proc->userPtr = mem::allocZ(); + if((rc = _create(proc,(inst_t*)proc->userPtr)) != kOkRC ) + std_destroy(proc); + return rc; + } + + template< typename inst_t > + rc_t std_value( proc_t* proc, variable_t* var ) + { return _value(proc,(inst_t*)proc->userPtr, var); } + + template< typename inst_t > + rc_t std_exec( proc_t* proc ) + { return _exec(proc,(inst_t*)proc->userPtr); } + + template< typename inst_t > + rc_t std_report( proc_t* proc ) + { return _report(proc,(inst_t*)proc->userPtr); } + + //------------------------------------------------------------------------------------------------------------------ // // Template @@ -36,50 +77,235 @@ namespace cw } inst_t; - rc_t create( instance_t* ctx ) + rc_t _create( proc_t* proc, inst_t* p ) { rc_t rc = kOkRC; - inst_t* inst = mem::allocZ(); - ctx->userPtr = inst; // Custom create code goes here return rc; } - rc_t destroy( instance_t* ctx ) + rc_t _destroy( proc_t* proc, inst_t* p ) { rc_t rc = kOkRC; - inst_t* inst = (inst_t*)ctx->userPtr; - // Custom clean-up code goes here - mem::release(inst); + return rc; + } + + rc_t _value( proc_t* proc, inst_t* p, variable_t* var ) + { + rc_t rc = kOkRC; + return rc; + } + + rc_t _exec( proc_t* proc, inst_t* p ) + { + rc_t rc = kOkRC; return rc; } - rc_t value( instance_t* ctx, variable_t* var ) + rc_t _report( proc_t* proc, inst_t* p ) + { return kOkRC; } + + class_members_t members = { + .create = std_create, + .destroy = std_destroy, + .value = std_value, + .exec = std_exec, + .report = std_report + }; + + } + + //------------------------------------------------------------------------------------------------------------------ + // + // subnet + // + namespace subnet + { + typedef struct + { + network_t net; + } inst_t; + + + rc_t _create( proc_t* proc, inst_t* p ) + { + rc_t rc = kOkRC; + const object_t* networkCfg = nullptr; + + if((rc = proc->class_desc->cfg->getv("network",networkCfg)) != kOkRC ) + { + rc = cwLogError(rc,"The subnet 'network' cfg. was not found."); + goto errLabel; + } + + if((rc = network_create(proc->ctx,networkCfg,p->net,proc->varL)) != kOkRC ) + { + rc = cwLogError(rc,"Creation failed on the subnet internal network."); + goto errLabel; + } + + // Set the internal net pointer in the base proc instance + // so that network based utilities can scan it + proc->internal_net = &p->net; + + errLabel: + return rc; + } + + rc_t _destroy( proc_t* proc, inst_t* p ) + { + rc_t rc = kOkRC; + + network_destroy(p->net); + + return rc; + } + + rc_t _value( proc_t* proc, inst_t* p, variable_t* var ) { rc_t rc = kOkRC; return rc; } - rc_t exec( instance_t* ctx ) + rc_t _exec( proc_t* proc, inst_t* p ) { - rc_t rc = kOkRC; - //inst_t* inst = (inst_t*)ctx->userPtr; + rc_t rc = kOkRC; + + if((rc = exec_cycle(p->net)) != kOkRC ) + rc = cwLogError(rc,"poly internal network exec failed."); + + return rc; + } + + rc_t _report( proc_t* proc, inst_t* p ) + { return kOkRC; } + + class_members_t members = { + .create = std_create, + .destroy = std_destroy, + .value = std_value, + .exec = std_exec, + .report = std_report + }; + + } + + //------------------------------------------------------------------------------------------------------------------ + // + // poly + // + namespace poly + { + enum + { + kCountPId, + kOrderPId, + }; + + typedef struct + { + unsigned count; + network_t net; + network_order_id_t orderId; + } inst_t; + + + rc_t create( proc_t* proc ) + { + rc_t rc = kOkRC; + inst_t* inst = mem::allocZ(); + const object_t* networkCfg = nullptr; + const char* order_label = nullptr; + variable_t* proxyVarL = nullptr; + + proc->userPtr = inst; + + if((rc = var_register_and_get( proc, kAnyChIdx, + kCountPId, "count", kBaseSfxId, inst->count, + kOrderPId, "order", kBaseSfxId, order_label )) != kOkRC ) + goto errLabel; + + if( inst->count == 0 ) + { + cwLogWarning("The 'poly' %s:%i was given a count of 0.",proc->label,proc->label_sfx_id); + goto errLabel; + } + + if((rc = proc->proc_cfg->getv("network",networkCfg)) != kOkRC ) + { + rc = cwLogError(rc,"The 'network' cfg. was not found."); + goto errLabel; + } + + // get the network exec. order type + if( textIsEqual(order_label,"net") ) + inst->orderId = kNetFirstPolyOrderId; + else + { + if( textIsEqual(order_label,"proc") ) + inst->orderId = kProcFirstPolyOrderId; + else + { + rc = cwLogError(kInvalidArgRC,"'%s' is not one of the valid order types (i.e. 'net','proc').",order_label); + goto errLabel; + } + } + + if((rc = network_create(proc->ctx,networkCfg,inst->net,proxyVarL,inst->count)) != kOkRC ) + { + rc = cwLogError(rc,"Creation failed on the internal network."); + goto errLabel; + } + + + // Set the internal net pointer in the base proc instance + // so that network based utilities can scan it + proc->internal_net = &inst->net; + + errLabel: + return rc; + } + + rc_t destroy( proc_t* proc ) + { + inst_t* p = (inst_t*)proc->userPtr; + network_destroy(p->net); + + + mem::release( proc->userPtr ); + return kOkRC; + } + + rc_t value( proc_t* proc, variable_t* var ) + { + return kOkRC; + } + + rc_t exec( proc_t* proc ) + { + inst_t* p = (inst_t*)proc->userPtr; + rc_t rc = kOkRC; + + if((rc = exec_cycle(p->net)) != kOkRC ) + { + rc = cwLogError(rc,"poly internal network exec failed."); + } return rc; } class_members_t members = { - .create = create, - .destroy = destroy, - .value = value, - .exec = exec, - .report = nullptr + .create = create, + .destroy = destroy, + .value = value, + .exec = exec, + .report = nullptr }; } @@ -99,22 +325,22 @@ namespace cw typedef struct { - real_t value; + coeff_t value; } inst_t; - rc_t create( instance_t* ctx ) + rc_t create( proc_t* proc ) { rc_t rc = kOkRC; - real_t in_value = 0.5; - ctx->userPtr = mem::allocZ(); + coeff_t in_value = 0.5; + proc->userPtr = mem::allocZ(); - if((rc = var_register_and_get( ctx, kAnyChIdx, kInPId, "in", in_value )) != kOkRC ) + if((rc = var_register_and_get( proc, kAnyChIdx, kInPId, "in", kBaseSfxId, in_value )) != kOkRC ) goto errLabel; - if((rc = var_register_and_set( ctx, kAnyChIdx, - kOutPId, "out", in_value, - kInvOutPId, "inv_out", (real_t)(1.0-in_value) )) != kOkRC ) + if((rc = var_register_and_set( proc, kAnyChIdx, + kOutPId, "out", kBaseSfxId, in_value, + kInvOutPId, "inv_out", kBaseSfxId, (coeff_t)(1.0-in_value) )) != kOkRC ) { goto errLabel; } @@ -123,27 +349,27 @@ namespace cw return rc; } - rc_t destroy( instance_t* ctx ) + rc_t destroy( proc_t* proc ) { - mem::release( ctx->userPtr ); + mem::release( proc->userPtr ); return kOkRC; } - rc_t value( instance_t* ctx, variable_t* var ) + rc_t value( proc_t* proc, variable_t* var ) { return kOkRC; } - rc_t exec( instance_t* ctx ) + rc_t exec( proc_t* proc ) { rc_t rc = kOkRC; - inst_t* inst = (inst_t*)(ctx->userPtr); + inst_t* inst = (inst_t*)(proc->userPtr); - real_t value = 1; + coeff_t value = 1; - var_get(ctx, kInPId, kAnyChIdx, value); - var_set(ctx, kOutPId, kAnyChIdx, value); - var_set(ctx, kInvOutPId, kAnyChIdx, (real_t)(1.0 - value) ); + var_get(proc, kInPId, kAnyChIdx, value); + var_set(proc, kOutPId, kAnyChIdx, value); + var_set(proc, kInvOutPId, kAnyChIdx, (coeff_t)(1.0 - value) ); if( inst->value != value ) { @@ -155,15 +381,260 @@ namespace cw } class_members_t members = { - .create = create, - .destroy = destroy, - .value = value, - .exec = exec, - .report = nullptr + .create = create, + .destroy = destroy, + .value = value, + .exec = exec, + .report = nullptr }; } + + //------------------------------------------------------------------------------------------------------------------ + // + // midi_in + // + + namespace midi_in + { + enum + { + kDevLabelPId, + kPortLabelPId, + kOutPId + }; + + typedef struct + { + midi::ch_msg_t* buf; + unsigned bufN; + bool dev_filt_fl; + bool port_filt_fl; + external_device_t* ext_dev; + } inst_t; + + rc_t create( proc_t* proc ) + { + rc_t rc = kOkRC; + const char* dev_label = nullptr; + const char* port_label = nullptr; + inst_t* inst = mem::allocZ(); + + proc->userPtr = inst; + + // Register variable and get their current value + if((rc = var_register_and_get( proc, kAnyChIdx, + kDevLabelPId, "dev_label", kBaseSfxId, dev_label, + kPortLabelPId, "port_label", kBaseSfxId, port_label )) != kOkRC ) + + { + goto errLabel; + } + + if((rc = var_register( proc, kAnyChIdx, kOutPId, "out", kBaseSfxId)) != kOkRC ) + { + goto errLabel; + } + + + inst->dev_filt_fl = true; + inst->port_filt_fl = true; + + if( textIsEqual(dev_label,"") ) + { + inst->dev_filt_fl = false; + dev_label = nullptr; + } + + if( textIsEqual(dev_label,"") ) + { + inst->port_filt_fl = false; + port_label = nullptr; + } + + + + if((inst->ext_dev = external_device_find( proc->ctx, dev_label, kMidiDevTypeId, kInFl, port_label )) == nullptr ) + { + rc = cwLogError(kOpFailRC,"The MIDI input device '%s' port '%s' could not be found.", cwStringNullGuard(dev_label), cwStringNullGuard(port_label)); + goto errLabel; + } + + // Allocate a buffer large enough to hold the max. number of messages arriving on a single call to exec(). + inst->bufN = inst->ext_dev->u.m.maxMsgCnt; + inst->buf = mem::allocZ( inst->bufN ); + + // create one output audio buffer + rc = var_register_and_set( proc, "out", kBaseSfxId, kOutPId, kAnyChIdx, nullptr, 0 ); + + + errLabel: + return rc; + } + + rc_t destroy( proc_t* proc ) + { + rc_t rc = kOkRC; + + inst_t* inst = (inst_t*)proc->userPtr; + mem::release(inst->buf); + + mem::release(inst); + + return rc; + } + + rc_t value( proc_t* proc, variable_t* var ) + { return kOkRC; } + + rc_t exec( proc_t* proc ) + { + rc_t rc = kOkRC; + + inst_t* inst = (inst_t*)proc->userPtr; + mbuf_t* mbuf = nullptr; + + + // get the output variable + if((rc = var_get(proc,kOutPId,kAnyChIdx,mbuf)) != kOkRC ) + { + rc = cwLogError(kInvalidStateRC,"The MIDI file instance '%s' does not have a valid MIDI output buffer.",proc->label); + } + else + { + // if the device filter is not set + if( !inst->dev_filt_fl) + { + mbuf->msgA = inst->ext_dev->u.m.msgArray; + mbuf->msgN = inst->ext_dev->u.m.msgCnt; + } + else // the device filter is set + { + const midi::ch_msg_t* m = inst->ext_dev->u.m.msgArray; + unsigned j = 0; + for(unsigned i=0; iext_dev->u.m.msgCnt && jbufN; ++i) + if( m->devIdx == inst->ext_dev->ioDevIdx && (!inst->port_filt_fl || m->portIdx == inst->ext_dev->ioPortIdx) ) + inst->buf[j++] = m[i]; + + mbuf->msgN = j; + mbuf->msgA = inst->buf; + } + + + } + + return rc; + } + + class_members_t members = { + .create = create, + .destroy = destroy, + .value = value, + .exec = exec, + .report = nullptr + }; + + } + + + //------------------------------------------------------------------------------------------------------------------ + // + // midi_out + // + + namespace midi_out + { + enum + { + kInPId, + kDevLabelPId, + kPortLabelPId + }; + + typedef struct + { + external_device_t* ext_dev; + } inst_t; + + rc_t create( proc_t* proc ) + { + rc_t rc = kOkRC; // + inst_t* inst = mem::allocZ(); // + const char* dev_label = nullptr; + const char* port_label = nullptr; + mbuf_t* mbuf = nullptr; + + proc->userPtr = inst; + + // Register variables and get their current value + if((rc = var_register_and_get( proc, kAnyChIdx, + kDevLabelPId, "dev_label", kBaseSfxId, dev_label, + kPortLabelPId,"port_label", kBaseSfxId, port_label, + kInPId, "in", kBaseSfxId, mbuf)) != kOkRC ) + { + goto errLabel; + } + + if((inst->ext_dev = external_device_find( proc->ctx, dev_label, kMidiDevTypeId, kOutFl, port_label )) == nullptr ) + { + rc = cwLogError(kOpFailRC,"The audio output device description '%s' could not be found.", cwStringNullGuard(dev_label)); + goto errLabel; + } + + errLabel: + return rc; + } + + rc_t destroy( proc_t* proc ) + { + rc_t rc = kOkRC; + inst_t* inst = (inst_t*)proc->userPtr; + + mem::release(inst); + + return rc; + } + + rc_t value( proc_t* proc, variable_t* var ) + { + rc_t rc = kOkRC; + return rc; + } + + rc_t exec( proc_t* proc ) + { + rc_t rc = kOkRC; + inst_t* inst = (inst_t*)proc->userPtr; + const mbuf_t* src_mbuf = nullptr; + + if((rc = var_get(proc,kInPId,kAnyChIdx,src_mbuf)) != kOkRC ) + rc = cwLogError(kInvalidStateRC,"The MIDI output instance '%s' does not have a valid input connection.",proc->label); + else + { + for(unsigned i=0; imsgN; ++i) + { + const midi::ch_msg_t* m = src_mbuf->msgA + i; + inst->ext_dev->u.m.sendTripleFunc( inst->ext_dev, m->ch, m->status, m->d0, m->d1 ); + } + } + + return rc; + } + + class_members_t members = { + .create = create, + .destroy = destroy, + .value = value, + .exec = exec, + .report = nullptr + }; + + } + + + + //------------------------------------------------------------------------------------------------------------------ // @@ -184,21 +655,21 @@ namespace cw external_device_t* ext_dev; } inst_t; - rc_t create( instance_t* ctx ) + rc_t create( proc_t* proc ) { rc_t rc = kOkRC; inst_t* inst = mem::allocZ(); - ctx->userPtr = inst; + proc->userPtr = inst; // Register variable and get their current value - if((rc = var_register_and_get( ctx, kAnyChIdx, kDevLabelPId, "dev_label", inst->dev_label )) != kOkRC ) + if((rc = var_register_and_get( proc, kAnyChIdx, kDevLabelPId, "dev_label", kBaseSfxId, inst->dev_label )) != kOkRC ) { goto errLabel; } - if((inst->ext_dev = external_device_find( ctx->ctx, inst->dev_label, kAudioDevTypeId, kInFl )) == nullptr ) + if((inst->ext_dev = external_device_find( proc->ctx, inst->dev_label, kAudioDevTypeId, kInFl )) == nullptr ) { rc = cwLogError(kOpFailRC,"The audio input device description '%s' could not be found.", cwStringNullGuard(inst->dev_label)); goto errLabel; @@ -206,40 +677,40 @@ namespace cw // create one output audio buffer - rc = var_register_and_set( ctx, "out", kOutPId, kAnyChIdx, inst->ext_dev->u.a.abuf->srate, inst->ext_dev->u.a.abuf->chN, ctx->ctx->framesPerCycle ); + rc = var_register_and_set( proc, "out", kBaseSfxId, kOutPId, kAnyChIdx, inst->ext_dev->u.a.abuf->srate, inst->ext_dev->u.a.abuf->chN, proc->ctx->framesPerCycle ); errLabel: return rc; } - rc_t destroy( instance_t* ctx ) + rc_t destroy( proc_t* proc ) { rc_t rc = kOkRC; - inst_t* inst = (inst_t*)ctx->userPtr; + inst_t* inst = (inst_t*)proc->userPtr; mem::release(inst); return rc; } - rc_t value( instance_t* ctx, variable_t* var ) + rc_t value( proc_t* proc, variable_t* var ) { rc_t rc = kOkRC; return rc; } - rc_t exec( instance_t* ctx ) + rc_t exec( proc_t* proc ) { rc_t rc = kOkRC; - inst_t* inst = (inst_t*)ctx->userPtr; + inst_t* inst = (inst_t*)proc->userPtr; abuf_t* abuf = nullptr; // verify that a source buffer exists - if((rc = var_get(ctx,kOutPId,kAnyChIdx,abuf)) != kOkRC ) + if((rc = var_get(proc,kOutPId,kAnyChIdx,abuf)) != kOkRC ) { - rc = cwLogError(kInvalidStateRC,"The audio file instance '%s' does not have a valid audio output buffer.",ctx->label); + rc = cwLogError(kInvalidStateRC,"The audio input instance '%s' does not have a valid audio output buffer.",proc->label); } else { @@ -282,22 +753,22 @@ namespace cw } inst_t; - rc_t create( instance_t* ctx ) + rc_t create( proc_t* proc ) { rc_t rc = kOkRC; // inst_t* inst = mem::allocZ(); // const abuf_t* src_abuf = nullptr; - ctx->userPtr = inst; + proc->userPtr = inst; // Register variables and get their current value - if((rc = var_register_and_get( ctx, kAnyChIdx, - kDevLabelPId, "dev_label", inst->dev_label, - kInPId, "in", src_abuf)) != kOkRC ) + if((rc = var_register_and_get( proc, kAnyChIdx, + kDevLabelPId, "dev_label", kBaseSfxId, inst->dev_label, + kInPId, "in", kBaseSfxId, src_abuf)) != kOkRC ) { goto errLabel; } - if((inst->ext_dev = external_device_find( ctx->ctx, inst->dev_label, kAudioDevTypeId, kOutFl )) == nullptr ) + if((inst->ext_dev = external_device_find( proc->ctx, inst->dev_label, kAudioDevTypeId, kOutFl )) == nullptr ) { rc = cwLogError(kOpFailRC,"The audio output device description '%s' could not be found.", cwStringNullGuard(inst->dev_label)); goto errLabel; @@ -307,30 +778,30 @@ namespace cw return rc; } - rc_t destroy( instance_t* ctx ) + rc_t destroy( proc_t* proc ) { rc_t rc = kOkRC; - inst_t* inst = (inst_t*)ctx->userPtr; + inst_t* inst = (inst_t*)proc->userPtr; mem::release(inst); return rc; } - rc_t value( instance_t* ctx, variable_t* var ) + rc_t value( proc_t* proc, variable_t* var ) { rc_t rc = kOkRC; return rc; } - rc_t exec( instance_t* ctx ) + rc_t exec( proc_t* proc ) { rc_t rc = kOkRC; - inst_t* inst = (inst_t*)ctx->userPtr; + inst_t* inst = (inst_t*)proc->userPtr; const abuf_t* src_abuf = nullptr; - if((rc = var_get(ctx,kInPId,kAnyChIdx,src_abuf)) != kOkRC ) - rc = cwLogError(kInvalidStateRC,"The audio file instance '%s' does not have a valid input connection.",ctx->label); + if((rc = var_get(proc,kInPId,kAnyChIdx,src_abuf)) != kOkRC ) + rc = cwLogError(kInvalidStateRC,"The audio file instance '%s' does not have a valid input connection.",proc->label); else { unsigned chN = std::min(inst->ext_dev->u.a.abuf->chN, src_abuf->chN); @@ -358,10 +829,10 @@ namespace cw //------------------------------------------------------------------------------------------------------------------ // - // AudioFileIn + // audio_file_in // - namespace audioFileIn + namespace audio_file_in { enum { @@ -376,31 +847,39 @@ namespace cw { audiofile::handle_t afH; bool eofFl; - const char* filename; + char* filename; } inst_t; - rc_t create( instance_t* ctx ) + rc_t create( proc_t* proc ) { rc_t rc = kOkRC; audiofile::info_t info; - real_t seekSecs; + ftime_t seekSecs; + const char* fname = nullptr; inst_t* inst = mem::allocZ(); - ctx->userPtr = inst; + proc->userPtr = inst; - if((rc = var_register( ctx, kAnyChIdx, kOnOffFlPId, "on_off" )) != kOkRC ) + if((rc = var_register( proc, kAnyChIdx, kOnOffFlPId, "on_off", kBaseSfxId)) != kOkRC ) { goto errLabel; } // Register variable and get their current value - if((rc = var_register_and_get( ctx, kAnyChIdx, - kFnamePId, "fname", inst->filename, - kSeekSecsPId, "seekSecs", seekSecs, - kEofFlPId, "eofFl", inst->eofFl )) != kOkRC ) + if((rc = var_register_and_get( proc, kAnyChIdx, + kFnamePId, "fname", kBaseSfxId, fname, + kSeekSecsPId, "seekSecs", kBaseSfxId, seekSecs, + kEofFlPId, "eofFl", kBaseSfxId, inst->eofFl )) != kOkRC ) { goto errLabel; } + if((inst->filename = proc_expand_filename(proc,fname)) == nullptr ) + { + rc = cwLogError(kInvalidArgRC,"The audio output filename could not be formed."); + goto errLabel; + } + + // open the audio file if((rc = audiofile::open(inst->afH,inst->filename,&info)) != kOkRC ) { @@ -418,35 +897,36 @@ namespace cw cwLogInfo("Audio '%s' srate:%f chs:%i frames:%i %f seconds.",inst->filename,info.srate,info.chCnt,info.frameCnt, info.frameCnt/info.srate ); // create one output audio buffer - with the same configuration as the source audio file - rc = var_register_and_set( ctx, "out", kOutPId, kAnyChIdx, info.srate, info.chCnt, ctx->ctx->framesPerCycle ); + rc = var_register_and_set( proc, "out", kBaseSfxId, kOutPId, kAnyChIdx, info.srate, info.chCnt, proc->ctx->framesPerCycle ); errLabel: return rc; } - rc_t destroy( instance_t* ctx ) + rc_t destroy( proc_t* proc ) { rc_t rc = kOkRC; - inst_t* inst = (inst_t*)ctx->userPtr; + inst_t* inst = (inst_t*)proc->userPtr; if((rc = audiofile::close(inst->afH)) != kOkRC ) { rc = cwLogError(kOpFailRC,"The close failed on the audio file '%s'.", cwStringNullGuard(inst->filename) ); } + mem::release(inst->filename); mem::release(inst); return rc; } - rc_t value( instance_t* ctx, variable_t* var ) + rc_t value( proc_t* proc, variable_t* var ) { rc_t rc = kOkRC; - real_t seekSecs = 0; - inst_t* inst = (inst_t*)ctx->userPtr; + ftime_t seekSecs = 0; + inst_t* inst = (inst_t*)proc->userPtr; - if((rc = var_get(ctx,kSeekSecsPId,kAnyChIdx,seekSecs)) != kOkRC ) + if((rc = var_get(proc,kSeekSecsPId,kAnyChIdx,seekSecs)) != kOkRC ) goto errLabel; if((rc = seek( inst->afH, (unsigned)lround(seekSecs * audiofile::sampleRate(inst->afH) ) )) != kOkRC ) @@ -460,22 +940,22 @@ namespace cw return kOkRC; } - rc_t exec( instance_t* ctx ) + rc_t exec( proc_t* proc ) { rc_t rc = kOkRC; unsigned actualFrameN = 0; - inst_t* inst = (inst_t*)ctx->userPtr; + inst_t* inst = (inst_t*)proc->userPtr; abuf_t* abuf = nullptr; bool onOffFl = false; // get the 'on-off; flag - if((rc = var_get(ctx,kOnOffFlPId,kAnyChIdx,onOffFl)) != kOkRC ) + if((rc = var_get(proc,kOnOffFlPId,kAnyChIdx,onOffFl)) != kOkRC ) goto errLabel; // verify that a source buffer exists - if((rc = var_get(ctx,kOutPId,kAnyChIdx,abuf)) != kOkRC ) + if((rc = var_get(proc,kOutPId,kAnyChIdx,abuf)) != kOkRC ) { - rc = cwLogError(kInvalidStateRC,"The audio file instance '%s' does not have a valid audio output buffer.",ctx->label); + rc = cwLogError(kInvalidStateRC,"The audio file instance '%s' does not have a valid audio output buffer.",proc->label); } else { @@ -515,10 +995,10 @@ namespace cw //------------------------------------------------------------------------------------------------------------------ // - // AudioFileOut + // audio_file_out // - namespace audioFileOut + namespace audio_file_out { enum { @@ -530,27 +1010,36 @@ namespace cw typedef struct { audiofile::handle_t afH; - const char* filename; + char* filename; unsigned durSmpN; } inst_t; - rc_t create( instance_t* ctx ) + rc_t create( proc_t* proc ) { rc_t rc = kOkRC; // unsigned audioFileBits = 0; // set audio file sample format to 'float32'. inst_t* inst = mem::allocZ(); // const abuf_t* src_abuf = nullptr; - ctx->userPtr = inst; + const char* fname = nullptr; + + proc->userPtr = inst; // Register variables and get their current value - if((rc = var_register_and_get( ctx, kAnyChIdx, - kFnamePId, "fname", inst->filename, - kBitsPId, "bits", audioFileBits, - kInPId, "in", src_abuf )) != kOkRC ) + if((rc = var_register_and_get( proc, kAnyChIdx, + kFnamePId, "fname", kBaseSfxId, fname, + kBitsPId, "bits", kBaseSfxId, audioFileBits, + kInPId, "in", kBaseSfxId, src_abuf )) != kOkRC ) { goto errLabel; } + + if((inst->filename = proc_expand_filename(proc,fname)) == nullptr ) + { + rc = cwLogError(kInvalidArgRC,"The audio output filename could not be formed."); + goto errLabel; + } + // create the audio file with the same channel count as the incoming signal if((rc = audiofile::create( inst->afH, inst->filename, src_abuf->srate, audioFileBits, src_abuf->chN)) != kOkRC ) { @@ -562,10 +1051,10 @@ namespace cw return rc; } - rc_t destroy( instance_t* ctx ) + rc_t destroy( proc_t* proc ) { rc_t rc = kOkRC; - inst_t* inst = (inst_t*)ctx->userPtr; + inst_t* inst = (inst_t*)proc->userPtr; // close the audio file if((rc = audiofile::close( inst->afH )) != kOkRC ) @@ -574,26 +1063,27 @@ namespace cw goto errLabel; } + mem::release(inst->filename); mem::release(inst); errLabel: return rc; } - rc_t value( instance_t* ctx, variable_t* var ) + rc_t value( proc_t* proc, variable_t* var ) { rc_t rc = kOkRC; return rc; } - rc_t exec( instance_t* ctx ) + rc_t exec( proc_t* proc ) { rc_t rc = kOkRC; - inst_t* inst = (inst_t*)ctx->userPtr; + inst_t* inst = (inst_t*)proc->userPtr; const abuf_t* src_abuf = nullptr; - if((rc = var_get(ctx,kInPId,kAnyChIdx,src_abuf)) != kOkRC ) - rc = cwLogError(kInvalidStateRC,"The audio file instance '%s' does not have a valid input connection.",ctx->label); + if((rc = var_get(proc,kInPId,kAnyChIdx,src_abuf)) != kOkRC ) + rc = cwLogError(kInvalidStateRC,"The audio file instance '%s' does not have a valid input connection.",proc->label); else { sample_t* chBuf[ src_abuf->chN ]; @@ -602,13 +1092,15 @@ namespace cw chBuf[i] = src_abuf->buf + (i*src_abuf->frameN); if((rc = audiofile::writeFloat(inst->afH, src_abuf->frameN, src_abuf->chN, chBuf )) != kOkRC ) - rc = cwLogError(rc,"Audio file write failed on instance: '%s'.", ctx->label ); + rc = cwLogError(rc,"Audio file write failed on instance: '%s'.", proc->label ); // print a minutes counter inst->durSmpN += src_abuf->frameN; - if( inst->durSmpN % ((unsigned)src_abuf->srate*60) == 0 ) + if( src_abuf->srate!=0 && inst->durSmpN % ((unsigned)src_abuf->srate*60) == 0 ) printf("audio file out: %5.1f min\n", inst->durSmpN/(src_abuf->srate*60)); - + + //if( 48000 <= inst->durSmpN && inst->durSmpN < 49000 ) + // printf("break\n"); } return rc; @@ -640,67 +1132,67 @@ namespace cw typedef struct inst_str { unsigned n; - real_t vgain; - real_t gain; + coeff_t vgain; + coeff_t gain; } inst_t; - rc_t create( instance_t* ctx ) + rc_t create( proc_t* proc ) { rc_t rc = kOkRC; const abuf_t* abuf = nullptr; // - ctx->userPtr = mem::allocZ(); + proc->userPtr = mem::allocZ(); // get the source audio buffer - if((rc = var_register_and_get(ctx, kAnyChIdx,kInPId,"in",abuf )) != kOkRC ) + if((rc = var_register_and_get(proc, kAnyChIdx,kInPId,"in",kBaseSfxId,abuf )) != kOkRC ) goto errLabel; // register the gain for(unsigned i=0; ichN; ++i) - if((rc = var_register( ctx, i, kGainPId, "gain" )) != kOkRC ) + if((rc = var_register( proc, i, kGainPId, "gain", kBaseSfxId )) != kOkRC ) goto errLabel; // create the output audio buffer - rc = var_register_and_set( ctx, "out", kOutPId, kAnyChIdx, abuf->srate, abuf->chN, abuf->frameN ); + rc = var_register_and_set( proc, "out", kBaseSfxId, kOutPId, kAnyChIdx, abuf->srate, abuf->chN, abuf->frameN ); errLabel: return rc; } - rc_t destroy( instance_t* ctx ) + rc_t destroy( proc_t* proc ) { - inst_t* inst = (inst_t*)(ctx->userPtr); + inst_t* inst = (inst_t*)(proc->userPtr); mem::release(inst); return kOkRC; } - rc_t value( instance_t* ctx, variable_t* var ) + rc_t value( proc_t* proc, variable_t* var ) { - real_t value = 0; - inst_t* inst = (inst_t*)ctx->userPtr; - var_get(ctx,kGainPId,0,value); + coeff_t value = 0; + inst_t* inst = (inst_t*)proc->userPtr; + var_get(proc,kGainPId,0,value); if( inst->vgain != value ) { inst->vgain = value; - //printf("VALUE GAIN: %s %s : %f\n", ctx->label, var->label, value ); + //printf("VALUE GAIN: %s %s : %f\n", proc->label, var->label, value ); } return kOkRC; } - rc_t exec( instance_t* ctx ) + rc_t exec( proc_t* proc ) { rc_t rc = kOkRC; const abuf_t* ibuf = nullptr; abuf_t* obuf = nullptr; - inst_t* inst = (inst_t*)(ctx->userPtr); + inst_t* inst = (inst_t*)(proc->userPtr); // get the src buffer - if((rc = var_get(ctx,kInPId, kAnyChIdx, ibuf )) != kOkRC ) + if((rc = var_get(proc,kInPId, kAnyChIdx, ibuf )) != kOkRC ) goto errLabel; // get the dst buffer - if((rc = var_get(ctx,kOutPId, kAnyChIdx, obuf)) != kOkRC ) + if((rc = var_get(proc,kOutPId, kAnyChIdx, obuf)) != kOkRC ) goto errLabel; // for each channel @@ -710,7 +1202,7 @@ namespace cw sample_t* osig = obuf->buf + i*obuf->frameN; sample_t gain = 1; - var_get(ctx,kGainPId,i,gain); + var_get(proc,kGainPId,i,gain); // apply the gain for(unsigned j=0; jframeN; ++j) @@ -719,8 +1211,8 @@ namespace cw if( i==0 && gain != inst->gain ) { inst->gain = gain; - //printf("EXEC GAIN: %s %f\n",ctx->label,gain); - //instance_print(ctx); + //printf("EXEC GAIN: %s %f\n",proc->label,gain); + //proc_print(proc); } } @@ -761,16 +1253,16 @@ namespace cw } inst_t; - rc_t create( instance_t* ctx ) + rc_t create( proc_t* proc ) { rc_t rc = kOkRC; const abuf_t* abuf = nullptr; // inst_t* inst = mem::allocZ(); - ctx->userPtr = inst; + proc->userPtr = inst; // get the source audio buffer - if((rc = var_register_and_get(ctx, kAnyChIdx,kInPId,"in",abuf )) != kOkRC ) + if((rc = var_register_and_get(proc, kAnyChIdx,kInPId,"in",kBaseSfxId, abuf )) != kOkRC ) goto errLabel; if( abuf->chN ) @@ -779,20 +1271,20 @@ namespace cw inst->chSelMap = mem::allocZ(abuf->chN); - if((rc = var_channel_count(ctx,"select",selChN)) != kOkRC ) + if((rc = var_channel_count(proc,"select",kBaseSfxId,selChN)) != kOkRC ) goto errLabel; // register the gain for(unsigned i=0; ichN; ++i) { if( i < selChN ) - if((rc = var_register_and_get( ctx, i, kSelectPId, "select", inst->chSelMap[i] )) != kOkRC ) + if((rc = var_register_and_get( proc, i, kSelectPId, "select", kBaseSfxId, inst->chSelMap[i] )) != kOkRC ) goto errLabel; if( inst->chSelMap[i] ) { // register an output gain control - if((rc = var_register( ctx, inst->outChN, kGainPId, "gain")) != kOkRC ) + if((rc = var_register( proc, inst->outChN, kGainPId, "gain", kBaseSfxId)) != kOkRC ) goto errLabel; // count the number of selected channels to determine the count of output channels @@ -802,20 +1294,20 @@ namespace cw // create the output audio buffer if( inst->outChN == 0 ) - cwLogWarning("The audio split instance '%s' has no selected channels.",ctx->label); + cwLogWarning("The audio split instance '%s' has no selected channels.",proc->label); else - rc = var_register_and_set( ctx, "out", kOutPId, kAnyChIdx, abuf->srate, inst->outChN, abuf->frameN ); + rc = var_register_and_set( proc, "out", kBaseSfxId, kOutPId, kAnyChIdx, abuf->srate, inst->outChN, abuf->frameN ); } errLabel: return rc; } - rc_t destroy( instance_t* ctx ) + rc_t destroy( proc_t* proc ) { rc_t rc = kOkRC; - inst_t* inst = (inst_t*)ctx->userPtr; + inst_t* inst = (inst_t*)proc->userPtr; mem::release(inst->chSelMap); @@ -824,28 +1316,28 @@ namespace cw return rc; } - rc_t value( instance_t* ctx, variable_t* var ) + rc_t value( proc_t* proc, variable_t* var ) { rc_t rc = kOkRC; return rc; } - rc_t exec( instance_t* ctx ) + rc_t exec( proc_t* proc ) { rc_t rc = kOkRC; const abuf_t* ibuf = nullptr; abuf_t* obuf = nullptr; - inst_t* inst = (inst_t*)ctx->userPtr; + inst_t* inst = (inst_t*)proc->userPtr; unsigned outChIdx = 0; if( inst->outChN ) { // get the src buffer - if((rc = var_get(ctx,kInPId, kAnyChIdx, ibuf )) != kOkRC ) + if((rc = var_get(proc,kInPId, kAnyChIdx, ibuf )) != kOkRC ) goto errLabel; // get the dst buffer - if((rc = var_get(ctx,kOutPId, kAnyChIdx, obuf)) != kOkRC ) + if((rc = var_get(proc,kOutPId, kAnyChIdx, obuf)) != kOkRC ) goto errLabel; // for each channel @@ -857,7 +1349,7 @@ namespace cw sample_t* osig = obuf->buf + outChIdx * obuf->frameN; sample_t gain = 1; - var_get(ctx,kGainPId,outChIdx,gain); + var_get(proc,kGainPId,outChIdx,gain); // apply the gain for(unsigned j=0; jframeN; ++j) @@ -902,16 +1394,16 @@ namespace cw } inst_t; - rc_t create( instance_t* ctx ) + rc_t create( proc_t* proc ) { rc_t rc = kOkRC; const abuf_t* abuf = nullptr; // inst_t* inst = mem::allocZ(); - ctx->userPtr = inst; + proc->userPtr = inst; // get the source audio buffer - if((rc = var_register_and_get(ctx, kAnyChIdx,kInPId,"in",abuf )) != kOkRC ) + if((rc = var_register_and_get(proc, kAnyChIdx,kInPId,"in",kBaseSfxId,abuf )) != kOkRC ) goto errLabel; if( abuf->chN ) @@ -921,13 +1413,13 @@ namespace cw // register the gain for(unsigned i=0; ichN; ++i) { - if((rc = var_register_and_get( ctx, i, kDuplicatePId, "duplicate", inst->chDuplMap[i] )) != kOkRC ) + if((rc = var_register_and_get( proc, i, kDuplicatePId, "duplicate", kBaseSfxId, inst->chDuplMap[i] )) != kOkRC ) goto errLabel; if( inst->chDuplMap[i] ) { // register an input gain control - if((rc = var_register( ctx, inst->outChN, kGainPId, "gain")) != kOkRC ) + if((rc = var_register( proc, inst->outChN, kGainPId, "gain", kBaseSfxId)) != kOkRC ) goto errLabel; // count the number of selected channels to determine the count of output channels @@ -937,20 +1429,20 @@ namespace cw // create the output audio buffer if( inst->outChN == 0 ) - cwLogWarning("The audio split instance '%s' has no selected channels.",ctx->label); + cwLogWarning("The audio split instance '%s' has no selected channels.",proc->label); else - rc = var_register_and_set( ctx, "out", kOutPId, kAnyChIdx, abuf->srate, inst->outChN, abuf->frameN ); + rc = var_register_and_set( proc, "out", kBaseSfxId, kOutPId, kAnyChIdx, abuf->srate, inst->outChN, abuf->frameN ); } errLabel: return rc; } - rc_t destroy( instance_t* ctx ) + rc_t destroy( proc_t* proc ) { rc_t rc = kOkRC; - inst_t* inst = (inst_t*)ctx->userPtr; + inst_t* inst = (inst_t*)proc->userPtr; mem::release(inst->chDuplMap); @@ -959,28 +1451,28 @@ namespace cw return rc; } - rc_t value( instance_t* ctx, variable_t* var ) + rc_t value( proc_t* proc, variable_t* var ) { rc_t rc = kOkRC; return rc; } - rc_t exec( instance_t* ctx ) + rc_t exec( proc_t* proc ) { rc_t rc = kOkRC; const abuf_t* ibuf = nullptr; abuf_t* obuf = nullptr; - inst_t* inst = (inst_t*)ctx->userPtr; + inst_t* inst = (inst_t*)proc->userPtr; unsigned outChIdx = 0; if( inst->outChN ) { // get the src buffer - if((rc = var_get(ctx,kInPId, kAnyChIdx, ibuf )) != kOkRC ) + if((rc = var_get(proc,kInPId, kAnyChIdx, ibuf )) != kOkRC ) goto errLabel; // get the dst buffer - if((rc = var_get(ctx,kOutPId, kAnyChIdx, obuf)) != kOkRC ) + if((rc = var_get(proc,kOutPId, kAnyChIdx, obuf)) != kOkRC ) goto errLabel; // for each input channel @@ -989,7 +1481,7 @@ namespace cw sample_t* isig = ibuf->buf + i * ibuf->frameN; sample_t gain = 1; - var_get(ctx,kGainPId,i,gain); + var_get(proc,kGainPId,i,gain); for(unsigned j=0; jchDuplMap[i]; ++j ) { @@ -1036,7 +1528,7 @@ namespace cw } inst_t; - rc_t create( instance_t* ctx ) + rc_t create( proc_t* proc ) { rc_t rc = kOkRC; unsigned outChN = 0; @@ -1045,7 +1537,7 @@ namespace cw inst_t* inst = mem::allocZ(); - ctx->userPtr = inst; + proc->userPtr = inst; for(unsigned i=0; 1; ++i) { @@ -1058,11 +1550,11 @@ namespace cw // TODO: allow non-contiguous source labels // the source labels must be contiguous - if( !var_has_value( ctx, label, kAnyChIdx ) ) + if( !var_has_value( proc, label, kBaseSfxId, kAnyChIdx ) ) break; // get the source audio buffer - if((rc = var_register_and_get(ctx, kAnyChIdx,kInBasePId+i,label,abuf )) != kOkRC ) + if((rc = var_register_and_get(proc, kAnyChIdx,kInBasePId+i,label,kBaseSfxId, abuf )) != kOkRC ) { goto errLabel; } @@ -1086,29 +1578,29 @@ namespace cw // register the gain for(unsigned i=0; iuserPtr; + inst_t* inst = (inst_t*)proc->userPtr; mem::release(inst); return kOkRC; } - rc_t value( instance_t* ctx, variable_t* var ) + rc_t value( proc_t* proc, variable_t* var ) { return kOkRC; } - unsigned _exec( instance_t* ctx, const abuf_t* ibuf, abuf_t* obuf, unsigned outChIdx ) + unsigned _exec( proc_t* proc, const abuf_t* ibuf, abuf_t* obuf, unsigned outChIdx ) { // for each channel for(unsigned i=0; ichN && outChIdxchN; ++i) @@ -1118,7 +1610,7 @@ namespace cw sample_t* osig = obuf->buf + outChIdx * obuf->frameN; sample_t gain = 1; - var_get(ctx,kGainPId,outChIdx,gain); + var_get(proc,kGainPId,outChIdx,gain); // apply the gain for(unsigned j=0; jframeN; ++j) @@ -1131,7 +1623,7 @@ namespace cw } /* - rc_t exec( instance_t* ctx ) + rc_t exec( proc_t* proc ) { rc_t rc = kOkRC; const abuf_t* ibuf0 = nullptr; @@ -1139,17 +1631,17 @@ namespace cw abuf_t* obuf = nullptr; unsigned oChIdx = 0; - if((rc = var_get(ctx,kIn0PId, kAnyChIdx, ibuf0 )) != kOkRC ) + if((rc = var_get(proc,kIn0PId, kAnyChIdx, ibuf0 )) != kOkRC ) goto errLabel; - if((rc = var_get(ctx,kIn1PId, kAnyChIdx, ibuf1 )) != kOkRC ) + if((rc = var_get(proc,kIn1PId, kAnyChIdx, ibuf1 )) != kOkRC ) goto errLabel; - if((rc = var_get(ctx,kOutPId, kAnyChIdx, obuf)) != kOkRC ) + if((rc = var_get(proc,kOutPId, kAnyChIdx, obuf)) != kOkRC ) goto errLabel; - oChIdx = _exec( ctx, ibuf0, obuf, oChIdx ); - oChIdx = _exec( ctx, ibuf1, obuf, oChIdx ); + oChIdx = _exec( proc, ibuf0, obuf, oChIdx ); + oChIdx = _exec( proc, ibuf1, obuf, oChIdx ); assert( oChIdx == obuf->chN ); @@ -1158,24 +1650,24 @@ namespace cw } */ - rc_t exec( instance_t* ctx ) + rc_t exec( proc_t* proc ) { rc_t rc = kOkRC; - inst_t* inst = (inst_t*)ctx->userPtr; + inst_t* inst = (inst_t*)proc->userPtr; abuf_t* obuf = nullptr; unsigned oChIdx = 0; - if((rc = var_get(ctx,kOutPId, kAnyChIdx, obuf)) != kOkRC ) + if((rc = var_get(proc,kOutPId, kAnyChIdx, obuf)) != kOkRC ) goto errLabel; for(unsigned i=0; isrcN; ++i) { const abuf_t* ibuf = nullptr; - if((rc = var_get(ctx,kInBasePId+i, kAnyChIdx, ibuf )) != kOkRC ) + if((rc = var_get(proc,kInBasePId+i, kAnyChIdx, ibuf )) != kOkRC ) goto errLabel; - oChIdx = _exec( ctx, ibuf, obuf, oChIdx ); + oChIdx = _exec( proc, ibuf, obuf, oChIdx ); } errLabel: @@ -1212,7 +1704,7 @@ namespace cw } inst_t; - rc_t create( instance_t* ctx ) + rc_t create( proc_t* proc ) { rc_t rc = kOkRC; const abuf_t* abuf0 = nullptr; // @@ -1221,9 +1713,9 @@ namespace cw double dum; // get the source audio buffer - if((rc = var_register_and_get(ctx, kAnyChIdx, - kIn0PId,"in0",abuf0, - kIn1PId,"in1",abuf1 )) != kOkRC ) + if((rc = var_register_and_get(proc, kAnyChIdx, + kIn0PId,"in0",kBaseSfxId,abuf0, + kIn1PId,"in1",kBaseSfxId,abuf1 )) != kOkRC ) { goto errLabel; } @@ -1233,28 +1725,28 @@ namespace cw outChN = std::max(abuf0->chN, abuf1->chN); // register the gain - var_register_and_get( ctx, kAnyChIdx, kGain0PId, "gain0", dum ); - var_register_and_get( ctx, kAnyChIdx, kGain1PId, "gain1", dum ); + var_register_and_get( proc, kAnyChIdx, kGain0PId, "gain0", kBaseSfxId, dum ); + var_register_and_get( proc, kAnyChIdx, kGain1PId, "gain1", kBaseSfxId, dum ); // create the output audio buffer - rc = var_register_and_set( ctx, "out", kOutPId, kAnyChIdx, abuf0->srate, outChN, abuf0->frameN ); + rc = var_register_and_set( proc, "out", kBaseSfxId, kOutPId, kAnyChIdx, abuf0->srate, outChN, abuf0->frameN ); errLabel: return rc; } - rc_t destroy( instance_t* ctx ) + rc_t destroy( proc_t* proc ) { return kOkRC; } - rc_t value( instance_t* ctx, variable_t* var ) + rc_t value( proc_t* proc, variable_t* var ) { return kOkRC; } - rc_t _mix( instance_t* ctx, unsigned inPId, unsigned gainPId, abuf_t* obuf ) + rc_t _mix( proc_t* proc, unsigned inPId, unsigned gainPId, abuf_t* obuf ) { rc_t rc = kOkRC; const abuf_t* ibuf = nullptr; - if((rc = var_get(ctx, inPId, kAnyChIdx, ibuf )) != kOkRC ) + if((rc = var_get(proc, inPId, kAnyChIdx, ibuf )) != kOkRC ) goto errLabel; @@ -1266,9 +1758,9 @@ namespace cw { const sample_t* isig = ibuf->buf + i*ibuf->frameN; sample_t* osig = obuf->buf + i*obuf->frameN; - real_t gain = 1; + coeff_t gain = 1; - if((rc = var_get(ctx, gainPId, kAnyChIdx, gain)) != kOkRC ) + if((rc = var_get(proc, gainPId, kAnyChIdx, gain)) != kOkRC ) goto errLabel; for(unsigned j=0; jframeN; ++j) @@ -1281,26 +1773,26 @@ namespace cw } - rc_t exec( instance_t* ctx ) + rc_t exec( proc_t* proc ) { rc_t rc = kOkRC; abuf_t* obuf = nullptr; //const abuf_t* ibuf0 = nullptr; //const abuf_t* ibuf1 = nullptr; - if((rc = var_get(ctx,kOutPId, kAnyChIdx, obuf)) != kOkRC ) + if((rc = var_get(proc,kOutPId, kAnyChIdx, obuf)) != kOkRC ) goto errLabel; - //if((rc = var_get(ctx,kIn0PId, kAnyChIdx, ibuf0 )) != kOkRC ) + //if((rc = var_get(proc,kIn0PId, kAnyChIdx, ibuf0 )) != kOkRC ) // goto errLabel; - //if((rc = var_get(ctx,kIn1PId, kAnyChIdx, ibuf1 )) != kOkRC ) + //if((rc = var_get(proc,kIn1PId, kAnyChIdx, ibuf1 )) != kOkRC ) // goto errLabel; vop::zero(obuf->buf, obuf->frameN*obuf->chN ); - _mix( ctx, kIn0PId, kGain0PId, obuf ); - _mix( ctx, kIn1PId, kGain1PId, obuf ); + _mix( proc, kIn0PId, kGain0PId, obuf ); + _mix( proc, kIn1PId, kGain1PId, obuf ); errLabel: return rc; @@ -1329,93 +1821,128 @@ namespace cw kSratePId, kChCntPid, kFreqHzPId, + kPhasePId, + kDcPId, kGainPId, kOutPId }; typedef struct { - real_t *phaseA; + double *phaseA; } inst_t; - rc_t create( instance_t* ctx ) + rc_t create( proc_t* proc ) { rc_t rc = kOkRC; inst_t* inst = mem::allocZ(); srate_t srate = 0; unsigned chCnt = 0; - real_t gain; - real_t hz; + coeff_t gain; + coeff_t hz; + coeff_t phase; + coeff_t dc; - ctx->userPtr = inst; + proc->userPtr = inst; // Register variables and get their current value - if((rc = var_register_and_get( ctx, kAnyChIdx, kChCntPid, "chCnt", chCnt)) != kOkRC ) + if((rc = var_register_and_get( proc, kAnyChIdx, + kChCntPid, "chCnt", kBaseSfxId, chCnt, + kSratePId, "srate", kBaseSfxId, srate)) != kOkRC ) { goto errLabel; } + // Sample rate logic: + // The sample rate may be set directly, or sourced. + // If the srate is 0 then this indicates that the system sample rate should be used. + // if the sample rate is sourced and 0 it is a configuration error. + + // if no sample rate was given then use the system sample rate. + if( srate == 0 ) + srate = proc->ctx->sample_rate; + // register each oscillator variable for(unsigned i=0; ictx->framesPerCycle ); + // if no srate was set on this channel then use the default sample rate + if( ch_srate == 0 ) + if((rc = var_set(proc,kSratePId,i,srate)) != kOkRC ) + goto errLabel; + } - inst->phaseA = mem::allocZ( chCnt ); + //printf("%s: sr:%f hz:%f phs:%f dc:%f gain:%f\n",proc->label,srate,hz,phase,dc,gain); + + // create one output audio buffer + rc = var_register_and_set( proc, "out", kBaseSfxId, + kOutPId, kAnyChIdx, srate, chCnt, proc->ctx->framesPerCycle ); + + inst->phaseA = mem::allocZ( chCnt ); errLabel: return rc; } - rc_t destroy( instance_t* ctx ) + rc_t destroy( proc_t* proc ) { rc_t rc = kOkRC; - inst_t* inst = (inst_t*)ctx->userPtr; + inst_t* inst = (inst_t*)proc->userPtr; + mem::release(inst->phaseA); mem::release(inst); return rc; } - rc_t value( instance_t* ctx, variable_t* var ) + rc_t value( proc_t* proc, variable_t* var ) { rc_t rc = kOkRC; return rc; } - rc_t exec( instance_t* ctx ) + rc_t exec( proc_t* proc ) { rc_t rc = kOkRC; - inst_t* inst = (inst_t*)ctx->userPtr; + inst_t* inst = (inst_t*)proc->userPtr; abuf_t* abuf = nullptr; // get the output signal buffer - if((rc = var_get(ctx,kOutPId,kAnyChIdx,abuf)) != kOkRC ) + if((rc = var_get(proc,kOutPId,kAnyChIdx,abuf)) != kOkRC ) { - rc = cwLogError(kInvalidStateRC,"The Sine Tone instance '%s' does not have a valid audio output buffer.",ctx->label); + rc = cwLogError(kInvalidStateRC,"The Sine Tone instance '%s' does not have a valid audio output buffer.",proc->label); } else { for(unsigned i=0; ichN; ++i) { - real_t gain = val_get( ctx, kGainPId, i ); - real_t hz = val_get( ctx, kFreqHzPId, i ); - srate_t srate = val_get( ctx, kSratePId, i ); + coeff_t gain = val_get( proc, kGainPId, i ); + coeff_t hz = val_get( proc, kFreqHzPId, i ); + coeff_t phase = val_get( proc, kPhasePId, i ); + coeff_t dc = val_get( proc, kDcPId, i ); + srate_t srate = val_get(proc, kSratePId, i ); sample_t* v = abuf->buf + (i*abuf->frameN); for(unsigned j=0; jframeN; ++j) - v[j] = (sample_t)(gain * sin( inst->phaseA[i] + (2.0 * M_PI * j * hz/srate))); + v[j] = (sample_t)((gain * sin( inst->phaseA[i] + phase + (2.0 * M_PI * j * hz/srate)))+dc); - inst->phaseA[i] += 2.0 * M_PI * abuf->frameN * hz/srate; + inst->phaseA[i] += 2.0 * M_PI * abuf->frameN * hz/srate; + + //if( i==0 ) + // printf("hz:%f gain:%f phs:%f : %f\n",hz,gain,inst->phaseA[i],v[0]); } } @@ -1438,7 +1965,7 @@ namespace cw // namespace pv_analysis { - typedef struct dsp::pv_anl::obj_str pv_t; + typedef struct dsp::pv_anl::obj_str pv_t; enum { kInPId, @@ -1460,15 +1987,15 @@ namespace cw } inst_t; - rc_t create( instance_t* ctx ) + rc_t create( proc_t* proc ) { rc_t rc = kOkRC; const abuf_t* srcBuf = nullptr; // unsigned flags = 0; inst_t* inst = mem::allocZ(); - ctx->userPtr = inst; + proc->userPtr = inst; - if((rc = var_register_and_get( ctx, kAnyChIdx,kInPId, "in", srcBuf )) != kOkRC ) + if((rc = var_register_and_get( proc, kAnyChIdx,kInPId, "in", kBaseSfxId, srcBuf )) != kOkRC ) { cwLogError(kInvalidArgRC,"Unable to access the 'src' buffer."); } @@ -1478,9 +2005,9 @@ namespace cw inst->pvN = srcBuf->chN; inst->pvA = mem::allocZ( inst->pvN ); // allocate pv channel array - const fd_real_t* magV[ srcBuf->chN ]; - const fd_real_t* phsV[ srcBuf->chN ]; - const fd_real_t* hzV[ srcBuf->chN ]; + const fd_sample_t* magV[ srcBuf->chN ]; + const fd_sample_t* phsV[ srcBuf->chN ]; + const fd_sample_t* hzV[ srcBuf->chN ]; unsigned maxBinNV[ srcBuf->chN ]; unsigned binNV[ srcBuf->chN ]; unsigned hopNV[ srcBuf->chN ]; @@ -1494,18 +2021,18 @@ namespace cw unsigned hopSmpN = 0; bool hzFl = false; - if((rc = var_register_and_get( ctx, i, - kMaxWndSmpNPId, "maxWndSmpN", maxWndSmpN, - kWndSmpNPId, "wndSmpN", wndSmpN, - kHopSmpNPId, "hopSmpN", hopSmpN, - kHzFlPId, "hzFl", hzFl )) != kOkRC ) + if((rc = var_register_and_get( proc, i, + kMaxWndSmpNPId, "maxWndSmpN", kBaseSfxId, maxWndSmpN, + kWndSmpNPId, "wndSmpN", kBaseSfxId, wndSmpN, + kHopSmpNPId, "hopSmpN", kBaseSfxId, hopSmpN, + kHzFlPId, "hzFl", kBaseSfxId, hzFl )) != kOkRC ) { goto errLabel; } - if((rc = create( inst->pvA[i], ctx->ctx->framesPerCycle, srcBuf->srate, maxWndSmpN, wndSmpN, hopSmpN, flags )) != kOkRC ) + if((rc = create( inst->pvA[i], proc->ctx->framesPerCycle, srcBuf->srate, maxWndSmpN, wndSmpN, hopSmpN, flags )) != kOkRC ) { - rc = cwLogError(kOpFailRC,"The PV analysis object create failed on the instance '%s'.",ctx->label); + rc = cwLogError(kOpFailRC,"The PV analysis object create failed on the instance '%s'.",proc->label); goto errLabel; } @@ -1520,7 +2047,7 @@ namespace cw // create the fbuf 'out' - if((rc = var_register_and_set(ctx, "out", kOutPId, kAnyChIdx, srcBuf->srate, srcBuf->chN, maxBinNV, binNV, hopNV, magV, phsV, hzV )) != kOkRC ) + if((rc = var_register_and_set(proc, "out", kBaseSfxId, kOutPId, kAnyChIdx, srcBuf->srate, srcBuf->chN, maxBinNV, binNV, hopNV, magV, phsV, hzV )) != kOkRC ) { cwLogError(kOpFailRC,"The output freq. buffer could not be created."); goto errLabel; @@ -1531,11 +2058,11 @@ namespace cw return rc; } - rc_t destroy( instance_t* ctx ) + rc_t destroy( proc_t* proc ) { rc_t rc = kOkRC; - inst_t* inst = (inst_t*)ctx->userPtr; + inst_t* inst = (inst_t*)proc->userPtr; for(unsigned i=0; ipvN; ++i) destroy(inst->pvA[i]); @@ -1546,10 +2073,10 @@ namespace cw return rc; } - rc_t value( instance_t* ctx, variable_t* var ) + rc_t value( proc_t* proc, variable_t* var ) { rc_t rc = kOkRC; - inst_t* inst = (inst_t*)ctx->userPtr; + inst_t* inst = (inst_t*)proc->userPtr; if( var->chIdx != kAnyChIdx && var->chIdx < inst->pvN ) { @@ -1570,24 +2097,24 @@ namespace cw return rc; } - rc_t exec( instance_t* ctx ) + rc_t exec( proc_t* proc ) { rc_t rc = kOkRC; - inst_t* inst = (inst_t*)ctx->userPtr; + inst_t* inst = (inst_t*)proc->userPtr; const abuf_t* srcBuf = nullptr; fbuf_t* dstBuf = nullptr; // verify that a source buffer exists - if((rc = var_get(ctx,kInPId, kAnyChIdx, srcBuf )) != kOkRC ) + if((rc = var_get(proc,kInPId, kAnyChIdx, srcBuf )) != kOkRC ) { - rc = cwLogError(rc,"The instance '%s' does not have a valid input connection.",ctx->label); + rc = cwLogError(rc,"The instance '%s' does not have a valid input connection.",proc->label); goto errLabel; } // verify that the dst buffer exits - if((rc = var_get(ctx,kOutPId, kAnyChIdx, dstBuf)) != kOkRC ) + if((rc = var_get(proc,kOutPId, kAnyChIdx, dstBuf)) != kOkRC ) { - rc = cwLogError(rc,"The instance '%s' does not have a valid output.",ctx->label); + rc = cwLogError(rc,"The instance '%s' does not have a valid output.",proc->label); goto errLabel; } @@ -1626,7 +2153,7 @@ namespace cw // namespace pv_synthesis { - typedef struct dsp::pv_syn::obj_str pv_t; + typedef struct dsp::pv_syn::obj_str pv_t; enum { kInPId, @@ -1644,14 +2171,14 @@ namespace cw } inst_t; - rc_t create( instance_t* ctx ) + rc_t create( proc_t* proc ) { rc_t rc = kOkRC; const fbuf_t* srcBuf = nullptr; // inst_t* inst = mem::allocZ(); - ctx->userPtr = inst; + proc->userPtr = inst; - if((rc = var_register_and_get( ctx, kAnyChIdx,kInPId, "in", srcBuf)) != kOkRC ) + if((rc = var_register_and_get( proc, kAnyChIdx,kInPId, "in", kBaseSfxId, srcBuf)) != kOkRC ) { goto errLabel; } @@ -1667,29 +2194,29 @@ namespace cw { unsigned wndSmpN = (srcBuf->binN_V[i]-1)*2; - if((rc = create( inst->pvA[i], ctx->ctx->framesPerCycle, srcBuf->srate, wndSmpN, srcBuf->hopSmpN_V[i] )) != kOkRC ) + if((rc = create( inst->pvA[i], proc->ctx->framesPerCycle, srcBuf->srate, wndSmpN, srcBuf->hopSmpN_V[i] )) != kOkRC ) { - rc = cwLogError(kOpFailRC,"The PV synthesis object create failed on the instance '%s'.",ctx->label); + rc = cwLogError(kOpFailRC,"The PV synthesis object create failed on the instance '%s'.",proc->label); goto errLabel; } } - if((rc = var_register( ctx, kAnyChIdx, kInPId, "in" )) != kOkRC ) + if((rc = var_register( proc, kAnyChIdx, kInPId, "in", kBaseSfxId)) != kOkRC ) goto errLabel; // create the abuf 'out' - rc = var_register_and_set( ctx, "out", kOutPId, kAnyChIdx, srcBuf->srate, srcBuf->chN, ctx->ctx->framesPerCycle ); + rc = var_register_and_set( proc, "out", kBaseSfxId, kOutPId, kAnyChIdx, srcBuf->srate, srcBuf->chN, proc->ctx->framesPerCycle ); } errLabel: return rc; } - rc_t destroy( instance_t* ctx ) + rc_t destroy( proc_t* proc ) { rc_t rc = kOkRC; - inst_t* inst = (inst_t*)ctx->userPtr; + inst_t* inst = (inst_t*)proc->userPtr; for(unsigned i=0; ipvN; ++i) destroy(inst->pvA[i]); @@ -1699,25 +2226,25 @@ namespace cw return rc; } - rc_t value( instance_t* ctx, variable_t* var ) + rc_t value( proc_t* proc, variable_t* var ) { rc_t rc = kOkRC; return rc; } - rc_t exec( instance_t* ctx ) + rc_t exec( proc_t* proc ) { rc_t rc = kOkRC; - inst_t* inst = (inst_t*)ctx->userPtr; + inst_t* inst = (inst_t*)proc->userPtr; const fbuf_t* srcBuf = nullptr; abuf_t* dstBuf = nullptr; // get the src buffer - if((rc = var_get(ctx,kInPId, kAnyChIdx, srcBuf )) != kOkRC ) + if((rc = var_get(proc,kInPId, kAnyChIdx, srcBuf )) != kOkRC ) goto errLabel; // get the dst buffer - if((rc = var_get(ctx,kOutPId, kAnyChIdx, dstBuf)) != kOkRC ) + if((rc = var_get(proc,kOutPId, kAnyChIdx, dstBuf)) != kOkRC ) goto errLabel; for(unsigned i=0; ichN; ++i) @@ -1752,7 +2279,7 @@ namespace cw // namespace spec_dist { - typedef struct dsp::spec_dist::obj_str spec_dist_t; + typedef struct dsp::spec_dist::obj_str spec_dist_t; enum { @@ -1775,18 +2302,18 @@ namespace cw } inst_t; - rc_t create( instance_t* ctx ) + rc_t create( proc_t* proc ) { rc_t rc = kOkRC; const fbuf_t* srcBuf = nullptr; // inst_t* inst = mem::allocZ(); - ctx->userPtr = inst; + proc->userPtr = inst; // verify that a source buffer exists - if((rc = var_register_and_get(ctx, kAnyChIdx,kInPId,"in",srcBuf )) != kOkRC ) + if((rc = var_register_and_get(proc, kAnyChIdx,kInPId,"in",kBaseSfxId,srcBuf )) != kOkRC ) { - rc = cwLogError(rc,"The instance '%s' does not have a valid input connection.",ctx->label); + rc = cwLogError(rc,"The instance '%s' does not have a valid input connection.",proc->label); goto errLabel; } else @@ -1795,11 +2322,11 @@ namespace cw inst->sdN = srcBuf->chN; inst->sdA = mem::allocZ( inst->sdN ); - const fd_real_t* magV[ srcBuf->chN ]; - const fd_real_t* phsV[ srcBuf->chN ]; - const fd_real_t* hzV[ srcBuf->chN ]; + const fd_sample_t* magV[ srcBuf->chN ]; + const fd_sample_t* phsV[ srcBuf->chN ]; + const fd_sample_t* hzV[ srcBuf->chN ]; - //if((rc = var_register(ctx, kAnyChIdx, kInPId, "in")) != kOkRC ) + //if((rc = var_register(proc, kAnyChIdx, kInPId, "in")) != kOkRC ) // goto errLabel; // create a spec_dist object for each input channel @@ -1807,7 +2334,7 @@ namespace cw { if((rc = create( inst->sdA[i], srcBuf->binN_V[i] )) != kOkRC ) { - rc = cwLogError(kOpFailRC,"The 'spec dist' object create failed on the instance '%s'.",ctx->label); + rc = cwLogError(kOpFailRC,"The 'spec dist' object create failed on the instance '%s'.",proc->label); goto errLabel; } @@ -1818,14 +2345,14 @@ namespace cw spec_dist_t* sd = inst->sdA[i]; - if((rc = var_register_and_get( ctx, i, - kBypassPId, "bypass", sd->bypassFl, - kCeilingPId, "ceiling", sd->ceiling, - kExpoPId, "expo", sd->expo, - kThreshPId, "thresh", sd->thresh, - kUprSlopePId, "upr", sd->uprSlope, - kLwrSlopePId, "lwr", sd->lwrSlope, - kMixPId, "mix", sd->mix )) != kOkRC ) + if((rc = var_register_and_get( proc, i, + kBypassPId, "bypass", kBaseSfxId, sd->bypassFl, + kCeilingPId, "ceiling", kBaseSfxId, sd->ceiling, + kExpoPId, "expo", kBaseSfxId, sd->expo, + kThreshPId, "thresh", kBaseSfxId, sd->thresh, + kUprSlopePId, "upr", kBaseSfxId, sd->uprSlope, + kLwrSlopePId, "lwr", kBaseSfxId, sd->lwrSlope, + kMixPId, "mix", kBaseSfxId, sd->mix )) != kOkRC ) { goto errLabel; } @@ -1833,7 +2360,7 @@ namespace cw } // create the output buffer - if((rc = var_register_and_set( ctx, "out", kOutPId, kAnyChIdx, srcBuf->srate, srcBuf->chN, srcBuf->maxBinN_V, srcBuf->binN_V, srcBuf->hopSmpN_V, magV, phsV, hzV )) != kOkRC ) + if((rc = var_register_and_set( proc, "out", kBaseSfxId, kOutPId, kAnyChIdx, srcBuf->srate, srcBuf->chN, srcBuf->maxBinN_V, srcBuf->binN_V, srcBuf->hopSmpN_V, magV, phsV, hzV )) != kOkRC ) goto errLabel; } @@ -1841,11 +2368,11 @@ namespace cw return rc; } - rc_t destroy( instance_t* ctx ) + rc_t destroy( proc_t* proc ) { rc_t rc = kOkRC; - inst_t* inst = (inst_t*)ctx->userPtr; + inst_t* inst = (inst_t*)proc->userPtr; for(unsigned i=0; isdN; ++i) destroy(inst->sdA[i]); @@ -1855,10 +2382,10 @@ namespace cw return rc; } - rc_t value( instance_t* ctx, variable_t* var ) + rc_t value( proc_t* proc, variable_t* var ) { rc_t rc = kOkRC; - inst_t* inst = (inst_t*)ctx->userPtr; + inst_t* inst = (inst_t*)proc->userPtr; if( var->chIdx != kAnyChIdx && var->chIdx < inst->sdN ) { @@ -1876,7 +2403,7 @@ namespace cw case kLwrSlopePId: rc = var_get( var, val ); sd->lwrSlope = val; break; case kMixPId: rc = var_get( var, val ); sd->mix = val; break; default: - cwLogWarning("Unhandled variable id '%i' on instance: %s.", var->vid, ctx->label ); + cwLogWarning("Unhandled variable id '%i' on instance: %s.", var->vid, proc->label ); } //printf("%i sd: ceil:%f expo:%f thresh:%f upr:%f lwr:%f mix:%f : rc:%i val:%f var:%s \n", @@ -1886,20 +2413,20 @@ namespace cw return rc; } - rc_t exec( instance_t* ctx ) + rc_t exec( proc_t* proc ) { rc_t rc = kOkRC; - inst_t* inst = (inst_t*)ctx->userPtr; + inst_t* inst = (inst_t*)proc->userPtr; const fbuf_t* srcBuf = nullptr; fbuf_t* dstBuf = nullptr; unsigned chN = 0; // get the src buffer - if((rc = var_get(ctx,kInPId, kAnyChIdx, srcBuf )) != kOkRC ) + if((rc = var_get(proc,kInPId, kAnyChIdx, srcBuf )) != kOkRC ) goto errLabel; // get the dst buffer - if((rc = var_get(ctx,kOutPId, kAnyChIdx, dstBuf)) != kOkRC ) + if((rc = var_get(proc,kOutPId, kAnyChIdx, dstBuf)) != kOkRC ) goto errLabel; chN = std::min(srcBuf->chN,inst->sdN); @@ -1963,18 +2490,18 @@ namespace cw } inst_t; - rc_t create( instance_t* ctx ) + rc_t create( proc_t* proc ) { rc_t rc = kOkRC; const abuf_t* srcBuf = nullptr; // inst_t* inst = mem::allocZ(); - ctx->userPtr = inst; + proc->userPtr = inst; // verify that a source buffer exists - if((rc = var_register_and_get(ctx, kAnyChIdx,kInPId,"in",srcBuf )) != kOkRC ) + if((rc = var_register_and_get(proc, kAnyChIdx,kInPId,"in",kBaseSfxId,srcBuf )) != kOkRC ) { - rc = cwLogError(rc,"The instance '%s' does not have a valid input connection.",ctx->label); + rc = cwLogError(rc,"The instance '%s' does not have a valid input connection.",proc->label); goto errLabel; } else @@ -1986,21 +2513,22 @@ namespace cw // create a compressor object for each input channel for(unsigned i=0; ichN; ++i) { - real_t igain, maxWnd_ms, wnd_ms, thresh, ratio, atk_ms, rls_ms, ogain; + coeff_t igain, thresh, ratio, ogain; + ftime_t maxWnd_ms, wnd_ms, atk_ms, rls_ms; bool bypassFl; // get the compressor variable values - if((rc = var_register_and_get( ctx, i, - kBypassPId, "bypass", bypassFl, - kInGainPId, "igain", igain, - kThreshPId, "thresh", thresh, - kRatioPId, "ratio", ratio, - kAtkMsPId, "atk_ms", atk_ms, - kRlsMsPId, "rls_ms", rls_ms, - kWndMsPId, "wnd_ms", wnd_ms, - kMaxWndMsPId, "maxWnd_ms", maxWnd_ms, - kOutGainPId, "ogain", ogain )) != kOkRC ) + if((rc = var_register_and_get( proc, i, + kBypassPId, "bypass", kBaseSfxId, bypassFl, + kInGainPId, "igain", kBaseSfxId, igain, + kThreshPId, "thresh", kBaseSfxId, thresh, + kRatioPId, "ratio", kBaseSfxId, ratio, + kAtkMsPId, "atk_ms", kBaseSfxId, atk_ms, + kRlsMsPId, "rls_ms", kBaseSfxId, rls_ms, + kWndMsPId, "wnd_ms", kBaseSfxId, wnd_ms, + kMaxWndMsPId, "maxWnd_ms", kBaseSfxId, maxWnd_ms, + kOutGainPId, "ogain", kBaseSfxId, ogain )) != kOkRC ) { goto errLabel; } @@ -2008,14 +2536,14 @@ namespace cw // create the compressor instance if((rc = dsp::compressor::create( inst->cmpA[i], srcBuf->srate, srcBuf->frameN, igain, maxWnd_ms, wnd_ms, thresh, ratio, atk_ms, rls_ms, ogain, bypassFl)) != kOkRC ) { - rc = cwLogError(kOpFailRC,"The 'compressor' object create failed on the instance '%s'.",ctx->label); + rc = cwLogError(kOpFailRC,"The 'compressor' object create failed on the instance '%s'.",proc->label); goto errLabel; } } // create the output audio buffer - if((rc = var_register_and_set( ctx, "out", kOutPId, kAnyChIdx, srcBuf->srate, srcBuf->chN, srcBuf->frameN )) != kOkRC ) + if((rc = var_register_and_set( proc, "out", kBaseSfxId, kOutPId, kAnyChIdx, srcBuf->srate, srcBuf->chN, srcBuf->frameN )) != kOkRC ) goto errLabel; } @@ -2023,11 +2551,11 @@ namespace cw return rc; } - rc_t destroy( instance_t* ctx ) + rc_t destroy( proc_t* proc ) { rc_t rc = kOkRC; - inst_t* inst = (inst_t*)ctx->userPtr; + inst_t* inst = (inst_t*)proc->userPtr; for(unsigned i=0; icmpN; ++i) destroy(inst->cmpA[i]); @@ -2037,11 +2565,11 @@ namespace cw return rc; } - rc_t value( instance_t* ctx, variable_t* var ) + rc_t value( proc_t* proc, variable_t* var ) { rc_t rc = kOkRC; - inst_t* inst = (inst_t*)ctx->userPtr; - real_t tmp; + inst_t* inst = (inst_t*)proc->userPtr; + ftime_t tmp; if( var->chIdx != kAnyChIdx && var->chIdx < inst->cmpN ) { @@ -2059,7 +2587,7 @@ namespace cw case kWndMsPId: rc = var_get( var, tmp ); dsp::compressor::set_rms_wnd_ms(c, tmp ); break; case kMaxWndMsPId: break; default: - cwLogWarning("Unhandled variable id '%i' on instance: %s.", var->vid, ctx->label ); + cwLogWarning("Unhandled variable id '%i' on instance: %s.", var->vid, proc->label ); } //printf("cmp byp:%i igain:%f ogain:%f rat:%f thresh:%f atk:%i rls:%i wnd:%i : rc:%i val:%f\n", // c->bypassFl, c->inGain, c->outGain,c->ratio_num,c->threshDb,c->atkSmp,c->rlsSmp,c->rmsWndCnt,rc,tmp); @@ -2069,20 +2597,20 @@ namespace cw return rc; } - rc_t exec( instance_t* ctx ) + rc_t exec( proc_t* proc ) { rc_t rc = kOkRC; - inst_t* inst = (inst_t*)ctx->userPtr; + inst_t* inst = (inst_t*)proc->userPtr; const abuf_t* srcBuf = nullptr; abuf_t* dstBuf = nullptr; unsigned chN = 0; // get the src buffer - if((rc = var_get(ctx,kInPId, kAnyChIdx, srcBuf )) != kOkRC ) + if((rc = var_get(proc,kInPId, kAnyChIdx, srcBuf )) != kOkRC ) goto errLabel; // get the dst buffer - if((rc = var_get(ctx,kOutPId, kAnyChIdx, dstBuf)) != kOkRC ) + if((rc = var_get(proc,kOutPId, kAnyChIdx, dstBuf)) != kOkRC ) goto errLabel; chN = std::min(srcBuf->chN,inst->cmpN); @@ -2099,15 +2627,15 @@ namespace cw return rc; } - rc_t report( instance_t* ctx ) + rc_t report( proc_t* proc ) { rc_t rc = kOkRC; - inst_t* inst = (inst_t*)ctx->userPtr; + inst_t* inst = (inst_t*)proc->userPtr; for(unsigned i=0; icmpN; ++i) { compressor_t* c = inst->cmpA[i]; cwLogInfo("%s ch:%i : sr:%f bypass:%i procSmpN:%i igain:%f threshdb:%f ratio:%f atkSmp:%i rlsSmp:%i ogain:%f rmsWndN:%i maxRmsWndN%i", - ctx->label,i,c->srate,c->bypassFl,c->procSmpCnt,c->inGain,c->threshDb,c->ratio_num,c->atkSmp,c->rlsSmp,c->outGain,c->rmsWndCnt,c->rmsWndAllocCnt + proc->label,i,c->srate,c->bypassFl,c->procSmpCnt,c->inGain,c->threshDb,c->ratio_num,c->atkSmp,c->rlsSmp,c->outGain,c->rmsWndCnt,c->rmsWndAllocCnt ); } @@ -2151,18 +2679,18 @@ namespace cw } inst_t; - rc_t create( instance_t* ctx ) + rc_t create( proc_t* proc ) { rc_t rc = kOkRC; const abuf_t* srcBuf = nullptr; // inst_t* inst = mem::allocZ(); - ctx->userPtr = inst; + proc->userPtr = inst; // verify that a source buffer exists - if((rc = var_register_and_get(ctx, kAnyChIdx,kInPId,"in",srcBuf )) != kOkRC ) + if((rc = var_register_and_get(proc, kAnyChIdx,kInPId,"in",kBaseSfxId,srcBuf )) != kOkRC ) { - rc = cwLogError(rc,"The instance '%s' does not have a valid input connection.",ctx->label); + rc = cwLogError(rc,"The instance '%s' does not have a valid input connection.",proc->label); goto errLabel; } else @@ -2174,16 +2702,16 @@ namespace cw // create a limiter object for each input channel for(unsigned i=0; ichN; ++i) { - real_t igain, thresh, ogain; + coeff_t igain, thresh, ogain; bool bypassFl; // get the limiter variable values - if((rc = var_register_and_get( ctx, i, - kBypassPId, "bypass", bypassFl, - kInGainPId, "igain", igain, - kThreshPId, "thresh", thresh, - kOutGainPId, "ogain", ogain )) != kOkRC ) + if((rc = var_register_and_get( proc, i, + kBypassPId, "bypass", kBaseSfxId, bypassFl, + kInGainPId, "igain", kBaseSfxId, igain, + kThreshPId, "thresh", kBaseSfxId, thresh, + kOutGainPId, "ogain", kBaseSfxId, ogain )) != kOkRC ) { goto errLabel; } @@ -2191,14 +2719,14 @@ namespace cw // create the limiter instance if((rc = dsp::limiter::create( inst->limA[i], srcBuf->srate, srcBuf->frameN, igain, thresh, ogain, bypassFl)) != kOkRC ) { - rc = cwLogError(kOpFailRC,"The 'limiter' object create failed on the instance '%s'.",ctx->label); + rc = cwLogError(kOpFailRC,"The 'limiter' object create failed on the instance '%s'.",proc->label); goto errLabel; } } // create the output audio buffer - if((rc = var_register_and_set( ctx, "out", kOutPId, kAnyChIdx, srcBuf->srate, srcBuf->chN, srcBuf->frameN )) != kOkRC ) + if((rc = var_register_and_set( proc, "out", kBaseSfxId, kOutPId, kAnyChIdx, srcBuf->srate, srcBuf->chN, srcBuf->frameN )) != kOkRC ) goto errLabel; } @@ -2206,11 +2734,11 @@ namespace cw return rc; } - rc_t destroy( instance_t* ctx ) + rc_t destroy( proc_t* proc ) { rc_t rc = kOkRC; - inst_t* inst = (inst_t*)ctx->userPtr; + inst_t* inst = (inst_t*)proc->userPtr; for(unsigned i=0; ilimN; ++i) destroy(inst->limA[i]); @@ -2220,11 +2748,11 @@ namespace cw return rc; } - rc_t value( instance_t* ctx, variable_t* var ) + rc_t value( proc_t* proc, variable_t* var ) { rc_t rc = kOkRC; - inst_t* inst = (inst_t*)ctx->userPtr; - real_t rtmp; + inst_t* inst = (inst_t*)proc->userPtr; + coeff_t rtmp; bool btmp; if( var->chIdx != kAnyChIdx && var->chIdx < inst->limN ) @@ -2238,7 +2766,7 @@ namespace cw case kOutGainPId: rc = var_get( var, rtmp ); c->ogain=rtmp; break; case kThreshPId: rc = var_get( var, rtmp ); c->thresh=rtmp; break; default: - cwLogWarning("Unhandled variable id '%i' on instance: %s.", var->vid, ctx->label ); + cwLogWarning("Unhandled variable id '%i' on instance: %s.", var->vid, proc->label ); } //printf("lim byp:%i igain:%f ogain:%f rat:%f thresh:%f atk:%i rls:%i wnd:%i : rc:%i val:%f\n", // c->bypassFl, c->inGain, c->outGain,c->ratio_num,c->threshDb,c->atkSmp,c->rlsSmp,c->rmsWndCnt,rc,tmp); @@ -2248,20 +2776,20 @@ namespace cw return rc; } - rc_t exec( instance_t* ctx ) + rc_t exec( proc_t* proc ) { rc_t rc = kOkRC; - inst_t* inst = (inst_t*)ctx->userPtr; + inst_t* inst = (inst_t*)proc->userPtr; const abuf_t* srcBuf = nullptr; abuf_t* dstBuf = nullptr; unsigned chN = 0; // get the src buffer - if((rc = var_get(ctx,kInPId, kAnyChIdx, srcBuf )) != kOkRC ) + if((rc = var_get(proc,kInPId, kAnyChIdx, srcBuf )) != kOkRC ) goto errLabel; // get the dst buffer - if((rc = var_get(ctx,kOutPId, kAnyChIdx, dstBuf)) != kOkRC ) + if((rc = var_get(proc,kOutPId, kAnyChIdx, dstBuf)) != kOkRC ) goto errLabel; chN = std::min(srcBuf->chN,inst->limN); @@ -2278,15 +2806,15 @@ namespace cw return rc; } - rc_t report( instance_t* ctx ) + rc_t report( proc_t* proc ) { rc_t rc = kOkRC; - inst_t* inst = (inst_t*)ctx->userPtr; + inst_t* inst = (inst_t*)proc->userPtr; for(unsigned i=0; ilimN; ++i) { limiter_t* c = inst->limA[i]; cwLogInfo("%s ch:%i : bypass:%i procSmpN:%i igain:%f threshdb:%f ogain:%f", - ctx->label,i,c->bypassFl,c->procSmpCnt,c->igain,c->thresh,c->ogain ); + proc->label,i,c->bypassFl,c->procSmpCnt,c->igain,c->thresh,c->ogain ); } return rc; @@ -2323,18 +2851,18 @@ namespace cw unsigned* idxV; // idxV[ chN ] per channel i/o idx } inst_t; - rc_t create( instance_t* ctx ) + rc_t create( proc_t* proc ) { rc_t rc = kOkRC; const abuf_t* abuf = nullptr; // inst_t* inst = mem::allocZ(); - real_t delayMs = 0; - real_t maxDelayMs = 0; + ftime_t delayMs = 0; + ftime_t maxDelayMs = 0; - ctx->userPtr = inst; + proc->userPtr = inst; // get the source audio buffer - if((rc = var_register_and_get(ctx, kAnyChIdx,kInPId,"in",abuf )) != kOkRC ) + if((rc = var_register_and_get(proc, kAnyChIdx,kInPId,"in",kBaseSfxId,abuf )) != kOkRC ) goto errLabel; @@ -2344,16 +2872,16 @@ namespace cw // register the gain for(unsigned i=0; ichN; ++i) { - if((rc = var_register_and_get( ctx, i, - kMaxDelayMsPId, "maxDelayMs", maxDelayMs, - kDelayMsPId, "delayMs", delayMs)) != kOkRC ) + if((rc = var_register_and_get( proc, i, + kMaxDelayMsPId, "maxDelayMs", kBaseSfxId, maxDelayMs, + kDelayMsPId, "delayMs", kBaseSfxId, delayMs)) != kOkRC ) { goto errLabel; } if( delayMs > maxDelayMs ) { - cwLogWarning("'delayMs' (%i) is being reduced to 'maxDelayMs' (%i) on the delay instance:%s.",delayMs,maxDelayMs,ctx->label); + cwLogWarning("'delayMs' (%i) is being reduced to 'maxDelayMs' (%i) on the delay instance:%s.",delayMs,maxDelayMs,proc->label); delayMs = maxDelayMs; } @@ -2366,16 +2894,16 @@ namespace cw inst->delayBuf = abuf_create( abuf->srate, abuf->chN, inst->maxDelayFrameN ); // create the output audio buffer - rc = var_register_and_set( ctx, "out", kOutPId, kAnyChIdx, abuf->srate, abuf->chN, abuf->frameN ); + rc = var_register_and_set( proc, "out", kBaseSfxId, kOutPId, kAnyChIdx, abuf->srate, abuf->chN, abuf->frameN ); errLabel: return rc; } - rc_t destroy( instance_t* ctx ) + rc_t destroy( proc_t* proc ) { - inst_t* inst = (inst_t*)ctx->userPtr; + inst_t* inst = (inst_t*)proc->userPtr; mem::release(inst->cntV); mem::release(inst->idxV); @@ -2385,15 +2913,15 @@ namespace cw return kOkRC; } - rc_t _update_delay( instance_t* ctx, variable_t* var ) + rc_t _update_delay( proc_t* proc, variable_t* var ) { rc_t rc = kOkRC; - inst_t* inst = (inst_t*)ctx->userPtr; + inst_t* inst = (inst_t*)proc->userPtr; abuf_t* ibuf = nullptr; - real_t delayMs = 0; + ftime_t delayMs = 0; unsigned delayFrameN = 0; - if((rc = var_get(ctx,kInPId, kAnyChIdx, ibuf )) != kOkRC ) + if((rc = var_get(proc,kInPId, kAnyChIdx, ibuf )) != kOkRC ) goto errLabel; if((rc = var_get( var, delayMs )) != kOkRC ) @@ -2419,34 +2947,34 @@ namespace cw return rc; } - rc_t value( instance_t* ctx, variable_t* var ) + rc_t value( proc_t* proc, variable_t* var ) { rc_t rc = kOkRC; switch( var->vid ) { case kDelayMsPId: - rc = _update_delay(ctx,var); + rc = _update_delay(proc,var); break; } return rc; } - rc_t exec( instance_t* ctx ) + rc_t exec( proc_t* proc ) { rc_t rc = kOkRC; - inst_t* inst = (inst_t*)ctx->userPtr; + inst_t* inst = (inst_t*)proc->userPtr; const abuf_t* ibuf = nullptr; abuf_t* obuf = nullptr; abuf_t* dbuf = inst->delayBuf; // get the src buffer - if((rc = var_get(ctx,kInPId, kAnyChIdx, ibuf )) != kOkRC ) + if((rc = var_get(proc,kInPId, kAnyChIdx, ibuf )) != kOkRC ) goto errLabel; // get the dst buffer - if((rc = var_get(ctx,kOutPId, kAnyChIdx, obuf)) != kOkRC ) + if((rc = var_get(proc,kOutPId, kAnyChIdx, obuf)) != kOkRC ) goto errLabel; // for each channel @@ -2515,18 +3043,18 @@ namespace cw } inst_t; - rc_t create( instance_t* ctx ) + rc_t create( proc_t* proc ) { rc_t rc = kOkRC; const abuf_t* srcBuf = nullptr; // inst_t* inst = mem::allocZ(); - ctx->userPtr = inst; + proc->userPtr = inst; // verify that a source buffer exists - if((rc = var_register_and_get(ctx, kAnyChIdx,kInPId,"in",srcBuf )) != kOkRC ) + if((rc = var_register_and_get(proc, kAnyChIdx,kInPId,"in",kBaseSfxId,srcBuf )) != kOkRC ) { - rc = cwLogError(rc,"The instance '%s' does not have a valid input connection.",ctx->label); + rc = cwLogError(rc,"The instance '%s' does not have a valid input connection.",proc->label); goto errLabel; } else @@ -2538,14 +3066,14 @@ namespace cw // create a dc_filter object for each input channel for(unsigned i=0; ichN; ++i) { - real_t gain; + coeff_t gain; bool bypassFl; // get the dc_filter variable values - if((rc = var_register_and_get( ctx, i, - kBypassPId, "bypass", bypassFl, - kGainPId, "gain", gain )) != kOkRC ) + if((rc = var_register_and_get( proc, i, + kBypassPId, "bypass", kBaseSfxId, bypassFl, + kGainPId, "gain", kBaseSfxId, gain )) != kOkRC ) { goto errLabel; } @@ -2553,14 +3081,14 @@ namespace cw // create the dc_filter instance if((rc = dsp::dc_filter::create( inst->dcfA[i], srcBuf->srate, srcBuf->frameN, gain, bypassFl)) != kOkRC ) { - rc = cwLogError(kOpFailRC,"The 'dc_filter' object create failed on the instance '%s'.",ctx->label); + rc = cwLogError(kOpFailRC,"The 'dc_filter' object create failed on the instance '%s'.",proc->label); goto errLabel; } } // create the output audio buffer - if((rc = var_register_and_set( ctx, "out", kOutPId, kAnyChIdx, srcBuf->srate, srcBuf->chN, srcBuf->frameN )) != kOkRC ) + if((rc = var_register_and_set( proc, "out", kBaseSfxId, kOutPId, kAnyChIdx, srcBuf->srate, srcBuf->chN, srcBuf->frameN )) != kOkRC ) goto errLabel; } @@ -2568,11 +3096,11 @@ namespace cw return rc; } - rc_t destroy( instance_t* ctx ) + rc_t destroy( proc_t* proc ) { rc_t rc = kOkRC; - inst_t* inst = (inst_t*)ctx->userPtr; + inst_t* inst = (inst_t*)proc->userPtr; for(unsigned i=0; idcfN; ++i) destroy(inst->dcfA[i]); @@ -2582,33 +3110,33 @@ namespace cw return rc; } - rc_t value( instance_t* ctx, variable_t* var ) + rc_t value( proc_t* proc, variable_t* var ) { return kOkRC; } - rc_t exec( instance_t* ctx ) + rc_t exec( proc_t* proc ) { rc_t rc = kOkRC; - inst_t* inst = (inst_t*)ctx->userPtr; + inst_t* inst = (inst_t*)proc->userPtr; const abuf_t* srcBuf = nullptr; abuf_t* dstBuf = nullptr; unsigned chN = 0; // get the src buffer - if((rc = var_get(ctx,kInPId, kAnyChIdx, srcBuf )) != kOkRC ) + if((rc = var_get(proc,kInPId, kAnyChIdx, srcBuf )) != kOkRC ) goto errLabel; // get the dst buffer - if((rc = var_get(ctx,kOutPId, kAnyChIdx, dstBuf)) != kOkRC ) + if((rc = var_get(proc,kOutPId, kAnyChIdx, dstBuf)) != kOkRC ) goto errLabel; chN = std::min(srcBuf->chN,inst->dcfN); for(unsigned i=0; i( ctx, kGainPId, i ); - bool bypassFl = val_get( ctx, kBypassPId, i ); + coeff_t gain = val_get( proc, kGainPId, i ); + bool bypassFl = val_get( proc, kBypassPId, i ); dsp::dc_filter::set( inst->dcfA[i], gain, bypassFl ); @@ -2619,15 +3147,15 @@ namespace cw return rc; } - rc_t report( instance_t* ctx ) + rc_t report( proc_t* proc ) { rc_t rc = kOkRC; - inst_t* inst = (inst_t*)ctx->userPtr; + inst_t* inst = (inst_t*)proc->userPtr; for(unsigned i=0; idcfN; ++i) { dc_filter_t* c = inst->dcfA[i]; cwLogInfo("%s ch:%i : bypass:%i gain:%f", - ctx->label,i,c->bypassFl,c->gain ); + proc->label,i,c->bypassFl,c->gain ); } return rc; @@ -2671,18 +3199,18 @@ namespace cw } inst_t; - rc_t create( instance_t* ctx ) + rc_t create( proc_t* proc ) { rc_t rc = kOkRC; const abuf_t* srcBuf = nullptr; // inst_t* inst = mem::allocZ(); - ctx->userPtr = inst; + proc->userPtr = inst; // verify that a source buffer exists - if((rc = var_register_and_get(ctx, kAnyChIdx,kInPId,"in",srcBuf )) != kOkRC ) + if((rc = var_register_and_get(proc, kAnyChIdx,kInPId,"in",kBaseSfxId,srcBuf )) != kOkRC ) { - rc = cwLogError(rc,"The instance '%s' does not have a valid input connection.",ctx->label); + rc = cwLogError(rc,"The instance '%s' does not have a valid input connection.",proc->label); goto errLabel; } else @@ -2694,33 +3222,34 @@ namespace cw // create a audio_meter object for each input channel for(unsigned i=0; ichN; ++i) { - real_t wndMs, peakThreshDb; + ftime_t wndMs; + coeff_t peakThreshDb; bool dbFl; // get the audio_meter variable values - if((rc = var_register_and_get( ctx, i, - kDbFlPId, "dbFl", dbFl, - kWndMsPId, "wndMs", wndMs, - kPeakDbPId, "peakDb", peakThreshDb )) != kOkRC ) + if((rc = var_register_and_get( proc, i, + kDbFlPId, "dbFl", kBaseSfxId, dbFl, + kWndMsPId, "wndMs", kBaseSfxId, wndMs, + kPeakDbPId, "peakDb", kBaseSfxId, peakThreshDb )) != kOkRC ) { goto errLabel; } // get the audio_meter variable values - if((rc = var_register( ctx, i, - kOutPId, "out", - kPeakFlPId, "peakFl", - kClipFlPId, "clipFl" )) != kOkRC ) + if((rc = var_register( proc, i, + kOutPId, "out", kBaseSfxId, + kPeakFlPId, "peakFl", kBaseSfxId, + kClipFlPId, "clipFl", kBaseSfxId )) != kOkRC ) { goto errLabel; } - unsigned maxWndMs = std::max(wndMs,1000.0f); + unsigned maxWndMs = std::max(wndMs,1000.0); // create the audio_meter instance if((rc = dsp::audio_meter::create( inst->mtrA[i], srcBuf->srate, maxWndMs, wndMs, peakThreshDb)) != kOkRC ) { - rc = cwLogError(kOpFailRC,"The 'audio_meter' object create failed on the instance '%s'.",ctx->label); + rc = cwLogError(kOpFailRC,"The 'audio_meter' object create failed on the instance '%s'.",proc->label); goto errLabel; } @@ -2732,11 +3261,11 @@ namespace cw return rc; } - rc_t destroy( instance_t* ctx ) + rc_t destroy( proc_t* proc ) { rc_t rc = kOkRC; - inst_t* inst = (inst_t*)ctx->userPtr; + inst_t* inst = (inst_t*)proc->userPtr; for(unsigned i=0; imtrN; ++i) destroy(inst->mtrA[i]); @@ -2746,20 +3275,20 @@ namespace cw return rc; } - rc_t value( instance_t* ctx, variable_t* var ) + rc_t value( proc_t* proc, variable_t* var ) { return kOkRC; } - rc_t exec( instance_t* ctx ) + rc_t exec( proc_t* proc ) { rc_t rc = kOkRC; - inst_t* inst = (inst_t*)ctx->userPtr; + inst_t* inst = (inst_t*)proc->userPtr; const abuf_t* srcBuf = nullptr; unsigned chN = 0; // get the src buffer - if((rc = var_get(ctx,kInPId, kAnyChIdx, srcBuf )) != kOkRC ) + if((rc = var_get(proc,kInPId, kAnyChIdx, srcBuf )) != kOkRC ) goto errLabel; chN = std::min(srcBuf->chN,inst->mtrN); @@ -2767,24 +3296,24 @@ namespace cw for(unsigned i=0; imtrA[i], srcBuf->buf + i*srcBuf->frameN, srcBuf->frameN ); - var_set(ctx, kOutPId, i, inst->mtrA[i]->outDb ); - var_set(ctx, kPeakFlPId, i, inst->mtrA[i]->peakFl ); - var_set(ctx, kClipFlPId, i, inst->mtrA[i]->clipFl ); + var_set(proc, kOutPId, i, inst->mtrA[i]->outDb ); + var_set(proc, kPeakFlPId, i, inst->mtrA[i]->peakFl ); + var_set(proc, kClipFlPId, i, inst->mtrA[i]->clipFl ); } errLabel: return rc; } - rc_t report( instance_t* ctx ) + rc_t report( proc_t* proc ) { rc_t rc = kOkRC; - inst_t* inst = (inst_t*)ctx->userPtr; + inst_t* inst = (inst_t*)proc->userPtr; for(unsigned i=0; imtrN; ++i) { audio_meter_t* c = inst->mtrA[i]; cwLogInfo("%s ch:%i : %f %f db : pk:%i %i clip:%i %i ", - ctx->label,i,c->outLin,c->outDb,c->peakFl,c->peakCnt,c->clipFl,c->clipCnt ); + proc->label,i,c->outLin,c->outDb,c->peakFl,c->peakCnt,c->clipFl,c->clipCnt ); } return rc; @@ -2814,60 +3343,60 @@ namespace cw typedef struct inst_str { - real_t mark; + sample_t mark; } inst_t; - rc_t create( instance_t* ctx ) + rc_t create( proc_t* proc ) { rc_t rc = kOkRC; const abuf_t* abuf = nullptr; // - ctx->userPtr = mem::allocZ(); + proc->userPtr = mem::allocZ(); // get the source audio buffer - if((rc = var_register_and_get(ctx, kAnyChIdx,kInPId,"in",abuf )) != kOkRC ) + if((rc = var_register_and_get(proc, kAnyChIdx,kInPId,"in",kBaseSfxId,abuf )) != kOkRC ) goto errLabel; // register the marker input - if((rc = var_register_and_set( ctx, kAnyChIdx, kMarkPId, "mark", 0.0f )) != kOkRC ) + if((rc = var_register_and_set( proc, kAnyChIdx, kMarkPId, "mark", kBaseSfxId, 0.0f )) != kOkRC ) goto errLabel; // create the output audio buffer - rc = var_register_and_set( ctx, "out", kOutPId, kAnyChIdx, abuf->srate, abuf->chN, abuf->frameN ); + rc = var_register_and_set( proc, "out", kBaseSfxId, kOutPId, kAnyChIdx, abuf->srate, abuf->chN, abuf->frameN ); errLabel: return rc; } - rc_t destroy( instance_t* ctx ) + rc_t destroy( proc_t* proc ) { - inst_t* inst = (inst_t*)(ctx->userPtr); + inst_t* inst = (inst_t*)(proc->userPtr); mem::release(inst); return kOkRC; } - rc_t value( instance_t* ctx, variable_t* var ) + rc_t value( proc_t* proc, variable_t* var ) { return kOkRC; } - rc_t exec( instance_t* ctx ) + rc_t exec( proc_t* proc ) { rc_t rc = kOkRC; const abuf_t* ibuf = nullptr; abuf_t* obuf = nullptr; - //inst_t* inst = (inst_t*)(ctx->userPtr); + //inst_t* inst = (inst_t*)(proc->userPtr); sample_t mark = 1; // get the src buffer - if((rc = var_get(ctx,kInPId, kAnyChIdx, ibuf )) != kOkRC ) + if((rc = var_get(proc,kInPId, kAnyChIdx, ibuf )) != kOkRC ) goto errLabel; // get the dst buffer - if((rc = var_get(ctx,kOutPId, kAnyChIdx, obuf)) != kOkRC ) + if((rc = var_get(proc,kOutPId, kAnyChIdx, obuf)) != kOkRC ) goto errLabel; - var_get(ctx,kMarkPId,kAnyChIdx,mark); + var_get(proc,kMarkPId,kAnyChIdx,mark); // for each channel for(unsigned i=0; ichN; ++i) @@ -2880,7 +3409,7 @@ namespace cw osig[j] = mark + isig[j]; } - var_set(ctx,kMarkPId,kAnyChIdx,0.0f); + var_set(proc,kMarkPId,kAnyChIdx,0.0f); errLabel: return rc; @@ -2896,7 +3425,1964 @@ namespace cw }; } + + //------------------------------------------------------------------------------------------------------------------ + // + // xfade_ctl + // + namespace xfade_ctl + { + enum { + kNetLabelPId, + kNetLabelSfxPId, + kSrateRefPId, + kDurMsPId, + kTriggerPId, + kPresetPId, + kGainPId, + }; + + typedef struct poly_ch_str + { + network_t net; + coeff_t target_gain; + coeff_t cur_gain; + } poly_ch_t; + + typedef struct + { + unsigned xfadeDurMs; // crossfade duration in milliseconds + proc_t* net_proc; // source 'poly' network + poly_ch_t* netA; // netA[ poly_ch_cnt ] internal proxy network + unsigned poly_ch_cnt; // count of poly channels in net_proc + unsigned net_proc_cnt; // count of proc's in a single poly-channel (net_proc->proc_arrayN/poly_cnt) + unsigned cur_poly_ch_idx; // This is the active channel. + unsigned next_poly_ch_idx; // This is the next channel that will be active. + srate_t srate; // Sample rate used for time base + bool preset_delta_fl; // Preset change trigger flag. + bool trigFl; // Cross-fade trigger flag. + } inst_t; + + void _trigger_xfade( inst_t* p ) + { + // begin fading out the cur channel + p->netA[p->cur_poly_ch_idx].target_gain = 0; + + // the next poly-ch become the cur poly-ch + p->cur_poly_ch_idx = p->next_poly_ch_idx; + + // the next poly-ch advances + p->next_poly_ch_idx = p->next_poly_ch_idx+1 >= p->poly_ch_cnt ? 0 : p->next_poly_ch_idx+1; + + // begin fading in the new cur channel + p->netA[p->cur_poly_ch_idx].target_gain = 1; + + // if the next channel is not already at 0 send it in that direction + p->netA[p->next_poly_ch_idx].target_gain = 0; + + //printf("xfad:%i %i : %i\n",p->cur_poly_ch_idx, p->next_poly_ch_idx,p->poly_ch_cnt); + + } + + rc_t create( proc_t* proc ) + { + rc_t rc = kOkRC; + const char* netLabel = nullptr; + const char* presetLabel = nullptr; + unsigned netLabelSfxId = kBaseSfxId; + abuf_t* srateSrc = nullptr; + coeff_t dum_dbl; + + inst_t* p = mem::allocZ(); + + proc->userPtr = p; + + if((rc = var_register(proc,kAnyChIdx,kTriggerPId,"trigger", kBaseSfxId )) != kOkRC ) + goto errLabel; + + if((rc = var_register_and_get(proc,kAnyChIdx, + kNetLabelPId, "net", kBaseSfxId, netLabel, + kNetLabelSfxPId, "netSfxId", kBaseSfxId, netLabelSfxId, + kSrateRefPId, "srateSrc", kBaseSfxId, srateSrc, + kDurMsPId, "durMs", kBaseSfxId, p->xfadeDurMs, + kPresetPId, "preset", kBaseSfxId, presetLabel, + kGainPId, "gain", kBaseSfxId, dum_dbl)) != kOkRC ) + { + goto errLabel; + } + + // locate the source poly-network for this xfad-ctl + if((rc = proc_find(*proc->net,netLabel,netLabelSfxId,p->net_proc)) != kOkRC ) + { + cwLogError(rc,"The xfade_ctl source network proc instance '%s:%i' was not found.",cwStringNullGuard(netLabel),netLabelSfxId); + goto errLabel; + } + + if( p->net_proc->internal_net->poly_cnt < 3 ) + { + cwLogError(rc,"The xfade_ctl source network must have at least 3 poly channels. %i < 3",p->net_proc->internal_net->poly_cnt); + goto errLabel; + } + + p->poly_ch_cnt = p->net_proc->internal_net->poly_cnt; + + // create the gain output variables - one output for each poly-channel + for(unsigned i=1; ipoly_ch_cnt; ++i) + { + variable_t* dum; + if((rc = var_create(proc, "gain", i, kGainPId+i, kAnyChIdx, nullptr, kInvalidTFl, dum )) != kOkRC ) + { + cwLogError(rc,"'gain:%i' create failed.",i); + goto errLabel; + } + } + + // count of proc's in one poly-ch of the poly network + p->net_proc_cnt = p->net_proc->internal_net->proc_arrayN / p->net_proc->internal_net->poly_cnt; + + p->netA = mem::allocZ(p->poly_ch_cnt); + + // create the proxy network networks + for(unsigned i=0; ipoly_ch_cnt; ++i) + { + p->netA[i].net.proc_arrayAllocN = p->net_proc_cnt; + p->netA[i].net.proc_arrayN = p->netA[i].net.proc_arrayAllocN; + p->netA[i].net.proc_array = mem::allocZ(p->netA[i].net.proc_arrayAllocN); + p->netA[i].net.presetsCfg = p->net_proc->internal_net->presetsCfg; + + p->netA[i].net.presetA = p->net_proc->internal_net->presetA; + p->netA[i].net.presetN = p->net_proc->internal_net->presetN; + + p->netA[i].net.preset_pairA = p->net_proc->internal_net->preset_pairA; + p->netA[i].net.preset_pairN = p->net_proc->internal_net->preset_pairN; + + for(unsigned j=0,k=0; jnet_proc->internal_net->proc_arrayN; ++j) + if( p->net_proc->internal_net->proc_array[j]->label_sfx_id == i ) + { + assert( k < p->net_proc_cnt ); + p->netA[i].net.proc_array[k++] = p->net_proc->internal_net->proc_array[j]; + } + } + + if( srateSrc == nullptr ) + p->srate = proc->ctx->sample_rate; + else + p->srate = srateSrc->srate; + + + // setup the channels such that the first active channel after _trigger_xfade() + // will be channel 0 + p->cur_poly_ch_idx = 1; + p->next_poly_ch_idx = 2; + _trigger_xfade(p); // cur=2 nxt=0 initialize inst ptrs in range: p->net[0:net_proc_cnt] + _trigger_xfade(p); // cur=0 nxt=1 initialize inst ptrs in range: p->net[net_proc_cnt:2*net_proc_cnt] + + errLabel: + return rc; + } + + rc_t destroy( proc_t* proc ) + { + inst_t* p = (inst_t*)proc->userPtr; + for(unsigned i=0; ipoly_ch_cnt; ++i) + mem::release(p->netA[i].net.proc_array); + + mem::release(p->netA); + mem::release(proc->userPtr); + + return kOkRC; + } + + rc_t value( proc_t* proc, variable_t* var ) + { + rc_t rc = kOkRC; + inst_t* p = (inst_t*)proc->userPtr; + + switch( var->vid ) + { + case kTriggerPId: + p->trigFl = true; + break; + + case kPresetPId: + p->preset_delta_fl = true; + break; + } + + return rc; + } + + // return sign of expression as a float + float _signum( float v ) { return (0.0f < v) - (v < 0.0f); } + + rc_t exec( proc_t* proc ) + { + rc_t rc = kOkRC; + inst_t* p = (inst_t*)proc->userPtr; + + // time in sample frames to complete a xfade + double xfade_dur_smp = p->xfadeDurMs * p->srate / 1000.0; + + // fraction of a xfade which will be completed in on exec() cycle + float delta_gain_per_cycle = (float)(proc->ctx->framesPerCycle / xfade_dur_smp); + + + if( p->preset_delta_fl ) + { + const char* preset_label = nullptr; + + p->preset_delta_fl = false; + + if((rc = var_get(proc,kPresetPId,kAnyChIdx,preset_label)) != kOkRC ) + { + rc = cwLogError(rc,"Preset label access failed."); + goto errLabel; + } + + if((rc = network_apply_preset(p->netA[p->next_poly_ch_idx].net, preset_label,p->next_poly_ch_idx)) != kOkRC ) + { + rc = cwLogError(rc,"Appy preset '%s' failed.",cwStringNullGuard(preset_label)); + goto errLabel; + } + } + + // check if a cross-fade has been triggered + if(p->trigFl ) + { + p->trigFl = false; + _trigger_xfade(p); + } + + // update the cross-fade gain outputs + for(unsigned i=0; inet_proc->internal_net->poly_cnt; ++i) + { + p->netA[i].cur_gain += _signum(p->netA[i].target_gain - p->netA[i].cur_gain) * delta_gain_per_cycle; + + p->netA[i].cur_gain = std::min(1.0f, std::max(0.0f, p->netA[i].cur_gain)); + + var_set(proc,kGainPId+i,kAnyChIdx,p->netA[i].cur_gain); + } + + + + + errLabel: + return rc; + } + + class_members_t members = { + .create = create, + .destroy = destroy, + .value = value, + .exec = exec, + .report = nullptr + }; + + } + //------------------------------------------------------------------------------------------------------------------ + // + // poly_merge + // + + namespace poly_merge + { + enum + { + kOutGainPId, + kOutPId, + kInBasePId, + }; + + typedef struct + { + unsigned inAudioVarCnt; + unsigned gainVarCnt; + unsigned baseGainPId; + } inst_t; + + + rc_t _create( proc_t* proc, inst_t* p ) + { + rc_t rc = kOkRC; + + unsigned inAudioChCnt = 0; + srate_t srate = 0; + unsigned audioFrameN = 0; + unsigned sfxIdAllocN = proc_var_count(proc); + unsigned sfxIdA[ sfxIdAllocN ]; + + + // register the output gain variable + if((rc = var_register(proc,kAnyChIdx,kOutGainPId,"out_gain",kBaseSfxId)) != kOkRC ) + goto errLabel; + + + // get the the sfx_id's of the input audio variables + if((rc = var_mult_sfx_id_array(proc, "in", sfxIdA, sfxIdAllocN, p->inAudioVarCnt )) != kOkRC ) + goto errLabel; + + // for each input audio variable + for(unsigned i=0; iinAudioVarCnt; ++i) + { + abuf_t* abuf; + + // register the input audio variable + if((rc = var_register_and_get(proc,kAnyChIdx,kInBasePId+i,"in",sfxIdA[i],abuf)) != kOkRC ) + goto errLabel; + + + // the sample rate of off input audio signals must be the same + if( srate != 0 && abuf->srate != srate ) + { + rc = cwLogError(kInvalidArgRC,"All signals on a poly merge must have the same sample rate."); + goto errLabel; + } + + srate = abuf->srate; + + // the count of frames in all audio signals must be the same + if( audioFrameN != 0 && abuf->frameN != audioFrameN ) + { + rc = cwLogError(kInvalidArgRC,"All signals on a poly merge must have the same frame count."); + goto errLabel; + } + + audioFrameN = abuf->frameN; + + inAudioChCnt += abuf->chN; + } + + // Get the sfx-id's of the input gain variables + if((rc = var_mult_sfx_id_array(proc, "gain", sfxIdA, sfxIdAllocN, p->gainVarCnt )) != kOkRC ) + goto errLabel; + + + // There must be one gain variable for each audio input or exactly one gain variable + if( p->gainVarCnt != p->inAudioVarCnt && p->gainVarCnt != 1 ) + { + rc = cwLogError(kInvalidArgRC,"The count of gain variables must be the same as the count of audio variables are there must be one gain variable."); + goto errLabel; + } + + // set the baseInGainPId + p->baseGainPId = kInBasePId + p->inAudioVarCnt; + + // register each of the input gain variables + for(unsigned i=0; igainVarCnt; ++i) + { + if((rc = var_register(proc,kAnyChIdx,p->baseGainPId + i,"gain",sfxIdA[i])) != kOkRC ) + goto errLabel; + + } + + rc = var_register_and_set( proc, "out", kBaseSfxId, kOutPId, kAnyChIdx, srate, inAudioChCnt, audioFrameN ); + + errLabel: + return rc; + } + + rc_t _destroy( proc_t* proc, inst_t* p ) + { + return kOkRC; + } + + rc_t _value( proc_t* proc, inst_t* p, variable_t* var ) + { + return kOkRC; + } + + unsigned _merge_in_one_audio_var( proc_t* proc, const abuf_t* ibuf, abuf_t* obuf, unsigned outChIdx, coeff_t gain ) + { + // for each channel + for(unsigned i=0; ichN && outChIdxchN; ++i) + { + sample_t* isig = ibuf->buf + i * ibuf->frameN; + sample_t* osig = obuf->buf + outChIdx * obuf->frameN; + + // apply the gain + for(unsigned j=0; jframeN; ++j) + osig[j] = gain * isig[j]; + + outChIdx += 1; + } + + return outChIdx; + } + + rc_t _exec( proc_t* proc, inst_t* p ) + { + rc_t rc = kOkRC; + abuf_t* obuf = nullptr; + unsigned oChIdx = 0; + coeff_t igain = 1; + coeff_t ogain = 1; + + // get the output audio buffer + if((rc = var_get(proc,kOutPId, kAnyChIdx, obuf)) != kOkRC ) + goto errLabel; + + // get the output audio gain + if((rc = var_get(proc,kOutGainPId, kAnyChIdx, ogain)) != kOkRC ) + goto errLabel; + + // for each audio input variable + for(unsigned i=0; iinAudioVarCnt; ++i) + { + const abuf_t* ibuf = nullptr; + + // get the input audio buffer + if((rc = var_get(proc,kInBasePId+i, kAnyChIdx, ibuf )) != kOkRC ) + goto errLabel; + + // get the input gain + if( i < p->gainVarCnt ) + var_get(proc,p->baseGainPId+i,kAnyChIdx,igain); + + // merge the input audio signal into the output audio buffer + oChIdx = _merge_in_one_audio_var( proc, ibuf, obuf, oChIdx, igain * ogain ); + } + + errLabel: + return rc; + } + + rc_t _report( proc_t* proc, inst_t* p ) + { return kOkRC; } + + class_members_t members = { + .create = std_create, + .destroy = std_destroy, + .value = std_value, + .exec = std_exec, + .report = std_report + }; + + } + + + //------------------------------------------------------------------------------------------------------------------ + // + // sample_hold + // + namespace sample_hold + { + enum + { + kInPId, + kPeriodMsPId, + kOutPId, + kMeanPId, + }; + + typedef struct inst_str + { + unsigned chN; // count of audio input channels and output sample variables. + unsigned bufAllocFrmN; // count of sample frames allocated in the sample buffer + unsigned periodFrmN; // count of sample frames in the sample period + unsigned ii; // next buf[][] frame index to receive an incoming audio sample + sample_t** buf; // buf[chN][bufSmpAllocN] + } inst_t; + + unsigned _period_ms_to_smp( srate_t srate, unsigned framesPerCycle, double periodMs ) + { + unsigned frmN = (unsigned)(srate * periodMs / 1000.0); + return std::max(framesPerCycle,frmN); + } + + unsigned _period_ms_to_smp( srate_t srate, unsigned framesPerCycle, unsigned bufSmpAllocN, double periodMs ) + { + unsigned frmN = _period_ms_to_smp(srate,framesPerCycle, periodMs ); + + // clip sample period to the max. buffer length. + return std::min(bufSmpAllocN,frmN); + } + + sample_t _mean( inst_t* p, unsigned chIdx, unsigned oi, unsigned n0, unsigned n1 ) + { + sample_t sum = 0; + + for(unsigned i=0; ibuf[chIdx][oi + i ]; + + for(unsigned i=0; ibuf[chIdx][i]; + + return n0+n1==0 ? 0 : sum/(n0+n1); + } + + void _destroy( inst_t* p ) + { + for(unsigned i=0; ichN; ++i) + mem::release(p->buf[i]); + mem::release(p->buf); + mem::release(p); + } + + rc_t create( proc_t* proc ) + { + rc_t rc = kOkRC; + const abuf_t* abuf = nullptr; // + double periodMs = 0; + + proc->userPtr = mem::allocZ(); + inst_t* p = (inst_t*)proc->userPtr; + + // get the source audio buffer + if((rc = var_register_and_get(proc, kAnyChIdx, + kInPId, "in", kBaseSfxId, abuf, + kPeriodMsPId, "period_ms",kBaseSfxId, periodMs)) != kOkRC ) + { + goto errLabel; + } + + p->chN = abuf->chN; + p->bufAllocFrmN = _period_ms_to_smp( abuf->srate, proc->ctx->framesPerCycle, periodMs ); + p->periodFrmN = p->bufAllocFrmN; + p->buf = mem::allocZ(abuf->chN); + + for(unsigned i=0; ichN; ++i) + { + p->buf[i] = mem::allocZ(p->bufAllocFrmN); + if((rc = var_register_and_set(proc, i, + kOutPId, "out", kBaseSfxId, 0.0f, + kMeanPId, "mean", kBaseSfxId, 0.0f)) != kOkRC ) + { + goto errLabel; + } + } + + errLabel: + if(rc != kOkRC ) + _destroy(p); + return rc; + } + + rc_t destroy( proc_t* proc ) + { + inst_t* p = (inst_t*)(proc->userPtr); + _destroy(p); + return kOkRC; + } + + rc_t value( proc_t* proc, variable_t* var ) + { + rc_t rc = kOkRC; + + switch( var->vid ) + { + case kPeriodMsPId: + { + double periodMs; + const abuf_t* abuf; + inst_t* p = (inst_t*)(proc->userPtr); + + var_get(proc,kInPId,kAnyChIdx,abuf); + + if((rc = var_get(var,periodMs)) == kOkRC ) + { + p->periodFrmN = _period_ms_to_smp( abuf->srate, proc->ctx->framesPerCycle, p->bufAllocFrmN, periodMs ); + } + } + break; + + default: + break; + + } + + return rc; + } + + rc_t exec( proc_t* proc ) + { + rc_t rc = kOkRC; + const abuf_t* ibuf = nullptr; + inst_t* p = (inst_t*)(proc->userPtr); + unsigned oi = 0; + unsigned n0 = 0; + unsigned n1 = 0; + unsigned chN = 0; + + // get the src buffer + if((rc = var_get(proc,kInPId, kAnyChIdx, ibuf )) != kOkRC ) + goto errLabel; + + chN = std::min(ibuf->chN,p->chN); + + // Copy samples into buf. + for(unsigned i=0; ichN; ++i) + { + sample_t* isig = ibuf->buf + i*ibuf->frameN; + sample_t* obuf = p->buf[i]; + unsigned k = p->ii; + + for(unsigned j=0; jframeN; ++j) + { + obuf[k++] = isig[j]; + if( k>= p->bufAllocFrmN ) + k -= p->bufAllocFrmN; + } + } + + // advance the input index + p->ii += ibuf->frameN; + if( p->ii >= p->bufAllocFrmN ) + p->ii -= p->bufAllocFrmN; + + + // if the sampling buf is in range oi:ii + if( p->ii >= p->periodFrmN ) + { + oi = p->ii - p->periodFrmN; + n0 = p->ii - oi; + n1 = 0; + } + else // the sampling buf is in two parts: bufAllocN-ii:bufAllocN, 0:ii + { + oi = p->bufAllocFrmN - (p->periodFrmN - p->ii); + n0 = p->bufAllocFrmN - oi; + n1 = p->ii; + } + + for(unsigned i=0; ichN; ++i) + { + // the output is the first sample in the buffer + var_set(proc,kOutPId,i, p->buf[i][oi] ); + + if( var_is_a_source(proc,kMeanPId,i) ) + var_set(proc,kMeanPId,i, _mean(p,i,oi,n0,n1)); + } + + errLabel: + return rc; + } + + + class_members_t members = { + .create = create, + .destroy = destroy, + .value = value, + .exec = exec, + .report = nullptr + }; + + } + + //------------------------------------------------------------------------------------------------------------------ + // + // Number + // + namespace number + { + enum { + kValuePId, + kStorePId, + }; + + typedef struct + { + bool store_fl; + } inst_t; + + rc_t _create( proc_t* proc, inst_t* p ) + { + rc_t rc = kOkRC; + + if((rc = var_register(proc,kAnyChIdx, + kValuePId,"value",kBaseSfxId, + kStorePId,"store",kBaseSfxId)) != kOkRC ) + { + goto errLabel; + } + + errLabel: + return rc; + } + + rc_t _destroy( proc_t* proc, inst_t* p ) + { return kOkRC; } + + rc_t _value( proc_t* proc, inst_t* p, variable_t* var ) + { + // skip the 'stored' value sent through prior to runtime. + if( var->vid == kStorePId /*&& proc->ctx->isInRuntimeFl*/) + p->store_fl = true; + + return kOkRC; + } + + rc_t _exec( proc_t* proc, inst_t* p ) + { + rc_t rc = kOkRC; + + if( p->store_fl ) + { + variable_t* var = nullptr; + // Set 'value' from 'store'. + // Note that we set the 'value' directly from var->value so that + // no extra type converersion is applied. In this case the value + // 'store' will be coerced to the type of 'value' + if((rc = var_find(proc, kStorePId, kAnyChIdx, var )) == kOkRC && var->value != nullptr && is_connected_to_source(var) ) + { + rc = var_set(proc,kValuePId,kAnyChIdx,var->value); + } + p->store_fl = false; + } + + return rc; + } + + rc_t _report( proc_t* proc, inst_t* p ) + { return kOkRC; } + + class_members_t members = { + .create = std_create, + .destroy = std_destroy, + .value = std_value, + .exec = std_exec, + .report = std_report + }; + + } + + + //------------------------------------------------------------------------------------------------------------------ + // + // Timer + // + namespace timer + { + enum { + kSratePId, + kPeriodMsPId, + kOutPId, + }; + + typedef struct + { + unsigned periodFrmN; + unsigned periodPhase; + } inst_t; + + unsigned _period_ms_to_frame_count( proc_t* proc, inst_t* p, srate_t srate, ftime_t periodMs ) + { + return std::max((unsigned)(srate * periodMs / 1000.0), proc->ctx->framesPerCycle); + } + + rc_t create( proc_t* proc ) + { + rc_t rc = kOkRC; + ftime_t periodMs = 0; + srate_t srate = 0; + inst_t* p = mem::allocZ(); + proc->userPtr = p; + + + if((rc = var_register_and_get(proc,kAnyChIdx, + kSratePId, "srate", kBaseSfxId,srate, + kPeriodMsPId, "period_ms",kBaseSfxId,periodMs)) != kOkRC ) + { + goto errLabel; + } + + if( srate == 0 ) + var_set(proc,kSratePId,kAnyChIdx,proc->ctx->sample_rate); + + if((rc = var_register_and_set(proc,kAnyChIdx, + kOutPId, "out", kBaseSfxId,false)) != kOkRC ) + { + goto errLabel; + } + + p->periodFrmN = _period_ms_to_frame_count(proc,p,srate,periodMs); + + errLabel: + return rc; + } + + rc_t destroy( proc_t* proc ) + { + rc_t rc = kOkRC; + inst_t* p = (inst_t*)proc->userPtr; + mem::release(p); + return rc; + } + + rc_t value( proc_t* proc, variable_t* var ) + { + rc_t rc = kOkRC; + switch( var->vid ) + { + case kPeriodMsPId: + { + double periodMs; + srate_t srate; + inst_t* p = (inst_t*)(proc->userPtr); + + var_get(proc,kSratePId,kAnyChIdx,srate); + + if((rc = var_get(var,periodMs)) == kOkRC ) + p->periodFrmN = _period_ms_to_frame_count( proc, p, srate, periodMs ); + } + break; + + default: + break; + } + return rc; + } + + rc_t exec( proc_t* proc ) + { + rc_t rc = kOkRC; + inst_t* p = (inst_t*)proc->userPtr; + + p->periodPhase += proc->ctx->framesPerCycle; + + //printf("%i %i\n",p->periodPhase,p->periodFrmN); + + if( p->periodPhase >= p->periodFrmN ) + { + p->periodPhase -= p->periodFrmN; + + bool val = false; + var_get(proc,kOutPId,kAnyChIdx,val); + + //printf("%i %i %i\n",p->periodPhase,p->periodFrmN,val); + + var_set(proc,kOutPId,kAnyChIdx,!val); + } + + return rc; + } + + class_members_t members = { + .create = create, + .destroy = destroy, + .value = value, + .exec = exec, + .report = nullptr + }; + + } + + + //------------------------------------------------------------------------------------------------------------------ + // + // Counter + // + namespace counter + { + enum { + kTriggerPId, + kResetPId, + kInitPId, + kMinPId, + kMaxPId, + kIncPId, + kRepeatPId, + kModePId, + kOutTypePId, + kOutPId + }; + + enum { + kModuloModeId, + kReverseModeId, + kClipModeId, + kInvalidModeId + }; + + typedef struct + { + unsigned mode_id; + + bool trig_val; + bool delta_fl; + + bool reset_val; + bool reset_fl; + + bool done_fl; + + double dir; + + } inst_t; + + idLabelPair_t modeArray[] = { + { kModuloModeId, "modulo" }, + { kReverseModeId, "reverse" }, + { kClipModeId, "clip" }, + { kInvalidId, ""} + }; + + + unsigned _string_to_mode_id( const char* mode_label, unsigned& mode_id_ref ) + { + mode_id_ref = kInvalidId; + for(unsigned i=0; modeArray[i].id != kInvalidId; ++i) + if( textIsEqual(modeArray[i].label,mode_label) ) + { + mode_id_ref = modeArray[i].id; + return kOkRC; + } + + return cwLogError(kInvalidArgRC,"'%s' is not a valid counter 'mode'.",cwStringNullGuard(mode_label)); + } + + rc_t create( proc_t* proc ) + { + rc_t rc = kOkRC; + inst_t* p = mem::allocZ(); + proc->userPtr = p; + + double init_val; + const char* mode_label; + variable_t* dum = nullptr; + const char* out_type_label; + unsigned out_type_fl; + + if((rc = var_register_and_get(proc, kAnyChIdx, + kTriggerPId, "trigger", kBaseSfxId, p->trig_val, + kResetPId, "reset", kBaseSfxId, p->reset_val, + kInitPId, "init", kBaseSfxId, init_val, + kModePId, "mode", kBaseSfxId, mode_label, + kOutTypePId, "out_type",kBaseSfxId, out_type_label)) != kOkRC ) + { + goto errLabel; + } + + + if((rc = var_register(proc, kAnyChIdx, + kMinPId, "min", kBaseSfxId, + kMaxPId, "max", kBaseSfxId, + kIncPId, "inc", kBaseSfxId, + kRepeatPId, "repeat_fl", kBaseSfxId)) != kOkRC ) + { + goto errLabel; + } + + // get the type of the output + if(out_type_label==nullptr || (out_type_fl = value_type_label_to_flag( out_type_label )) == kInvalidTFl ) + { + rc = cwLogError(kInvalidArgRC,"The output type '%s' is not a valid type.",cwStringNullGuard(out_type_label)); + goto errLabel; + } + + + if((rc = var_create( proc, "out", kBaseSfxId, kOutPId, kAnyChIdx, nullptr, out_type_fl, dum )) != kOkRC ) + { + goto errLabel; + } + + if((rc = var_set( proc, kOutPId, kAnyChIdx, 0u )) != kOkRC ) + { + rc = cwLogError(rc,"Unable to set the initial counter value to %f.",init_val); + goto errLabel; + } + + + if((rc = _string_to_mode_id(mode_label,p->mode_id)) != kOkRC ) + goto errLabel; + + p->dir = 1.0; + + errLabel: + return rc; + } + + rc_t destroy( proc_t* proc ) + { + rc_t rc = kOkRC; + + inst_t* p = (inst_t*)proc->userPtr; + mem::release(p); + + return rc; + } + + rc_t value( proc_t* proc, variable_t* var ) + { + rc_t rc = kOkRC; + inst_t* p = (inst_t*)proc->userPtr; + + switch( var->vid ) + { + case kTriggerPId: + { + bool v; + if((rc = var_get(var,v)) == kOkRC ) + { + if( !p->delta_fl ) + p->delta_fl = p->trig_val != v; + + p->trig_val = v; + } + } + break; + + case kModePId: + { + const char* s; + if((rc = var_get(var,s)) == kOkRC ) + rc = _string_to_mode_id(s,p->mode_id); + } + break; + + } + + return rc; + } + + rc_t exec( proc_t* proc ) + { + rc_t rc = kOkRC; + inst_t* p = (inst_t*)proc->userPtr; + double cnt,inc,minv,maxv; + bool v; + + if( !p->delta_fl ) + return rc; + + p->delta_fl = false; + if((rc = var_get(proc,kTriggerPId,kAnyChIdx,v)) != kOkRC ) + { + cwLogError(rc,"Fail!"); + goto errLabel; + } + + p->trig_val = v; + + var_get(proc,kOutPId,kAnyChIdx,cnt); + var_get(proc,kIncPId,kAnyChIdx,inc); + var_get(proc,kMinPId,kAnyChIdx,minv); + var_get(proc,kMaxPId,kAnyChIdx,maxv); + + cnt += p->dir * inc; + + if( minv > cnt || cnt > maxv ) + { + bool repeat_fl; + var_get(proc,kRepeatPId,kAnyChIdx,repeat_fl); + + if( !repeat_fl ) + p->done_fl = true; + else + { + if( cnt > maxv) + { + switch( p->mode_id ) + { + case kModuloModeId: + while(cnt > maxv ) + cnt = minv + (cnt-maxv); + break; + + case kReverseModeId: + p->dir = -1 * p->dir; + while( cnt > maxv ) + cnt = maxv - (cnt-maxv); + break; + + case kClipModeId: + cnt = maxv; + break; + + default: + assert(0); + + } + } + + if( cnt < minv) + { + switch( p->mode_id ) + { + case kModuloModeId: + while( cnt < minv ) + cnt = maxv - (minv-cnt); + break; + + case kReverseModeId: + p->dir = -1 * p->dir; + while(cnt < minv ) + cnt = minv + (minv-cnt); + break; + + case kClipModeId: + cnt = minv; + break; + + default: + assert(0); + } + } + } + } + + // if the counter has not reached it's terminal state + if( !p->done_fl ) + var_set(proc,kOutPId,kAnyChIdx,cnt); + + + errLabel: + return rc; + } + + class_members_t members = { + .create = create, + .destroy = destroy, + .value = value, + .exec = exec, + .report = nullptr + }; + + } + + //------------------------------------------------------------------------------------------------------------------ + // + // List + // + namespace list + { + enum + { + kInPId, + kListPId, + kOutPId, + kValueBasePId + }; + + typedef struct + { + unsigned listN; // the length of the list + const object_t* list; // the list + unsigned typeFl; // the output type + unsigned index; // the last index referenced + bool deltaFl; + } inst_t; + + rc_t _determine_type( const object_t* list, unsigned& typeFl_ref ) + { + rc_t rc = kOkRC; + + typeFl_ref = kInvalidTFl; + + enum { bool_idx, uint_idx, int_idx, float_idx, double_idx, string_idx, cfg_idx, typeN }; + typedef struct type_map_str + { + unsigned idx; + unsigned typeFl; + unsigned cnt; + } type_map_t; + + type_map_t typeA[] = { + { bool_idx, kBoolTFl, 0 }, + { uint_idx, kUIntTFl, 0 }, + { int_idx, kIntTFl, 0 }, + { float_idx, kFloatTFl, 0 }, + { double_idx, kDoubleTFl, 0 }, + { string_idx, kStringTFl, 0 }, + { cfg_idx, kCfgTFl, 0 }, + }; + + + // count the number of each type of element in the list. + for(unsigned i=0; ichild_count(); ++i) + { + const object_t* c = list->child_ele(i); + + switch( c->type->id ) + { + case kCharTId: typeA[uint_idx].cnt+=1; break; + case kInt8TId: typeA[int_idx].cnt +=1; break; + case kUInt8TId: typeA[uint_idx].cnt+=1; break; + case kInt16TId: typeA[int_idx].cnt +=1; break; + case kUInt16TId: typeA[uint_idx].cnt+=1; break; + case kInt32TId: typeA[int_idx].cnt +=1; break; + case kUInt32TId: typeA[uint_idx].cnt+=1; break; + case kFloatTId: typeA[float_idx].cnt+=1; break; + case kDoubleTId: typeA[double_idx].cnt+=1; break; + case kBoolTId: typeA[bool_idx].cnt+=1; break; + case kStringTId: typeA[string_idx].cnt+=1; break; + case kCStringTId:typeA[string_idx].cnt+=1; break; + break; + + default: + switch( c->type->id ) + { + case kVectTId: + case kPairTId: + case kListTId: + case kDictTId: + typeA[cfg_idx].cnt +=1; + break; + + default: + rc = cwLogError(kSyntaxErrorRC,"The object type '0x%x' is not a valid list entry type. %i",c->type->flags,list->child_count()); + goto errLabel; + } + } + + unsigned type_flag = kInvalidTFl; // type flag of one of the reference types + unsigned type_cnt = 0; // count of types + + for(unsigned i=0; i 0 ) + { + type_cnt += 1; + type_flag = typeA[i].typeFl; + } + + // it is an error if more than one type of element was included in the list - + // and one of those types was string or cfg - having multiple numeric types + // is ok because they can be converted between each other - but string, and cfg's + // cannot be converted to numbers, nor can the be converted between each other. + if( type_cnt > 1 && (typeA[string_idx].cnt>0 || typeA[cfg_idx].cnt>0) ) + { + rc = cwLogError(kInvalidArgRC,"The list types. The list must be all numerics, all strings, or all cfg. types."); + for(unsigned i=0; i 0 ) + cwLogInfo("%i %s",typeA[i].cnt, value_type_flag_to_label(typeA[i].typeFl)); + + goto errLabel; + } + + typeFl_ref = type_flag; + } + + + errLabel: + return rc; + } + + template< typename T > + rc_t _set_out_tmpl( proc_t* proc, inst_t* p, unsigned idx, unsigned vid, T& v ) + { + rc_t rc; + const object_t* ele; + + // get the list element to output + if((ele = p->list->child_ele(idx)) == nullptr ) + { + rc = cwLogError(kEleNotFoundRC,"The list element at index %i could not be accessed.",idx); + goto errLabel; + } + + // get the value of the list element + if((rc = ele->value(v)) != kOkRC ) + { + rc = cwLogError(rc,"List value access failed on index %i",idx); + goto errLabel; + } + + // set the output + if((rc = var_set(proc,vid,kAnyChIdx,v)) != kOkRC ) + { + rc = cwLogError(rc,"List output failed on index %i",idx); + goto errLabel; + } + + errLabel: + return rc; + } + + rc_t _set_output( proc_t* proc, inst_t* p, unsigned idx, unsigned vid ) + { + rc_t rc; + + switch( p->typeFl ) + { + case kUIntTFl: + { + unsigned v; + rc = _set_out_tmpl(proc,p,idx,vid,v); + } + break; + + case kIntTFl: + { + int v; + rc = _set_out_tmpl(proc,p,idx,vid,v); + } + break; + + case kFloatTFl: + { + float v; + rc = _set_out_tmpl(proc,p,idx,vid,v); + } + break; + + case kDoubleTFl: + { + double v; + rc = _set_out_tmpl(proc,p,idx,vid,v); + } + break; + + case kStringTFl: + { + const char* v; + rc = _set_out_tmpl(proc,p,idx,vid,v); + } + break; + + case kCfgTFl: + { + const object_t* v; + rc = _set_out_tmpl(proc,p,idx,vid,v); + } + break; + + default: + rc = cwLogError(kInvalidArgRC,"The list type flag %s (0x%x) is not valid.",value_type_flag_to_label(p->typeFl),p->typeFl); + goto errLabel; + break; + + } + + errLabel: + return rc; + } + + rc_t _set_output( proc_t* proc, inst_t* p ) + { + rc_t rc; + unsigned idx; + + if((rc = var_get(proc,kInPId,kAnyChIdx,idx)) != kOkRC ) + { + rc = cwLogError(rc,"Unable to get the list index."); + goto errLabel; + } + + // if the index has not changed then there is nothing to do + if( idx == p->index ) + goto errLabel; + + if((rc = _set_output(proc,p,idx, kOutPId )) != kOkRC ) + goto errLabel; + + p->index = idx; + + errLabel: + return rc; + } + + + + rc_t create( proc_t* proc ) + { + rc_t rc = kOkRC; + inst_t* p = mem::allocZ(); + unsigned index; + proc->userPtr = p; + + variable_t* dum = nullptr; + + p->index = kInvalidIdx; + p->typeFl = kInvalidTFl; + p->deltaFl = false; + + if((rc = var_register_and_get(proc, kAnyChIdx, + kInPId, "in", kBaseSfxId, index, + kListPId,"list", kBaseSfxId, p->list)) != kOkRC ) + { + goto errLabel; + } + + if( !p->list->is_list() ) + { + cwLogError(kSyntaxErrorRC,"The list cfg. value is not a list."); + goto errLabel; + } + + p->listN = p->list->child_count(); + + // determine what type of element is in the list + // (all elements in the this list must be of the same type: numeric,string,cfg) + if((rc = _determine_type( p->list, p->typeFl )) != kOkRC ) + goto errLabel; + + // create the output variable + if((rc = var_create( proc, "out", kBaseSfxId, kOutPId, kAnyChIdx, nullptr, p->typeFl, dum )) != kOkRC ) + { + rc = cwLogError(rc,"'out' var create failed."); + goto errLabel; + } + + // set the initial value of the output + if((rc = _set_output(proc,p)) != kOkRC ) + goto errLabel; + + // create the output variable + for(unsigned i=0; ilistN; ++i) + { + if((rc = var_create( proc, "value", i, kValueBasePId+i, kAnyChIdx, nullptr, p->typeFl, dum )) != kOkRC ) + { + rc = cwLogError(rc,"'value%i' var create failed.",i); + goto errLabel; + } + + if((rc = _set_output(proc, p, i, kValueBasePId+i )) != kOkRC ) + { + rc = cwLogError(rc,"'value%i' output failed.",i); + goto errLabel; + } + + } + + + errLabel: + return rc; + } + + rc_t destroy( proc_t* proc ) + { + rc_t rc = kOkRC; + + inst_t* p = (inst_t*)proc->userPtr; + + mem::release(p); + + return rc; + } + + rc_t value( proc_t* proc, variable_t* var ) + { + rc_t rc = kOkRC; + if( var->vid == kInPId ) + { + inst_t* p = (inst_t*)proc->userPtr; + unsigned idx; + if( var_get(var,idx) == kOkRC && idx != p->index) + p->deltaFl = true; + } + return rc; + } + + rc_t exec( proc_t* proc ) + { + rc_t rc = kOkRC; + inst_t* p = (inst_t*)proc->userPtr; + + if( p->deltaFl ) + { + rc = _set_output(proc, p ); + p->deltaFl = false; + } + return rc; + } + + class_members_t members = { + .create = create, + .destroy = destroy, + .value = value, + .exec = exec, + .report = nullptr + }; + + } + + //------------------------------------------------------------------------------------------------------------------ + // + // add + // + namespace add + { + enum { + kOutPId, + kOTypePId, + kInPId + }; + + typedef struct + { + bool delta_fl; + unsigned inN; + } inst_t; + + + template< typename T > + rc_t _sum( proc_t* proc, variable_t* var ) + { + rc_t rc = kOkRC; + inst_t* p = (inst_t*)proc->userPtr; + + T sum = 0; + + // read and sum the inputs + for(unsigned i=0; iinN; ++i) + { + T val; + if((rc = var_get(proc,kInPId+i,kAnyChIdx,val)) == kOkRC ) + sum += val; + else + { + rc = cwLogError(rc,"Operand index %i read failed.",i); + goto errLabel; + } + } + + // set the output + if((rc = var_set(var,sum)) != kOkRC ) + { + rc = cwLogError(rc,"Result set failed."); + goto errLabel; + } + + errLabel: + return rc; + } + + rc_t _exec( proc_t* proc, variable_t* out_var=nullptr ) + { + rc_t rc = kOkRC; + inst_t* p = (inst_t*)(proc->userPtr); + + if( !p->delta_fl ) + return rc; + + p->delta_fl = false; + + if( out_var == nullptr ) + if((rc = var_find(proc,kOutPId,kAnyChIdx,out_var)) != kOkRC ) + { + rc = cwLogError(rc,"The output variable could not be found."); + goto errLabel; + } + + switch( out_var->varDesc->type ) + { + case kBoolTFl: rc = _sum(proc,out_var); break; + case kUIntTFl: rc = _sum(proc,out_var); break; + case kIntTFl: rc = _sum(proc,out_var); break; + case kFloatTFl: rc = _sum(proc,out_var); break; + case kDoubleTFl: rc = _sum(proc,out_var); break; + default: + rc = cwLogError(kInvalidArgRC,"The output type %s (0x%x) is not valid.",value_type_flag_to_label(out_var->value->tflag),out_var->value->tflag); + goto errLabel; + } + + if(rc != kOkRC ) + rc = cwLogError(kOpFailRC,"Sum failed."); + + errLabel: + return rc; + + } + + rc_t create( proc_t* proc ) + { + rc_t rc = kOkRC; + inst_t* p = mem::allocZ(); + proc->userPtr = p; + + variable_t* out_var = nullptr; + const char* out_type_label = nullptr; + unsigned out_type_flag = kInvalidTFl; + unsigned sfxIdAllocN = proc_var_count(proc); + unsigned sfxIdA[ sfxIdAllocN ]; + p->inN = 0; + + // get a count of the number of input variables + if((rc = var_mult_sfx_id_array(proc, "in", sfxIdA, sfxIdAllocN, p->inN )) != kOkRC ) + { + rc = cwLogError(rc,"Unable to obtain the array of mult label-sfx-id's for the variable 'in'."); + goto errLabel; + } + + // if the adder has no inputs + if( p->inN == 0 ) + { + rc = cwLogError(rc,"The 'add' unit '%s' appears to not have any inputs.",cwStringNullGuard(proc->label)); + goto errLabel; + } + + // sort the input id's in ascending order + std::sort(sfxIdA, sfxIdA + p->inN, [](unsigned& a,unsigned& b){ return ainN; ++i) + { + variable_t* dum; + if((rc = var_register(proc, "in", sfxIdA[i], kInPId+i, kAnyChIdx, nullptr, dum )) != kOkRC ) + { + rc = cwLogError(rc,"Variable registration failed for the variable 'in:%i'.",sfxIdA[i]);; + goto errLabel; + } + } + + // Get the output type label as a string + if((rc = var_register_and_get(proc,kAnyChIdx,kOTypePId,"otype",kBaseSfxId,out_type_label)) != kOkRC ) + { + rc = cwLogError(rc,"Variable registration failed for the variable 'otype:0'.");; + goto errLabel; + } + + // Convert the output type label into a flag + if((out_type_flag = value_type_label_to_flag(out_type_label)) == kInvalidTFl ) + { + rc = cwLogError(rc,"The type label '%s' does not identify a valid type.",cwStringNullGuard(out_type_label));; + goto errLabel; + } + + // Create the output var + if((rc = var_create( proc, "out", kBaseSfxId, kOutPId, kAnyChIdx, nullptr, out_type_flag, out_var )) != kOkRC ) + { + rc = cwLogError(rc,"The output variable create failed."); + goto errLabel; + } + + /* + if((rc = var_set(proc,kOutPId,kAnyChIdx,0.0)) != kOkRC ) + { + rc = cwLogError(rc,"Initial output variable set failed."); + goto errLabel; + } + */ + + p->delta_fl=true; + _exec(proc,out_var); + errLabel: + return rc; + } + + rc_t destroy( proc_t* proc ) + { + rc_t rc = kOkRC; + + inst_t* p = (inst_t*)proc->userPtr; + + mem::release(p); + + return rc; + } + + rc_t value( proc_t* proc, variable_t* var ) + { + rc_t rc = kOkRC; + inst_t* p = (inst_t*)(proc->userPtr); + + // The check for 'isInRuntimeFl' prevents the adder from issuing an output + // on cycle 0 - otherwise the delta flag will be set by the adder + // receiving pre-runtime messages. + if( kInPId <= var->vid && var->vid < kInPId+p->inN && proc->ctx->isInRuntimeFl ) + p->delta_fl = true; + + return rc; + } + + rc_t exec( proc_t* proc ) + { + return _exec(proc); + } + + class_members_t members = { + .create = create, + .destroy = destroy, + .value = value, + .exec = exec, + .report = nullptr + }; + } + + + //------------------------------------------------------------------------------------------------------------------ + // + // preset + // + namespace preset + { + + enum { kInPId }; + + enum { kPresetLabelCharN=255 }; + + typedef struct + { + char preset_label[ kPresetLabelCharN+1]; + bool delta_fl; + } inst_t; + + + rc_t _set_preset( proc_t* proc, inst_t* p ) + { + rc_t rc = kOkRC; + unsigned presetLabelCharN = 0; + const char* preset_label = nullptr; + + // get the preset label + if((rc = var_get(proc, kInPId, kAnyChIdx, preset_label)) != kOkRC ) + { + rc = cwLogError(rc,"The variable 'in read failed."); + goto errLabel; + } + + // at this point a valid preset-label must exist + if( preset_label == nullptr || (presetLabelCharN=textLength(preset_label))==0 ) + { + rc = cwLogError(kInvalidArgRC,"Preset application failed due to blank preset label."); + goto errLabel; + } + + // if the preset-label has not changed since the last preset application - then there is nothing to do + if( textIsEqual(preset_label,p->preset_label) ) + goto errLabel; + + + // verify the preset-label is not too long + if( presetLabelCharN > kPresetLabelCharN ) + { + rc = cwLogError(kBufTooSmallRC,"The preset label '%s' is to long.",cwStringNullGuard(preset_label)); + goto errLabel; + } + + cwRuntimeCheck(proc->net != nullptr ); + + // apply the preset + if((rc = network_apply_preset(*proc->net, preset_label)) != kOkRC ) + { + rc = cwLogError(rc,"Appy preset '%s' failed.",cwStringNullGuard(preset_label)); + goto errLabel; + } + + // store the applied preset-label + textCopy(p->preset_label,kPresetLabelCharN,preset_label,presetLabelCharN); + + errLabel: + return rc; + + } + + rc_t _create( proc_t* proc, inst_t* p ) + { + rc_t rc = kOkRC; + + // Custom create code goes here + const char* label = nullptr; + + p->preset_label[0] = 0; + p->delta_fl = true; + + if((rc = var_register_and_get(proc,kAnyChIdx,kInPId,"in",kBaseSfxId,label)) != kOkRC ) + goto errLabel; + + // we can't apply a preset here because the network is not yet constructed + errLabel: + return rc; + } + + rc_t _destroy( proc_t* proc, inst_t* p ) + { return kOkRC; } + + rc_t _value( proc_t* proc, inst_t* p, variable_t* var ) + { + rc_t rc = kOkRC; + if( var->vid == kInPId ) + p->delta_fl = true; + + return rc; + } + + rc_t _exec( proc_t* proc, inst_t* p ) + { + rc_t rc = kOkRC; + + if( p->delta_fl ) + rc = _set_preset(proc,p); + + return rc; + } + + rc_t _report( proc_t* proc, inst_t* p ) + { return kOkRC; } + + + class_members_t members = { + .create = std_create, + .destroy = std_destroy, + .value = std_value, + .exec = std_exec, + .report = std_report + }; + + } + + //------------------------------------------------------------------------------------------------------------------ + // + // Print + // + namespace print + { + enum { + kTextPId, + kBaseInPId + }; + + typedef struct + { + unsigned eolPId; + unsigned inVarN; + const char** labelA; + unsigned labelN; + } inst_t; + + rc_t _parse_label_array( proc_t* proc, inst_t* p ) + { + rc_t rc = kOkRC; + const object_t* textListCfg = nullptr; + unsigned textListN = 0; + + // get the text list + if((rc = var_get(proc,kTextPId,kAnyChIdx,textListCfg)) != kOkRC ) + { + goto errLabel; + } + + if(( textListN = textListCfg->child_count()) != p->labelN ) + { + cwLogWarning("The count of labels does in print proc '%s' does not match the count of inputs plus one. %i != %i",proc->label,textListN,textListCfg->child_count()); + } + + // for each string in the list + for(unsigned i=0; ilabelN; ++i) + { + const object_t* textCfg = textListCfg->child_ele(i); + + if( textCfg==nullptr || !textCfg->is_string() ) + rc = cwLogError(kSyntaxErrorRC,"The print proc '%s' text list must be a list of strings.",proc->label); + + if((rc = textCfg->value(p->labelA[i])) != kOkRC ) + rc = cwLogError(kSyntaxErrorRC,"The print proc '%s' text label at index could not be read."); + } + + // fill in any unspecified labels with blank strings + for(unsigned i=textListN; ilabelN; ++i) + p->labelA[i] = ""; + + errLabel: + return rc; + } + + + rc_t _print_field( proc_t* proc, inst_t* p, unsigned field_idx, const value_t* value ) + { + if( field_idx >= p->inVarN ) + { + assert( p->labelA[p->labelN-1] != nullptr ); + cwLogPrint("%s\n",p->labelA[p->labelN-1]); + } + else + { + assert( field_idxlabelN && p->labelA[field_idx] != nullptr ); + cwLogPrint("%s ",p->labelA[field_idx]); + value_print(value); + } + + return kOkRC; + } + + rc_t _create( proc_t* proc, inst_t* p ) + { + rc_t rc = kOkRC; + unsigned inVarN = var_mult_count(proc, "in" ); + unsigned inVarSfxIdA[ inVarN ]; + + if((rc = var_register(proc,kAnyChIdx,kTextPId,"text",kBaseSfxId)) != kOkRC ) + { + goto errLabel; + } + + + if((rc = var_mult_sfx_id_array(proc, "in", inVarSfxIdA, inVarN, p->inVarN )) != kOkRC ) + { + goto errLabel; + } + + for(unsigned i=0; iinVarN; ++i) + { + if((rc = var_register(proc,kAnyChIdx,kBaseInPId+i,"in",inVarSfxIdA[i])) != kOkRC ) + { + goto errLabel; + } + } + + // There must be one label for each input plus an end of line label + p->labelN = p->inVarN+1; + p->labelA = mem::allocZ( p->labelN ); + p->eolPId = kBaseInPId + p->inVarN; + + // Register the eol_fl with the highest variable id - so that it is called last during the later stage + // of proc initialization where the value() function is called for each variable. + // This way the EOL message will occur after all the 'in' values have been printed. + if((rc = var_register(proc,kAnyChIdx,p->eolPId,"eol_fl",kBaseSfxId)) != kOkRC ) + { + goto errLabel; + } + + for(unsigned i=0; ilabelN; ++i) + p->labelA[i] = ""; + + rc = _parse_label_array(proc,p); + + errLabel: + + return rc; + } + + rc_t _destroy( proc_t* proc, inst_t* p ) + { + mem::release(p->labelA); + p->labelN=0; + p->inVarN=0; + return kOkRC; + } + + rc_t _value( proc_t* proc, inst_t* p, variable_t* var ) + { + switch( var->vid ) + { + + case kTextPId: + _parse_label_array(proc,p); + break; + + default: + //printf("[%i %i] ",proc->ctx->cycleIndex,var->vid); + + if( var->vid == p->eolPId ) + _print_field(proc,p,p->inVarN,nullptr); + else + { + if( kBaseInPId <= var->vid && var->vid <= kBaseInPId + p->inVarN ) + { + _print_field(proc,p,var->vid - kBaseInPId,var->value); + } + } + } + + // always report success - don't let print() interrupt the network + return kOkRC; + } + + rc_t _exec( proc_t* proc, inst_t* p ) + { return kOkRC; } + + rc_t _report( proc_t* proc, inst_t* p ) + { return kOkRC; } + + class_members_t members = { + .create = std_create, + .destroy = std_destroy, + .value = std_value, + .exec = std_exec, + .report = std_report + }; + + } } // flow } // cw diff --git a/cwFlowProc.h b/cwFlowProc.h index 15fee86..9d30f49 100644 --- a/cwFlowProc.h +++ b/cwFlowProc.h @@ -2,10 +2,14 @@ namespace cw { namespace flow { + namespace subnet { extern class_members_t members; } + namespace poly { extern class_members_t members; } + namespace midi_in { extern class_members_t members; } + namespace midi_out { extern class_members_t members; } namespace audio_in { extern class_members_t members; } namespace audio_out { extern class_members_t members; } - namespace audioFileIn { extern class_members_t members; } - namespace audioFileOut { extern class_members_t members; } + namespace audio_file_in { extern class_members_t members; } + namespace audio_file_out { extern class_members_t members; } namespace audio_gain { extern class_members_t members; } namespace audio_split { extern class_members_t members; } namespace audio_merge { extern class_members_t members; } @@ -22,5 +26,16 @@ namespace cw namespace balance { extern class_members_t members; } namespace audio_meter { extern class_members_t members; } namespace audio_marker { extern class_members_t members; } + namespace xfade_ctl { extern class_members_t members; } + namespace poly_merge { extern class_members_t members; } + namespace sample_hold { extern class_members_t members; } + namespace number { extern class_members_t members; } + namespace timer { extern class_members_t members; } + namespace counter { extern class_members_t members; } + namespace list { extern class_members_t members; } + namespace add { extern class_members_t members; } + namespace preset { extern class_members_t members; } + namespace print { extern class_members_t members; } + } } diff --git a/cwFlowTest.cpp b/cwFlowTest.cpp new file mode 100644 index 0000000..30c4050 --- /dev/null +++ b/cwFlowTest.cpp @@ -0,0 +1,81 @@ +#include "cwCommon.h" +#include "cwLog.h" +#include "cwCommonImpl.h" +#include "cwTest.h" +#include "cwMem.h" +#include "cwText.h" +#include "cwNumericConvert.h" +#include "cwObject.h" + +#include "cwTime.h" +#include "cwMidiDecls.h" +#include "cwMidi.h" +#include "cwFlowDecl.h" +#include "cwFlow.h" +#include "cwFlowTest.h" + + +cw::rc_t cw::flow::test( const test::test_args_t& args ) +{ + rc_t rc = kOkRC; + const char* proc_cfg_fname = nullptr; + const char* subnet_cfg_fname = nullptr; + object_t* class_cfg = nullptr; + object_t* subnet_cfg = nullptr; + handle_t flowH; + + if( args.module_args == nullptr ) + { + rc = cwLogError(kInvalidArgRC,"The flow test cases require module args."); + goto errLabel; + } + + if((rc = args.module_args->readv("proc_cfg_fname",0,proc_cfg_fname, + "subnet_cfg_fname",0,subnet_cfg_fname)) != kOkRC ) + { + rc = cwLogError(rc,"Flow module arg's parse failed."); + goto errLabel; + } + + // parse the proc dict. file + if((rc = objectFromFile(proc_cfg_fname,class_cfg)) != kOkRC ) + { + rc = cwLogError(rc,"The flow proc dictionary could not be read from '%s'.",cwStringNullGuard(proc_cfg_fname)); + goto errLabel; + } + + // parse the subnet dict file + if((rc = objectFromFile(subnet_cfg_fname,subnet_cfg)) != kOkRC ) + { + rc = cwLogError(rc,"The flow subnet dictionary could not be read from '%s'.",cwStringNullGuard(subnet_cfg_fname)); + goto errLabel; + } + + // create the flow object + if((rc = create( flowH, class_cfg, args.test_args, subnet_cfg, args.out_dir)) != kOkRC ) + { + rc = cwLogError(rc,"Flow object create failed."); + goto errLabel; + } + + // run the network + if((rc = exec( flowH )) != kOkRC ) + rc = cwLogError(rc,"Execution failed."); + +errLabel: + // destroy the flow object + if((rc = destroy(flowH)) != kOkRC ) + { + rc = cwLogError(rc,"Close the flow object."); + goto errLabel; + } + + if( class_cfg != nullptr ) + class_cfg->free(); + + if( subnet_cfg != nullptr ) + subnet_cfg->free(); + + return rc; + +} diff --git a/cwFlowTest.h b/cwFlowTest.h new file mode 100644 index 0000000..5fb828a --- /dev/null +++ b/cwFlowTest.h @@ -0,0 +1,9 @@ + +namespace cw +{ + namespace flow + { + + rc_t test( const test::test_args_t& args ); + } +} diff --git a/cwFlowTypes.cpp b/cwFlowTypes.cpp index 02ccf1f..43e71da 100644 --- a/cwFlowTypes.cpp +++ b/cwFlowTypes.cpp @@ -1,12 +1,17 @@ #include "cwCommon.h" #include "cwLog.h" #include "cwCommonImpl.h" +#include "cwTest.h" #include "cwMem.h" +#include "cwMath.h" #include "cwText.h" #include "cwObject.h" +#include "cwFileSys.h" #include "cwVectOps.h" #include "cwMtx.h" #include "cwDspTypes.h" // real_t, sample_t +#include "cwTime.h" +#include "cwMidiDecls.h" #include "cwFlowDecl.h" #include "cwFlow.h" #include "cwFlowTypes.h" @@ -16,35 +21,57 @@ namespace cw { namespace flow { - idLabelPair_t typeLabelFlagsA[] = { + + idLabelPair_t _varDescAttrFlagsA[] = + { + { kSrcVarDescFl, "src" }, + { kSrcOptVarDescFl, "src_opt" }, + { kNoSrcVarDescFl, "no_src" }, + { kInitVarDescFl, "init" }, + { kMultVarDescFl, "mult" }, + { kSubnetOutVarDescFl, "out" }, + { kInvalidVarDescFl, "" } + }; + + + idLabelPair_t _typeLabelFlagsA[] = { - { kBoolTFl, "bool" }, - { kUIntTFl, "uint" }, - { kIntTFl, "int", }, - { kFloatTFl, "float"}, - { kRealTFl, "real"}, - { kDoubleTFl, "double"}, + { kBoolTFl, "bool" }, + { kUIntTFl, "uint" }, + { kIntTFl, "int", }, + { kFloatTFl, "float"}, + { kDoubleTFl,"double"}, - { kBoolMtxTFl, "bool_mtx" }, - { kUIntMtxTFl, "uint_mtx" }, - { kIntMtxTFl, "int_mtx" }, - { kFloatMtxTFl, "float_mtx" }, - { kDoubleMtxTFl, "double_mtx" }, + { kBoolMtxTFl, "bool_mtx" }, + { kUIntMtxTFl, "uint_mtx" }, + { kIntMtxTFl, "int_mtx" }, + { kFloatMtxTFl, "float_mtx" }, + { kDoubleMtxTFl,"double_mtx" }, { kABufTFl, "audio" }, { kFBufTFl, "spectrum" }, + { kMBufTFl, "midi" }, { kStringTFl, "string" }, { kTimeTFl, "time" }, - { kInvalidTFl, nullptr } + { kCfgTFl, "cfg" }, + + // alias types to map to cwDspTypes.h + { kFloatTFl, "srate"}, + { kFloatTFl, "sample"}, + { kFloatTFl, "coeff"}, + { kDoubleTFl, "ftime" }, + + { kNumericTFl, "numeric" }, + { kAllTFl, "all" }, + + { kRuntimeTFl, "runtime" }, + + { kInvalidTFl, "" } }; const char* _typeFlagToLabel( unsigned flag ) { - for(unsigned i=0; typeLabelFlagsA[i].id != kInvalidTFl; ++i) - if( typeLabelFlagsA[i].id == flag ) - return typeLabelFlagsA[i].label; - - return ""; + return idToLabel(_typeLabelFlagsA,flag,kInvalidTFl); } @@ -53,7 +80,7 @@ namespace cw if( v == nullptr ) return; - switch( v->flags & kTypeMask ) + switch( v->tflag & kTypeMask ) { case kInvalidTFl: break; @@ -73,6 +100,9 @@ namespace cw fbuf_destroy( v->u.fbuf ); break; + case kMBufTFl: + mbuf_destroy( v->u.mbuf ); + break; case kBoolMtxTFl: case kUIntMtxTFl: @@ -85,23 +115,25 @@ namespace cw case kStringTFl: mem::release( v->u.s ); break; - + case kTimeTFl: assert(0); break; + case kCfgTFl: + break; + default: assert(0); break; } - v->flags = kInvalidTFl; + v->tflag = kInvalidTFl; } void _value_duplicate( value_t& dst, const value_t& src ) - { - - switch( src.flags & kTypeMask ) + { + switch( src.tflag & kTypeMask ) { case kInvalidTFl: break; @@ -117,14 +149,18 @@ namespace cw case kABufTFl: dst.u.abuf = src.u.abuf == nullptr ? nullptr : abuf_duplicate(src.u.abuf); - dst.flags = src.flags; + dst.tflag = src.tflag; break; case kFBufTFl: dst.u.fbuf = src.u.fbuf == nullptr ? nullptr : fbuf_duplicate(src.u.fbuf); - dst.flags = src.flags; + dst.tflag = src.tflag; break; + case kMBufTFl: + dst.u.mbuf = src.u.mbuf == nullptr ? nullptr : mbuf_duplicate(src.u.mbuf); + dst.tflag = src.tflag; + break; case kBoolMtxTFl: case kUIntMtxTFl: @@ -136,13 +172,17 @@ namespace cw case kStringTFl: dst.u.s = mem::duplStr( dst.u.s ); - dst.flags = src.flags; + dst.tflag = src.tflag; break; case kTimeTFl: assert(0); break; + case kCfgTFl: + dst = src; + break; + default: assert(0); break; @@ -150,36 +190,94 @@ namespace cw } - void _value_print( const value_t* v ) + void _value_print( const value_t* v, bool info_fl=true ) { if( v == nullptr ) return; - switch( v->flags & kTypeMask ) + switch( v->tflag & kTypeMask ) { case kInvalidTFl: break; - case kBoolTFl: printf("%s ", v->u.b ? "True" : "False" ); break; - case kUIntTFl: printf("%i ", v->u.u ); break; - case kIntTFl: printf("%i ", v->u.i ); break; - case kFloatTFl: printf("%f ", v->u.f ); break; - case kDoubleTFl:printf("%f ", v->u.d ); break; + case kBoolTFl: + + cwLogPrint("%s%s ", info_fl ? "b:" : "", v->u.b ? "true" : "false" ); + break; + + case kUIntTFl: + cwLogPrint("%s%i ", info_fl ? "u:" : "", v->u.u ); + break; + + case kIntTFl: + cwLogPrint("%s%i ", info_fl ? "i:" : "", v->u.i ); + break; + + case kFloatTFl: + cwLogPrint("%s%f ", info_fl ? "f:" : "", v->u.f ); + break; + + case kDoubleTFl: + cwLogPrint("%s%f ", info_fl ? "d:" : "", v->u.d ); + break; + case kABufTFl: - if( v->u.abuf == nullptr ) - printf("abuf: "); + if( info_fl ) + { + if( v->u.abuf == nullptr ) + cwLogPrint("abuf: "); + else + cwLogPrint("abuf: chN:%i frameN:%i srate:%8.1f ", v->u.abuf->chN, v->u.abuf->frameN, v->u.abuf->srate ); + } else - printf("abuf: chN:%i frameN:%i srate:%8.1f ", v->u.abuf->chN, v->u.abuf->frameN, v->u.abuf->srate ); + { + bool null_fl = v->u.abuf==nullptr || v->u.abuf->buf == nullptr; + cwLogPrint("("); + for(unsigned i=0; iu.abuf->chN; ++i) + cwLogPrint("%f ",null_fl ? 0.0 : vop::rms(v->u.abuf->buf + i*v->u.abuf->frameN, v->u.abuf->frameN)); + cwLogPrint(") "); + } break; case kFBufTFl: - if( v->u.fbuf == nullptr ) - printf("fbuf: "); + if( info_fl ) + { + if( v->u.fbuf == nullptr ) + cwLogPrint("fbuf: "); + else + { + cwLogPrint("fbuf: chN:%i srate:%8.1f ", v->u.fbuf->chN, v->u.fbuf->srate ); + for(unsigned i=0; iu.fbuf->chN; ++i) + cwLogPrint("(binN:%i hopSmpN:%i) ", v->u.fbuf->binN_V[i], v->u.fbuf->hopSmpN_V[i] ); + } + } else { - printf("fbuf: chN:%i srate:%8.1f ", v->u.fbuf->chN, v->u.fbuf->srate ); + + bool null_fl = v->u.fbuf==nullptr || v->u.fbuf->magV == nullptr; + cwLogPrint("("); for(unsigned i=0; iu.fbuf->chN; ++i) - printf("(binN:%i hopSmpN:%i) ", v->u.fbuf->binN_V[i], v->u.fbuf->hopSmpN_V[i] ); + cwLogPrint("%f ",null_fl ? 0.0 : vop::mean(v->u.fbuf->magV[i], v->u.fbuf->binN_V[i])); + cwLogPrint(") "); + + } + break; + + case kMBufTFl: + if( info_fl ) + { + if( v->u.mbuf == nullptr ) + cwLogPrint("mbuf: "); + else + { + cwLogPrint("mbuf: cnt: %i", v->u.mbuf->msgN ); + } + } + else + { + //bool null_fl = v->u.mbuf==nullptr || v->u.mbuf->msgA == nullptr; + for(unsigned i=0; iu.mbuf->msgN; ++i) + cwLogPrint("(0x%x 0x%x 0x%x) ",v->u.mbuf->msgA[i].status + v->u.mbuf->msgA[i].ch,v->u.mbuf->msgA[i].d0,v->u.mbuf->msgA[i].d1); } break; @@ -192,41 +290,69 @@ namespace cw break; case kStringTFl: - printf("%s ", v->u.s); + cwLogPrint("%s ", v->u.s); break; case kTimeTFl: assert(0); break; + case kCfgTFl: + if( v->u.cfg != nullptr ) + v->u.cfg->print(); + break; + default: assert(0); break; } } - + rc_t _val_get( const value_t* val, bool& valRef ) { rc_t rc = kOkRC; - switch( val->flags & kTypeMask ) + switch( val->tflag & kTypeMask ) { case kBoolTFl: valRef = val->u.b; break; case kUIntTFl: valRef = val->u.u!=0; break; case kIntTFl: valRef = val->u.i!=0; break; case kFloatTFl: valRef = val->u.f!=0; break; - case kDoubleTFl: valRef = val->u.d!=0; break; + case kDoubleTFl: valRef = val->u.d!=0; break; default: - rc = cwLogError(kTypeMismatchRC,"The type 0x%x could not be converted to a bool.",val->flags); + rc = cwLogError(kTypeMismatchRC,"The type %s (0x%x) could not be converted to a bool.",_typeFlagToLabel(val->tflag),val->tflag); } return rc; } + + rc_t _val_set( value_t* val, bool v ) + { + rc_t rc = kOkRC; + + switch( val->tflag & kTypeMask ) + { + case kBoolTFl: val->u.b=v; break; + case kUIntTFl: val->u.u=v; break; + case kIntTFl: val->u.i=v; break; + case kFloatTFl: val->u.f=v; break; + case kDoubleTFl: val->u.d=v; break; + case kInvalidTFl: + val->u.b = v; + val->tflag = kBoolTFl; + break; + + default: + rc = cwLogError(kTypeMismatchRC,"A bool could not be converted to a %s (0x%x).",_typeFlagToLabel(val->tflag),val->tflag); + } + + return rc; + } rc_t _val_get( const value_t* val, uint_t& valRef ) { rc_t rc = kOkRC; - switch( val->flags & kTypeMask ) + switch( val->tflag & kTypeMask ) { case kBoolTFl: valRef = val->u.b ? 1 : 0; break; case kUIntTFl: valRef = val->u.u; break; @@ -234,15 +360,38 @@ namespace cw case kFloatTFl: valRef = (uint_t)val->u.f; break; case kDoubleTFl: valRef = (uint_t)val->u.d; break; default: - rc = cwLogError(kTypeMismatchRC,"The type 0x%x could not be converted to a uint_t.",val->flags); + rc = cwLogError(kTypeMismatchRC,"The type %s (0x%x) could not be converted to a uint.",_typeFlagToLabel(val->tflag),val->tflag); } return rc; } + + rc_t _val_set( value_t* val, uint_t v ) + { + rc_t rc = kOkRC; + switch( val->tflag & kTypeMask ) + { + case kBoolTFl: val->u.b=v!=0; break; + case kUIntTFl: val->u.u=v; break; + case kIntTFl: val->u.i=v; break; + case kFloatTFl: val->u.f=v; break; + case kDoubleTFl: val->u.d=v; break; + case kInvalidTFl: + val->u.u = v; + val->tflag = kUIntTFl; + break; + + default: + rc = cwLogError(kTypeMismatchRC,"A uint could not be converted to a %s (0x%x).",_typeFlagToLabel(val->tflag),val->tflag); + } + + return rc; + } + rc_t _val_get( const value_t* val, int_t& valRef ) { rc_t rc = kOkRC; - switch( val->flags & kTypeMask ) + switch( val->tflag & kTypeMask ) { case kBoolTFl: valRef = val->u.b ? 1 : 0; break; case kUIntTFl: valRef = (int_t)val->u.u; break; @@ -250,15 +399,40 @@ namespace cw case kFloatTFl: valRef = (int_t)val->u.f; break; case kDoubleTFl: valRef = (int_t)val->u.d; break; default: - rc = cwLogError(kTypeMismatchRC,"The type 0x%x could not be converted to a int_t.",val->flags); + rc = cwLogError(kTypeMismatchRC,"The type %s (0x%x) could not be converted to an int.",_typeFlagToLabel(val->tflag),val->tflag); + } return rc; } + rc_t _val_set( value_t* val, int_t v ) + { + rc_t rc = kOkRC; + + switch( val->tflag & kTypeMask ) + { + case kBoolTFl: val->u.b=v!=0; break; + case kUIntTFl: val->u.u=v; break; + case kIntTFl: val->u.i=v; break; + case kFloatTFl: val->u.f=v; break; + case kDoubleTFl: val->u.d=v; break; + case kInvalidTFl: + val->u.i = v; + val->tflag = kIntTFl; + break; + + default: + rc = cwLogError(kTypeMismatchRC,"An int could not be converted to a %s (0x%x).",_typeFlagToLabel(val->tflag),val->tflag); + } + + return rc; + } + + rc_t _val_get( const value_t* val, float& valRef ) { rc_t rc = kOkRC; - switch( val->flags & kTypeMask ) + switch( val->tflag & kTypeMask ) { case kBoolTFl: valRef = val->u.b ? 1 : 0; break; case kUIntTFl: valRef = (float)val->u.u; break; @@ -266,15 +440,38 @@ namespace cw case kFloatTFl: valRef = (float)val->u.f; break; case kDoubleTFl: valRef = (float)val->u.d; break; default: - rc = cwLogError(kTypeMismatchRC,"The type 0x%x could not be converted to a float.", val->flags); + rc = cwLogError(kTypeMismatchRC,"The type %s (0x%x) could not be converted to a float.",_typeFlagToLabel(val->tflag),val->tflag); } return rc; } + rc_t _val_set( value_t* val, float v ) + { + rc_t rc = kOkRC; + + switch( val->tflag & kTypeMask ) + { + case kBoolTFl: val->u.b=v!=0; break; + case kUIntTFl: val->u.u=(unsigned)v; break; + case kIntTFl: val->u.i=(int)v; break; + case kFloatTFl: val->u.f=v; break; + case kDoubleTFl: val->u.d=v; break; + case kInvalidTFl: + val->u.f = v; + val->tflag = kFloatTFl; + break; + + default: + rc = cwLogError(kTypeMismatchRC,"A float could not be converted to a %s (0x%x).",_typeFlagToLabel(val->tflag),val->tflag); + } + + return rc; + } + rc_t _val_get( const value_t* val, double& valRef ) { rc_t rc = kOkRC; - switch( val->flags & kTypeMask ) + switch( val->tflag & kTypeMask ) { case kBoolTFl: valRef = val->u.b ? 1 : 0; break; case kUIntTFl: valRef = (double)val->u.u; break; @@ -282,33 +479,76 @@ namespace cw case kFloatTFl: valRef = (double)val->u.f; break; case kDoubleTFl: valRef = val->u.d; break; default: - rc = cwLogError(kTypeMismatchRC,"The type 0x%x could not be converted to a double.",val->flags); + rc = cwLogError(kTypeMismatchRC,"The type %s (0x%x) could not be converted to a double.",_typeFlagToLabel(val->tflag),val->tflag); } return rc; } + rc_t _val_set( value_t* val, double v ) + { + rc_t rc = kOkRC; + + switch( val->tflag & kTypeMask ) + { + case kBoolTFl: val->u.b=v!=0; break; + case kUIntTFl: val->u.u=(unsigned)v; break; + case kIntTFl: val->u.i=(int)v; break; + case kFloatTFl: val->u.f=(float)v; break; + case kDoubleTFl: val->u.d=v; break; + case kInvalidTFl: + val->u.d = v; + val->tflag = kDoubleTFl; + break; + + default: + rc = cwLogError(kTypeMismatchRC,"A double could not be converted to a %s (0x%x).",_typeFlagToLabel(val->tflag),val->tflag); + } + + return rc; + } + rc_t _val_get( const value_t* val, const char*& valRef ) { rc_t rc = kOkRC; - if( cwIsFlag(val->flags & kTypeMask, kStringTFl) ) + if( cwIsFlag(val->tflag & kTypeMask, kStringTFl) ) valRef = val->u.s; else { - rc = cwLogError(kTypeMismatchRC,"The type 0x%x could not be converted to a string.",val->flags); + rc = cwLogError(kTypeMismatchRC,"The type %s (0x%x) could not be converted to a string.",_typeFlagToLabel(val->tflag),val->tflag); valRef = nullptr; } return rc; } + rc_t _val_set( value_t* val, const char* v ) + { + rc_t rc = kOkRC; + + switch( val->tflag & kTypeMask ) + { + case kStringTFl: + val->u.s=mem::duplStr(v); break; + + case kInvalidTFl: + val->u.s = mem::duplStr(v); + val->tflag = kStringTFl; + break; + default: + rc = cwLogError(kTypeMismatchRC,"A string could not be converted to a %s (0x%x).",_typeFlagToLabel(val->tflag),val->tflag); + } + + return rc; + } + rc_t _val_get( value_t* val, abuf_t*& valRef ) { rc_t rc = kOkRC; - if( cwIsFlag(val->flags & kTypeMask, kABufTFl) ) + if( cwIsFlag(val->tflag & kTypeMask, kABufTFl) ) valRef = val->u.abuf; else { - rc = cwLogError(kTypeMismatchRC,"The type 0x%x could not be converted to an abuf_t.",val->flags); + rc = cwLogError(kTypeMismatchRC,"The type %s (0x%x) could not be converted to an abuf.",_typeFlagToLabel(val->tflag),val->tflag); valRef = nullptr; } return rc; @@ -323,15 +563,37 @@ namespace cw return rc; } + rc_t _val_set( value_t* val, abuf_t* v ) + { + rc_t rc = kOkRC; + + switch( val->tflag & kTypeMask ) + { + case kABufTFl: + val->u.abuf=v; + break; + + case kInvalidTFl: + val->u.abuf=v; + val->tflag = kABufTFl; + break; + + default: + rc = cwLogError(kTypeMismatchRC,"A audio signal could not be converted to a %s (0x%x).",_typeFlagToLabel(val->tflag),val->tflag); + } + + return rc; + } + rc_t _val_get( value_t* val, fbuf_t*& valRef ) { rc_t rc = kOkRC; - if( cwIsFlag(val->flags & kTypeMask, kFBufTFl) ) + if( cwIsFlag(val->tflag & kTypeMask, kFBufTFl) ) valRef = val->u.fbuf; else { valRef = nullptr; - rc = cwLogError(kTypeMismatchRC,"The type 0x%x could not be converted to an fbuf_t.",val->flags); + rc = cwLogError(kTypeMismatchRC,"The type %s (0x%x) could not be converted to an fbuf.",_typeFlagToLabel(val->tflag),val->tflag); } return rc; } @@ -345,6 +607,109 @@ namespace cw return rc; } + rc_t _val_set( value_t* val, fbuf_t* v ) + { + rc_t rc = kOkRC; + + switch( val->tflag & kTypeMask ) + { + case kFBufTFl: + val->u.fbuf=v; + break; + + case kInvalidTFl: + val->u.fbuf=v; + val->tflag = kFBufTFl; + break; + + default: + rc = cwLogError(kTypeMismatchRC,"A spectrum signal could not be converted to a %s (0x%x).",_typeFlagToLabel(val->tflag),val->tflag); + } + + return rc; + } + + rc_t _val_get( value_t* val, mbuf_t*& valRef ) + { + rc_t rc = kOkRC; + if( cwIsFlag(val->tflag & kTypeMask, kMBufTFl) ) + valRef = val->u.mbuf; + else + { + valRef = nullptr; + rc = cwLogError(kTypeMismatchRC,"The type %s (0x%x) could not be converted to an mbuf.",_typeFlagToLabel(val->tflag),val->tflag); + } + return rc; + } + + rc_t _val_set( value_t* val, mbuf_t* v ) + { + rc_t rc = kOkRC; + + switch( val->tflag & kTypeMask ) + { + case kMBufTFl: + val->u.mbuf=v; + break; + + case kInvalidTFl: + val->u.mbuf=v; + val->tflag = kMBufTFl; + break; + + default: + rc = cwLogError(kTypeMismatchRC,"A MIDI signal could not be converted to a %s (0x%x).",_typeFlagToLabel(val->tflag),val->tflag); + } + + return rc; + } + + + rc_t _val_get( value_t* val, const mbuf_t*& valRef ) + { + mbuf_t* non_const_val; + rc_t rc = kOkRC; + if((rc = _val_get(val,non_const_val)) == kOkRC ) + valRef = non_const_val; + return rc; + } + + rc_t _val_get( value_t* val, const object_t*& valRef ) + { + rc_t rc = kOkRC; + + if( cwIsFlag(val->tflag & kTypeMask, kCfgTFl) ) + valRef = val->u.cfg; + else + { + valRef = nullptr; + rc = cwLogError(kTypeMismatchRC,"The type %s (0x%x) could not be converted to a cfg.",_typeFlagToLabel(val->tflag),val->tflag); + + } + return rc; + } + + rc_t _val_set( value_t* val, const object_t* v ) + { + rc_t rc = kOkRC; + + switch( val->tflag & kTypeMask ) + { + case kCfgTFl: + val->u.cfg=v; + break; + + case kInvalidTFl: + val->u.cfg=v; + val->tflag = kCfgTFl; + break; + + default: + rc = cwLogError(kTypeMismatchRC,"A cfg. could not be converted to a %s (0x%x).",_typeFlagToLabel(val->tflag),val->tflag); + } + + return rc; + } template< typename T > rc_t _val_get_driver( const variable_t* var, T& valRef ) @@ -353,35 +718,19 @@ namespace cw return cwLogError(kInvalidArgRC,"Cannnot get the value of a non-existent variable."); if( var->value == nullptr ) - return cwLogError(kInvalidStateRC,"No value has been assigned to the variable: %s.%s ch:%i.",cwStringNullGuard(var->inst->label),cwStringNullGuard(var->label),var->chIdx); + return cwLogError(kInvalidStateRC,"No value has been assigned to the variable: %s:%i.%s:%i ch:%i.",cwStringNullGuard(var->proc->label),var->proc->label_sfx_id,cwStringNullGuard(var->label),var->label_sfx_id,var->chIdx); return _val_get(var->value,valRef); - } - - rc_t _var_find_to_set( instance_t* inst, unsigned vid, unsigned chIdx, unsigned typeFl, variable_t*& varRef ) - { - rc_t rc = kOkRC; - varRef = nullptr; - - // - if((rc = var_find(inst,vid,chIdx,varRef)) == kOkRC ) - { - // validate the type of the variable against the description - if( !cwIsFlag(varRef->varDesc->type,typeFl ) ) - rc = cwLogError(kTypeMismatchRC,"Type mismatch. Instance:%s variable:%s with type 0x%x does not match requested type:0x%x.",varRef->inst->label,varRef->label,varRef->varDesc->type,typeFl); - } - - return rc; } - + // Variable lookup: Exact match on vid and chIdx - rc_t _var_find_on_vid_and_ch( instance_t* inst, unsigned vid, unsigned chIdx, variable_t*& varRef ) + rc_t _var_find_on_vid_and_ch( proc_t* proc, unsigned vid, unsigned chIdx, variable_t*& varRef ) { varRef = nullptr; - for(variable_t* var = inst->varL; var!=nullptr; var=var->var_link) + for(variable_t* var = proc->varL; var!=nullptr; var=var->var_link) { // the variable vid and chIdx should form a unique pair if( var->vid==vid && var->chIdx == chIdx ) @@ -390,155 +739,171 @@ namespace cw return kOkRC; } } - return cwLogError(kInvalidIdRC,"The variable matching id:%i ch:%i on instance '%s' could not be found.", vid, chIdx, inst->label); + return cwLogError(kInvalidIdRC,"The variable matching id:%i ch:%i on proc '%s:%i' could not be found.", vid, chIdx, proc->label, proc->label_sfx_id); } // Variable lookup: Exact match on label and chIdx - variable_t* _var_find_on_label_and_ch( instance_t* inst, const char* var_label, unsigned chIdx ) + variable_t* _var_find_on_label_and_ch( proc_t* proc, const char* var_label, unsigned sfx_id, unsigned chIdx ) { - for(variable_t* var = inst->varL; var!=nullptr; var=var->var_link) + for(variable_t* var = proc->varL; var!=nullptr; var=var->var_link) { // the variable vid and chIdx should form a unique pair - if( textCompare(var->label,var_label)==0 && var->chIdx == chIdx ) + if( var->label_sfx_id==sfx_id && textCompare(var->label,var_label)==0 && var->chIdx == chIdx ) return var; } return nullptr; } + rc_t _var_find_on_label_and_ch( proc_t* proc, const char* var_label, unsigned sfx_id, unsigned chIdx, variable_t*& var_ref ) + { + rc_t rc = kOkRC; + if((var_ref = _var_find_on_label_and_ch(proc,var_label,sfx_id,chIdx)) == nullptr ) + rc = cwLogError(kEleNotFoundRC,"The variable '%s:%i' cannot be found on the proc:%s.",cwStringNullGuard(var_label),sfx_id,cwStringNullGuard(proc->label)); + return rc; + } rc_t _validate_var_assignment( variable_t* var, unsigned typeFl ) { - if( cwIsFlag(var->varDesc->flags, kSrcVarFl ) ) - return cwLogError(kInvalidStateRC, "The variable '%s' on instance '%s' cannot be set because it is a 'src' variable.", var->label, var->inst->label); - + if( cwIsFlag(var->varDesc->flags, kSrcVarDescFl ) ) + return cwLogError(kInvalidStateRC, "The variable '%s:%i' on proc '%s:%i' cannot be set because it is a 'src' variable.", var->label, var->label_sfx_id, var->proc->label,var->proc->label_sfx_id); + /* if( !cwIsFlag(var->varDesc->type, typeFl ) ) - return cwLogError(kTypeMismatchRC, "The variable '%s' on instance '%s' is not a '%s'.", var->label, var->inst->label, _typeFlagToLabel( typeFl )); - + return cwLogError(kTypeMismatchRC, "The variable '%s:%i' on proc '%s:%i' is not a '%s'.", var->label, var->label_sfx_id, var->proc->label, var->proc->label_sfx_id, _typeFlagToLabel( typeFl )); + */ + return kOkRC; } + void _var_print_addr( const char* title, const variable_t* v ) + { cwLogPrint("%s:%s:%i.%s:%i ",title,v->proc->label,v->proc->label_sfx_id,v->label,v->label_sfx_id); } + + void _var_print( const variable_t* var ) + { + const char* conn_label = is_connected_to_source(var) ? "extern" : " "; + + cwLogPrint(" %20s:%5i id:%4i ch:%3i : %s : ", var->label, var->label_sfx_id, var->vid, var->chIdx, conn_label ); + + if( var->value == nullptr ) + _value_print( &var->local_value[0] ); + else + _value_print( var->value ); + + if( var->src_var != nullptr ) + cwLogPrint(" src:%s:%i.%s:%i",var->src_var->proc->label,var->src_var->proc->label_sfx_id,var->src_var->label,var->src_var->label_sfx_id); + + if( var->dst_head != nullptr ) + { + for(variable_t* v = var->dst_head; v!=nullptr; v=v->dst_link) + cwLogPrint(" dst:%s:%i.%s:%i",v->proc->label,v->proc->label_sfx_id,v->label,v->label_sfx_id); + } + + cwLogPrint("\n"); + } + + rc_t _var_broadcast_new_value( variable_t* var ) { rc_t rc = kOkRC; - /* + // notify each connected var that the value has changed - for(variable_t* con_var = var->connect_link; con_var!=nullptr; con_var=con_var->connect_link) - if((rc = con_var->inst->class_desc->members->value( con_var->inst, con_var )) != kOkRC ) + for(variable_t* con_var = var->dst_head; con_var!=nullptr; con_var=con_var->dst_link) + { + // the var->local_value[] slot used by the source variable may have changed - update the destination variable + // so that it points to the correct value. + con_var->value = var->value; + + cwLogMod("%s:%i %s:%i -> %s:%i %s:%i", + var->proc->label,var->proc->label_sfx_id, + var->label,var->label_sfx_id, + con_var->proc->label,con_var->proc->label_sfx_id, + con_var->label,con_var->label_sfx_id ); + + // Call the value() function on the connected variable + if((rc = var_call_custom_value_func(con_var)) != kOkRC ) break; - */ + + } return rc; } - template< typename T > - void _var_setter( variable_t* var, unsigned local_value_idx, T val ) - { - cwLogError(kAssertFailRC,"Unimplemented variable setter."); - assert(0); - } - template<> - void _var_setter( variable_t* var, unsigned local_value_idx, bool val ) - { - var->local_value[ local_value_idx ].u.b = val; - var->local_value[ local_value_idx ].flags = kBoolTFl; - cwLogMod("%s.%s ch:%i %i (bool).",var->inst->label,var->label,var->chIdx,val); - } - - template<> - void _var_setter( variable_t* var, unsigned local_value_idx, unsigned val ) - { - var->local_value[ local_value_idx ].u.u = val; - var->local_value[ local_value_idx ].flags = kUIntTFl; - cwLogMod("%s.%s ch:%i %i (uint).",var->inst->label,var->label,var->chIdx,val); - } - - template<> - void _var_setter( variable_t* var, unsigned local_value_idx, int val ) - { - var->local_value[ local_value_idx ].u.i = val; - var->local_value[ local_value_idx ].flags = kIntTFl; - cwLogMod("%s.%s ch:%i %i (int).",var->inst->label,var->label,var->chIdx,val); - } - - template<> - void _var_setter( variable_t* var, unsigned local_value_idx, float val ) - { - var->local_value[ local_value_idx ].u.f = val; - var->local_value[ local_value_idx ].flags = kFloatTFl; - cwLogMod("%s.%s ch:%i %f (float).",var->inst->label,var->label,var->chIdx,val); - } - - template<> - void _var_setter( variable_t* var, unsigned local_value_idx, double val ) - { - var->local_value[ local_value_idx ].u.d = val; - var->local_value[ local_value_idx ].flags = kDoubleTFl; - cwLogMod("%s.%s ch:%i %f (double).",var->inst->label,var->label,var->chIdx,val); - } - - template<> - void _var_setter( variable_t* var, unsigned local_value_idx, const char* val ) - { - var->local_value[ local_value_idx ].u.s = mem::duplStr(val); - var->local_value[ local_value_idx ].flags = kStringTFl; - cwLogMod("%s.%s ch:%i %s (string).",var->inst->label,var->label,var->chIdx,val); - } - - template<> - void _var_setter( variable_t* var, unsigned local_value_idx, abuf_t* val ) - { - var->local_value[ local_value_idx ].u.abuf = val; - var->local_value[ local_value_idx ].flags = kABufTFl; - cwLogMod("%s.%s ch:%i %s (abuf).",var->inst->label,var->label,var->chIdx,abuf==nullptr ? "null" : "valid"); - } - template<> - void _var_setter( variable_t* var, unsigned local_value_idx, fbuf_t* val ) + // 'argTypeFlag' is the type (tflag) of 'val'. + template< typename T > + rc_t _var_set_template( variable_t* var, unsigned argTypeFlag, T val ) { - var->local_value[ local_value_idx ].u.fbuf = val; - var->local_value[ local_value_idx ].flags = kFBufTFl; - cwLogMod("%s.%s ch:%i %s (fbuf).",var->inst->label,var->label,var->chIdx,fbuf==nullptr ? "null" : "valid"); - } - - template< typename T > - rc_t _var_set_template( variable_t* var, unsigned typeFlag, T val ) - { - rc_t rc; + rc_t rc = kOkRC; + // it is not legal to set the value of a variable that is connected to a 'source' variable. + if( var->src_var != nullptr ) + return cwLogError(kInvalidStateRC, "The variable '%s:%i %s:%i' cannot be set because it is connected to a source variable.", var->proc->label,var->proc->label_sfx_id, var->label, var->label_sfx_id); + + + // var->type is the allowable type for this var's value. + // It may be set to kInvalidTFl if the type has not yet been determined. + unsigned value_type_flag = var->type; + + // Pick the slot in local_value[] that we will use to try out this new value. unsigned next_local_value_idx = (var->local_value_idx + 1) % kLocalValueN; // store the pointer to the current value of this variable value_t* original_value = var->value; unsigned original_value_idx = var->local_value_idx; - - // verify that this is a legal assignment - if((rc = _validate_var_assignment( var, typeFlag )) != kOkRC ) - { - goto errLabel; - } + // release the previous value in the next slot _value_release(&var->local_value[next_local_value_idx]); - // set the new local value - _var_setter(var,next_local_value_idx,val); + // if the value type of this variable has not been established + if( value_type_flag == kInvalidTFl ) + { + // if the var desc is a single type then use that .... + if( math::isPowerOfTwo(var->varDesc->type) ) + { + value_type_flag = var->varDesc->type; + } + else // ... Otherwise select a type from the one of the possible flags given by the var desc + { + value_type_flag = var->varDesc->type & argTypeFlag; + } + + // if the incoming type is not in the set of allowable types then it is an error + if( value_type_flag == 0 ) + { + rc = cwLogError(kTypeMismatchRC,"The type 0x%x is not valid for the variable: %s:%i %s:%i type:0x%x.", + argTypeFlag,var->proc->label,var->proc->label_sfx_id,var->label,var->label_sfx_id,var->varDesc->type); + goto errLabel; + } + } + + // set the type of the LHS to force the incoming value to be coerced to this type + var->local_value[ next_local_value_idx ].tflag = value_type_flag; + + // set the new local value in var->local_value[next_local_value_idx] + if((rc = _val_set(var->local_value + next_local_value_idx, val )) != kOkRC ) + { + rc = cwLogError(rc,"Value set failed on '%s:%i %s:%i",var->proc->label,var->proc->label_sfx_id,var->label,var->label_sfx_id); + goto errLabel; + } // make the new local value current var->value = var->local_value + next_local_value_idx; var->local_value_idx = next_local_value_idx; - // If the instance is fully initialized ... - if( var->inst->varMapA != nullptr ) + // If the proc is fully initialized ... + if( var->proc->varMapA != nullptr ) { // ... then inform the proc. that the value changed // Note 1: We don't want to this call to occur if we are inside or prior to 'proc.create()' - // call because calls' to 'proc.value()' will see the instance in a incomplete state) + // call because calls' to 'proc.value()' will see the proc in a incomplete state) // Note 2: If this call returns an error then the value assignment is cancelled // and the value does not change. - rc = var->inst->class_desc->members->value( var->inst, var ); + rc = var_call_custom_value_func( var ); } + //printf("%p set: %s:%s 0x%x\n",var->value, var->proc->label,var->label,var->value->tflag); + if( rc == kOkRC ) { // send the value to connected downstream proc's @@ -556,51 +921,44 @@ namespace cw } - bool is_connected_to_external_proc( const variable_t* var ) - { - // if this var does not have a 'src_ptr' then it can't be connected to an external proc - if( var->src_var == nullptr || var->value == nullptr ) - return false; - - // if this var is using a local value then it can't be connected to an external proc - for(unsigned i=0; ivalue == var->local_value + i ) - return false; - - return true; - } + + // 'valueTypeFlag' is the type (tflag) of 'value' template< typename T > - rc_t _var_set_driver( variable_t* var, unsigned typeFlag, T value ) + rc_t _var_set_driver( variable_t* var, unsigned valueTypeFlag, T value ) { rc_t rc; // if this variable is fed from the output of an external proc - then it's local value cannot be set - if(is_connected_to_external_proc(var) ) - return kOkRC; - + if(is_connected_to_source(var) ) + { + return cwLogError(kInvalidStateRC,"Cannot set the value on the connected variable %s:%i-%s:%i.",var->proc->label,var->proc->label_sfx_id,var->label,var->label_sfx_id); + } // if this assignment targets a specific channel ... if( var->chIdx != kAnyChIdx ) { - rc = _var_set_template( var, typeFlag, value ); // ... then set it alone + rc = _var_set_template( var, valueTypeFlag, value ); // ... then set it alone } else // ... otherwise set all channels. { for(; var!=nullptr; var=var->ch_link) - if((rc = _var_set_template( var, typeFlag, value )) != kOkRC) + if((rc = _var_set_template( var, valueTypeFlag, value )) != kOkRC) break; } + if(rc != kOkRC ) + rc = cwLogError(rc,"Variable value set failed on '%s:%i %s:%i",cwStringNullGuard(var->proc->label),var->proc->label_sfx_id,cwStringNullGuard(var->label),var->label_sfx_id); + return rc; } - rc_t _var_register_and_set( instance_t* inst, const char* var_label, unsigned vid, unsigned chIdx, abuf_t* abuf ) + rc_t _var_register_and_set( proc_t* proc, const char* var_label, unsigned sfx_id, unsigned vid, unsigned chIdx, abuf_t* abuf ) { rc_t rc; variable_t* var = nullptr; - if((rc = var_register_and_set( inst, var_label, vid, chIdx, var)) != kOkRC ) + if((rc = var_register_and_set( proc, var_label, sfx_id, vid, chIdx, var)) != kOkRC ) return rc; if( var != nullptr ) @@ -609,11 +967,24 @@ namespace cw return rc; } - rc_t _var_register_and_set( instance_t* inst, const char* var_label, unsigned vid, unsigned chIdx, fbuf_t* fbuf ) + rc_t _var_register_and_set( proc_t* proc, const char* var_label, unsigned sfx_id, unsigned vid, unsigned chIdx, mbuf_t* mbuf ) { rc_t rc; variable_t* var = nullptr; - if((rc = var_register_and_set( inst, var_label, vid, chIdx, var)) != kOkRC ) + if((rc = var_register_and_set( proc, var_label, sfx_id, vid, chIdx, var)) != kOkRC ) + return rc; + + if( var != nullptr ) + _var_set_driver( var, kMBufTFl, mbuf ); + + return rc; + } + + rc_t _var_register_and_set( proc_t* proc, const char* var_label, unsigned sfx_id, unsigned vid, unsigned chIdx, fbuf_t* fbuf ) + { + rc_t rc; + variable_t* var = nullptr; + if((rc = var_register_and_set( proc, var_label, sfx_id, vid, chIdx, var)) != kOkRC ) return rc; if( var != nullptr ) @@ -622,100 +993,33 @@ namespace cw return rc; } - rc_t _set_var_value_from_cfg( variable_t* var, const object_t* value ) + + rc_t _var_map_id_to_index( proc_t* proc, unsigned vid, unsigned chIdx, unsigned& idxRef ) { - rc_t rc = kOkRC; - - unsigned typeFlag = var->varDesc->type & kTypeMask; - - switch( typeFlag ) - { - case kBoolTFl: - { - bool v; - if((rc = value->value(v)) == kOkRC ) - rc = _var_set_driver( var, typeFlag, v ); - } - break; - - case kUIntTFl: - { - unsigned v; - if((rc = value->value(v)) == kOkRC ) - rc = _var_set_driver( var, typeFlag, v ); - } - break; - - case kIntTFl: - { - int v; - if((rc = value->value(v)) == kOkRC ) - rc = _var_set_driver( var, typeFlag, v ); - } - break; - - case kFloatTFl: - { - float v; - if((rc = value->value(v)) == kOkRC ) - rc = _var_set_driver( var, typeFlag, v ); - } - break; - - case kDoubleTFl: - { - double v; - if((rc = value->value(v)) == kOkRC ) - rc = _var_set_driver( var, typeFlag, v ); - } - break; - - case kStringTFl: - { - const char* v; - if((rc = value->value(v)) == kOkRC ) - rc = _var_set_driver( var, typeFlag, v ); - } - break; - - default: - rc = cwLogError(kOpFailRC,"The variable type 0x%x cannot yet be set via a preset.", var->varDesc->type ); - goto errLabel; - } - - errLabel: - if( rc != kOkRC ) - rc = cwLogError(kSyntaxErrorRC,"The %s.%s could not extract a type:%s from a configuration value.",var->inst->label,var->label,_typeFlagToLabel(var->varDesc->type & kTypeMask)); - return rc; - - } - - rc_t _var_map_id_to_index( instance_t* inst, unsigned vid, unsigned chIdx, unsigned& idxRef ) - { - unsigned idx = vid * inst->varMapChN + (chIdx == kAnyChIdx ? 0 : (chIdx+1)); + unsigned idx = vid * proc->varMapChN + (chIdx == kAnyChIdx ? 0 : (chIdx+1)); // verify that the map idx is valid - if( idx >= inst->varMapN ) - return cwLogError(kAssertFailRC,"The variable map positioning location %i is out of the range %i on instance '%s' vid:%i ch:%i.", idx, inst->varMapN, inst->label,vid,chIdx); + if( idx >= proc->varMapN ) + return cwLogError(kAssertFailRC,"The variable map positioning location %i is out of the range %i on proc '%s:%i' vid:%i ch:%i.", idx, proc->varMapN, proc->label,proc->label_sfx_id,vid,chIdx); idxRef = idx; return kOkRC; } - rc_t _var_map_label_to_index( instance_t* inst, const char* var_label, unsigned chIdx, unsigned& idxRef ) + rc_t _var_map_label_to_index( proc_t* proc, const char* var_label, unsigned sfx_id, unsigned chIdx, unsigned& idxRef ) { rc_t rc = kOkRC; variable_t* var = nullptr; idxRef = kInvalidIdx; - if((rc = var_find(inst, var_label, chIdx, var )) == kOkRC) - rc = _var_map_id_to_index( inst, var->vid, chIdx, idxRef ); + if((rc = var_find(proc, var_label, sfx_id, chIdx, var )) == kOkRC) + rc = _var_map_id_to_index( proc, var->vid, chIdx, idxRef ); return rc; } - rc_t _var_add_to_ch_list( instance_t* inst, variable_t* new_var ) + rc_t _var_add_to_ch_list( proc_t* proc, variable_t* new_var ) { rc_t rc = kOkRC; @@ -726,9 +1030,9 @@ namespace cw if( new_var->chIdx == kAnyChIdx ) return kOkRC; - if((base_var = _var_find_on_label_and_ch( inst, new_var->label, kAnyChIdx )) == nullptr ) + if((base_var = _var_find_on_label_and_ch( proc, new_var->label, new_var->label_sfx_id, kAnyChIdx )) == nullptr ) { - rc = cwLogError(kInvalidStateRC,"The base channel variable does not exist for '%s.%s'. This is an illegal state.", inst->label, new_var->label ); + rc = cwLogError(kInvalidStateRC,"The base channel variable does not exist for '%s:%i.%s:%i'. This is an illegal state.", proc->label, proc->label_sfx_id, new_var->label, new_var->label_sfx_id ); goto errLabel; } @@ -752,9 +1056,34 @@ namespace cw } + rc_t _var_set_type( variable_t* var, unsigned type_flag ) + { + rc_t rc = kOkRC; + + if( cwIsNotFlag(var->classVarDesc->type,kRuntimeTFl) ) + { + rc = cwLogError(kOpFailRC,"It is invalid to change the type of a statically (non-runtime) type variable."); + goto errLabel; + } + + // Duplicate the varDesc with the 'type' field set to type_flag + if( var->localVarDesc == nullptr ) + { + var->localVarDesc = mem::allocZ(); + *(var->localVarDesc) = *(var->classVarDesc); + var->localVarDesc->link = nullptr; + } + + var->localVarDesc->type = type_flag; + var->varDesc = var->localVarDesc; + + errLabel: + return rc; + } + // Create a variable and set it's value from 'value_cfg'. // If 'value_cfg' is null then use the value from var->varDesc->val_cfg. - rc_t _var_create( instance_t* inst, const char* var_label, unsigned id, unsigned chIdx, const object_t* value_cfg, variable_t*& varRef ) + rc_t _var_create( proc_t* proc, const char* var_label, unsigned sfx_id, unsigned id, unsigned chIdx, const object_t* value_cfg, unsigned altTypeFl, variable_t*& varRef ) { rc_t rc = kOkRC; variable_t* var = nullptr; @@ -763,99 +1092,101 @@ namespace cw varRef = nullptr; // if this var already exists - it can't be created again - if((var = _var_find_on_label_and_ch(inst,var_label,chIdx)) != nullptr ) + if((var = _var_find_on_label_and_ch(proc,var_label, sfx_id, chIdx)) != nullptr ) { - rc = cwLogError(kInvalidStateRC,"The variable '%s' ch:%i has already been created on the instance: '%s'.",var_label,chIdx,inst->label); + rc = cwLogError(kInvalidStateRC,"The variable '%s:%i' ch:%i has already been created on the proc: '%s:%i'.",var_label,sfx_id,chIdx,proc->label,proc->label_sfx_id); goto errLabel; } // locate the var desc - if((vd = var_desc_find( inst->class_desc, var_label)) == nullptr ) + if((vd = var_desc_find( proc->class_desc, var_label)) == nullptr ) { - rc = cwLogError(kInvalidIdRC,"Unable to locate the variable '%s' in class '%s'.", var_label, inst->class_desc->label ); + rc = cwLogError(kInvalidIdRC,"Unable to locate the variable '%s:%i' in class '%s'.", var_label, sfx_id, proc->class_desc->label ); goto errLabel; } // create the var var = mem::allocZ(); - var->varDesc = vd; - var->inst = inst; - var->label = mem::duplStr(var_label); - var->vid = id; - var->chIdx = chIdx; - var->value = nullptr; + var->varDesc = vd; + var->classVarDesc = vd; + var->proc = proc; + var->label = mem::duplStr(var_label); + var->label_sfx_id = sfx_id; + var->vid = id; + var->chIdx = chIdx; + var->value = nullptr; + var->type = kInvalidTFl; + if( altTypeFl != kInvalidTFl ) + _var_set_type(var,altTypeFl); + // if no value was given then set the value to the value given in the class if( value_cfg == nullptr ) value_cfg = var->varDesc->val_cfg; - + // if value_cfg is valid set the variable value - if( value_cfg != nullptr ) - if((rc = _set_var_value_from_cfg( var, value_cfg )) != kOkRC ) + if( value_cfg != nullptr && cwIsNotFlag(vd->type,kRuntimeTFl)) + if((rc = var_set_from_cfg( var, value_cfg )) != kOkRC ) goto errLabel; - var->var_link = inst->varL; - inst->varL = var; + // Add the variable to the end of the chain + if( proc->varL == nullptr ) + proc->varL = var; + else + { + variable_t* v = proc->varL; + while( v->var_link != nullptr ) + v=v->var_link; + v->var_link = var; + + } + //var->var_link = proc->varL; + //proc->varL = var; // link the new var into the ch_link list - if((rc = _var_add_to_ch_list(inst, var )) != kOkRC ) + if((rc = _var_add_to_ch_list(proc, var )) != kOkRC ) goto errLabel; errLabel: if( rc != kOkRC ) { - _var_destroy(var); - cwLogError(kOpFailRC,"Variable creation failed on '%s.%s' ch:%i.", inst->label, var_label, chIdx ); + var_destroy(var); + cwLogError(kOpFailRC,"Variable creation failed on '%s:%i.%s:%i' ch:%i.", proc->label, proc->label_sfx_id, var_label, sfx_id, chIdx ); } else { varRef = var; - cwLogMod("Created var: %s.%s ch:%i.", inst->label, var_label, chIdx ); + cwLogMod("Created var: %s:%i.%s:%i ch:%i.", proc->label, proc->label_sfx_id, var_label, sfx_id, chIdx ); } return rc; } - void _var_print( const variable_t* var ) + void _class_desc_print( const class_desc_t* cd ) { - const char* conn_label = is_connected_to_external_proc(var) ? "extern" : " "; - - printf(" %20s id:%4i ch:%3i : %s : ", var->label, var->vid, var->chIdx, conn_label ); - - if( var->value == nullptr ) - _value_print( &var->local_value[0] ); - else - _value_print( var->value ); - - printf("\n"); - } - - - rc_t _preset_set_var_value( instance_t* inst, const char* var_label, unsigned chIdx, const object_t* value ) - { - rc_t rc = kOkRC; - variable_t* var = nullptr; - - // get the variable - if((rc = var_find( inst, var_label, chIdx, var )) != kOkRC ) - goto errLabel; + const var_desc_t* vd = cd->varDescL; + cwLogPrint("%s\n",cwStringNullGuard(cd->label)); - rc = _set_var_value_from_cfg( var, value ); - - errLabel: - if( rc != kOkRC ) - rc = cwLogError(rc,"The value of instance:%s variable:%s could not be set via a preset.", inst->label, var_label ); - - return rc; + for(; vd!=nullptr; vd=vd->link) + { + const char* srcFlStr = vd->flags & kSrcVarDescFl ? "src" : " "; + const char* srcOptFlStr = vd->flags & kSrcOptVarDescFl ? "srcOpt" : " "; + const char* plyMltFlStr = vd->flags & kMultVarDescFl ? "mult" : " "; + + cwLogPrint(" %10s 0x%08x %s %s %s %s\n", cwStringNullGuard(vd->label), vd->type, srcFlStr, srcOptFlStr, plyMltFlStr, cwStringNullGuard(vd->docText) ); + } } - - } } +void cw::flow::value_print( const value_t* value, bool info_fl) +{ + _value_print(value,info_fl); +} + cw::flow::abuf_t* cw::flow::abuf_create( srate_t srate, unsigned chN, unsigned frameN ) { @@ -904,7 +1235,7 @@ const cw::flow::sample_t* cw::flow::abuf_get_channel( abuf_t* abuf, unsigned c return abuf->buf + (chIdx*abuf->frameN); } -cw::flow::fbuf_t* cw::flow::fbuf_create( srate_t srate, unsigned chN, const unsigned* maxBinN_V, const unsigned* binN_V, const unsigned* hopSmpN_V, const fd_real_t** magV, const fd_real_t** phsV, const fd_real_t** hzV ) +cw::flow::fbuf_t* cw::flow::fbuf_create( srate_t srate, unsigned chN, const unsigned* maxBinN_V, const unsigned* binN_V, const unsigned* hopSmpN_V, const fd_sample_t** magV, const fd_sample_t** phsV, const fd_sample_t** hzV ) { for(unsigned i=0; i maxBinN_V[i] ) @@ -920,9 +1251,9 @@ cw::flow::fbuf_t* cw::flow::fbuf_create( srate_t srate, unsigned chN, const unsi f->maxBinN_V = mem::allocZ(chN); f->binN_V = mem::allocZ(chN); f->hopSmpN_V = mem::allocZ(chN); - f->magV = mem::allocZ(chN); - f->phsV = mem::allocZ(chN); - f->hzV = mem::allocZ(chN); + f->magV = mem::allocZ(chN); + f->phsV = mem::allocZ(chN); + f->hzV = mem::allocZ(chN); f->readyFlV = mem::allocZ(chN); vop::copy( f->binN_V, binN_V, chN ); @@ -933,17 +1264,17 @@ cw::flow::fbuf_t* cw::flow::fbuf_create( srate_t srate, unsigned chN, const unsi { for(unsigned chIdx=0; chIdxmagV[ chIdx ] = (fd_real_t*)magV[chIdx]; - f->phsV[ chIdx ] = (fd_real_t*)phsV[chIdx]; - f->hzV[ chIdx ] = (fd_real_t*)hzV[chIdx]; + f->magV[ chIdx ] = (fd_sample_t*)magV[chIdx]; + f->phsV[ chIdx ] = (fd_sample_t*)phsV[chIdx]; + f->hzV[ chIdx ] = (fd_sample_t*)hzV[chIdx]; } } else { unsigned maxTotalBinsN = vop::sum( maxBinN_V, chN ); - fd_real_t* buf = mem::allocZ( kFbufVectN * maxTotalBinsN ); - fd_real_t* m = buf; + fd_sample_t* buf = mem::allocZ( kFbufVectN * maxTotalBinsN ); + fd_sample_t* m = buf; for(unsigned chIdx=0; chIdxmagV[chIdx] = m + 0 * f->binN_V[chIdx]; @@ -960,8 +1291,7 @@ cw::flow::fbuf_t* cw::flow::fbuf_create( srate_t srate, unsigned chN, const unsi return f; } - -cw::flow::fbuf_t* cw::flow::fbuf_create( srate_t srate, unsigned chN, unsigned maxBinN, unsigned binN, unsigned hopSmpN, const fd_real_t** magV, const fd_real_t** phsV, const fd_real_t** hzV ) +cw::flow::fbuf_t* cw::flow::fbuf_create( srate_t srate, unsigned chN, unsigned maxBinN, unsigned binN, unsigned hopSmpN, const fd_sample_t** magV, const fd_sample_t** phsV, const fd_sample_t** hzV ) { unsigned maxBinN_V[ chN ]; unsigned binN_V[ chN ]; @@ -971,11 +1301,8 @@ cw::flow::fbuf_t* cw::flow::fbuf_create( srate_t srate, unsigned chN, unsigned vop::fill( binN_V, chN, binN ); vop::fill( hopSmpN_V, chN, binN ); return fbuf_create( srate, chN, maxBinN_V, binN_V, hopSmpN_V, magV, phsV, hzV ); - } - - void cw::flow::fbuf_destroy( fbuf_t*& fbuf ) { if( fbuf == nullptr ) @@ -1010,27 +1337,110 @@ cw::flow::fbuf_t* cw::flow::fbuf_duplicate( const fbuf_t* src ) } +cw::flow::mbuf_t* cw::flow::mbuf_create( const midi::ch_msg_t* msgA, unsigned msgN ) +{ + mbuf_t* m = mem::allocZ(); + m->msgA = msgA; + m->msgN = msgN; + return m; +} + +void cw::flow::mbuf_destroy( mbuf_t*& buf ) +{ + mem::release(buf); +} + +cw::flow::mbuf_t* cw::flow::mbuf_duplicate( const mbuf_t* src ) +{ + return mbuf_create(src->msgA,src->msgN); +} + unsigned cw::flow::value_type_label_to_flag( const char* s ) { - unsigned flags = labelToId(typeLabelFlagsA,s,kInvalidTFl); + unsigned flags = labelToId(_typeLabelFlagsA,s,kInvalidTFl); if( flags == kInvalidTFl ) cwLogError(kInvalidArgRC,"Invalid type flag: '%s'",cwStringNullGuard(s)); return flags; } +const char* cw::flow::value_type_flag_to_label( unsigned flag ) +{ return _typeFlagToLabel(flag); } + +cw::flow::var_desc_t* cw::flow::var_desc_create( const char* label, const object_t* cfg ) +{ + var_desc_t* vd = mem::allocZ(); + vd->label = label; + vd->cfg = cfg; + return vd; +} + +void cw::flow::var_desc_destroy( var_desc_t* var_desc ) +{ + if( var_desc != nullptr ) + { + mem::release(var_desc->proxyProcLabel); + mem::release(var_desc->proxyVarLabel); + mem::release(var_desc); + } +} + +unsigned cw::flow::var_desc_attr_label_to_flag( const char* attr_label ) +{ + return labelToId(_varDescAttrFlagsA,attr_label,kInvalidVarDescFl); +} + +const char* cw::flow::var_desc_flag_to_attribute( unsigned flag ) +{ + return idToLabel(_varDescAttrFlagsA,flag,kInvalidVarDescFl); +} + +const cw::idLabelPair_t* cw::flow::var_desc_flag_array( unsigned& array_cnt_ref ) +{ + array_cnt_ref = sizeof(_varDescAttrFlagsA)/sizeof(_varDescAttrFlagsA[0]); + return _varDescAttrFlagsA; +} + +void cw::flow::class_desc_destroy( class_desc_t* class_desc) +{ + // release the var desc list + var_desc_t* vd0 = class_desc->varDescL; + var_desc_t* vd1 = nullptr; + while( vd0 != nullptr ) + { + vd1 = vd0->link; + var_desc_destroy(vd0); + vd0 = vd1; + } + + // release the preset list + class_preset_t* pr0 = class_desc->presetL; + class_preset_t* pr1 = nullptr; + while( pr0 != nullptr ) + { + pr1 = pr0->link; + mem::release(pr0); + pr0 = pr1; + } + +} cw::flow::class_desc_t* cw::flow::class_desc_find( flow_t* p, const char* label ) { for(unsigned i=0; iclassDescN; ++i) - if( textCompare(p->classDescA[i].label,label) == 0 ) + if( textIsEqual(p->classDescA[i].label,label)) return p->classDescA + i; + + for(unsigned i=0; isubnetDescN; ++i) + if( textIsEqual(p->subnetDescA[i].label,label)) + return p->subnetDescA + i; + return nullptr; } -cw::flow::var_desc_t* cw::flow::var_desc_find( class_desc_t* cd, const char* label ) +const cw::flow::var_desc_t* cw::flow::var_desc_find( const class_desc_t* cd, const char* label ) { - var_desc_t* vd = cd->varDescL; + const var_desc_t* vd = cd->varDescL; for(; vd != nullptr; vd=vd->link ) if( textCompare(vd->label,label) == 0 ) @@ -1038,6 +1448,11 @@ cw::flow::var_desc_t* cw::flow::var_desc_find( class_desc_t* cd, const char* lab return nullptr; } +cw::flow::var_desc_t* cw::flow::var_desc_find( class_desc_t* cd, const char* label ) +{ + return (var_desc_t*)var_desc_find( (const class_desc_t*)cd, label); +} + cw::rc_t cw::flow::var_desc_find( class_desc_t* cd, const char* label, var_desc_t*& vdRef ) { if((vdRef = var_desc_find(cd,label)) == nullptr ) @@ -1045,54 +1460,216 @@ cw::rc_t cw::flow::var_desc_find( class_desc_t* cd, const char* label, var_desc_ return kOkRC; } +const cw::flow::class_preset_t* cw::flow::class_preset_find( const class_desc_t* cd, const char* preset_label ) +{ + const class_preset_t* pr; + for(pr=cd->presetL; pr!=nullptr; pr=pr->link) + if( textCompare(pr->label,preset_label) == 0 ) + return pr; + + return nullptr; +} void cw::flow::class_dict_print( flow_t* p ) { for(unsigned i=0; iclassDescN; ++i) + _class_desc_print(p->classDescA+i); + + for(unsigned i=0; isubnetDescN; ++i) + _class_desc_print(p->subnetDescA+i); +} + + +void cw::flow::network_print( const network_t& net ) +{ + // for each proc in the network + for(unsigned i=0; iclassDescA + i; - var_desc_t* vd = cd->varDescL; - printf("%s\n",cwStringNullGuard(cd->label)); - - for(; vd!=nullptr; vd=vd->link) + proc_t* proc = net.proc_array[i]; + proc_print(proc); + + // if this proc has an internal network + if( proc->internal_net != nullptr ) { - const char* srcFlStr = vd->flags & kSrcVarFl ? "src" : " "; - const char* srcOptFlStr = vd->flags & kSrcOptVarFl ? "srcOpt" : " "; - - printf(" %10s 0x%08x %s %s %s\n", cwStringNullGuard(vd->label), vd->type, srcFlStr, srcOptFlStr, cwStringNullGuard(vd->docText) ); + cwLogPrint("------- Begin Nested Network: %s -------\n",cwStringNullGuard(proc->label)); + network_print(*(proc->internal_net)); + cwLogPrint("------- End Nested Network: %s -------\n",cwStringNullGuard(proc->label)); } + + } + + if(net.presetN > 0 ) + { + cwLogPrint("Presets:\n"); + for(unsigned i=0; ilabel); + switch( net_preset->tid ) + { + case kPresetVListTId: + { + const preset_value_t* net_val = net_preset->u.vlist.value_head; + for(; net_val!=nullptr; net_val=net_val->link) + { + cwLogPrint(" %s:%i %s:%i ",cwStringNullGuard(net_val->proc->label),net_val->proc->label_sfx_id,cwStringNullGuard(net_val->var->label),net_val->var->label_sfx_id); + _value_print( &net_val->value ); + cwLogPrint("\n"); + } + } + break; + + case kPresetDualTId: + cwLogPrint(" %s %s %f",net_preset->u.dual.pri->label,net_preset->u.dual.sec->label,net_preset->u.dual.coeff); + break; + } + } + cwLogPrint("\n"); } } -void cw::flow::network_print( flow_t* p ) +const cw::flow::network_preset_t* cw::flow::network_preset_from_label( const network_t& net, const char* preset_label ) { - for(instance_t* inst = p->network_head; inst!=nullptr; inst=inst->link) - instance_print(inst); + for(unsigned i=0; inetwork_head; inst!=nullptr; inst=inst->link ) - if( textCompare(inst_label,inst->label) == 0 ) - return inst; + unsigned multN = 0; + for(unsigned i=0; ilabel,proc_label) ) + multN += 1; + + return multN; +} + +cw::rc_t cw::flow::proc_mult_sfx_id_array( const network_t& net, const char* proc_label, unsigned* idA, unsigned idAllocN, unsigned& idN_ref ) +{ + rc_t rc = kOkRC; + unsigned multN = 0; + + idN_ref = 0; + + for(unsigned i=0; ilabel,proc_label) ) + { + if( multN >= idAllocN ) + { + rc = cwLogError(kBufTooSmallRC,"The mult-sfx-id result array is too small for the proc:'%s'.",cwStringNullGuard(proc_label)); + goto errLabel; + } + + idA[multN] = net.proc_array[i]->label_sfx_id; + multN += 1; + } + + idN_ref = multN; + +errLabel: + + return rc; +} + +void cw::flow::proc_destroy( proc_t* proc ) +{ + if( proc == nullptr ) + return; + + if( proc->class_desc->members != nullptr && proc->class_desc->members->destroy != nullptr && proc->userPtr != nullptr ) + proc->class_desc->members->destroy( proc ); + + // destroy the proc instance variables + variable_t* var0 = proc->varL; + variable_t* var1 = nullptr; + while( var0 != nullptr ) + { + var1 = var0->var_link; + var_destroy(var0); + var0 = var1; + } + + proc->varL = nullptr; + + mem::release(proc->label); + mem::release(proc->varMapA); + mem::release(proc); +} + +cw::rc_t cw::flow::proc_validate( proc_t* proc ) +{ + rc_t rc = kOkRC; + + for(variable_t* var=proc->varL; var!=nullptr; var=var->var_link) + { + if( var->label == nullptr ) + { + rc = cwLogError(kInvalidStateRC,"A var with no label was encountered."); + continue; + } + + if( var->value == nullptr ) + { + rc = cwLogError(kInvalidStateRC,"The var '%s:%i' has no value.",var->label,var->label_sfx_id); + continue; + } + + // the assigned value must have exactly one type + if(!math::isPowerOfTwo( var->value->tflag ) ) + { + rc = cwLogError(kInvalidStateRC,"The var '%s:%i' has the invalid type flag:0x%x",var->label,var->label_sfx_id,var->value->tflag); + continue; + } + + // if var is using a local value (not connected to a source variable) then the type of the value must be valid with the variable class + if( !is_connected_to_source(var) && !(var->varDesc->type & var->value->tflag) ) + { + rc = cwLogError(kInvalidStateRC, "The value type flag '%s' (0x%x) of '%s:%i-%s:%i' is not found in the variable class type flags: '%s' (0x%x)", + _typeFlagToLabel(var->value->tflag),var->value->tflag, + var->proc->label,var->proc->label_sfx_id, + var->label,var->label_sfx_id, + _typeFlagToLabel(var->varDesc->type),var->varDesc->type); + continue; + } + + // By setting the var->type field all future assignments to this variable + // must be coercible to this type. See _var_set_template() + var->type = var->value->tflag; + } + + return rc; +} + + +cw::flow::proc_t* cw::flow::proc_find( network_t& net, const char* proc_label, unsigned sfx_id ) +{ + for(unsigned i=0; ilabel_sfx_id==sfx_id && textIsEqual(proc_label,net.proc_array[i]->label) ) + return net.proc_array[i]; return nullptr; } -cw::rc_t cw::flow::instance_find( flow_t* p, const char* inst_label, instance_t*& instPtrRef ) +cw::rc_t cw::flow::proc_find( network_t& net, const char* proc_label, unsigned sfx_id, proc_t*& procPtrRef ) { rc_t rc = kOkRC; - if((instPtrRef = instance_find(p,inst_label)) != nullptr ) + if((procPtrRef = proc_find(net,proc_label,sfx_id)) != nullptr ) return rc; - return cwLogError(kInvalidArgRC,"The instance '%s' was not found.", inst_label ); + return cwLogError(kInvalidArgRC,"The proc '%s:%i' was not found.", proc_label, sfx_id ); } -cw::flow::external_device_t* cw::flow::external_device_find( flow_t* p, const char* device_label, unsigned typeId, unsigned inOrOutFl ) +cw::flow::external_device_t* cw::flow::external_device_find( flow_t* p, const char* device_label, unsigned typeId, unsigned inOrOutFl, const char* midiPortLabel ) { for(unsigned i=0; ideviceN; ++i) - if( cw::textIsEqual(p->deviceA[i].label,device_label) && p->deviceA[i].typeId==typeId && cwIsFlag(p->deviceA[i].flags,inOrOutFl )) + if( (device_label==nullptr || cw::textIsEqual(p->deviceA[i].devLabel,device_label)) + && p->deviceA[i].typeId==typeId + && cwIsFlag(p->deviceA[i].flags,inOrOutFl) + && (midiPortLabel==nullptr || cw::textIsEqual(p->deviceA[i].portLabel,midiPortLabel)) ) return p->deviceA + i; cwLogError(kInvalidArgRC,"The %s device named '%s' could not be found.", cwIsFlag(inOrOutFl,kInFl) ? "in" : "out", device_label ); @@ -1100,96 +1677,172 @@ cw::flow::external_device_t* cw::flow::external_device_find( flow_t* p, const ch return nullptr; } -void cw::flow::instance_print( instance_t* inst ) +void cw::flow::proc_print( proc_t* proc ) { - printf("%s\n", inst->label); - for(variable_t* var = inst->varL; var!=nullptr; var=var->var_link) + cwLogPrint("%s:%i\n", proc->label,proc->label_sfx_id); + for(variable_t* var = proc->varL; var!=nullptr; var=var->var_link) if( var->chIdx == kAnyChIdx ) for(variable_t* v0 = var; v0!=nullptr; v0=v0->ch_link) _var_print(v0); - if( inst->class_desc->members->report ) - inst->class_desc->members->report( inst ); + if( proc->class_desc->members->report ) + proc->class_desc->members->report( proc ); } +unsigned cw::flow::proc_var_count( proc_t* proc ) +{ + unsigned n = 0; + for(variable_t* var=proc->varL; var!=nullptr; var=var->var_link) + ++n; + + return n; +} + +char* cw::flow::proc_expand_filename( const proc_t* proc, const char* fname ) +{ + bool useProjDirFl = proc->ctx->proj_dir != nullptr && textLength(fname) > 1 && fname[0] == '$'; + char* fn0 = nullptr; + + if( useProjDirFl ) + fn0 = filesys::makeFn(proc->ctx->proj_dir,fname+1,nullptr,nullptr); + + char* fn1 = filesys::expandPath(useProjDirFl ? fn0 : fname); + + mem::release(fn0); + + return fn1; +} -void cw::flow::_var_destroy( variable_t* var ) + +cw::rc_t cw::flow::var_create( proc_t* proc, const char* var_label, unsigned sfx_id, unsigned id, unsigned chIdx, const object_t* value_cfg, unsigned altTypeFl, variable_t*& varRef ) +{ + rc_t rc = kOkRC; + + rc = _var_create( proc, var_label, sfx_id, id, chIdx, value_cfg, altTypeFl, varRef ); + + return rc; +} + +void cw::flow::var_destroy( variable_t* var ) { if( var != nullptr ) { for(unsigned i=0; ilocal_value+i); + + if( var->localVarDesc != nullptr ) + mem::release(var->localVarDesc); + mem::release(var->label); mem::release(var); } } - -cw::rc_t cw::flow::var_create( instance_t* inst, const char* var_label, unsigned id, unsigned chIdx, const object_t* value_cfg, variable_t*& varRef ) -{ - rc_t rc = kOkRC; - - rc = _var_create( inst, var_label, id, chIdx, value_cfg, varRef ); - - return rc; -} - -cw::rc_t cw::flow::var_channelize( instance_t* inst, const char* var_label, unsigned chIdx, const object_t* value_cfg, unsigned vid, variable_t*& varRef ) +cw::rc_t cw::flow::var_channelize( proc_t* proc, const char* var_label, unsigned sfx_id, unsigned chIdx, const object_t* value_cfg, unsigned vid, variable_t*& varRef ) { rc_t rc = kOkRC; variable_t* var = nullptr; variable_t* base_var = nullptr; varRef = nullptr; - if((base_var = _var_find_on_label_and_ch( inst, var_label, kAnyChIdx)) == nullptr) + if((base_var = _var_find_on_label_and_ch( proc, var_label, sfx_id, kAnyChIdx)) == nullptr) { - rc = cwLogError(kInvalidStateRC,"The base ('any') channel variable could not be located on '%s.%s'.",inst->label,var_label); + rc = cwLogError(kInvalidStateRC,"The base ('any') channel variable could not be located on '%s:%i.%s:%i'.",proc->label,proc->label_sfx_id,var_label,sfx_id); goto errLabel; } - - + // locate the variable with the stated chIdx - var = _var_find_on_label_and_ch( inst, var_label, chIdx ); + var = _var_find_on_label_and_ch( proc, var_label, sfx_id, chIdx ); - // 'src' variables cannot be channelized + /* if( cwIsFlag(base_var->varDesc->flags,kSrcVarFl) ) { rc = cwLogError(rc,"'src' variables cannot be channelized."); goto errLabel; } - + */ + // if the requested var was not found then create a new variable with the requested channel index if( var == nullptr && chIdx != kAnyChIdx ) { // create the channelized var - if((rc = _var_create( inst, var_label, vid, chIdx, value_cfg, var )) != kOkRC ) + if((rc = _var_create( proc, var_label, sfx_id, vid, chIdx, value_cfg, kInvalidTFl, var )) != kOkRC ) goto errLabel; // if no value was set then set the value from the 'any' channel if( value_cfg == nullptr ) { - // Set the value of the new variable to the value of the 'any' channel - _value_duplicate( var->local_value[ var->local_value_idx], base_var->local_value[ base_var->local_value_idx ] ); + // if the base-var is connected to a source ... + if( is_connected_to_source(base_var) ) + { + // ... then connect the new var to a source also + + // Attempt to find a matching channel on the source + variable_t* src_ch_var = base_var->src_var; + variable_t* last_src_ch_var = base_var->src_var; + unsigned src_ch_cnt = 0; + bool anyChFl = false; + bool zeroChFl = false; + + for(; src_ch_var!=nullptr; src_ch_var=src_ch_var->ch_link) + { + last_src_ch_var = src_ch_var; + if( src_ch_var->chIdx == var->chIdx ) + break; - // If the 'any' channel value was set to point to it's local value then do same with this value - if( base_var->local_value + base_var->local_value_idx == base_var->value ) - var->value = var->local_value + var->local_value_idx; + if( src_ch_var->chIdx == kAnyChIdx ) + anyChFl = true; + + if( src_ch_var->chIdx == 0 ) + zeroChFl = true; + + src_ch_cnt += 1; + } + + // If there is more than one channel available, in addition to the kAnyCh, and the src and dst var's do not have matching ch indexes + // then there is a possibility that this is an unexpected connection between different channels. + // However if there only any-ch and/or ch=0 is available then this is simply a one channel to many channels which is common. + if( src_ch_var == nullptr && (anyChFl==false || (src_ch_cnt>1 && zeroChFl==false)) && last_src_ch_var->chIdx != var->chIdx ) + { + cwLogWarning("A connection is being made where channel src and dst. channels don't match and more than one src channel is available. src:%s:%i-%s:%i ch:%i of %i dst:%s:%i-%s:%i ch:%i", + last_src_ch_var->proc->label, last_src_ch_var->proc->label_sfx_id, + last_src_ch_var->label, last_src_ch_var->label_sfx_id, last_src_ch_var->chIdx, src_ch_cnt, + var->proc->label, var->proc->label_sfx_id, + var->label, var->label_sfx_id, var->chIdx ); + } + + // if no matching channel is found connect to the last valid source channel + // (Connecting to the last valid source is better than connecting to base_var->src_var + // because if a var has more than a base var it is unlikely to update the base_var.) + var_connect( last_src_ch_var, var ); + + } + else // the base-var is not connected, and no value was provided for the new var + { + + // Set the value of the new variable to the value of the 'any' channel + _value_duplicate( var->local_value[ var->local_value_idx], base_var->local_value[ base_var->local_value_idx ] ); + + // If the 'any' channel value was set to point to it's local value then do same with this value + if( base_var->local_value + base_var->local_value_idx == base_var->value ) + var->value = var->local_value + var->local_value_idx; + } } } - else + else // the var was found - set the value { // a correctly channelized var was found - but we still may need to set the value if( value_cfg != nullptr ) { - rc = _set_var_value_from_cfg( var, value_cfg ); + rc = var_set_from_cfg( var, value_cfg ); } else { - cwLogWarning("An existing var (%s.%s ch:%i) was specified for channelizing but no value was provided.", inst->label, var_label, chIdx ); + cwLogWarning("An existing var (%s:i.%s:i ch:%i) was specified for channelizing but no value was provided.", proc->label, proc->label_sfx_id, var_label, sfx_id, chIdx ); } } @@ -1198,27 +1851,127 @@ cw::rc_t cw::flow::var_channelize( instance_t* inst, const char* var_label, uns errLabel: if( rc != kOkRC ) - rc = cwLogError(rc,"Channelize failed for variable '%s' on instance '%s' ch:%i.", var_label, inst->label, chIdx ); + rc = cwLogError(rc,"Channelize failed for variable '%s:%i' on proc '%s:%i' ch:%i.", var_label, sfx_id, proc->label, proc->label_sfx_id, chIdx ); return rc; } -bool cw::flow::var_exists( instance_t* inst, const char* label, unsigned chIdx ) -{ return _var_find_on_label_and_ch(inst,label,chIdx) != nullptr; } +cw::rc_t cw::flow::var_call_custom_value_func( variable_t* var ) +{ + rc_t rc; + if((rc = var->proc->class_desc->members->value( var->proc, var )) != kOkRC ) + goto errLabel; + + if( var->flags & kLogVarFl ) + { + cwLogPrint("cycle: %8i ",var->proc->ctx->cycleIndex); + cwLogPrint("%10s:%5i", var->proc->label,var->proc->label_sfx_id); + + if( var->chIdx == kAnyChIdx ) + { + _var_print(var); + } + else + { + printf("\n"); + for(variable_t* ch_var = var; ch_var!=nullptr; ch_var=ch_var->ch_link) + { + _var_print(ch_var); + } + + } + } + +errLabel: + return rc; + +} -bool cw::flow::var_has_value( instance_t* inst, const char* label, unsigned chIdx ) +cw::rc_t cw::flow::var_flags( proc_t* proc, unsigned chIdx, const char* var_label, unsigned sfx_id, unsigned& flags_ref ) +{ + rc_t rc = kOkRC; + variable_t* var = nullptr; + + flags_ref = 0; + + if((rc = _var_find_on_label_and_ch(proc,var_label,sfx_id,chIdx,var)) != kOkRC ) + goto errLabel; + + flags_ref = var->flags; + +errLabel: + return rc; +} + +cw::rc_t cw::flow::var_set_flags( proc_t* proc, unsigned chIdx, const char* var_label, unsigned sfx_id, unsigned flag ) +{ + rc_t rc = kOkRC; + variable_t* var = nullptr; + + if((rc = _var_find_on_label_and_ch(proc,var_label,sfx_id,chIdx,var)) != kOkRC ) + goto errLabel; + + + var->flags |= flag; + +errLabel: + return rc; +} + +cw::rc_t cw::flow::var_clr_flags( proc_t* proc, unsigned chIdx, const char* var_label, unsigned sfx_id, unsigned flag ) +{ + rc_t rc = kOkRC; + variable_t* var = nullptr; + + if((rc = _var_find_on_label_and_ch(proc,var_label,sfx_id,chIdx,var)) != kOkRC ) + goto errLabel; + + var->flags = cwClrFlag(var->flags,flag); +errLabel: + return rc; +} + +bool cw::flow::var_exists( proc_t* proc, const char* label, unsigned sfx_id, unsigned chIdx ) +{ return _var_find_on_label_and_ch(proc,label,sfx_id,chIdx) != nullptr; } + +bool cw::flow::var_has_value( proc_t* proc, const char* label, unsigned sfx_id, unsigned chIdx ) { variable_t* varPtr = nullptr; rc_t rc; - if((rc = var_find( inst, label, chIdx, varPtr )) != kOkRC ) + if((rc = var_find( proc, label, sfx_id, chIdx, varPtr )) != kOkRC ) return false; return varPtr->value != nullptr; } +bool cw::flow::var_is_a_source( proc_t* proc, const char* label, unsigned sfx_id, unsigned chIdx ) +{ + rc_t rc; + variable_t* varPtr = nullptr; + if((rc = var_find( proc, label, sfx_id, chIdx, varPtr)) != kOkRC ) + { + cwLogError(kEleNotFoundRC,"The variable '%s:%i' was not found on proc:'%s:%i'. 'source' state query is invalid.",cwStringNullGuard(label),sfx_id,cwStringNullGuard(proc->label),proc->label_sfx_id); + return false; + } + + return is_a_source_var(varPtr); +} -cw::rc_t cw::flow::var_find( instance_t* inst, unsigned vid, unsigned chIdx, variable_t*& varRef ) +bool cw::flow::var_is_a_source( proc_t* proc, unsigned vid, unsigned chIdx ) +{ + rc_t rc; + variable_t* varPtr = nullptr; + if((rc = var_find( proc, vid, chIdx, varPtr)) != kOkRC ) + { + cwLogError(kEleNotFoundRC,"The variable with vid '%i' was not found on proc:'%s:%i'. 'source' state query is invalid.",vid,cwStringNullGuard(proc->label),proc->label_sfx_id); + return false; + } + + return is_a_source_var(varPtr); +} + +cw::rc_t cw::flow::var_find( proc_t* proc, unsigned vid, unsigned chIdx, variable_t*& varRef ) { rc_t rc = kOkRC; unsigned idx = kInvalidIdx; @@ -1226,25 +1979,25 @@ cw::rc_t cw::flow::var_find( instance_t* inst, unsigned vid, unsigned chIdx, var varRef = nullptr; - // if the varMapA[] has not yet been formed (we are inside the instance constructor) then do a slow lookup of the variable - if( inst->varMapA == nullptr ) + // if the varMapA[] has not yet been formed (we are inside the proc constructor) then do a slow lookup of the variable + if( proc->varMapA == nullptr ) { - if((rc = _var_find_on_vid_and_ch(inst,vid,chIdx,var)) != kOkRC ) + if((rc = _var_find_on_vid_and_ch(proc,vid,chIdx,var)) != kOkRC ) goto errLabel; } else { - // otherwise do a fast lookup using inst->varMapA[] - if((rc = _var_map_id_to_index(inst, vid, chIdx, idx )) == kOkRC && (idx != kInvalidIdx )) - var = inst->varMapA[idx]; + // otherwise do a fast lookup using proc->varMapA[] + if((rc = _var_map_id_to_index(proc, vid, chIdx, idx )) == kOkRC && (idx != kInvalidIdx )) + var = proc->varMapA[idx]; else { - rc = cwLogError(kInvalidIdRC,"The index of variable vid:%i chIdx:%i on instance '%s' could not be calculated and the variable value could not be retrieved.", vid, chIdx, inst->label); + rc = cwLogError(kInvalidIdRC,"The index of variable vid:%i chIdx:%i on proc '%s:%i' could not be calculated and the variable value could not be retrieved.", vid, chIdx, proc->label,proc->label_sfx_id); goto errLabel; } } - // if we get here var must be non-null + // if we get here var must be non-null - (was the var registered?) assert( var != nullptr && rc == kOkRC ); varRef = var; @@ -1253,36 +2006,35 @@ cw::rc_t cw::flow::var_find( instance_t* inst, unsigned vid, unsigned chIdx, var return rc; } - - -cw::rc_t cw::flow::var_find( instance_t* inst, const char* label, unsigned chIdx, variable_t*& vRef ) +cw::rc_t cw::flow::var_find( proc_t* proc, const char* label, unsigned sfx_id, unsigned chIdx, variable_t*& vRef ) { variable_t* var; vRef = nullptr; - if((var = _var_find_on_label_and_ch(inst,label,chIdx)) != nullptr ) + if((var = _var_find_on_label_and_ch(proc,label,sfx_id,chIdx)) != nullptr ) { vRef = var; return kOkRC; } - return cwLogError(kInvalidIdRC,"The instance '%s' does not have a variable named '%s'.", inst->label, label ); + return cwLogError(kInvalidIdRC,"The proc '%s:%i' does not have a variable named '%s:%i'.", proc->label, proc->label_sfx_id, label, sfx_id ); } -cw::rc_t cw::flow::var_find( instance_t* inst, const char* label, unsigned chIdx, const variable_t*& vRef ) +cw::rc_t cw::flow::var_find( proc_t* proc, const char* label, unsigned sfx_id, unsigned chIdx, const variable_t*& vRef ) { variable_t* v = nullptr; - rc_t rc = var_find(inst,label,chIdx,v); + rc_t rc = var_find(proc,label,sfx_id,chIdx,v); vRef = v; return rc; } -cw::rc_t cw::flow::var_channel_count( instance_t* inst, const char* label, unsigned& chCntRef ) + +cw::rc_t cw::flow::var_channel_count( proc_t* proc, const char* label, unsigned sfx_id, unsigned& chCntRef ) { rc_t rc = kOkRC; const variable_t* var= nullptr; - if((rc = var_find(inst,label,kAnyChIdx,var)) != kOkRC ) - return cwLogError(rc,"Channel count was not available because the variable '%s.%s' does not exist.",cwStringNullGuard(inst->label),cwStringNullGuard(label)); + if((rc = var_find(proc,label,sfx_id,kAnyChIdx,var)) != kOkRC ) + return cwLogError(rc,"Channel count was not available because the variable '%s:%i.%s:%i' does not exist.",cwStringNullGuard(proc->label),proc->label_sfx_id,cwStringNullGuard(label),sfx_id); return var_channel_count(var,chCntRef); } @@ -1294,9 +2046,9 @@ cw::rc_t cw::flow::var_channel_count( const variable_t* var, unsigned& chCntRef chCntRef = 0; - if((rc = var_find( var->inst, var->label, kAnyChIdx, v )) != kOkRC ) + if((rc = var_find( var->proc, var->label, var->label_sfx_id, kAnyChIdx, v )) != kOkRC ) { - rc = cwLogError(kInvalidStateRC,"The base channel variable instance could not be found for the variable '%s.%s'.",var->inst->label,var->label); + rc = cwLogError(kInvalidStateRC,"The base channel variable proc could not be found for the variable '%s:%i.%s:%i'.",var->proc->label,var->proc->label_sfx_id,var->label,var->label_sfx_id); goto errLabel; } @@ -1309,79 +2061,203 @@ cw::rc_t cw::flow::var_channel_count( const variable_t* var, unsigned& chCntRef -cw::rc_t cw::flow::var_register( instance_t* inst, const char* var_label, unsigned vid, unsigned chIdx, const object_t* value_cfg, variable_t*& varRef ) +cw::rc_t cw::flow::var_register( proc_t* proc, const char* var_label, unsigned sfx_id, unsigned vid, unsigned chIdx, const object_t* value_cfg, variable_t*& varRef ) { rc_t rc = kOkRC; variable_t* var = nullptr; varRef = nullptr; - // TODO: check for duplicate 'vid'-'chIdx' pairs on this instance + // TODO: check for duplicate 'vid'-'chIdx' pairs on this proc // The concatenation of 'vid' and 'chIdx' should be unique // if an exact match to label/chIdx was found - if((var = _var_find_on_label_and_ch(inst,var_label,chIdx)) != nullptr ) + if((var = _var_find_on_label_and_ch(proc,var_label,sfx_id,chIdx)) != nullptr ) { // if a value was given - then update the value if( value_cfg != nullptr ) - if((rc = _set_var_value_from_cfg( var, value_cfg )) != kOkRC ) + if((rc = var_set_from_cfg( var, value_cfg )) != kOkRC ) goto errLabel; } else // an exact match was not found - channelize the variable { - if((rc = var_channelize(inst,var_label,chIdx,value_cfg,vid,var)) != kOkRC ) + if((rc = var_channelize(proc,var_label,sfx_id,chIdx,value_cfg,vid,var)) != kOkRC ) goto errLabel; } var->vid = vid; varRef = var; - if((var = _var_find_on_label_and_ch(inst,var_label,kAnyChIdx)) != nullptr ) + if((var = _var_find_on_label_and_ch(proc,var_label,sfx_id,kAnyChIdx)) != nullptr ) var->vid = vid; else - rc = cwLogError(kInvalidStateRC,"The variable '%s' instance '%s' has no base channel.", var_label, inst->label, chIdx); + rc = cwLogError(kInvalidStateRC,"The variable '%s:%i' proc '%s:%i' has no base channel.", var_label, sfx_id, proc->label, proc->label_sfx_id, chIdx); errLabel: if( rc != kOkRC ) - rc = cwLogError(rc,"Registration failed on variable '%s' instance '%s' ch: %i.", var_label, inst->label, chIdx); + rc = cwLogError(rc,"Registration failed on variable '%s:%i' proc '%s:%i' ch: %i.", var_label, sfx_id, proc->label, proc->label_sfx_id, chIdx); return rc; } - -cw::rc_t cw::flow::var_register_and_set( instance_t* inst, const char* var_label, unsigned vid, unsigned chIdx, variable_t*& varRef ) +bool cw::flow::is_connected_to_source( const variable_t* var ) { - return var_register( inst, var_label, vid, chIdx, nullptr, varRef ); + // if this var does not have a 'src_ptr' then it can't be connected to an external proc + if( var->src_var == nullptr || var->value == nullptr ) + return false; + + // if this var is using a local value then it can't be connected to an external proc + for(unsigned i=0; ivalue == var->local_value + i ) + return false; + + return true; } -cw::rc_t cw::flow::var_register_and_set( instance_t* inst, const char* var_label, unsigned vid, unsigned chIdx, srate_t srate, unsigned chN, unsigned frameN ) +bool cw::flow::is_a_source_var( const variable_t* var ) +{ return var->dst_head != nullptr; } + + +void cw::flow::var_connect( variable_t* src_var, variable_t* in_var ) +{ + assert( in_var->dst_link == nullptr ); + + // connect in_var into src_var's outgoing var chain + if( src_var->dst_head == nullptr ) + src_var->dst_head = in_var; + else + src_var->dst_tail->dst_link = in_var; + + src_var->dst_tail = in_var; + + in_var->value = src_var->value; + in_var->src_var = src_var; +} + +void cw::flow::var_disconnect( variable_t* in_var ) +{ + if( in_var->src_var != nullptr ) + { + // remote the in_var from the src var's output list + variable_t* v0 = nullptr; + variable_t* v = in_var->src_var->dst_head; + for(; v!=nullptr; v=v->dst_link) + { + if( v == in_var ) + { + if( v0 == nullptr ) + in_var->src_var->dst_head = v->dst_link; + else + v0->dst_link = v->dst_link; + break; + } + + v0 = v; + } + + // the in_var is always in the src-var's output list + assert(v == in_var ); + + in_var->src_var = nullptr; + } +} + +unsigned cw::flow::var_mult_count( proc_t* proc, const char* var_label ) +{ + unsigned n = 0; + for(variable_t* var=proc->varL; var!=nullptr; var=var->var_link) + if( textIsEqual(var->label,var_label) ) + ++n; + + return n; +} + +cw::rc_t cw::flow::var_mult_sfx_id_array( proc_t* proc, const char* var_label, unsigned* idA, unsigned idAllocN, unsigned& idN_ref ) +{ + rc_t rc = kOkRC; + + idN_ref = 0; + + // for each variable whose 'label' is 'var_label' + for(variable_t* var=proc->varL; var!=nullptr; var=var->var_link) + if( textIsEqual(var->label,var_label) ) + { + // scan idA[] for a matching sfx_id + unsigned i=0; + for(; ilabel_sfx_id ) + break; + + // if the sfx_id of this var has not yet been included in idA[] + if( i == idN_ref ) + { + // ... and we still have space left in the output arrau + if( idN_ref >= idAllocN ) + { + rc = cwLogError(kBufTooSmallRC,"The mult-sfx-id result array is too small for the var:'%s'.",cwStringNullGuard(var_label)); + goto errLabel; + } + + // store this sfx_id in idA[] + idA[idN_ref++] = var->label_sfx_id; + } + } + +errLabel: + if( rc != kOkRC ) + idN_ref = 0; + + return rc; +} + +cw::rc_t cw::flow::var_register_and_set( proc_t* proc, const char* var_label, unsigned sfx_id, unsigned vid, unsigned chIdx, variable_t*& varRef ) +{ + return var_register( proc, var_label, sfx_id, vid, chIdx, nullptr, varRef ); +} + +cw::rc_t cw::flow::var_register_and_set( proc_t* proc, const char* var_label, unsigned sfx_id, unsigned vid, unsigned chIdx, srate_t srate, unsigned chN, unsigned frameN ) { rc_t rc = kOkRC; abuf_t* abuf; if((abuf = abuf_create( srate, chN, frameN )) == nullptr ) - return cwLogError(kOpFailRC,"abuf create failed on instance:'%s' variable:'%s'.", inst->label, var_label); + return cwLogError(kOpFailRC,"abuf create failed on proc:'%s:%i' variable:'%s:%i'.", proc->label, proc->label_sfx_id, var_label,sfx_id); - if((rc = _var_register_and_set( inst, var_label, vid, chIdx, abuf )) != kOkRC ) + if((rc = _var_register_and_set( proc, var_label, sfx_id, vid, chIdx, abuf )) != kOkRC ) abuf_destroy(abuf); return rc; } -cw::rc_t cw::flow::var_register_and_set( instance_t* inst, const char* var_label, unsigned vid, unsigned chIdx, srate_t srate, unsigned chN, const unsigned* maxBinN_V, const unsigned* binN_V, const unsigned* hopSmpN_V, const fd_real_t** magV, const fd_real_t** phsV, const fd_real_t** hzV ) +cw::rc_t cw::flow::var_register_and_set( proc_t* proc, const char* var_label, unsigned sfx_id, unsigned vid, unsigned chIdx, srate_t srate, unsigned chN, const unsigned* maxBinN_V, const unsigned* binN_V, const unsigned* hopSmpN_V, const fd_sample_t** magV, const fd_sample_t** phsV, const fd_sample_t** hzV ) { rc_t rc = kOkRC; fbuf_t* fbuf; if((fbuf = fbuf_create( srate, chN, maxBinN_V, binN_V, hopSmpN_V, magV, phsV, hzV )) == nullptr ) - return cwLogError(kOpFailRC,"fbuf create failed on instance:'%s' variable:'%s'.", inst->label, var_label); + return cwLogError(kOpFailRC,"fbuf create failed on proc:'%s:%i' variable:'%s:%i'.", proc->label, proc->label_sfx_id, var_label,sfx_id); - if((rc = _var_register_and_set( inst, var_label, vid, chIdx, fbuf )) != kOkRC ) + if((rc = _var_register_and_set( proc, var_label, sfx_id, vid, chIdx, fbuf )) != kOkRC ) fbuf_destroy(fbuf); return rc; } -cw::rc_t cw::flow::var_register_and_set( instance_t* inst, const char* var_label, unsigned vid, unsigned chIdx, srate_t srate, unsigned chN, unsigned maxBinN, unsigned binN, unsigned hopSmpN, const fd_real_t** magV, const fd_real_t** phsV, const fd_real_t** hzV ) +cw::rc_t cw::flow::var_register_and_set( proc_t* proc, const char* var_label, unsigned sfx_id, unsigned vid, unsigned chIdx, midi::ch_msg_t* msgA, unsigned msgN ) +{ + rc_t rc = kOkRC; + mbuf_t* mbuf; + + if((mbuf = mbuf_create(msgA,msgN)) == nullptr ) + return cwLogError(kOpFailRC,"mbuf create failed on proc:'%s:%i' variable:'%s:%i'.", proc->label, proc->label_sfx_id, var_label, sfx_id); + + if((rc = _var_register_and_set( proc, var_label, sfx_id, vid, chIdx, mbuf )) != kOkRC ) + mbuf_destroy(mbuf); + + return rc; +} + + +cw::rc_t cw::flow::var_register_and_set( proc_t* proc, const char* var_label, unsigned sfx_id, unsigned vid, unsigned chIdx, srate_t srate, unsigned chN, unsigned maxBinN, unsigned binN, unsigned hopSmpN, const fd_sample_t** magV, const fd_sample_t** phsV, const fd_sample_t** hzV ) { unsigned maxBinN_V[ chN ]; unsigned binN_V[ chN ]; @@ -1389,7 +2265,7 @@ cw::rc_t cw::flow::var_register_and_set( instance_t* inst, const char* var_label vop::fill(maxBinN_V,chN,maxBinN); vop::fill(binN_V,chN,binN); vop::fill(hopSmpN_V,chN, hopSmpN ); - return var_register_and_set(inst,var_label,vid,chIdx,srate, chN, maxBinN_V, binN_V, hopSmpN_V, magV, phsV, hzV); + return var_register_and_set(proc,var_label,sfx_id,vid,chIdx,srate, chN, maxBinN_V, binN_V, hopSmpN_V, magV, phsV, hzV); } @@ -1423,79 +2299,221 @@ cw::rc_t cw::flow::var_get( const variable_t* var, const fbuf_t*& valRef ) cw::rc_t cw::flow::var_get( variable_t* var, fbuf_t*& valRef ) { return _val_get_driver(var,valRef); } -cw::rc_t cw::flow::var_set( instance_t* inst, unsigned vid, unsigned chIdx, bool val ) +cw::rc_t cw::flow::var_get( const variable_t* var, const mbuf_t*& valRef ) +{ return _val_get_driver(var,valRef); } + +cw::rc_t cw::flow::var_get( variable_t* var, mbuf_t*& valRef ) +{ return _val_get_driver(var,valRef); } + +cw::rc_t cw::flow::var_get( const variable_t* var, const object_t*& valRef ) +{ return _val_get_driver(var,valRef); } + +cw::rc_t cw::flow::cfg_to_value( const object_t* cfg, value_t& value_ref ) +{ + rc_t rc = kOkRC; + + switch( cfg->type->id ) + { + case kCharTId: + case kUInt8TId: + case kUInt16TId: + case kUInt32TId: + value_ref.tflag = kUIntTFl; + if((rc = cfg->value(value_ref.u.u)) != kOkRC ) + rc = cwLogError(rc,"Conversion to uint failed."); + break; + + case kInt8TId: + case kInt16TId: + case kInt32TId: + value_ref.tflag = kIntTFl; + if((rc = cfg->value(value_ref.u.i)) != kOkRC ) + rc = cwLogError(rc,"Conversion to int failed."); + break; + + case kInt64TId: + case kUInt64TId: + rc = cwLogError(kInvalidArgRC,"The flow system does not currently implement 64bit integers."); + goto errLabel; + break; + + case kFloatTId: + value_ref.tflag = kFloatTFl; + if((rc = cfg->value(value_ref.u.f)) != kOkRC ) + rc = cwLogError(rc,"Conversion to float failed."); + break; + + case kDoubleTId: + value_ref.tflag = kDoubleTFl; + if((rc = cfg->value(value_ref.u.d)) != kOkRC ) + rc = cwLogError(rc,"Conversion to double failed."); + break; + + case kBoolTId: + value_ref.tflag = kBoolTFl; + if((rc = cfg->value(value_ref.u.b)) != kOkRC ) + rc = cwLogError(rc,"Conversion to bool failed."); + break; + + case kStringTId: + case kCStringTId: + value_ref.tflag = kStringTFl; + if((rc = cfg->value(value_ref.u.s)) != kOkRC ) + rc = cwLogError(rc,"Conversion to string failed."); + break; + + default: + value_ref.tflag = kCfgTFl; + value_ref.u.cfg = cfg; + + } +errLabel: + + return rc; +} + + +cw::rc_t cw::flow::var_set_from_cfg( variable_t* var, const object_t* cfg_value ) +{ + rc_t rc = kOkRC; + value_t v; + + if((rc = cfg_to_value(cfg_value, v)) != kOkRC ) + goto errLabel; + + if((rc = var_set(var,&v)) != kOkRC ) + goto errLabel; + +errLabel: + if( rc != kOkRC ) + rc = cwLogError(kSyntaxErrorRC,"The %s:%i.%s:%i could not extract a type:%s from a configuration value.",var->proc->label,var->proc->label_sfx_id,var->label,var->label_sfx_id,_typeFlagToLabel(var->varDesc->type & kTypeMask)); + + return rc; + +} + +cw::rc_t cw::flow::var_set( variable_t* var, const value_t* val ) +{ + rc_t rc = kOkRC; + + switch( val->tflag ) + { + case kBoolTFl: rc = _var_set_driver(var,val->tflag,val->u.b); break; + case kUIntTFl: rc = _var_set_driver(var,val->tflag,val->u.u); break; + case kIntTFl: rc = _var_set_driver(var,val->tflag,val->u.i); break; + case kFloatTFl: rc = _var_set_driver(var,val->tflag,val->u.f); break; + case kDoubleTFl: rc = _var_set_driver(var,val->tflag,val->u.d); break; + case kStringTFl: rc = _var_set_driver(var,val->tflag,val->u.s); break; + case kCfgTFl: rc = _var_set_driver(var,val->tflag,val->u.cfg); break; + case kABufTFl: rc = _var_set_driver(var,val->tflag,val->u.abuf); break; + case kFBufTFl: rc = _var_set_driver(var,val->tflag,val->u.fbuf); break; + case kMBufTFl: rc = _var_set_driver(var,val->tflag,val->u.mbuf); break; + default: + rc = cwLogError(kNotImplementedRC,"The var_set() from value_t has not been implemented for the type 0x%x.",val->tflag); + } + + return rc; +} + + +cw::rc_t cw::flow::var_set( variable_t* var, bool val ) { return _var_set_driver(var,kBoolTFl,val); } +cw::rc_t cw::flow::var_set( variable_t* var, uint_t val ) { return _var_set_driver(var,kUIntTFl,val); } +cw::rc_t cw::flow::var_set( variable_t* var, int_t val ) { return _var_set_driver(var,kIntTFl,val); } +cw::rc_t cw::flow::var_set( variable_t* var, float val ) { return _var_set_driver(var,kFloatTFl,val); } +cw::rc_t cw::flow::var_set( variable_t* var, double val ) { return _var_set_driver(var,kDoubleTFl,val); } +cw::rc_t cw::flow::var_set( variable_t* var, const char* val ) { return _var_set_driver(var,kStringTFl,val); } +cw::rc_t cw::flow::var_set( variable_t* var, abuf_t* val ) { return _var_set_driver(var,kABufTFl,val); } +cw::rc_t cw::flow::var_set( variable_t* var, fbuf_t* val ) { return _var_set_driver(var,kFBufTFl,val); } +cw::rc_t cw::flow::var_set( variable_t* var, mbuf_t* val ) { return _var_set_driver(var,kMBufTFl,val); } +cw::rc_t cw::flow::var_set( variable_t* var, const object_t* val ) { return _var_set_driver(var,kCfgTFl,val); } + +cw::rc_t cw::flow::var_set( proc_t* proc, unsigned vid, unsigned chIdx, const value_t* val ) { rc_t rc = kOkRC; variable_t* var = nullptr; - if((rc = _var_find_to_set(inst, vid, chIdx, kBoolTFl, var )) == kOkRC ) - _var_set_driver( var, kBoolTFl, val ); + if((rc = var_find(proc, vid, chIdx, var )) == kOkRC ) + rc = var_set(var,val); - return rc; -} + return rc; +} -cw::rc_t cw::flow::var_set( instance_t* inst, unsigned vid, unsigned chIdx, uint_t val ) +cw::rc_t cw::flow::var_set( proc_t* proc, unsigned vid, unsigned chIdx, bool val ) { rc_t rc = kOkRC; variable_t* var = nullptr; - if((rc = _var_find_to_set(inst, vid, chIdx, kUIntTFl, var )) == kOkRC ) - _var_set_driver( var, kUIntTFl, val ); + if((rc = var_find(proc, vid, chIdx, var )) == kOkRC ) + rc = _var_set_driver( var, kBoolTFl, val ); return rc; } -cw::rc_t cw::flow::var_set( instance_t* inst, unsigned vid, unsigned chIdx, int_t val ) +cw::rc_t cw::flow::var_set( proc_t* proc, unsigned vid, unsigned chIdx, uint_t val ) { rc_t rc = kOkRC; variable_t* var = nullptr; - if((rc = _var_find_to_set(inst, vid, chIdx, kIntTFl, var )) == kOkRC ) - _var_set_driver( var, kIntTFl, val ); + if((rc = var_find(proc, vid, chIdx, var )) == kOkRC ) + rc = _var_set_driver( var, kUIntTFl, val ); return rc; } -cw::rc_t cw::flow::var_set( instance_t* inst, unsigned vid, unsigned chIdx, float val ) +cw::rc_t cw::flow::var_set( proc_t* proc, unsigned vid, unsigned chIdx, int_t val ) { rc_t rc = kOkRC; variable_t* var = nullptr; - if((rc = _var_find_to_set(inst, vid, chIdx, kFloatTFl, var )) == kOkRC ) - _var_set_driver( var, kFloatTFl, val ); + if((rc = var_find(proc, vid, chIdx, var )) == kOkRC ) + rc = _var_set_driver( var, kIntTFl, val ); return rc; } -cw::rc_t cw::flow::var_set( instance_t* inst, unsigned vid, unsigned chIdx, double val ) +cw::rc_t cw::flow::var_set( proc_t* proc, unsigned vid, unsigned chIdx, float val ) { rc_t rc = kOkRC; variable_t* var = nullptr; - if((rc = _var_find_to_set(inst, vid, chIdx, kDoubleTFl, var )) == kOkRC ) - _var_set_driver( var, kDoubleTFl, val ); + if((rc = var_find(proc, vid, chIdx, var )) == kOkRC ) + rc = _var_set_driver( var, kFloatTFl, val ); return rc; } -cw::rc_t cw::flow::var_set( instance_t* inst, unsigned vid, unsigned chIdx, const char* val ) +cw::rc_t cw::flow::var_set( proc_t* proc, unsigned vid, unsigned chIdx, double val ) { rc_t rc = kOkRC; variable_t* var = nullptr; - if((rc = _var_find_to_set(inst, vid, chIdx, kStringTFl, var )) == kOkRC ) - _var_set_driver( var, kStringTFl, val ); + if((rc = var_find(proc, vid, chIdx, var )) == kOkRC ) + rc = _var_set_driver( var, kDoubleTFl, val ); return rc; } -const cw::flow::preset_t* cw::flow::class_preset_find( class_desc_t* cd, const char* preset_label ) +cw::rc_t cw::flow::var_set( proc_t* proc, unsigned vid, unsigned chIdx, const char* val ) { - const preset_t* pr; - for(pr=cd->presetL; pr!=nullptr; pr=pr->link) - if( textCompare(pr->label,preset_label) == 0 ) - return pr; + rc_t rc = kOkRC; + variable_t* var = nullptr; - return nullptr; + if((rc = var_find(proc, vid, chIdx, var )) == kOkRC ) + rc = _var_set_driver( var, kStringTFl, val ); + + return rc; } +cw::rc_t cw::flow::var_set( proc_t* proc, unsigned vid, unsigned chIdx, const object_t* val ) +{ + rc_t rc = kOkRC; + variable_t* var = nullptr; + + if((rc = var_find(proc, vid, chIdx, var )) == kOkRC ) + rc = _var_set_driver( var, kCfgTFl, val ); + + return rc; +} + + + diff --git a/cwFlowTypes.h b/cwFlowTypes.h index eb1ffbf..221f0ad 100644 --- a/cwFlowTypes.h +++ b/cwFlowTypes.h @@ -3,20 +3,28 @@ namespace cw namespace flow { - #define kRealTFl kFloatTFl - typedef dsp::real_t real_t; - typedef dsp::sample_t sample_t; - typedef dsp::fd_real_t fd_real_t; - typedef dsp::srate_t srate_t; - typedef unsigned uint_t; - typedef int int_t; + typedef dsp::coeff_t coeff_t; + typedef dsp::sample_t sample_t; + typedef dsp::fd_sample_t fd_sample_t; + typedef dsp::srate_t srate_t; + typedef dsp::ftime_t ftime_t; + typedef unsigned uint_t; + typedef int int_t; typedef unsigned vid_t; + enum { + kBaseSfxId = 0, + kFbufVectN = 3, // count of signal vectors in fbuf (mag,phs,hz) + kAnyChIdx = kInvalidIdx, + kLocalValueN = 2, + kDefaultFramesPerCycle=64, + kDefaultSampleRate=48000 + }; + typedef struct abuf_str { - struct value_str* base; srate_t srate; // signal sample rate unsigned chN; // count of channels unsigned frameN; // count of sample frames per channel @@ -24,28 +32,27 @@ namespace cw } abuf_t; - enum { - kFbufVectN = 3, // count of signal vectors in fbuf (mag,phs,hz) - kAnyChIdx = kInvalidIdx, - kLocalValueN = 2 - }; - typedef struct fbuf_str { - struct value_str* base; srate_t srate; // signal sample rate unsigned flags; // See kXXXFbufFl unsigned chN; // count of channels unsigned* maxBinN_V; // max value that binN_V[i] is allowed to take unsigned* binN_V; // binN_V[ chN ] count of sample frames per channel unsigned* hopSmpN_V; // hopSmpN_V[ chN ] hop sample count - fd_real_t** magV; // magV[ chN ][ binN ] - fd_real_t** phsV; // phsV[ chN ][ binN ] - fd_real_t** hzV; // hzV[ chN ][ binN ] + fd_sample_t** magV; // magV[ chN ][ binN ] + fd_sample_t** phsV; // phsV[ chN ][ binN ] + fd_sample_t** hzV; // hzV[ chN ][ binN ] bool* readyFlV; // readyFlV[chN] true if this channel is ready to be processed (used to sync. fbuf rate to abuf rate) - fd_real_t* buf; // memory used by this buffer (or NULL if magV,phsV,hzV point are proxied to another buffer) + fd_sample_t* buf; // memory used by this buffer (or NULL if magV,phsV,hzV point are proxied to another buffer) } fbuf_t; + typedef struct mbuf_str + { + const midi::ch_msg_t* msgA; + unsigned msgN; + } mbuf_t; + enum { kInvalidTFl = 0x00000000, @@ -58,17 +65,23 @@ namespace cw kBoolMtxTFl = 0x00000020, kUIntMtxTFl = 0x00000040, kIntMtxTFl = 0x00000080, - kRealMtxTFl = 0x00000100, - kFloatMtxTFl = 0x00000200, - kDoubleMtxTFl= 0x00000400, + kFloatMtxTFl = 0x00000100, + kDoubleMtxTFl= 0x00000200, - kABufTFl = 0x00000800, - kFBufTFl = 0x00001000, + kABufTFl = 0x00000400, + kFBufTFl = 0x00000800, + kMBufTFl = 0x00001000, kStringTFl = 0x00002000, kTimeTFl = 0x00004000, + kCfgTFl = 0x00008000, - kTypeMask = 0x00007fff, + kTypeMask = 0x0000ffff, + kRuntimeTFl = 0x80000000, + + kNumericTFl = kBoolTFl | kUIntTFl | kIntTFl | kFloatTFl | kDoubleTFl, + kMtxTFl = kBoolMtxTFl | kUIntMtxTFl | kIntMtxTFl | kFloatMtxTFl | kDoubleMtxTFl, + kAllTFl = kTypeMask }; typedef struct mtx_str @@ -76,7 +89,6 @@ namespace cw union { struct mtx::mtx_str< unsigned >* u; struct mtx::mtx_str< int >* i; - struct mtx::mtx_str< real_t >* r; struct mtx::mtx_str< float >* f; struct mtx::mtx_str< double >* d; } u; @@ -84,21 +96,24 @@ namespace cw typedef struct value_str { - unsigned flags; + unsigned tflag; + union { - bool b; - uint_t u; - int_t i; - float f; - double d; - - mtx_t* mtx; + bool b; + uint_t u; + int_t i; + float f; + double d; - abuf_t* abuf; - fbuf_t* fbuf; + mtx_t* mtx; + abuf_t* abuf; + fbuf_t* fbuf; + mbuf_t* mbuf; - char* s; - char* fname; + char* s; + + const object_t* cfg; + void* p; } u; @@ -106,20 +121,23 @@ namespace cw } value_t; - - inline bool is_numeric( const value_t* v ) { return cwIsFlag(v->flags,kBoolTFl|kUIntTFl|kIntTFl|kFloatTFl|kDoubleTFl); } - inline bool is_matrix( const value_t* v ) { return cwIsFlag(v->flags,kBoolMtxTFl|kUIntMtxTFl|kIntMtxTFl|kFloatMtxTFl|kDoubleMtxTFl); } - struct instance_str; + struct proc_str; struct variable_str; - typedef rc_t (*member_func_t)( struct instance_str* ctx ); - typedef rc_t (*member_value_func_t)( struct instance_str* ctx, struct variable_str* var ); + typedef rc_t (*member_func_t)( struct proc_str* ctx ); + typedef rc_t (*member_value_func_t)( struct proc_str* ctx, struct variable_str* var ); + + // var_desc_t attribute flags enum { - kSrcVarFl = 0x01, - kSrcOptVarFl = 0x02 - + kInvalidVarDescFl = 0x00, + kSrcVarDescFl = 0x01, + kSrcOptVarDescFl = 0x02, + kNoSrcVarDescFl = 0x04, + kInitVarDescFl = 0x08, + kMultVarDescFl = 0x10, + kSubnetOutVarDescFl = 0x20 }; typedef struct class_members_str @@ -139,54 +157,84 @@ namespace cw unsigned type; // Value type id (e.g. kBoolTFl, kIntTFl, ...) unsigned flags; // Attributes for this var. (e.g. kSrcVarFl ) const char* docText; // User help string for this var. + + char* proxyProcLabel; + char* proxyVarLabel; + struct var_desc_str* link; // class_desc->varDescL list link } var_desc_t; - typedef struct preset_str + typedef struct class_preset_str { - const char* label; - const object_t* cfg; - struct preset_str* link; - } preset_t; + const char* label; + const object_t* cfg; + struct class_preset_str* link; + } class_preset_t; typedef struct class_desc_str { - const object_t* cfg; // - const char* label; // class label; - var_desc_t* varDescL; // varDescA[varDescN] value description list - preset_t* presetL; // presetA[ presetN ] - class_members_t* members; // member functions for this class + const object_t* cfg; // class cfg + const char* label; // class label; + var_desc_t* varDescL; // varDescL variable description linked on var_desc_t.link + class_preset_t* presetL; // presetA[ presetN ] + class_members_t* members; // member functions for this class + unsigned polyLimitN; // max. poly copies of this class per network_t or 0 if no limit } class_desc_t; + enum { + kInvalidVarFl = 0x00, + kLogVarFl = 0x01, + kProxiedVarFl = 0x02, + kProxiedOutVarFl = 0x04 + }; // Note: The concatenation of 'vid' and 'chIdx' should form a unique identifier among all variables // on a given 'instance'. typedef struct variable_str { - struct instance_str* inst; // pointer to this variables instance + struct proc_str* proc; // pointer to this variables instance + char* label; // this variables label - unsigned vid; // this variables numeric id ( cat(vid,chIdx) forms a unique variable identifier on this 'inst' - var_desc_t* varDesc; // the variable description for this variable - value_t local_value[ kLocalValueN ]; // the local value instance (actual value if this is not a 'src' variable) - unsigned local_value_idx; // local_value[] is double buffered to allow the cur value of the buf[] to be held while the next value is validated (see _var_set_template()) - value_t* value; // pointer to the value associated with this variable + unsigned label_sfx_id; // the label suffix id of this variable or kBaseSfxId if this has no suffix + + unsigned vid; // this variables numeric id ( cat(vid,chIdx) forms a unique variable identifier on this 'proc' unsigned chIdx; // channel index - struct variable_str* src_var; // pointer to this input variables source link (or null if it uses the local_value) - struct variable_str* var_link; // instance.varL link list - struct variable_str* connect_link; // list of outgoing connections + unsigned flags; // kLogVarFl + unsigned type; // This is the value type as established when the var is initialized - it never changes for the life of the var. + + var_desc_t* classVarDesc; // pointer to this variables class var desc + var_desc_t* localVarDesc; // pointer to this variables local var desc - if it doesn't match classVarDesc. + var_desc_t* varDesc; // the effective variable description for this variable (set to classVarDesc or localVarDesc) + + value_t local_value[ kLocalValueN ]; // the local value instance (actual value if this is not a 'src' variable) + unsigned local_value_idx; // local_value[] is double buffered to allow the cur value of the buf[] to be held while the next value is validated (see _var_set_template()) + struct variable_str* src_var; // pointer to this input variables source link (or null if it uses the local_value) + value_t* value; // pointer to the value associated with this variable + + struct variable_str* var_link; // instance.varL list link struct variable_str* ch_link; // list of channels that share this variable (rooted on 'any' channel - in order by channel number) + + struct variable_str* dst_head; // Pointer to list of out-going connections (null on var's that do not have out-going connections) + struct variable_str* dst_tail; // + struct variable_str* dst_link; // Link used by dst_head list. + } variable_t; + + struct network_str; - typedef struct instance_str + typedef struct proc_str { - struct flow_str* ctx; // global system context + struct flow_str* ctx; // global system context + struct network_str* net; // network which owns this proc class_desc_t* class_desc; // - const char* label; // instance label - const object_t* inst_cfg; // instance configuration + char* label; // instance label + unsigned label_sfx_id; // label suffix id (set to kBaseSfxId (0) unless poly is non-null) + const object_t* proc_cfg; // instance configuration + const char* arg_label; // optional args label const object_t* arg_cfg; // optional args configuration @@ -198,39 +246,130 @@ namespace cw unsigned varMapIdN; // max 'vid' among all variables on this instance unsigned varMapN; // varMapN = varMapIdN * varMapChN variable_t** varMapA; // varMapA[ varMapN ] = allows fast lookup from ('vid','chIdx) to variable + + struct network_str* internal_net; - struct instance_str* link; - } instance_t; + } proc_t; + + // preset_value_t holds a preset value and the proc/var to which it will be applied. + typedef struct preset_value_str + { + proc_t* proc; // proc target for this preset value + variable_t* var; // var target for this preset value + value_t value; // Preset value. + unsigned pairTblIdx; // Index into the preset pair table for this preset value + struct preset_value_str* link; + } preset_value_t; + + typedef struct preset_value_list_str + { + preset_value_t* value_head; // List of preset_value_t for this preset. + preset_value_t* value_tail; // Last preset value in the list. + } preset_value_list_t; + + struct network_preset_str; + + typedef struct dual_preset_str + { + const struct network_preset_str* pri; + const struct network_preset_str* sec; + double coeff; + } dual_preset_t; + + typedef enum { + kPresetVListTId, + kPresetDualTId + } preset_type_id_t; + typedef struct network_preset_str + { + const char* label; // Preset label + preset_type_id_t tid; + + union { + preset_value_list_t vlist; + dual_preset_t dual; + } u; + } network_preset_t; + // Preset-pair record used to apply dual presets. + typedef struct network_preset_pair_str + { + const proc_t* proc; // + const variable_t* var; // + unsigned chIdx; // + unsigned chN; // + const value_t* value; // + } network_preset_pair_t; + + typedef struct network_str + { + const object_t* procsCfg; // network proc list + const object_t* presetsCfg; // presets designed for this network + unsigned poly_cnt; // count of duplicated networks in the list + + struct proc_str** proc_array; + + unsigned proc_arrayAllocN; + unsigned proc_arrayN; + + network_preset_t* presetA; + unsigned presetN; + + // Preset pair table used by network_apply_dual_preset() + network_preset_pair_t* preset_pairA; + unsigned preset_pairN; + + } network_t; + + typedef struct flow_str { - const object_t* networkCfg; // complete cfg used to create this network + const object_t* flowCfg; // complete cfg used to create this flow + + unsigned framesPerCycle; // sample frames per cycle (64) + srate_t sample_rate; // default sample rate (48000.0) + unsigned maxCycleCount; // count of cycles to run on flow::exec() or 0 if there is no limit. + const char* init_net_preset_label;// network initialization preset label or nullptr if there is no net. init. preset + + bool isInRuntimeFl; // Set when compile-time is complete + + + unsigned cycleIndex; // Incremented with each processing cycle - const object_t* presetCfg; // presets designed for this network - unsigned framesPerCycle; // sample frames per cycle (64) bool multiPriPresetProbFl; // If set then probability is used to choose presets on multi-preset application bool multiSecPresetProbFl; // - bool multiPresetInterpFl; // If set then interpolation is applied between two selectedd presets on multi-preset application - unsigned cycleIndex; // Incremented with each processing cycle - unsigned maxCycleCount; // count of cycles to run on flow::exec() or 0 if there is no limit. + bool multiPresetInterpFl; // If set then interpolation is applied between two selectedd presets on multi-preset application - class_desc_t* classDescA; // - unsigned classDescN; // + class_desc_t* classDescA; // + unsigned classDescN; // - external_device_t* deviceA; // deviceA[ deviceN ] external device description array - unsigned deviceN; // + class_desc_t* subnetDescA; // + unsigned subnetDescN; // - struct instance_str* network_head; // first instance - struct instance_str* network_tail; // last insance + external_device_t* deviceA; // deviceA[ deviceN ] external device description array + unsigned deviceN; // + + const char* proj_dir; // default input/output directory + + network_t net; + } flow_t; //------------------------------------------------------------------------------------------------------------------------ // // Value Only // + + inline void set_null( value_t& v, unsigned tflag ) { v.tflag=tflag; v.u.p=nullptr; } + inline bool is_numeric( const value_t* v ) { return cwIsFlag(v->tflag,kNumericTFl); } + inline bool is_matrix( const value_t* v ) { return cwIsFlag(v->tflag,kMtxTFl); } + + // if all of the src flags are set in the dst flags then the two types are convertable. + inline bool can_convert( unsigned src_tflag, unsigned dst_tflag ) { return (src_tflag&dst_tflag)==src_tflag; } + abuf_t* abuf_create( srate_t srate, unsigned chN, unsigned frameN ); void abuf_destroy( abuf_t*& buf ); @@ -238,46 +377,82 @@ namespace cw rc_t abuf_set_channel( abuf_t* buf, unsigned chIdx, const sample_t* v, unsigned vN ); const sample_t* abuf_get_channel( abuf_t* buf, unsigned chIdx ); - fbuf_t* fbuf_create( srate_t srate, unsigned chN, const unsigned* maxBinN_V, const unsigned* binN_V, const unsigned* hopSmpN_V, const fd_real_t** magV=nullptr, const fd_real_t** phsV=nullptr, const fd_real_t** hzV=nullptr ); - fbuf_t* fbuf_create( srate_t srate, unsigned chN, unsigned maxBinN, unsigned binN, unsigned hopSmpN, const fd_real_t** magV=nullptr, const fd_real_t** phsV=nullptr, const fd_real_t** hzV=nullptr ); + fbuf_t* fbuf_create( srate_t srate, unsigned chN, const unsigned* maxBinN_V, const unsigned* binN_V, const unsigned* hopSmpN_V, const fd_sample_t** magV=nullptr, const fd_sample_t** phsV=nullptr, const fd_sample_t** hzV=nullptr ); + fbuf_t* fbuf_create( srate_t srate, unsigned chN, unsigned maxBinN, unsigned binN, unsigned hopSmpN, const fd_sample_t** magV=nullptr, const fd_sample_t** phsV=nullptr, const fd_sample_t** hzV=nullptr ); void fbuf_destroy( fbuf_t*& buf ); fbuf_t* fbuf_duplicate( const fbuf_t* src ); + + mbuf_t* mbuf_create( const midi::ch_msg_t* msgA=nullptr, unsigned msgN=0 ); + void mbuf_destroy( mbuf_t*& buf ); + mbuf_t* mbuf_duplicate( const mbuf_t* src ); - inline bool value_is_abuf( const value_t* v ) { return v->flags & kABufTFl; } - inline bool value_is_fbuf( const value_t* v ) { return v->flags & kFBufTFl; } + inline bool value_is_abuf( const value_t* v ) { return v->tflag & kABufTFl; } + inline bool value_is_fbuf( const value_t* v ) { return v->tflag & kFBufTFl; } unsigned value_type_label_to_flag( const char* type_desc ); + const char* value_type_flag_to_label( unsigned flag ); + void value_print( const value_t* value, bool info_fl=false); + //------------------------------------------------------------------------------------------------------------------------ // // Class and Variable Description // - - var_desc_t* var_desc_find( class_desc_t* cd, const char* var_label ); - rc_t var_desc_find( class_desc_t* cd, const char* label, var_desc_t*& vdRef ); - class_desc_t* class_desc_find( flow_t* p, const char* class_desc_label ); + var_desc_t* var_desc_create( const char* label, const object_t* value_cfg ); + void var_desc_destroy( var_desc_t* var_desc ); - void class_dict_print( flow_t* p ); + unsigned var_desc_attr_label_to_flag( const char* attr_label ); + const char* var_desc_flag_to_attribute( unsigned flag ); + const idLabelPair_t* var_desc_flag_array( unsigned& array_cnt_ref ); + + void class_desc_destroy( class_desc_t* class_desc); + class_desc_t* class_desc_find( flow_t* p, const char* class_desc_label ); + + var_desc_t* var_desc_find( class_desc_t* cd, const char* var_label ); + const var_desc_t* var_desc_find( const class_desc_t* cd, const char* var_label ); + rc_t var_desc_find( class_desc_t* cd, const char* var_label, var_desc_t*& vdRef ); + + const class_preset_t* class_preset_find( const class_desc_t* cd, const char* preset_label ); + + void class_dict_print( flow_t* p ); //------------------------------------------------------------------------------------------------------------------------ // // Network // - void network_print( flow_t* p ); + void network_print(const network_t& net ); + const network_preset_t* network_preset_from_label( const network_t& net, const char* preset_label ); + + unsigned proc_mult_count( const network_t& net, const char* proc_label ); + + rc_t proc_mult_sfx_id_array( const network_t& net, const char* proc_label, unsigned* idA, unsigned idAllocN, unsigned& idN_ref ); + + //------------------------------------------------------------------------------------------------------------------------ // - // Instance + // Proc // + + void proc_destroy( proc_t* proc ); + rc_t proc_validate( proc_t* proc ); - instance_t* instance_find( flow_t* p, const char* inst_label ); - rc_t instance_find( flow_t* p, const char* inst_label, instance_t*& instPtrRef ); - external_device_t* external_device_find( flow_t* p, const char* device_label, unsigned typeId, unsigned inOrOutFl ); + proc_t* proc_find( network_t& net, const char* proc_label, unsigned sfx_id ); + rc_t proc_find( network_t& net, const char* proc_label, unsigned sfx_id, proc_t*& procPtrRef ); - void instance_print( instance_t* inst ); + external_device_t* external_device_find( flow_t* p, const char* device_label, unsigned typeId, unsigned inOrOutFl, const char* midiPortLabel=nullptr ); + void proc_print( proc_t* proc ); + + // Count of all var instances on this proc. This is a count of the length of proc->varL. + unsigned proc_var_count( proc_t* proc ); + + // If fname has a '$' prefix then the system project directory is prepended to it. + // If fname has a '~' then the users home directory is prepended to it. + // The returned string must be release with a call to mem::free(). + char* proc_expand_filename( const proc_t* proc, const char* fname ); //------------------------------------------------------------------------------------------------------------------------ @@ -286,41 +461,68 @@ namespace cw // // Create a variable but do not assign it a value. Return a pointer to the new variable. - // Note: `value_cfg` is optional. Set it to NULL to ignore - rc_t var_create( instance_t* inst, const char* var_label, unsigned vid, unsigned chIdx, const object_t* value_cfg, variable_t*& varRef ); + // Notes: + // 1) `value_cfg` is optional. Set it to NULL to ignore + // 2) If `altTypeFl` is not set to kInvalidTFl then the var is assigned this type. + rc_t var_create( proc_t* proc, const char* var_label, unsigned sfx_id, unsigned vid, unsigned chIdx, const object_t* value_cfg, unsigned altTypeFlag, variable_t*& varRef ); + void var_destroy( variable_t* var ); // Channelizing creates a new var record with an explicit channel index to replace the // automatically generated variable whose channel index is set to 'all'. - rc_t var_channelize( instance_t* inst, const char* var_label, unsigned chIdx, const object_t* value_cfg, unsigned vid, variable_t*& varRef ); + rc_t var_channelize( proc_t* proc, const char* var_label, unsigned sfx_id, unsigned chIdx, const object_t* value_cfg, unsigned vid, variable_t*& varRef ); + + // Wrapper around call to var->proc->members->value() + rc_t var_call_custom_value_func( variable_t* var ); + + // Sets and get the var->flags field + unsigned var_flags( proc_t* proc, unsigned chIdx, const char* var_label, unsigned sfx_id, unsigned& flags_ref ); + rc_t var_set_flags( proc_t* proc, unsigned chIdx, const char* var_label, unsigned sfx_id, unsigned flags ); + rc_t var_clr_flags( proc_t* proc, unsigned chIdx, const char* var_label, unsigned sfx_id, unsigned flags ); // `value_cfg` is optional. Set it to NULL to ignore - rc_t var_register( instance_t* inst, const char* var_label, unsigned vid, unsigned chIdx, const object_t* value_cfg, variable_t*& varRef ); + rc_t var_register( proc_t* proc, const char* var_label, unsigned sfx_id, unsigned vid, unsigned chIdx, const object_t* value_cfg, variable_t*& varRef ); - // Returns true if this var is connected to an external proc variable - bool is_connected_to_external_proc( const variable_t* var ); + // Returns true if this var is connected to a source proc variable + bool is_connected_to_source( const variable_t* var ); + // Return true if this var is acting as a source for another var. + bool is_a_source_var( const variable_t* var ); + + // Connect in_var to src_var. + void var_connect( variable_t* src_var, variable_t* in_var ); + + // Disconnect an in_var from it's source + void var_disconnect( variable_t* in_var ); + + + // Get the count of 'mult' vars associated with this var label. + unsigned var_mult_count( proc_t* proc, const char* var_label ); + + // Get all the label-sfx-id's associated with a give var label + rc_t var_mult_sfx_id_array( proc_t* proc, const char* var_label, unsigned* idA, unsigned idAllocN, unsigned& idN_ref ); + //----------------- // // var_register // - inline rc_t _var_reg(cw::flow::instance_t*, unsigned int ) { return kOkRC; } + inline rc_t _var_reg(cw::flow::proc_t*, unsigned int ) { return kOkRC; } template< typename T0, typename T1, typename... ARGS > - rc_t _var_reg( instance_t* inst, unsigned chIdx, T0 vid, T1 var_label, ARGS&&... args ) + rc_t _var_reg( proc_t* proc, unsigned chIdx, T0 vid, T1 var_label, unsigned sfx_id, ARGS&&... args ) { rc_t rc; variable_t* dummy = nullptr; - if((rc = var_register( inst, var_label, vid, chIdx, nullptr, dummy )) == kOkRC ) - if((rc = _var_reg( inst, chIdx, std::forward(args)...)) != kOkRC ) + if((rc = var_register( proc, var_label, sfx_id, vid, chIdx, nullptr, dummy )) == kOkRC ) + if((rc = _var_reg( proc, chIdx, std::forward(args)...)) != kOkRC ) return rc; return rc; } // Call var_register() on a list of variables. template< typename... ARGS > - rc_t var_register( instance_t* inst, unsigned chIdx, unsigned vid, const char* var_label, ARGS&&... args ) - { return _var_reg( inst, chIdx, vid, var_label, std::forward(args)...); } + rc_t var_register( proc_t* proc, unsigned chIdx, unsigned vid, const char* var_label, unsigned sfx_id, ARGS&&... args ) + { return _var_reg( proc, chIdx, vid, var_label, sfx_id, std::forward(args)...); } @@ -329,28 +531,28 @@ namespace cw // var_register_and_get // - inline rc_t _var_register_and_get(cw::flow::instance_t*, unsigned int ) { return kOkRC; } + inline rc_t _var_register_and_get(cw::flow::proc_t*, unsigned int ) { return kOkRC; } template< typename T> - rc_t var_register_and_get( instance_t* inst, const char* var_label, unsigned vid, unsigned chIdx, T& valRef ) + rc_t var_register_and_get( proc_t* proc, const char* var_label, unsigned sfx_id, unsigned vid, unsigned chIdx, T& valRef ) { rc_t rc; variable_t* var; - if((rc = var_register(inst,var_label,vid,chIdx,nullptr,var)) == kOkRC ) + if((rc = var_register(proc,var_label,sfx_id,vid,chIdx,nullptr,var)) == kOkRC ) rc = var_get(var,valRef); return rc; } - inline rc_t _var_reg_and_get(cw::flow::instance_t*, unsigned int ) { return kOkRC; } + inline rc_t _var_reg_and_get(cw::flow::proc_t*, unsigned int ) { return kOkRC; } template< typename T0, typename T1, typename T2, typename... ARGS > - rc_t _var_reg_and_get( instance_t* inst, unsigned chIdx, T0 vid, T1 var_label, T2& valRef, ARGS&&... args ) + rc_t _var_reg_and_get( proc_t* proc, unsigned chIdx, T0 vid, T1 var_label, unsigned sfx_id, T2& valRef, ARGS&&... args ) { rc_t rc; - if((rc = var_register_and_get( inst, var_label, vid, chIdx, valRef )) == kOkRC ) - if((rc = _var_reg_and_get( inst, chIdx, std::forward(args)...)) != kOkRC ) + if((rc = var_register_and_get( proc, var_label, sfx_id, vid, chIdx, valRef )) == kOkRC ) + if((rc = _var_reg_and_get( proc, chIdx, std::forward(args)...)) != kOkRC ) return rc; return rc; @@ -358,8 +560,8 @@ namespace cw // Call var_register_and_get() on a list of variables. template< typename... ARGS > - rc_t var_register_and_get( instance_t* inst, unsigned chIdx, unsigned vid, const char* var_label, ARGS&&... args ) - { return _var_reg_and_get( inst, chIdx, vid, var_label, std::forward(args)...); } + rc_t var_register_and_get( proc_t* proc, unsigned chIdx, unsigned vid, const char* var_label, unsigned sfx_id, ARGS&&... args ) + { return _var_reg_and_get( proc, chIdx, vid, var_label, sfx_id, std::forward(args)...); } @@ -371,25 +573,27 @@ namespace cw // var_register_and_set(). If the variable has not yet been created then it is created and assigned a value. // If the variable has already been created then 'vid' and the value are updated. // (Note that abuf and fbuf values are not changed by this function only the 'vid' is updated.) - rc_t var_register_and_set( instance_t* inst, const char* label, unsigned vid, unsigned chIdx, variable_t*& varRef ); + rc_t var_register_and_set( proc_t* proc, const char* label, unsigned sfx_id, unsigned vid, unsigned chIdx, variable_t*& varRef ); - rc_t var_register_and_set( instance_t* inst, const char* var_label, unsigned vid, unsigned chIdx, srate_t srate, unsigned chN, unsigned frameN ); - rc_t var_register_and_set( instance_t* inst, const char* var_label, unsigned vid, unsigned chIdx, srate_t srate, unsigned chN, const unsigned* maxBinN_V, const unsigned* binN_V, const unsigned* hopSmpN_V, const fd_real_t** magV=nullptr, const fd_real_t** phsV=nullptr, const fd_real_t** hzV=nullptr ); - rc_t var_register_and_set( instance_t* inst, const char* var_label, unsigned vid, unsigned chIdx, srate_t srate, unsigned chN, unsigned maxBinN, unsigned binN, unsigned hopSmpN, const fd_real_t** magV=nullptr, const fd_real_t** phsV=nullptr, const fd_real_t** hzV=nullptr ); + rc_t var_register_and_set( proc_t* proc, const char* var_label, unsigned sfx_id, unsigned vid, unsigned chIdx, srate_t srate, unsigned chN, unsigned frameN ); + rc_t var_register_and_set( proc_t* proc, const char* var_label, unsigned sfx_id, unsigned vid, unsigned chIdx, midi::ch_msg_t* midiA, unsigned midiN ); + rc_t var_register_and_set( proc_t* proc, const char* var_label, unsigned sfx_id, unsigned vid, unsigned chIdx, srate_t srate, unsigned chN, const unsigned* maxBinN_V, const unsigned* binN_V, const unsigned* hopSmpN_V, const fd_sample_t** magV=nullptr, const fd_sample_t** phsV=nullptr, const fd_sample_t** hzV=nullptr ); + rc_t var_register_and_set( proc_t* proc, const char* var_label, unsigned sfx_id, unsigned vid, unsigned chIdx, srate_t srate, unsigned chN, unsigned maxBinN, unsigned binN, unsigned hopSmpN, const fd_sample_t** magV=nullptr, const fd_sample_t** phsV=nullptr, const fd_sample_t** hzV=nullptr ); - inline rc_t _var_register_and_set(cw::flow::instance_t*, unsigned int ) { return kOkRC; } + inline rc_t _var_register_and_set(cw::flow::proc_t*, unsigned int ) { return kOkRC; } template< typename T0, typename T1, typename T2, typename... ARGS > - rc_t _var_register_and_set( instance_t* inst, unsigned chIdx, T0 vid, T1 var_label, T2 val, ARGS&&... args ) + rc_t _var_register_and_set( proc_t* proc, unsigned chIdx, T0 vid, T1 var_label, unsigned sfx_id, T2 val, ARGS&&... args ) { rc_t rc; variable_t* var = nullptr; - if((rc = var_register_and_set( inst, var_label, vid, chIdx, var)) == kOkRC ) + if((rc = var_register_and_set( proc, var_label, sfx_id, vid, chIdx, var)) == kOkRC ) { - var_set( inst, vid, chIdx, val ); + if((rc = var_set( proc, vid, chIdx, val )) != kOkRC ) + return rc; - if((rc = _var_register_and_set( inst, chIdx, std::forward(args)...)) != kOkRC ) + if((rc = _var_register_and_set( proc, chIdx, std::forward(args)...)) != kOkRC ) return rc; } @@ -398,66 +602,97 @@ namespace cw // Call var_register_and_set() on a list of variables. template< typename... ARGS > - rc_t var_register_and_set( instance_t* inst, unsigned chIdx, unsigned vid, const char* var_label, ARGS&&... args ) - { return _var_register_and_set( inst, chIdx, vid, var_label, std::forward(args)...); } + rc_t var_register_and_set( proc_t* proc, unsigned chIdx, unsigned vid, const char* var_label, unsigned sfx_id, ARGS&&... args ) + { return _var_register_and_set( proc, chIdx, vid, var_label, sfx_id, std::forward(args)...); } void _var_destroy( variable_t* var ); - bool var_exists( instance_t* inst, const char* label, unsigned chIdx ); - bool var_has_value( instance_t* inst, const char* label, unsigned chIdx ); + bool var_exists( proc_t* proc, const char* label, unsigned sfx_id, unsigned chIdx ); + bool var_has_value( proc_t* proc, const char* label, unsigned sfx_id, unsigned chIdx ); + bool var_is_a_source( proc_t* proc, const char* label, unsigned sfx_id, unsigned chIdx ); + bool var_is_a_source( proc_t* proc, unsigned vid, unsigned chIdx ); - rc_t var_find( instance_t* inst, const char* var_label, unsigned chIdx, const variable_t*& varRef ); - rc_t var_find( instance_t* inst, const char* var_label, unsigned chIdx, variable_t*& varRef ); - rc_t var_find( instance_t* inst, unsigned vid, unsigned chIdx, variable_t*& varRef ); + rc_t var_find( proc_t* proc, const char* var_label, unsigned sfx_id, unsigned chIdx, const variable_t*& varRef ); + rc_t var_find( proc_t* proc, const char* var_label, unsigned sfx_id, unsigned chIdx, variable_t*& varRef ); + rc_t var_find( proc_t* proc, unsigned vid, unsigned chIdx, variable_t*& varRef ); + // Count of numbered channels - does not count the kAnyChIdx variable instance. - rc_t var_channel_count( instance_t* inst, const char* label, unsigned& chCntRef ); + rc_t var_channel_count( proc_t* proc, const char* label, unsigned sfx_idx, unsigned& chCntRef ); rc_t var_channel_count( const variable_t* var, unsigned& chCntRef ); + + rc_t cfg_to_value( const object_t* cfg, value_t& value_ref ); + + + // + // var_get() coerces the value of the variable to the type of the returned value. + // - - rc_t var_get( const variable_t* var, bool& valRef ); - rc_t var_get( const variable_t* var, uint_t& valRef ); - rc_t var_get( const variable_t* var, int_t& valRef ); - rc_t var_get( const variable_t* var, float& valRef ); - rc_t var_get( const variable_t* var, double& valRef ); - rc_t var_get( const variable_t* var, const char*& valRef ); - rc_t var_get( const variable_t* var, const abuf_t*& valRef ); - rc_t var_get( variable_t* var, abuf_t*& valRef ); - rc_t var_get( const variable_t* var, const fbuf_t*& valRef ); - rc_t var_get( variable_t* var, fbuf_t*& valRef ); + rc_t var_get( const variable_t* var, bool& valRef ); + rc_t var_get( const variable_t* var, uint_t& valRef ); + rc_t var_get( const variable_t* var, int_t& valRef ); + rc_t var_get( const variable_t* var, float& valRef ); + rc_t var_get( const variable_t* var, double& valRef ); + rc_t var_get( const variable_t* var, const char*& valRef ); + rc_t var_get( const variable_t* var, const abuf_t*& valRef ); + rc_t var_get( variable_t* var, abuf_t*& valRef ); + rc_t var_get( const variable_t* var, const fbuf_t*& valRef ); + rc_t var_get( variable_t* var, fbuf_t*& valRef ); + rc_t var_get( const variable_t* var, const mbuf_t*& valRef ); + rc_t var_get( variable_t* var, mbuf_t*& valRef ); + rc_t var_get( const variable_t* var, const object_t*& valRef ); template< typename T> - rc_t var_get( instance_t* inst, unsigned vid, unsigned chIdx, T& valRef) + rc_t var_get( proc_t* proc, unsigned vid, unsigned chIdx, T& valRef) { rc_t rc = kOkRC; variable_t* var = nullptr; - if((rc = var_find(inst, vid, chIdx, var )) == kOkRC ) + if((rc = var_find(proc, vid, chIdx, var )) == kOkRC ) rc = var_get(var,valRef); return rc; } template< typename T > - T val_get( instance_t* inst, unsigned vid, unsigned chIdx ) + T val_get( proc_t* proc, unsigned vid, unsigned chIdx ) { T value; - var_get(inst,vid,chIdx,value); + var_get(proc,vid,chIdx,value); return value; } - rc_t var_set( instance_t* inst, unsigned vid, unsigned chIdx, bool val ); - rc_t var_set( instance_t* inst, unsigned vid, unsigned chIdx, uint_t val ); - rc_t var_set( instance_t* inst, unsigned vid, unsigned chIdx, int_t val ); - rc_t var_set( instance_t* inst, unsigned vid, unsigned chIdx, float val ); - rc_t var_set( instance_t* inst, unsigned vid, unsigned chIdx, double val ); - rc_t var_set( instance_t* inst, unsigned vid, unsigned chIdx, const char* val ); - rc_t var_set( instance_t* inst, unsigned vid, unsigned chIdx, abuf_t* val ); - rc_t var_set( instance_t* inst, unsigned vid, unsigned chIdx, fbuf_t* val ); + // + // var_set() coerces the incoming value to the type of the variable (var->type) + // + + rc_t var_set_from_cfg( variable_t* var, const object_t* cfg_value ); + + rc_t var_set( variable_t* var, const value_t* val ); + rc_t var_set( variable_t* var, bool val ); + rc_t var_set( variable_t* var, uint_t val ); + rc_t var_set( variable_t* var, int_t val ); + rc_t var_set( variable_t* var, float val ); + rc_t var_set( variable_t* var, double val ); + rc_t var_set( variable_t* var, const char* val ); + rc_t var_set( variable_t* var, abuf_t* val ); + rc_t var_set( variable_t* var, fbuf_t* val ); + rc_t var_set( variable_t* var, mbuf_t* val ); + rc_t var_set( variable_t* var, const object_t* val ); + + rc_t var_set( proc_t* proc, unsigned vid, unsigned chIdx, const value_t* val ); + rc_t var_set( proc_t* proc, unsigned vid, unsigned chIdx, bool val ); + rc_t var_set( proc_t* proc, unsigned vid, unsigned chIdx, uint_t val ); + rc_t var_set( proc_t* proc, unsigned vid, unsigned chIdx, int_t val ); + rc_t var_set( proc_t* proc, unsigned vid, unsigned chIdx, float val ); + rc_t var_set( proc_t* proc, unsigned vid, unsigned chIdx, double val ); + rc_t var_set( proc_t* proc, unsigned vid, unsigned chIdx, const char* val ); + rc_t var_set( proc_t* proc, unsigned vid, unsigned chIdx, abuf_t* val ); + rc_t var_set( proc_t* proc, unsigned vid, unsigned chIdx, fbuf_t* val ); + rc_t var_set( proc_t* proc, unsigned vid, unsigned chIdx, const object_t* val ); - const preset_t* class_preset_find( class_desc_t* cd, const char* preset_label ); } } diff --git a/cwGutimReg.cpp b/cwGutimReg.cpp index 32e746c..dc28247 100644 --- a/cwGutimReg.cpp +++ b/cwGutimReg.cpp @@ -1,6 +1,7 @@ #include "cwCommon.h" #include "cwLog.h" #include "cwCommonImpl.h" +#include "cwTest.h" #include "cwMem.h" #include "cwText.h" #include "cwObject.h" diff --git a/cwIo.cpp b/cwIo.cpp index b9ff36a..1b374b0 100644 --- a/cwIo.cpp +++ b/cwIo.cpp @@ -1,6 +1,7 @@ #include "cwCommon.h" #include "cwLog.h" #include "cwCommonImpl.h" +#include "cwTest.h" #include "cwMem.h" #include "cwObject.h" #include "cwText.h" @@ -611,7 +612,7 @@ namespace cw // // MIDI // - void _midiCallback( const midi::packet_t* pktArray, unsigned pktCnt ) + void _midiCallback( void* cbArg, const midi::packet_t* pktArray, unsigned pktCnt ) { unsigned i; for(i=0; i(pkt->cbArg); + io_t* p = reinterpret_cast(cbArg); rc_t rc = kOkRC; @@ -2114,10 +2115,12 @@ namespace cw rc = cwLogError(rc,"Audio device configuration failed."); goto errLabel; } - - audio::device::report( p->audioH ); errLabel: + + if( rc != kOkRC && p->audioH.isValid() ) + audio::device::report( p->audioH ); + return rc; } @@ -2439,7 +2442,8 @@ cw::rc_t cw::io::stop( handle_t h ) return rc; } -cw::rc_t cw::io::exec( handle_t h, void* execCbArg ) + +cw::rc_t cw::io::exec( handle_t h, unsigned timeOutMs, void* execCbArg ) { rc_t rc = kOkRC; io_t* p = _handleToPtr(h); @@ -2447,8 +2451,9 @@ cw::rc_t cw::io::exec( handle_t h, void* execCbArg ) if( p->wsUiH.isValid() ) { ui::flushCache( ui::ws::uiHandle( p->wsUiH )); + // Note this call blocks on the websocket handle: See cwUi.h:ws:exec() - rc = ui::ws::exec( p->wsUiH ); + rc = ui::ws::exec( p->wsUiH, timeOutMs ); } time::get(p->t0); @@ -2490,14 +2495,23 @@ void cw::io::report( handle_t h ) } for(unsigned i=0; iaudioH ); + midi::device::report(p->midiH); +} + + void cw::io::realTimeReport( handle_t h ) { io_t* p = _handleToPtr(h); audio::device::realTimeReport(p->audioH); - + uiRealTimeReport(h); } @@ -2747,6 +2761,24 @@ cw::rc_t cw::io::midiDeviceSend( handle_t h, unsigned devIdx, unsigned portIdx, return midi::device::send( p->midiH, devIdx, portIdx, status, d0, d1 ); } +unsigned cw::io::midiDeviceMaxBufferMsgCount( handle_t h ) +{ + io_t* p = _handleToPtr(h); + return midi::device::maxBufferMsgCount(p->midiH ); +} + +const cw::midi::ch_msg_t* cw::io::midiDeviceBuffer( handle_t h, unsigned& msgCntRef ) +{ + io_t* p = _handleToPtr(h); + return midi::device::getBuffer(p->midiH, msgCntRef ); +} + +cw::rc_t cw::io::midiDeviceClearBuffer( handle_t h, unsigned msgCnt ) +{ + io_t* p = _handleToPtr(h); + return midi::device::clearBuffer(p->midiH, msgCnt ); +} + cw::rc_t cw::io::midiOpenMidiFile( handle_t h, unsigned devIdx, unsigned portIdx, const char* fname ) { return midi::device::openMidiFile( _handleToPtr(h)->midiH, devIdx, portIdx, fname ); @@ -3967,8 +3999,8 @@ void cw::io::uiReport( handle_t h ) void cw::io::uiRealTimeReport( handle_t h ) { - ui::handle_t uiH; - if(_handleToUiHandle(h,uiH) == kOkRC ) - ui::realTimeReport(uiH); + ui::ws::handle_t uiH; + if(_handleToWsUiHandle(h,uiH) == kOkRC ) + ui::ws::realTimeReport(uiH); } diff --git a/cwIo.h b/cwIo.h index 3c8ea26..769b1a3 100644 --- a/cwIo.h +++ b/cwIo.h @@ -164,14 +164,17 @@ namespace cw rc_t start( handle_t h ); rc_t pause( handle_t h ); rc_t stop( handle_t h ); - - // Note that this call blocks on the the UI websocket handle. + + + // Note that this call blocks on the the UI websocket handle for up to 'timeOutMs'. // See ui:ws:exec(). - rc_t exec( handle_t h, void* execCbArg=nullptr ); + rc_t exec( handle_t h, unsigned timeOutMs, void* execCbArg=nullptr ); bool isShuttingDown( handle_t h ); void report( handle_t h ); + void hardwareReport( handle_t h ); void realTimeReport( handle_t h ); + //---------------------------------------------------------------------------------------------------------- // @@ -228,6 +231,11 @@ namespace cw const char* midiDevicePortName( handle_t h, unsigned devIdx, bool inputFl, unsigned portIdx ); unsigned midiDevicePortIndex( handle_t h, unsigned devIdx, bool inputFl, const char* portName ); rc_t midiDeviceSend( handle_t h, unsigned devIdx, unsigned portIdx, uint8_t status, uint8_t d0, uint8_t d1 ); + + unsigned midiDeviceMaxBufferMsgCount( handle_t h ); + const midi::ch_msg_t* midiDeviceBuffer( handle_t h, unsigned& msgCntRef ); + rc_t midiDeviceClearBuffer( handle_t h, unsigned msgCnt ); + rc_t midiOpenMidiFile( handle_t h, unsigned devIdx, unsigned portIdx, const char* fname ); rc_t midiLoadMsgPacket( handle_t h, const midi::packet_t& pkt ); // Note: Set devIdx/portIdx via pkt.devIdx/pkt.portIdx diff --git a/cwIoAudioMidi.cpp b/cwIoAudioMidi.cpp index b7ebc07..e13be74 100644 --- a/cwIoAudioMidi.cpp +++ b/cwIoAudioMidi.cpp @@ -1,6 +1,7 @@ #include "cwCommon.h" #include "cwLog.h" #include "cwCommonImpl.h" +#include "cwTest.h" #include "cwMem.h" #include "cwObject.h" #include "cwFileSys.h" @@ -1024,8 +1025,16 @@ cw::rc_t cw::io::audio_midi::main( const object_t* cfg ) // execute the io framework while( !isShuttingDown(app.ioH)) { - exec(app.ioH); - sleepMs(50); + const unsigned wsTimeOutMs = 50; + time::spec_t t0 = time::current_time(); + + exec(app.ioH,wsTimeOutMs); + + time::spec_t t1 = time::current_time(); + unsigned dMs = time::elapsedMs(t0,t1); + + if( dMs < wsTimeOutMs ) + sleepMs(wsTimeOutMs-dMs); } errLabel: diff --git a/cwIoAudioMidiApp.cpp b/cwIoAudioMidiApp.cpp index 2ac0322..9f45845 100644 --- a/cwIoAudioMidiApp.cpp +++ b/cwIoAudioMidiApp.cpp @@ -1,6 +1,7 @@ #include "cwCommon.h" #include "cwLog.h" #include "cwCommonImpl.h" +#include "cwTest.h" #include "cwMem.h" #include "cwObject.h" #include "cwText.h" @@ -580,8 +581,16 @@ cw::rc_t cw::audio_midi_app::main( const object_t* cfg ) // execute the io framework while( !isShuttingDown(app.ioH)) { - exec(app.ioH); - sleepMs(50); + const unsigned wsTimeOutMs = 50; + time::spec_t t0 = time::current_time(); + + exec(app.ioH,wsTimeOutMs); + + time::spec_t t1 = time::current_time(); + unsigned dMs = time::elapsedMs(t0,t1); + + if( dMs < wsTimeOutMs ) + sleepMs(wsTimeOutMs-dMs); } errLabel: diff --git a/cwIoAudioPanel.cpp b/cwIoAudioPanel.cpp index 27d5b06..609f91b 100644 --- a/cwIoAudioPanel.cpp +++ b/cwIoAudioPanel.cpp @@ -1,6 +1,7 @@ #include "cwCommon.h" #include "cwLog.h" #include "cwCommonImpl.h" +#include "cwTest.h" #include "cwMem.h" #include "cwTime.h" #include "cwObject.h" diff --git a/cwIoAudioRecordPlay.cpp b/cwIoAudioRecordPlay.cpp index 831ddb4..a8abc5b 100644 --- a/cwIoAudioRecordPlay.cpp +++ b/cwIoAudioRecordPlay.cpp @@ -1,6 +1,7 @@ #include "cwCommon.h" #include "cwLog.h" #include "cwCommonImpl.h" +#include "cwTest.h" #include "cwMem.h" #include "cwObject.h" #include "cwFileSys.h" diff --git a/cwIoFlow.cpp b/cwIoFlow.cpp index 7d3bf79..3492127 100644 --- a/cwIoFlow.cpp +++ b/cwIoFlow.cpp @@ -1,6 +1,7 @@ #include "cwCommon.h" #include "cwLog.h" #include "cwCommonImpl.h" +#include "cwTest.h" #include "cwMem.h" #include "cwObject.h" #include "cwFileSys.h" @@ -8,6 +9,8 @@ #include "cwTime.h" #include "cwVectOps.h" #include "cwMtx.h" +#include "cwTime.h" +#include "cwMidiDecls.h" #include "cwDspTypes.h" #include "cwFlowDecl.h" @@ -87,7 +90,7 @@ namespace cw mem::release(p->audioGroupA); mem::release(p); - + return kOkRC; } @@ -95,10 +98,13 @@ namespace cw { unsigned devN = 0; - //devN += midiDeviceCount(p->ioH); devN += socketCount(p->ioH); devN += serialDeviceCount(p->ioH); + unsigned midiDevN = midiDeviceCount(p->ioH); + for(unsigned i=0; iioH,i,true) + midiDevicePortCount(p->ioH,i,false); + for(unsigned i=0; iaudioGroupN; ++i) devN += p->audioGroupA[i].iDeviceN + p->audioGroupA[i].oDeviceN; @@ -109,7 +115,6 @@ namespace cw { dev->ioDevIdx = ioDevIdx; dev->ioDevId = audioDeviceUserId( p->ioH, ioDevIdx ); - dev->abuf.base = nullptr; dev->abuf.srate = audioDeviceSampleRate( p->ioH, ioDevIdx ); dev->abuf.chN = audioDeviceChannelCount( p->ioH, ioDevIdx, inOrOutFl ); dev->abuf.frameN = dspFrameCnt; @@ -146,23 +151,38 @@ namespace cw } } - - void _setup_device_cfg( flow::external_device_t* d, const char* devLabel, unsigned ioDevId, unsigned typeId, unsigned flags ) + rc_t _send_midi_triple( flow::external_device_t* dev, uint8_t ch, uint8_t status, uint8_t d0, uint8_t d1 ) { - d->label = devLabel; - d->ioDevId = ioDevId; - d->typeId = typeId; - d->flags = flags; + return midiDeviceSend(((io_flow_t*)dev->reserved)->ioH, dev->ioDevIdx, dev->ioPortIdx, status |= ch, d0, d1); } + void _setup_device_cfg( io_flow_t* p, flow::external_device_t* d, const char* devLabel, unsigned ioDevIdx, unsigned typeId, unsigned flags, const char* midiPortLabel=nullptr, unsigned midiPortIdx=kInvalidIdx ) + { + d->reserved = p; + d->devLabel = devLabel; + d->portLabel = midiPortLabel; + d->typeId = typeId; + d->flags = flags; + d->ioDevIdx = ioDevIdx; + d->ioPortIdx = midiPortIdx; + } + + void _setup_midi_device_cfg( io_flow_t* p, flow::external_device_t* d, const char* devLabel, unsigned ioDevIdx, unsigned flags, unsigned ioMidiPortIdx ) + { + const char* midiPortLabel = io::midiDevicePortName(p->ioH,ioDevIdx, flags & flow::kInFl ? true : false,ioMidiPortIdx); + _setup_device_cfg( p, d, devLabel, ioDevIdx, flow::kMidiDevTypeId, flags, midiPortLabel, ioMidiPortIdx ); + d->u.m.maxMsgCnt = io::midiDeviceMaxBufferMsgCount(p->ioH); + d->u.m.sendTripleFunc = _send_midi_triple; + } + void _setup_audio_device_cfg( io_flow_t* p, flow::external_device_t* d, audio_group_t* ag, audio_dev_t* ad, unsigned flags ) { - _setup_device_cfg( d, io::audioDeviceLabel(p->ioH,ad->ioDevIdx), ad->ioDevId, flow::kAudioDevTypeId, flags ); + _setup_device_cfg( p, d, io::audioDeviceLabel(p->ioH,ad->ioDevIdx), ad->ioDevIdx, flow::kAudioDevTypeId, flags ); // Each audio device is given a flow::abuf to hold incoming or outgoing audio. // This buffer also allows the 'audio_in' and 'audio_out' flow procs to configure themselves. d->u.a.abuf = &ad->abuf; - } + } void _setup_generic_device_array( io_flow_t* p ) { @@ -175,17 +195,27 @@ namespace cw // get serial devices for(unsigned di=0; ideviceN && diioH); ++di,++i) - _setup_device_cfg( p->deviceA + i, io::serialDeviceLabel(p->ioH,di), io::serialDeviceId(p->ioH,di), flow::kSerialDevTypeId, flow::kInFl | flow::kOutFl ); - - // get midi devices - //for(unsigned di=0; ideviceN && diioH); ++di,++i) - // _setup_device_cfg( p->deviceA + i, io::midiDeviceLabel(p->ioH,di), di, flow::kMidiDevTypeId, flow::kInFl | flow::kOutFl ); + _setup_device_cfg( p, p->deviceA + i, io::serialDeviceLabel(p->ioH,di), di, flow::kSerialDevTypeId, flow::kInFl | flow::kOutFl ); // get sockets for(unsigned di=0; ideviceN && diioH); ++di,++i) - _setup_device_cfg( p->deviceA + i, io::socketLabel(p->ioH,di), io::socketUserId(p->ioH,di), flow::kSocketDevTypeId, flow::kInFl | flow::kOutFl ); + _setup_device_cfg( p, p->deviceA + i, io::socketLabel(p->ioH,di), di, flow::kSocketDevTypeId, flow::kInFl | flow::kOutFl ); + // get midi devices + for(unsigned di=0; ideviceN && diioH); ++di) + { + // input port setup + for(unsigned pi=0; piioH,di,true); ++pi,++i) + _setup_midi_device_cfg( p, p->deviceA + i, io::midiDeviceName(p->ioH,di), di, flow::kInFl, pi); + + // output port setup + for(unsigned pi=0; piioH,di,false); ++pi,++i) + _setup_midi_device_cfg( p, p->deviceA + i, io::midiDeviceName(p->ioH,di), di, flow::kOutFl, pi); + + } + + // get the audio devices for(unsigned gi=0; giaudioGroupN; ++gi) { @@ -198,6 +228,9 @@ namespace cw _setup_audio_device_cfg( p, p->deviceA + i, ag, ag->oDeviceA + di, flow::kOutFl ); } + + assert( i == p->deviceN ); + } rc_t _device_index_to_abuf( io_flow_t* p, unsigned ioGroupIdx, unsigned ioDevIdx, unsigned inOrOutFl, flow::abuf_t*& abuf_ref ) @@ -254,6 +287,18 @@ namespace cw rc_t rc = kOkRC; flow::abuf_t* abuf = nullptr; + // Get an array of incoming MIDI events which have occurred since the last call to 'io::midiDeviceBuffer()' + unsigned midiBufMsgCnt = 0; + const midi::ch_msg_t* midiBuf = midiDeviceBuffer(p->ioH,midiBufMsgCnt); + + // Give each MIDI input device a pointer to the incoming MIDI msgs + for(unsigned i=0; ideviceN; ++i) + if( p->deviceA[i].typeId == flow::kMidiDevTypeId && cwIsFlag(p->deviceA[i].flags,flow::kInFl) ) + { + p->deviceA[i].u.m.msgArray = midiBuf; + p->deviceA[i].u.m.msgCnt = midiBufMsgCnt; + } + // if there is incoming (recorded) audio if( m.iBufChCnt > 0 ) { @@ -318,6 +363,9 @@ namespace cw } errLabel: + // Drop the MIDI messages that were processed on this call. + midiDeviceClearBuffer(p->ioH,midiBufMsgCnt); + return rc; } diff --git a/cwIoMidiRecordPlay.cpp b/cwIoMidiRecordPlay.cpp index 161e2ff..26c35b4 100644 --- a/cwIoMidiRecordPlay.cpp +++ b/cwIoMidiRecordPlay.cpp @@ -1,6 +1,7 @@ #include "cwCommon.h" #include "cwLog.h" #include "cwCommonImpl.h" +#include "cwTest.h" #include "cwMem.h" #include "cwText.h" #include "cwObject.h" diff --git a/cwIoMinTest.cpp b/cwIoMinTest.cpp index bbeffea..fa05d69 100644 --- a/cwIoMinTest.cpp +++ b/cwIoMinTest.cpp @@ -1,6 +1,7 @@ #include "cwCommon.h" #include "cwLog.h" #include "cwCommonImpl.h" +#include "cwTest.h" #include "cwKeyboard.h" #include "cwMem.h" #include "cwObject.h" @@ -118,8 +119,16 @@ cw::rc_t cw::min_test( const object_t* cfg ) // execuite the io framework while( !isShuttingDown(app.ioH)) { - exec(app.ioH); - sleepMs(500); + const unsigned wsTimeOutMs = 50; + time::spec_t t0 = time::current_time(); + + exec(app.ioH,wsTimeOutMs); + + time::spec_t t1 = time::current_time(); + unsigned dMs = time::elapsedMs(t0,t1); + + if( dMs < wsTimeOutMs ) + sleepMs(wsTimeOutMs-dMs); if( isKeyWaiting() ) break; diff --git a/cwIoPresetSelApp.cpp b/cwIoPresetSelApp.cpp index cbb7906..5ac5b56 100644 --- a/cwIoPresetSelApp.cpp +++ b/cwIoPresetSelApp.cpp @@ -1,6 +1,7 @@ #include "cwCommon.h" #include "cwLog.h" #include "cwCommonImpl.h" +#include "cwTest.h" #include "cwMem.h" #include "cwText.h" #include "cwNumericConvert.h" @@ -47,6 +48,7 @@ namespace cw kPanelDivId = 1000, kQuitBtnId, kIoReportBtnId, + kIoHwReportBtnId, kIoRtReportBtnId, kPresetReportBtnId, kMRP_ReportBtnId, @@ -161,11 +163,12 @@ namespace cw { ui::kRootAppId, kPanelDivId, "panelDivId" }, { kPanelDivId, kQuitBtnId, "quitBtnId" }, { kPanelDivId, kIoReportBtnId, "ioReportBtnId" }, - { kPanelDivId, kIoRtReportBtnId,"ioRtReportBtnId" }, + { kPanelDivId, kIoHwReportBtnId, "ioHwReportBtnId" }, + { kPanelDivId, kIoRtReportBtnId, "ioRtReportBtnId" }, + { kPanelDivId, kMRP_ReportBtnId, "MRP_ReportBtnId" }, + { kPanelDivId, kPresetReportBtnId, "presetReportBtnId" }, { kPanelDivId, kNetPrintBtnId, "netPrintBtnId" }, { kPanelDivId, kReportBtnId, "reportBtnId" }, - { kPanelDivId, kPresetReportBtnId, "presetReportBtnId" }, - { kPanelDivId, kMRP_ReportBtnId, "MRP_ReportBtnId" }, { kPanelDivId, kLatencyBtnId, "latencyBtnId" }, { kPanelDivId, kStartBtnId, "startBtnId" }, @@ -307,7 +310,7 @@ namespace cw const char* record_fn_ext; const char* record_backup_dir; - const char* scoreFn; + //const char* scoreFn; const object_t* perfDirL; const char* velTableFname; const char* velTableBackupDir; @@ -386,6 +389,7 @@ namespace cw const char* dflt_perf_label; unsigned dflt_perf_app_id; + unsigned run_dur_secs; } app_t; @@ -401,13 +405,13 @@ namespace cw app->record_fn = argv[i+1]; goto found_fl; } - + /* if( textCompare(argv[i],"score_fn") == 0 ) { app->scoreFn = argv[i+1]; goto found_fl; } - + */ if( textCompare(argv[i],"beg_play_loc") == 0 ) { string_to_number( argv[i+1], app->beg_play_loc ); @@ -449,7 +453,7 @@ namespace cw if((rc = params_cfgRef->getv( "record_dir", app->record_dir, "record_fn", app->record_fn, "record_fn_ext", app->record_fn_ext, - "score_fn", app->scoreFn, + //"score_fn", app->scoreFn, "perfDirL", app->perfDirL, "flow_proc_dict_fn", flow_proc_dict_fn, "midi_play_record", app->midi_play_record_cfg, @@ -460,6 +464,7 @@ namespace cw "beg_play_loc", app->beg_play_loc, "end_play_loc", app->end_play_loc, "dflt_perf_label", app->dflt_perf_label, + "run_dur_secs", app->run_dur_secs, "live_mode_fl", app->useLiveMidiFl, "enable_recording_fl", app->enableRecordFl, "midi_record_dir", midi_record_dir, @@ -482,11 +487,13 @@ namespace cw _apply_command_line_args(app,argc,argv); + /* if((app->scoreFn = filesys::expandPath( app->scoreFn )) == nullptr ) { rc = cwLogError(kInvalidArgRC,"The score file name is invalid."); goto errLabel; } + */ if((app->record_dir = filesys::expandPath(app->record_dir)) == nullptr ) { @@ -565,10 +572,10 @@ namespace cw void _log_output_func( void* arg, unsigned level, const char* text ) { - app_t* app = (app_t*)arg; - unsigned logUuId = uiFindElementUuId( app->ioH, kLogId); - - uiSetLogLine( app->ioH, logUuId, text ); + //app_t* app = (app_t*)arg; + //unsigned logUuId = uiFindElementUuId( app->ioH, kLogId); + + //uiSetLogLine( app->ioH, logUuId, text ); log::defaultOutput(nullptr,level,text); } @@ -599,7 +606,7 @@ namespace cw mem::release((char*&)app.record_backup_dir); mem::release((char*&)app.record_dir); - mem::release((char*&)app.scoreFn); + //mem::release((char*&)app.scoreFn); mem::release(app.midiRecordDir); mem::release(app.midiLoadFname); vtbl::destroy(app.vtH); @@ -725,6 +732,7 @@ namespace cw char* perf_fname = nullptr; char* meta_fname = nullptr; bool skip_fl = false; + // create the performance recording file path if((perf_fname = filesys::makeFn(dir,fname,nullptr,recording_folder,nullptr)) == nullptr ) { @@ -796,8 +804,11 @@ namespace cw mem::release(meta_fname); mem::release(perf_fname); - return rc; + + if( meta_cfg != nullptr ) + meta_cfg->free(); + return rc; } rc_t _parse_perf_recording_dir( app_t* app, const char* dir, const char* fname, const object_t* velTblCfg ) @@ -1000,9 +1011,9 @@ namespace cw // apply the fragment defined gain settings if( app->ioFlowH.isValid() ) { - io_flow::set_variable_value( app->ioFlowH, flow_cross::kNextDestId, "wet_in_gain", "gain", flow::kAnyChIdx, (dsp::real_t)frag->igain ); - io_flow::set_variable_value( app->ioFlowH, flow_cross::kNextDestId, "wet_out_gain","gain", flow::kAnyChIdx, (dsp::real_t)frag->ogain ); - io_flow::set_variable_value( app->ioFlowH, flow_cross::kNextDestId, "wd_bal", "in", flow::kAnyChIdx, (dsp::real_t)frag->wetDryGain ); + io_flow::set_variable_value( app->ioFlowH, flow_cross::kNextDestId, "wet_in_gain", "gain", flow::kAnyChIdx, (dsp::coeff_t)frag->igain ); + io_flow::set_variable_value( app->ioFlowH, flow_cross::kNextDestId, "wet_out_gain","gain", flow::kAnyChIdx, (dsp::coeff_t)frag->ogain ); + io_flow::set_variable_value( app->ioFlowH, flow_cross::kNextDestId, "wd_bal", "in", flow::kAnyChIdx, (dsp::coeff_t)frag->wetDryGain ); // activate the cross-fade io_flow::begin_cross_fade( app->ioFlowH, frag->fadeOutMs ); @@ -1158,8 +1169,6 @@ namespace cw { if( preset_sel::track_loc( app->psH, loc, f ) ) { - - printf("Loc:%i\n",loc); _apply_preset( app, loc, (const perf_score::event_t*)msg_arg, f ); if( f != nullptr ) @@ -1879,7 +1888,7 @@ namespace cw errLabel: if(rc != kOkRC ) - rc = cwLogError(rc,"Preset control index '%i' create failed."); + rc = cwLogError(rc,"Preset control index '%i' create failed.", preset_idx); return rc; } @@ -1966,7 +1975,7 @@ namespace cw // read the preset data file if((rc = preset_sel::read( app->psH, fn)) != kOkRC ) { - rc = cwLogError(rc,"File write failed on preset select."); + rc = cwLogError(rc,"File read failed on preset select."); goto errLabel; } @@ -2193,7 +2202,7 @@ rc_t _on_ui_play_loc(app_t* app, unsigned appId, unsigned loc); m[i].id = e->uid; m[i].loc = e->loc; m[i].arg = e; - + app->locMap[i].loc = e->loc; app->locMap[i].timestamp = m[i].timestamp; @@ -2284,7 +2293,7 @@ rc_t _on_ui_play_loc(app_t* app, unsigned appId, unsigned loc); return rc; } - rc_t _do_load_perf_score( app_t* app, const char* perf_fn, const vel_tbl_t* vtA=nullptr, unsigned vtN=0 ) + rc_t _do_load_perf_score( app_t* app, const char* perf_fn, const vel_tbl_t* vtA=nullptr, unsigned vtN=0, unsigned beg_loc=score_parse::kInvalidLocId, unsigned end_loc=score_parse::kInvalidLocId ) { rc_t rc = kOkRC; unsigned midiEventN = 0; @@ -2330,8 +2339,8 @@ rc_t _on_ui_play_loc(app_t* app, unsigned appId, unsigned loc); // set the UI begin/end play to the locations of the newly loaded performance if( !lockLoctnFl ) { - app->end_play_loc = app->maxPerfLoc; - app->beg_play_loc = app->minPerfLoc; + app->end_play_loc = end_loc==score_parse::kInvalidLocId ? app->maxPerfLoc : end_loc; + app->beg_play_loc = beg_loc==score_parse::kInvalidLocId ? app->minPerfLoc : beg_loc; } // Update the master range of the play beg/end number widgets @@ -2397,7 +2406,7 @@ rc_t _on_ui_play_loc(app_t* app, unsigned appId, unsigned loc); printf("Loading:%s\n",prp->fname ); // load the requested performance - if((rc = _do_load_perf_score(app,prp->fname,prp->vel_tblA, prp->vel_tblN)) != kOkRC ) + if((rc = _do_load_perf_score(app,prp->fname,prp->vel_tblA, prp->vel_tblN, prp->beg_loc, prp->end_loc)) != kOkRC ) { rc = cwLogError(kSyntaxErrorRC,"The performance load failed."); goto errLabel; @@ -2906,7 +2915,7 @@ rc_t _on_ui_play_loc(app_t* app, unsigned appId, unsigned loc); else { if( app->ioFlowH.isValid() ) - if((rc = io_flow::set_variable_value( app->ioFlowH, flow_cross::kAllDestId, inst_label, var_label, flow::kAnyChIdx, (dsp::real_t)value )) != kOkRC ) + if((rc = io_flow::set_variable_value( app->ioFlowH, flow_cross::kAllDestId, inst_label, var_label, flow::kAnyChIdx, (dsp::coeff_t)value )) != kOkRC ) rc = cwLogError(rc,"Master value send failed on %s.%s.",cwStringNullGuard(inst_label),cwStringNullGuard(var_label)); } return rc; @@ -3001,7 +3010,7 @@ rc_t _on_ui_play_loc(app_t* app, unsigned appId, unsigned loc); } if( m.value->tid == ui::kDoubleTId && app->ioFlowH.isValid() ) - rc = io_flow::set_variable_value( app->ioFlowH, flow_cross::kAllDestId, "sd", var_label, flow::kAnyChIdx, (dsp::real_t)m.value->u.d ); + rc = io_flow::set_variable_value( app->ioFlowH, flow_cross::kAllDestId, "sd", var_label, flow::kAnyChIdx, (dsp::coeff_t)m.value->u.d ); if(rc != kOkRC ) rc = cwLogError(rc,"Attempt to set a spec-dist variable '%s'",var_label ); @@ -3012,7 +3021,7 @@ rc_t _on_ui_play_loc(app_t* app, unsigned appId, unsigned loc); rc_t _on_live_midi_checkbox( app_t* app, bool useLiveMidiFl ) { rc_t rc = kOkRC; - dsp::real_t value; + dsp::ftime_t value; if( useLiveMidiFl ) { @@ -3032,7 +3041,7 @@ rc_t _on_ui_play_loc(app_t* app, unsigned appId, unsigned loc); } if( app->ioFlowH.isValid() ) - if((rc = io_flow::set_variable_value( app->ioFlowH, flow_cross::kAllDestId, "sync_delay", "delayMs", flow::kAnyChIdx, (dsp::real_t)value )) != kOkRC ) + if((rc = io_flow::set_variable_value( app->ioFlowH, flow_cross::kAllDestId, "sync_delay", "delayMs", flow::kAnyChIdx, (dsp::ftime_t)value )) != kOkRC ) rc = cwLogError(rc,"Error setting sync delay 'flow' value."); @@ -3097,10 +3106,14 @@ rc_t _on_ui_play_loc(app_t* app, unsigned appId, unsigned loc); io::report( app->ioH ); break; - case kIoRtReportBtnId: - io::realTimeReport(app->ioH); + case kIoHwReportBtnId: + io::hardwareReport( app->ioH ); break; + case kIoRtReportBtnId: + io::realTimeReport( app->ioH ); + break; + case kNetPrintBtnId: if( app->ioFlowH.isValid() ) io_flow::print_network(app->ioFlowH,flow_cross::kCurDestId); @@ -3698,6 +3711,7 @@ cw::rc_t cw::preset_sel_app::main( const object_t* cfg, int argc, const char* ar unsigned bigMapN = mapN + vtMapN; ui::appIdMap_t bigMap[ bigMapN ]; double sysSampleRate = 0; + time::spec_t start_time = time::current_time(); for(unsigned i=0; i app.run_dur_secs ) + { + printf("Run duration expired (%i secs). Shutting down.\n",app.run_dur_secs); + io::stop(app.ioH); + } } // stop the io framework diff --git a/cwIoSocketChat.cpp b/cwIoSocketChat.cpp index 6343804..8efe48a 100644 --- a/cwIoSocketChat.cpp +++ b/cwIoSocketChat.cpp @@ -1,6 +1,7 @@ #include "cwCommon.h" #include "cwLog.h" #include "cwCommonImpl.h" +#include "cwTest.h" #include "cwMem.h" #include "cwObject.h" #include "cwTime.h" diff --git a/cwIoTest.cpp b/cwIoTest.cpp index 298736b..801e4b5 100644 --- a/cwIoTest.cpp +++ b/cwIoTest.cpp @@ -1,6 +1,7 @@ #include "cwCommon.h" #include "cwLog.h" #include "cwCommonImpl.h" +#include "cwTest.h" #include "cwMem.h" #include "cwObject.h" #include "cwTime.h" @@ -308,8 +309,16 @@ cw::rc_t cw::io::test( const object_t* cfg ) // execuite the io framework while( !isShuttingDown(app.ioH)) { - exec(app.ioH); - sleepMs(50); + const unsigned wsTimeOutMs = 50; + time::spec_t t0 = time::current_time(); + + exec(app.ioH,wsTimeOutMs); + + time::spec_t t1 = time::current_time(); + unsigned dMs = time::elapsedMs(t0,t1); + + if( dMs < wsTimeOutMs ) + sleepMs(wsTimeOutMs-dMs); } errLabel: diff --git a/cwLex.cpp b/cwLex.cpp index 2333b17..a68501d 100644 --- a/cwLex.cpp +++ b/cwLex.cpp @@ -3,7 +3,8 @@ #include "cwCommonImpl.h" #include "cwMem.h" #include "cwFile.h" - +#include "cwTest.h" +#include "cwObject.h" #include "cwLex.h" @@ -853,7 +854,7 @@ namespace cw //) //( - rc_t test() + rc_t test( const test::test_args_t& args ) { rc_t rc = kOkRC; unsigned tid = kInvalidId; @@ -866,7 +867,9 @@ namespace cw "/* block \n" "comment */" "\"quoted string\"" - "ident1" + "ident1 " + "1234.56f" + "345u" " // last line comment"; // initialize a lexer with a buffer of text @@ -884,7 +887,7 @@ namespace cw while( (tid = lex::getNextToken(h)) != kEofLexTId ) { // print information about each token - cwLogInfo("%i %i %s '%.*s' (%i) ", + cwLogInfo("ln:%i col:%i tok:%s '%.*s' len:%i ", lex::currentLineNumber(h), lex::currentColumnNumber(h), lex::idToLabel(h,tid), @@ -899,11 +902,9 @@ namespace cw int iv = lex::tokenInt(h); double dv = lex::tokenDouble(h); - cwLogInfo("%i %f",iv,dv); + cwLogInfo("Number: int:%i dbl:%f unsigned:%i float:%i",iv,dv,tokenIsUnsigned(h),tokenIsSinglePrecision(h)); } - cwLogInfo("\n"); - // handle errors if( tid == kErrorLexTId ) { diff --git a/cwLex.h b/cwLex.h index c77aafd..9e5a72f 100644 --- a/cwLex.h +++ b/cwLex.h @@ -129,7 +129,7 @@ namespace cw const char* idToLabel( handle_t h, unsigned typeId ); // Lexer testing stub. - rc_t test( ); + rc_t test( const test::test_args_t& args ); } } diff --git a/cwLib.cpp b/cwLib.cpp index a66cd6d..eaf9747 100644 --- a/cwLib.cpp +++ b/cwLib.cpp @@ -1,6 +1,7 @@ #include "cwCommon.h" #include "cwLog.h" #include "cwCommonImpl.h" +#include "cwTest.h" #include "cwMem.h" #include "cwFileSys.h" #include "cwLib.h" diff --git a/cwLog.cpp b/cwLog.cpp index bf494c0..f37863f 100644 --- a/cwLog.cpp +++ b/cwLog.cpp @@ -2,6 +2,7 @@ #include "cwLog.h" #include "cwCommonImpl.h" +#include "cwTest.h" #include "cwMem.h" #include "cwTime.h" @@ -112,7 +113,7 @@ cw::rc_t cw::log::msg( handle_t h, unsigned level, const char* function, const c cw::rc_t cw::log::msg( handle_t h, unsigned level, const char* function, const char* filename, unsigned line, int systemErrorCode, rc_t returnCode, const char* fmt, ... ) { rc_t rc = returnCode; - if( level >= _handleToPtr(h)->level ) + if( level >= _handleToPtr(h)->level || level == kPrint_LogLevel ) { va_list vl; va_start(vl,fmt); @@ -146,6 +147,29 @@ unsigned cw::log::flags( handle_t h ) return p->flags; } +void* cw::log::outputCbArg( handle_t h ) +{ + log_t* p = _handleToPtr(h); + return p->outCbArg; +} + +cw::log::logOutputCbFunc_t cw::log::outputCb( handle_t h ) +{ + log_t* p = _handleToPtr(h); + return p->outCbFunc; +} + +void* cw::log::formatCbArg( handle_t h ) +{ + log_t* p = _handleToPtr(h); + return p->fmtCbArg; +} + +cw::log::logFormatCbFunc_t cw::log::formatCb( handle_t h ) +{ + log_t* p = _handleToPtr(h); + return p->fmtCbFunc; +} void cw::log::setOutputCb( handle_t h, logOutputCbFunc_t outFunc, void* outCbArg ) { @@ -164,13 +188,7 @@ void cw::log::setFormatCb( handle_t h, logFormatCbFunc_t fmtFunc, void* fmtCbArg const char* cw::log::levelToLabel( unsigned level ) -{ - const char* label; - if((label = idToLabel(logLevelLabelArray,level,kInvalid_LogLevel)) == nullptr) - label = ""; - - return label; -} +{ return idToLabel(logLevelLabelArray,level,kInvalid_LogLevel); } @@ -220,7 +238,7 @@ void cw::log::defaultFormatter( void* cbArg, logOutputCbFunc_t outFunc, void* ou // don't print the function,file,line when this is an 'info' msg. - if( level == kInfo_LogLevel ) + if( level == kInfo_LogLevel || level == kPrint_LogLevel ) { loStr = ""; syStr = ""; diff --git a/cwLog.h b/cwLog.h index 708a174..5ccf3a0 100644 --- a/cwLog.h +++ b/cwLog.h @@ -39,6 +39,12 @@ namespace cw void setLevel( handle_t h, unsigned level ); unsigned level( handle_t h ); + void* outputCbArg( handle_t h ); + logOutputCbFunc_t outputCb( handle_t h ); + + void* formatCbArg( handle_t h ); + logFormatCbFunc_t formatCb( handle_t h ); + void setOutputCb( handle_t h, logOutputCbFunc_t outFunc, void* outCbArg ); void setFormatCb( handle_t h, logFormatCbFunc_t fmtFunc, void* fmtCbArg ); diff --git a/cwMath.cpp b/cwMath.cpp index 9f855d3..d5a737a 100644 --- a/cwMath.cpp +++ b/cwMath.cpp @@ -93,7 +93,7 @@ void cw::math::doubleToX80(double val, unsigned char rate[10]) bool cw::math::isPowerOfTwo( unsigned x ) { - return !( (x < 2) || (x & (x-1)) ); + return x==1 || (!( (x < 2) || (x & (x-1)) )); } unsigned cw::math::nextPowerOfTwo( unsigned val ) diff --git a/cwMdns.cpp b/cwMdns.cpp index 55d5760..8d1221a 100644 --- a/cwMdns.cpp +++ b/cwMdns.cpp @@ -1,6 +1,7 @@ #include "cwCommon.h" #include "cwLog.h" #include "cwCommonImpl.h" +#include "cwTest.h" #include "cwMem.h" #include "cwThread.h" diff --git a/cwMidi.cpp b/cwMidi.cpp index f73b9c4..3d19722 100644 --- a/cwMidi.cpp +++ b/cwMidi.cpp @@ -1,6 +1,7 @@ #include "cwCommon.h" #include "cwLog.h" #include "cwCommonImpl.h" +#include "cwTest.h" #include "cwMem.h" #include "cwTime.h" #include "cwMidi.h" diff --git a/cwMidiAlsa.cpp b/cwMidiAlsa.cpp index cc969a7..7b6c85c 100644 --- a/cwMidiAlsa.cpp +++ b/cwMidiAlsa.cpp @@ -1,6 +1,7 @@ #include "cwCommon.h" #include "cwLog.h" #include "cwCommonImpl.h" +#include "cwTest.h" #include "cwMem.h" #include "cwTime.h" #include "cwText.h" @@ -283,6 +284,7 @@ namespace cw time::spec_t ts; ts.tv_sec = ev->time.time.tv_sec; ts.tv_nsec = ev->time.time.tv_nsec; + parser::midiTriple(p->prvRcvPort->parserH, &ts, status | ch, d0, d1 ); p->prvTimeMicroSecs = microSecs1; @@ -439,7 +441,7 @@ namespace cw { assert(jdevArray[i].iPortCnt); p->devArray[i].iPortArray[j].inputFl = true; - p->devArray[i].iPortArray[j].nameStr = mem::duplStr(cwStringNullGuard(port)); + p->devArray[i].iPortArray[j].nameStr = mem::duplStr(port==nullptr ? "" : port); p->devArray[i].iPortArray[j].alsa_type = type; p->devArray[i].iPortArray[j].alsa_cap = caps; p->devArray[i].iPortArray[j].alsa_addr = addr; @@ -461,7 +463,7 @@ namespace cw { assert(kdevArray[i].oPortCnt); p->devArray[i].oPortArray[k].inputFl = false; - p->devArray[i].oPortArray[k].nameStr = mem::duplStr(cwStringNullGuard(port)); + p->devArray[i].oPortArray[k].nameStr = mem::duplStr(port==nullptr ? "" : port); p->devArray[i].oPortArray[k].alsa_type = type; p->devArray[i].oPortArray[k].alsa_cap = caps; p->devArray[i].oPortArray[k].alsa_addr = addr; @@ -475,6 +477,11 @@ namespace cw ++k; } } + + // The capabilities of some ports may not have been as expected + // decrease the in/out port count to account for these ports + p->devArray[i].iPortCnt = j; + p->devArray[i].oPortCnt = k; } errLabel: @@ -736,7 +743,7 @@ const char* cw::midi::device::alsa::portName( handle_t h, unsigned devIdx, if( cwIsFlag(flags,kInMpFl) ) { if( portIdx >= p->devArray[devIdx].iPortCnt ) - return 0; + return nullptr; return p->devArray[devIdx].iPortArray[portIdx].nameStr; } @@ -782,7 +789,7 @@ cw::rc_t cw::midi::device::alsa::send( handle_t h, unsigned devIdx, unsigned po rc_t rc = kOkRC; snd_seq_event_t ev; int arc; - alsa_device_t* p = _handleToPtr(h); + alsa_device_t* p = _handleToPtr(h); assert( p!=NULL && devIdx < p->devCnt && portIdx < p->devArray[devIdx].oPortCnt ); @@ -796,8 +803,11 @@ cw::rc_t cw::midi::device::alsa::send( handle_t h, unsigned devIdx, unsigned po snd_seq_ev_set_direct(&ev); snd_seq_ev_set_fixed(&ev); + uint8_t status_wo_ch = status; + if( midi::isChStatus(status) ) + status_wo_ch = status & 0xf0; - switch( status & 0xf0 ) + switch( status_wo_ch ) { case kNoteOffMdId: ev.type = SND_SEQ_EVENT_NOTEOFF; @@ -854,8 +864,32 @@ cw::rc_t cw::midi::device::alsa::send( handle_t h, unsigned devIdx, unsigned po } break; + case kSysRtClockMdId: + ev.type = SND_SEQ_EVENT_CLOCK; + break; + + case kSysRtStartMdId: + ev.type =SND_SEQ_EVENT_START; + break; + + case kSysRtContMdId: + ev.type = SND_SEQ_EVENT_CONTINUE; + break; + + case kSysRtStopMdId: + ev.type = SND_SEQ_EVENT_STOP; + break; + + case kSysRtSenseMdId: + ev.type = SND_SEQ_EVENT_SENSING; + break; + + case kSysRtResetMdId: + ev.type = SND_SEQ_EVENT_RESET; + break; + default: - rc = _cmMpErrMsg1(kInvalidArgRC,0,"Cannot send an invalid MIDI status byte:0x%x.",status & 0xf0); + rc = _cmMpErrMsg1(kInvalidArgRC,0,"Cannot send an invalid MIDI status byte:0x%x.",status); goto errLabel; } diff --git a/cwMidiDecls.h b/cwMidiDecls.h index 9746385..b282ccc 100644 --- a/cwMidiDecls.h +++ b/cwMidiDecls.h @@ -14,10 +14,10 @@ namespace cw uint8_t d0; // midi data byte 0 uint8_t d1; // midi data byte 1 } msg_t; - + typedef struct packet_str { - void* cbArg; // Application supplied reference value + //void* cbArg; // Application supplied reference value unsigned devIdx; // The device the msg originated from unsigned portIdx; // The port index on the source device msg_t* msgArray; // Pointer to an array of 'msgCnt' mdMsg records or NULL if sysExMsg is non-NULL @@ -25,8 +25,19 @@ namespace cw unsigned msgCnt; // Count of mdMsg records or sys-ex bytes } packet_t; - typedef void (*cbFunc_t)( const packet_t* pktArray, unsigned pktCnt ); + typedef void (*cbFunc_t)( void* cbArg, const packet_t* pktArray, unsigned pktCnt ); + typedef struct ch_msg_str + { + time::spec_t timeStamp; + unsigned devIdx; // The device the msg originated from + unsigned portIdx; // The port index on the source device + unsigned uid; // application specified id + uint8_t ch; // midi channel + uint8_t status; // midi status byte (channel has been removed) + uint8_t d0; // midi data byte 0 + uint8_t d1; // midi data byte 1 + } ch_msg_t; } } #endif diff --git a/cwMidiDevice.cpp b/cwMidiDevice.cpp index d487eb9..fc290bf 100644 --- a/cwMidiDevice.cpp +++ b/cwMidiDevice.cpp @@ -1,6 +1,7 @@ #include "cwCommon.h" #include "cwLog.h" #include "cwCommonImpl.h" +#include "cwTest.h" #include "cwMem.h" #include "cwTime.h" #include "cwObject.h" @@ -29,7 +30,7 @@ namespace cw kPausedStateId, kPlayingStateId } transportStateId_t; - + typedef struct device_str { cbFunc_t cbFunc; @@ -54,6 +55,11 @@ namespace cw unsigned long long offset_micros; unsigned long long last_posn_micros; time::spec_t start_time; + + ch_msg_t* buf; + unsigned bufN; + std::atomic buf_ii; + std::atomic buf_oi; } device_t; @@ -101,9 +107,12 @@ namespace cw rc = cwLogError(rc,"MIDI port thread destroy failed."); goto errLabel; } + destroy(p->alsaDevH); destroy(p->fileDevH); + + mem::release(p->buf); mem::release(p); errLabel: @@ -173,6 +182,45 @@ namespace cw return true; } + void _callback( void* cbArg, const packet_t* pktArray, unsigned pktCnt ) + { + device_t* p = (device_t*)cbArg; + + for(unsigned i=0; imsgArray != nullptr ) + { + unsigned ii = p->buf_ii.load(); + unsigned oi = p->buf_oi.load(); + for(unsigned j=0; jmsgCnt; ++j) + { + ch_msg_t* m = p->buf + ii; + m->devIdx = pkt->devIdx; + m->portIdx = pkt->portIdx; + m->timeStamp = pkt->msgArray[j].timeStamp; + m->uid = pkt->msgArray[j].uid; + m->ch = pkt->msgArray[j].ch; + m->status = pkt->msgArray[j].status; + m->d0 = pkt->msgArray[j].d0; + m->d1 = pkt->msgArray[j].d1; + + ii = (ii+1 == p->bufN ? 0 : ii+1); + if( ii == oi ) + { + cwLogError(kBufTooSmallRC,"The MIDI device buffer is full %i.",p->bufN); + } + } + + p->buf_ii.store(ii); + + } + } + + if( p->cbFunc != nullptr ) + p->cbFunc(p->cbArg,pktArray,pktCnt); + } + } // device } // midi @@ -188,7 +236,9 @@ cw::rc_t cw::midi::device::create( handle_t& hRef, const char* appNameStr, const char* fileDevName, unsigned fileDevReadAheadMicros, - unsigned parserBufByteCnt ) + unsigned parserBufByteCnt, + bool enableBufFl, + unsigned bufferMsgCnt ) { rc_t rc = kOkRC; rc_t rc1 = kOkRC; @@ -198,7 +248,11 @@ cw::rc_t cw::midi::device::create( handle_t& hRef, device_t* p = mem::allocZ(); - if((rc = create( p->alsaDevH, cbFunc, cbArg, parserBufByteCnt, appNameStr )) != kOkRC ) + if((rc = create( p->alsaDevH, + enableBufFl ? _callback : cbFunc, + enableBufFl ? p : cbArg, + parserBufByteCnt, + appNameStr )) != kOkRC ) { rc = cwLogError(rc,"ALSA MIDI device create failed."); goto errLabel; @@ -206,16 +260,29 @@ cw::rc_t cw::midi::device::create( handle_t& hRef, p->alsa_dev_cnt = count(p->alsaDevH); - if((rc = create( p->fileDevH, cbFunc, cbArg, p->alsa_dev_cnt, filePortLabelA, max_file_cnt, fileDevName, fileDevReadAheadMicros )) != kOkRC ) + if((rc = create( p->fileDevH, + enableBufFl ? _callback : cbFunc, + enableBufFl ? p : cbArg, + p->alsa_dev_cnt, + filePortLabelA, + max_file_cnt, + fileDevName, + fileDevReadAheadMicros )) != kOkRC ) { rc = cwLogError(rc,"MIDI file device create failed."); goto errLabel; } + p->cbFunc = cbFunc; + p->cbArg = cbArg; p->file_dev_cnt = count(p->fileDevH); p->total_dev_cnt = p->alsa_dev_cnt + p->file_dev_cnt; p->alsaPollfdA = pollFdArray(p->alsaDevH,p->alsaPollfdN); p->fileDevStateId = kStoppedStateId; + p->buf = mem::allocZ( bufferMsgCnt ); + p->bufN = bufferMsgCnt; + p->buf_ii.store(0); + p->buf_oi.store(0); if((rc = thread::create(p->threadH, _thread_func, @@ -257,6 +324,8 @@ cw::rc_t cw::midi::device::create( handle_t& h, const char* fileDevName = "file_dev"; unsigned fileDevReadAheadMicros = 3000; unsigned parseBufByteCnt = 1024; + bool enableBufFl = false; + unsigned bufMsgCnt = 0; const object_t* file_ports = nullptr; const object_t* port = nullptr; @@ -264,6 +333,8 @@ cw::rc_t cw::midi::device::create( handle_t& h, "fileDevName",fileDevName, "fileDevReadAheadMicros",fileDevReadAheadMicros, "parseBufByteCnt",parseBufByteCnt, + "enableBufFl",enableBufFl, + "bufferMsgCnt",bufMsgCnt, "file_ports",file_ports)) != kOkRC ) { rc = cwLogError(rc,"MIDI port parse args. failed."); @@ -290,7 +361,7 @@ cw::rc_t cw::midi::device::create( handle_t& h, } } - rc = create(h,cbFunc,cbArg,labelArray,fpi,appNameStr,fileDevName,fileDevReadAheadMicros,parseBufByteCnt); + rc = create(h,cbFunc,cbArg,labelArray,fpi,appNameStr,fileDevName,fileDevReadAheadMicros,parseBufByteCnt,enableBufFl,bufMsgCnt); } @@ -437,15 +508,15 @@ const char* cw::midi::device::portName( handle_t h, unsigned devIdx, unsigned const char* name = nullptr; if((alsaDevIdx = _devIdxToAlsaDevIdx(p,devIdx)) != kInvalidIdx ) - name = portName(p->alsaDevH,alsaDevIdx,flags,portIdx); + name = portName(p->alsaDevH,alsaDevIdx,flags,portIdx); else if((fileDevIdx = _devIdxToFileDevIdx(p,devIdx)) != kInvalidIdx ) - name = portName(p->fileDevH,fileDevIdx,flags,portIdx); + name = portName(p->fileDevH,fileDevIdx,flags,portIdx); else cwLogError(kInvalidArgRC,"The device index %i is not valid."); if( name == nullptr ) - cwLogError(kOpFailRC,"The access to port name on device index %i port index %i failed.",devIdx,portIdx); + cwLogError(kOpFailRC,"The access to %s port name on device index %i port index %i failed.",flags & kInMpFl ? "input" : "output", devIdx,portIdx); return name; } @@ -573,7 +644,7 @@ errLabel: cw::rc_t cw::midi::device::setEndMsg( handle_t h, unsigned devIdx, unsigned portIdx, unsigned msgIdx ) { rc_t rc = kOkRC; - device_t* p = _handleToPtr(h); + device_t* p = _handleToPtr(h); if(_devIdxToFileDevIdx(p,devIdx) == kInvalidIdx ) { @@ -591,6 +662,43 @@ errLabel: } +unsigned cw::midi::device::maxBufferMsgCount( handle_t h ) +{ + device_t* p = _handleToPtr(h); + return p->bufN; +} + +const cw::midi::ch_msg_t* cw::midi::device::getBuffer( handle_t h, unsigned& msgCntRef ) +{ + device_t* p = _handleToPtr(h); + unsigned ii = p->buf_ii.load(); + unsigned oi = p->buf_oi.load(); + ch_msg_t* m = nullptr; + + msgCntRef = ii >= oi ? ii-oi : p->bufN - oi; + + if( msgCntRef > 0 ) + m = p->buf + oi; + + return m; +} + +cw::rc_t cw::midi::device::clearBuffer( handle_t h, unsigned msgCnt ) +{ + if( msgCnt > 0 ) + { + device_t* p = _handleToPtr(h); + unsigned oi = p->buf_oi.load(); + + oi = (oi + msgCnt) % p->bufN; + + p->buf_oi.store(oi); + } + + return kOkRC; +} + + cw::rc_t cw::midi::device::start( handle_t h ) { rc_t rc = kOkRC; @@ -668,6 +776,8 @@ cw::rc_t cw::midi::device::report( handle_t h ) goto errLabel; report(h,tbH); + + printf("%s\n",text(tbH)); errLabel: destroy(tbH); diff --git a/cwMidiDevice.h b/cwMidiDevice.h index 6c8b16d..237052f 100644 --- a/cwMidiDevice.h +++ b/cwMidiDevice.h @@ -30,7 +30,9 @@ namespace cw const char* appNameStr, const char* fileDevName = "file_dev", unsigned fileDevReadAheadMicros = 3000, - unsigned parserBufByteCnt = 1024 ); + unsigned parserBufByteCnt = 1024, + bool enableBufFl = false, // Enable buffer to hold all incoming msg's until RT thread can pick them up. + unsigned bufferMsgCnt = 4096); // Count of messages in input buffer. rc_t create( handle_t& h, cbFunc_t cbFunc, @@ -47,7 +49,7 @@ namespace cw const char* portName( handle_t h, unsigned devIdx, unsigned flags, unsigned portIdx ); unsigned portNameToIndex( handle_t h, unsigned devIdx, unsigned flags, const char* portName ); rc_t portEnable( handle_t h, unsigned devIdx, unsigned flags, unsigned portIdx, bool enableFl ); - + rc_t send( handle_t h, unsigned devIdx, unsigned portIdx, uint8_t st, uint8_t d0, uint8_t d1 ); rc_t sendData( handle_t h, unsigned devIdx, unsigned portIdx, const uint8_t* dataPtr, unsigned byteCnt ); @@ -57,6 +59,10 @@ namespace cw rc_t seekToMsg( handle_t h, unsigned devIdx, unsigned portIdx, unsigned msgIdx ); rc_t setEndMsg( handle_t h, unsigned devIdx, unsigned portidx, unsigned msgIdx ); + unsigned maxBufferMsgCount( handle_t h ); // max number of msg's which will ever be returned in a buffer + const ch_msg_t* getBuffer( handle_t h, unsigned& msgCntRef ); + rc_t clearBuffer( handle_t h, unsigned msgCnt ); + rc_t start( handle_t h ); rc_t stop( handle_t h ); rc_t pause( handle_t h, bool pause_fl ); diff --git a/cwMidiDeviceTest.cpp b/cwMidiDeviceTest.cpp index 75049e4..9f4df64 100644 --- a/cwMidiDeviceTest.cpp +++ b/cwMidiDeviceTest.cpp @@ -1,6 +1,7 @@ #include "cwCommon.h" #include "cwLog.h" #include "cwCommonImpl.h" +#include "cwTest.h" #include "cwMem.h" #include "cwTime.h" #include "cwObject.h" @@ -86,7 +87,7 @@ namespace cw return kOkRC; } - void _test_callback( const packet_t* pktArray, unsigned pktCnt ) + void _test_callback( void* cbArg, const packet_t* pktArray, unsigned pktCnt ) { unsigned i,j; time::spec_t cur_time = time::current_time(); @@ -95,7 +96,7 @@ namespace cw { const packet_t* p = pktArray + i; - test_t* t = (test_t*)p->cbArg; + test_t* t = (test_t*)cbArg; for(j=0; jmsgCnt; ++j) if( p->msgArray != NULL ) diff --git a/cwMidiFile.cpp b/cwMidiFile.cpp index f7699c2..a5110b4 100644 --- a/cwMidiFile.cpp +++ b/cwMidiFile.cpp @@ -1,6 +1,7 @@ #include "cwCommon.h" #include "cwLog.h" #include "cwCommonImpl.h" +#include "cwTest.h" #include "cwMem.h" #include "cwFile.h" #include "cwObject.h" diff --git a/cwMidiFileDev.cpp b/cwMidiFileDev.cpp index ba4a064..42fd52f 100644 --- a/cwMidiFileDev.cpp +++ b/cwMidiFileDev.cpp @@ -1,6 +1,7 @@ #include "cwCommon.h" #include "cwLog.h" #include "cwCommonImpl.h" +#include "cwTest.h" #include "cwMem.h" #include "cwTime.h" #include "cwFile.h" @@ -428,13 +429,12 @@ namespace cw if( p->cbFunc != nullptr ) { packet_t pkt = {}; - pkt.cbArg = p->cbArg; pkt.devIdx = p->base_dev_idx; pkt.portIdx = file_idx; pkt.msgArray = msgA; pkt.msgCnt = msgN; - p->cbFunc( &pkt, 1 ); + p->cbFunc( p->cbArg, &pkt, 1 ); } } diff --git a/cwMidiParser.cpp b/cwMidiParser.cpp index cfb0bb0..458ef27 100644 --- a/cwMidiParser.cpp +++ b/cwMidiParser.cpp @@ -1,6 +1,7 @@ #include "cwCommon.h" #include "cwLog.h" #include "cwCommonImpl.h" +#include "cwTest.h" #include "cwMem.h" #include "cwTime.h" #include "cwMidi.h" @@ -58,8 +59,8 @@ namespace cw cbRecd_t* c = p->cbChain; for(; c!=NULL; c=c->linkPtr) { - pkt->cbArg = c->cbDataPtr; - c->cbFunc( pkt, pktCnt ); + //pkt->cbArg = c->cbDataPtr; + c->cbFunc( c->cbDataPtr, pkt, pktCnt ); } } @@ -102,10 +103,19 @@ namespace cw // get a pointer to the next msg in the buffer msg_t* msgPtr = (msg_t*)(p->buf + p->bufIdx); + if( midi::isChStatus(p->status) ) + { + msgPtr->status = p->status & 0xf0; + msgPtr->ch = p->status & 0x0f; + } + else + { + msgPtr->status = p->status; + msgPtr->ch = 0; + } + // fill the buffer msg msgPtr->timeStamp = *timeStamp; - msgPtr->status = p->status & 0xf0; - msgPtr->ch = p->status & 0x0f; msgPtr->uid = kInvalidId; switch( p->dataCnt ) diff --git a/cwMidiState.cpp b/cwMidiState.cpp index 197603f..8781d2a 100644 --- a/cwMidiState.cpp +++ b/cwMidiState.cpp @@ -1,6 +1,7 @@ #include "cwCommon.h" #include "cwLog.h" #include "cwCommonImpl.h" +#include "cwTest.h" #include "cwMem.h" #include "cwFile.h" #include "cwText.h" diff --git a/cwMtx.cpp b/cwMtx.cpp index 30eb58e..27982d2 100644 --- a/cwMtx.cpp +++ b/cwMtx.cpp @@ -2,6 +2,7 @@ #include "cwLog.h" #include "cwCommonImpl.h" #include "cwMem.h" +#include "cwTest.h" #include "cwObject.h" #include "cwVectOps.h" #include "cwMtx.h" diff --git a/cwMutex.cpp b/cwMutex.cpp index 4b8df35..af98d46 100644 --- a/cwMutex.cpp +++ b/cwMutex.cpp @@ -1,6 +1,7 @@ #include "cwCommon.h" #include "cwLog.h" #include "cwCommonImpl.h" +#include "cwTest.h" #include "cwMem.h" #include "cwMutex.h" #include "cwTime.h" diff --git a/cwNbMpScQueue.cpp b/cwNbMpScQueue.cpp index 6708965..d64e698 100644 --- a/cwNbMpScQueue.cpp +++ b/cwNbMpScQueue.cpp @@ -1,6 +1,7 @@ #include "cwCommon.h" #include "cwLog.h" #include "cwCommonImpl.h" +#include "cwTest.h" #include "cwMem.h" #include "cwTime.h" #include "cwObject.h" @@ -30,12 +31,15 @@ namespace cw typedef struct node_str { - std::atomic next; - block_t* block; - unsigned blobByteN; + std::atomic next; // 0 + block_t* block; // 8 + unsigned blobByteN; // 16 + unsigned pad; // 20-24 (mult. of 8) // blob data follows } node_t; + static_assert( sizeof(node_t) % 8 == 0 ); + typedef struct nbmpscq_str { uint8_t* mem; // Pointer to a single area of memory which holds all blocks. @@ -51,6 +55,8 @@ namespace cw std::atomic head; // last-in node_t* tail; // first-out + + node_t* peek; } nbmpscq_t; @@ -62,8 +68,18 @@ namespace cw rc_t rc = kOkRC; if( p != nullptr ) { + + block_t* b = p->blockL; + while( b != nullptr ) + { + block_t* b0 = b->link; + mem::release(b->buf); + mem::release(b); + b=b0; + } + + mem::release(p->stub); - mem::release(p->mem); mem::release(p); } return rc; @@ -98,6 +114,22 @@ namespace cw p->cleanProcN += 1; } + void _init_blob( blob_t& b, node_t* node ) + { + if( node == nullptr ) + { + b.blob = nullptr; + b.blobByteN = 0; + } + else + { + b.blob = (uint8_t*)(node+1); + b.blobByteN = node->blobByteN; + } + + b.rc = kOkRC; + + } typedef struct shared_str { @@ -133,6 +165,19 @@ namespace cw return true; } + + void _block_report( nbmpscq_t* p ) + { + block_t* b = p->blockL; + for(; b!=nullptr; b=b->link) + { + bool full_fl = b->full_flag.load(std::memory_order_acquire); + unsigned index = b->index.load(std::memory_order_acquire); + int eleN = b->eleN.load(std::memory_order_acquire); + + printf("full:%i idx:%i eleN:%i\n",full_fl,index,eleN); + } + } } @@ -142,7 +187,6 @@ cw::rc_t cw::nbmpscq::create( handle_t& hRef, unsigned initBlkN, unsigned blkByt { rc_t rc = kOkRC; nbmpscq_t* p = nullptr; - unsigned byteN = 0; if((rc = destroy(hRef)) != kOkRC ) goto errLabel; @@ -151,18 +195,18 @@ cw::rc_t cw::nbmpscq::create( handle_t& hRef, unsigned initBlkN, unsigned blkByt p->stub = mem::allocZ(); p->head = p->stub; // last-in - p->tail = p->stub; // first-out + p->tail = p->stub; // first-out + p->peek = nullptr; p->cleanBlkN = 0; p->blkN = initBlkN; p->blkByteN = blkByteN; - byteN = initBlkN * (sizeof(block_t) + blkByteN ); - p->mem = mem::allocZ(byteN); - - for(unsigned i=0; imem+i); - b->buf = (uint8_t*)(b + 1); + block_t* b = mem::allocZ(); + b->buf = mem::allocZ(blkByteN); + b->bufByteN = blkByteN; b->full_flag.store(false); @@ -171,6 +215,7 @@ cw::rc_t cw::nbmpscq::create( handle_t& hRef, unsigned initBlkN, unsigned blkByt b->link = p->blockL; p->blockL = b; + } hRef.set(p); @@ -215,6 +260,17 @@ cw::rc_t cw::nbmpscq::push( handle_t h, const void* blob, unsigned blobByteN ) // Note that this case will immediately overflow the queue. unsigned nodeByteN = blobByteN + sizeof(node_t); + + // force the size of the node to be a multiple of 8 + nodeByteN = ((nodeByteN-1) & 0xfffffff8) + 8; + + // We will eventually be addressing node_t records stored in pre-allocated blocks + // of memory - be sure that they always begin on 8 byte alignment to conform + // to Intel standard. + assert( nodeByteN % 8 == 0 ); + + if( nodeByteN > p->blkByteN ) + return cwLogError(kInvalidArgRC,"The blob size is too large:%i > %i.",nodeByteN,p->blkByteN); for(; b!=nullptr; b=b->link) { @@ -265,7 +321,13 @@ cw::rc_t cw::nbmpscq::push( handle_t h, const void* blob, unsigned blobByteN ) { // TODO: continue to iterate through the blocks waiting for the consumer // to make more space available. + //_block_report(p); + + // BEWARE: BUG BUG BUG: Since the cwLog makes calls to cwWebSocket + // this error message, and subsequent error messages, + // will result in a recursive loop which will crash the program. rc = cwLogError(kBufTooSmallRC,"NbMpScQueue overflow."); + } return rc; @@ -274,50 +336,102 @@ cw::rc_t cw::nbmpscq::push( handle_t h, const void* blob, unsigned blobByteN ) cw::nbmpscq::blob_t cw::nbmpscq::get( handle_t h ) { - blob_t blob; - nbmpscq_t* p = _handleToPtr(h); - - node_t* t = p->tail; - node_t* n = t->next.load(std::memory_order_acquire); // ACQUIRE 'next' from producer - - if( n == nullptr ) - { - blob.blob = nullptr; - blob.blobByteN = 0; - } - else - { - blob.blob = (uint8_t*)(n+1); - blob.blobByteN = n->blobByteN; - } - - return blob; + blob_t blob; + nbmpscq_t* p = _handleToPtr(h); + + // We always access the tail element through tail->next. + node_t* node = p->tail->next.load(std::memory_order_acquire); // ACQUIRE 'next' from producer + + _init_blob( blob, node ); + return blob; } -cw::rc_t cw::nbmpscq::advance( handle_t h ) -{ - nbmpscq_t* p = _handleToPtr(h); - rc_t rc = kOkRC; - node_t* t = p->tail; - node_t* next = t->next.load(std::memory_order_acquire); // ACQUIRE 'next' from producer +cw::nbmpscq::blob_t cw::nbmpscq::advance( handle_t h ) +{ + blob_t blob; + nbmpscq_t* p = _handleToPtr(h); + node_t* t = p->tail; + // We always access the tail element through tail->next. + node_t* next = t->next.load(std::memory_order_acquire); // ACQUIRE 'next' from producer + + // We always leave the last element on the queue to act as 'stub'. if( next != nullptr ) { p->tail = next; - block_t* b = next->block; - int eleN = b->eleN.fetch_add(-1,std::memory_order_acq_rel); + // first 'stub' will not have a valid block pointer + if( t->block != nullptr ) + { + int eleN = t->block->eleN.fetch_add(-1,std::memory_order_acq_rel); - // next was valid and so eleN must be >= 1 - assert( eleN >= 1 ); + // next was valid and so eleN must be >= 1 + assert( eleN >= 1 ); + } } if( p->cleanBlkN.load(std::memory_order_relaxed) > 0 ) _clean(p); - return rc; + + _init_blob(blob,next); + + return blob; +} + + +cw::nbmpscq::blob_t cw::nbmpscq::peek( handle_t h ) +{ + blob_t blob; + nbmpscq_t* p = _handleToPtr(h); + node_t* n = p->peek; + + // if p->peek is not set ... + if( n == nullptr ) + { + // ... then set it to the tail + n = p->tail->next.load(std::memory_order_acquire); // ACQUIRE 'next' from producer + + } + + _init_blob(blob,n); + + if( n != nullptr ) + p->peek = n->next.load(std::memory_order_acquire); + + return blob; +} + +void ::cw::nbmpscq::peek_reset(handle_t h) +{ + nbmpscq_t* p = _handleToPtr(h); + p->peek = nullptr; +} + + + +bool cw::nbmpscq::is_empty( handle_t h ) +{ + nbmpscq_t* p = _handleToPtr(h); + + node_t* t = p->tail; + node_t* next = t->next.load(std::memory_order_acquire); // ACQUIRE 'next' from producer + + return next == nullptr; +} + +unsigned cw::nbmpscq::count( handle_t h ) +{ + nbmpscq_t* p = _handleToPtr(h); + + block_t* b = p->blockL; + int eleN = 0; + for(; b!=nullptr; b=b->link) + eleN += b->eleN.load(std::memory_order_acquire); + + return eleN; } cw::rc_t cw::nbmpscq::test( const object_t* cfg ) diff --git a/cwNbMpScQueue.h b/cwNbMpScQueue.h index 2e775da..591d6bf 100644 --- a/cwNbMpScQueue.h +++ b/cwNbMpScQueue.h @@ -34,30 +34,58 @@ namespace cw namespace nbmpscq { typedef handle handle_t; - + rc_t create( handle_t& hRef, unsigned initBlkN, unsigned blkByteN ); rc_t destroy( handle_t& hRef ); + // + // Producer Function + // + // push() is called by multiple producer threads to insert // an element in the queue. Note that the 'blob' is copied into // the queue and therefore can be released by the caller. rc_t push( handle_t h, const void* blob, unsigned blobByteN ); + + // + // Consumer Functions + // + typedef struct blob_str { + rc_t rc; const void* blob; unsigned blobByteN; } blob_t; // get() is called by the single consumer thread to access the - // current blob at the front of the queue. Note that this call + // oldest record in the queue. Note that this call // does not change the state of the queue. blob_t get( handle_t h ); - // advance() disposes of the blob at the front of the + // advance() disposes of the oldest blob in the // queue and makes the next blob current. - rc_t advance( handle_t h ); + blob_t advance( handle_t h ); + + // The queue maintains a single internal iterator which the consumer + // may use to traverse stored records without removing them. + // The first call to peek() will return the oldest stored record. + // Each subsequent call to peek() will return the next stored record + // until no records are available - at which point blob_t.blob will be + // set to 'nullptr'. The following call will then revert to returning + // the oldest stored record. + blob_t peek( handle_t h ); + + // Reset peek to point to the oldest stored record. + void peek_reset( handle_t h ); + + // Return true if the queue is empty. + bool is_empty( handle_t h ); + + // Count of elements in the queue. + unsigned count( handle_t h ); rc_t test( const object_t* cfg ); diff --git a/cwObject.cpp b/cwObject.cpp index a84a6c8..4120583 100644 --- a/cwObject.cpp +++ b/cwObject.cpp @@ -5,6 +5,7 @@ #include "cwCommonImpl.h" #include "cwMem.h" #include "cwFile.h" +#include "cwTest.h" #include "cwLex.h" #include "cwText.h" #include "cwNumericConvert.h" @@ -152,37 +153,37 @@ namespace cw void _objTypePrintIndent( const char* text, unsigned indent, const char* indentStr=" " ) { for(unsigned i=0; itype->print(o,c); - printf("%s",eolStr); + cwLogPrint("%s",eolStr); } - void _objTypePrintNull( const object_t* o, print_ctx_t& c ) { printf("NULL "); } - void _objTypePrintError( const object_t* o, print_ctx_t& c ) { printf("Error "); } - void _objTypePrintChar( const object_t* o, print_ctx_t& c ) { printf("%c",o->u.c); } - void _objTypePrintInt8( const object_t* o, print_ctx_t& c ) { printf("%i",o->u.i8); } - void _objTypePrintUInt8( const object_t* o, print_ctx_t& c ) { printf("%i",o->u.u8); } - void _objTypePrintInt16( const object_t* o, print_ctx_t& c ) { printf("%i",o->u.i16); } - void _objTypePrintUInt16( const object_t* o, print_ctx_t& c ) { printf("%i",o->u.u16); } - void _objTypePrintInt32( const object_t* o, print_ctx_t& c ) { printf("%i",o->u.i32); } - void _objTypePrintUInt32( const object_t* o, print_ctx_t& c ) { printf("%i",o->u.u32); } - void _objTypePrintInt64( const object_t* o, print_ctx_t& c ) { printf("%lli", o->u.i64); } - void _objTypePrintUInt64( const object_t* o, print_ctx_t& c ) { printf("%lli", o->u.u64); } - void _objTypePrintBool( const object_t* o, print_ctx_t& c ) { printf("%s",o->u.b ? "true" : "false"); } - void _objTypePrintFloat( const object_t* o, print_ctx_t& c ) { printf("%f",o->u.f); } - void _objTypePrintDouble( const object_t* o, print_ctx_t& c ) { printf("%f",o->u.d); } - void _objTypePrintString( const object_t* o, print_ctx_t& c ) { printf("%s",o->u.str); } - void _objTypePrintVect( const object_t* o, print_ctx_t& c ) { printf(""); } + void _objTypePrintNull( const object_t* o, print_ctx_t& c ) { cwLogPrint("NULL "); } + void _objTypePrintError( const object_t* o, print_ctx_t& c ) { cwLogPrint("Error "); } + void _objTypePrintChar( const object_t* o, print_ctx_t& c ) { cwLogPrint("%c",o->u.c); } + void _objTypePrintInt8( const object_t* o, print_ctx_t& c ) { cwLogPrint("%i",o->u.i8); } + void _objTypePrintUInt8( const object_t* o, print_ctx_t& c ) { cwLogPrint("%i",o->u.u8); } + void _objTypePrintInt16( const object_t* o, print_ctx_t& c ) { cwLogPrint("%i",o->u.i16); } + void _objTypePrintUInt16( const object_t* o, print_ctx_t& c ) { cwLogPrint("%i",o->u.u16); } + void _objTypePrintInt32( const object_t* o, print_ctx_t& c ) { cwLogPrint("%i",o->u.i32); } + void _objTypePrintUInt32( const object_t* o, print_ctx_t& c ) { cwLogPrint("%i",o->u.u32); } + void _objTypePrintInt64( const object_t* o, print_ctx_t& c ) { cwLogPrint("%lli", o->u.i64); } + void _objTypePrintUInt64( const object_t* o, print_ctx_t& c ) { cwLogPrint("%lli", o->u.u64); } + void _objTypePrintBool( const object_t* o, print_ctx_t& c ) { cwLogPrint("%s",o->u.b ? "true" : "false"); } + void _objTypePrintFloat( const object_t* o, print_ctx_t& c ) { cwLogPrint("%f",o->u.f); } + void _objTypePrintDouble( const object_t* o, print_ctx_t& c ) { cwLogPrint("%f",o->u.d); } + void _objTypePrintString( const object_t* o, print_ctx_t& c ) { cwLogPrint("%s",o->u.str); } + void _objTypePrintVect( const object_t* o, print_ctx_t& c ) { cwLogPrint(""); } void _objTypePrintPair( const object_t* o, print_ctx_t& c ) { o->u.children->type->print(o->u.children,c); - printf(": "); + cwLogPrint(": "); o->u.children->sibling->type->print(o->u.children->sibling,c); } @@ -869,11 +870,17 @@ cw::rc_t cw::objectFromString( const char* s, object_t*& objRef ) break; case lex::kRealLexTId: - _objCreateValueNode( cnp, lex::tokenDouble(lexH), "real" ); + if( tokenIsSinglePrecision(lexH) ) + _objCreateValueNode( cnp, lex::tokenFloat(lexH),"float" ); + else + _objCreateValueNode( cnp, lex::tokenDouble(lexH), "double" ); break; case lex::kIntLexTId: - _objCreateValueNode( cnp, lex::tokenInt(lexH), "int" ); + if( tokenIsUnsigned(lexH) ) + _objCreateValueNode( cnp, lex::tokenUInt(lexH), "uint" ); + else + _objCreateValueNode( cnp, lex::tokenInt(lexH), "int" ); break; case lex::kHexLexTId: @@ -907,7 +914,7 @@ cw::rc_t cw::objectFromString( const char* s, object_t*& objRef ) s[n] = 0; //char* v = mem::duplStr(lex::tokenText(lexH),lex::tokenCharCount(lexH)); - unsigned identFl = lexId == lex::kIdentLexTId ? kIdentFl : 0; + unsigned identFl = lexId != lex::kQStrLexTId ? kIdentFl : 0; _objCreateValueNode( cnp, s, "string", identFl ); } @@ -1007,4 +1014,118 @@ cw::rc_t cw::objectToFile( const char* fn, const object_t* obj ) return rc; } +namespace cw +{ + rc_t _object_test_basic( const test::test_args_t& args ) + { + rc_t rc = kOkRC; + cw::object_t* o = nullptr; + const char s [] = "{ a:1, b:2, c:[ 1.23, 4.56 ], d:true, e:false, f:true }"; + + int a = 0; + int b = 0; + const cw::object_t* c = nullptr; + bool d,e,f; + + const unsigned bufN = 128; + char buf[bufN]; + + unsigned i = 0; + + cw::object_t* oo = nullptr; + + if((rc = cw::objectFromString(s,o)) != kOkRC ) + goto errLabel; + + int v; + if((rc = o->get("b",v)) != kOkRC ) + goto errLabel; + + cwLogPrint("value:%i\n",v); + + o->print(); + + + if((rc = o->getv("a",a,"b",b)) != kOkRC ) + goto errLabel; + + cwLogPrint("G: %i %i\n",a,b); + + + if((rc = o->readv("a",0,a, + "b",0,b, + "c",cw::kOptFl | cw::kListTId,c, + "d",0,d, + "e",0,e, + "f",0,f)) != kOkRC ) + { + goto errLabel; + } + + cwLogPrint("R: %i %i : %i %i %i\n",a,b,d,e,f); + + + i = o->to_string(buf,bufN); + cwLogPrint("%i : %s\n",i, buf); + + oo = o->duplicate(); + + oo->print(); + + oo->free(); + + + o->free(); + + errLabel: + return rc; + } + + rc_t _object_test_to_json( const test::test_args_t& args ) + { + double v0[] = {1.23,2.34,3.45}; + unsigned v0N = sizeof(v0)/sizeof(v0[0]); + int v1[] = {-1,0,1,2,3,4}; + unsigned v1N = sizeof(v1)/sizeof(v1[0]); + + cw::object_t* d = cw::newDictObject(); + + d->putv("A","Abc","B",1.234); + d->put_numeric_list("v0",v0,v0N); + d->put_numeric_list("v1",v1,v1N); + + char* s = d->to_string(); + cwLogPrint("%s\n",s); + cw::mem::release(s); + + d->free(); + + return kOkRC; + + } + +} + + +cw::rc_t cw::object_test( const test::test_args_t& args ) +{ + rc_t rc = kOkRC; + + if( textIsEqual(args.test_label,"basic") ) + { + rc = _object_test_basic(args); + goto errLabel; + } + + if( textIsEqual(args.test_label,"to_json") ) + { + rc = _object_test_to_json(args); + goto errLabel; + } + + rc = cwLogError(kInvalidArgRC,"Unknown test case module:%s test:%s.",args.module_label,args.test_label); + +errLabel: + return rc; +} diff --git a/cwObject.h b/cwObject.h index e86bc12..8bc78d7 100644 --- a/cwObject.h +++ b/cwObject.h @@ -31,7 +31,8 @@ namespace cw kRootTId = 0x00100000, kHexFl = 0x10000000, - kIdentFl = 0x20000000 + kIdentFl = 0x20000000, + kOptFl = 0x40000000 }; @@ -135,12 +136,19 @@ namespace cw // Value containers are parents of leaf nodes. (A dictionary is not a value container because it's children are pairs with are not leaf nodes.) inline bool is_value_container() const { return type != nullptr && cwIsFlag(type->flags,kValueContainerFl); } + inline unsigned type_id() const { return type==nullptr ? (unsigned)kInvalidTId : type->id; } + // Containers have children and use the object.u.children pointer. inline bool is_container() const { return type != nullptr && cwIsFlag(type->flags,kContainerFl); } inline bool is_pair() const { return type != nullptr && type->id == kPairTId; } inline bool is_dict() const { return type != nullptr && type->id == kDictTId; } inline bool is_list() const { return type != nullptr && type->id == kListTId; } inline bool is_string() const { return type != nullptr && (type->id == kStringTId || type->id == kCStringTId); } + inline bool is_unsigned_integer() const { return type->id==kCharTId || type->id==kUInt8TId || type->id==kUInt16TId || type->id==kUInt32TId || type->id==kUInt64TId; } + inline bool is_signed_integer() const { return type->id==kInt8TId || type->id==kInt16TId || type->id==kInt32TId || type->id==kInt64TId; } + inline bool is_floating_point() const { return type->id==kFloatTId || type->id==kDoubleTId; } + inline bool is_integer() const { return is_unsigned_integer() || is_signed_integer(); } + inline bool is_numeric() const { return is_integer() || is_floating_point(); } inline bool is_type( unsigned tid ) const { return type != nullptr && type->id == tid; } rc_t value( void* dst, unsigned dstTypeId ); @@ -199,7 +207,115 @@ namespace cw const struct object_str* next_child_ele( const struct object_str* ele) const; struct object_str* next_child_ele( struct object_str* ele); + typedef struct read_str + { + const char* label; + unsigned flags; + const struct read_str* link; + } read_t; + + template< typename T > + rc_t read( const char* label, unsigned flags, T& v ) const + { + const struct object_str* o; + if((o = find(label, 0)) == nullptr ) + { + if( cwIsNotFlag(flags, kOptFl) ) + return cwLogError(kInvalidIdRC,"The pair label '%s' could not be found.",cwStringNullGuard(label)); + + return kEleNotFoundRC; + } + else + { + flags = cwClrFlag(flags,kOptFl); + if( flags && cwIsNotFlag(o->type->id,flags) ) + return cwLogError(kInvalidDataTypeRC,"The field '%s' data type 0x%x does not match 0x%x.",cwStringNullGuard(label),o->type->id,flags); + } + + + return o->value(v); + } + rc_t _readv(const read_t* list) const + { + rc_t rc = kOkRC; + + unsigned childN = child_count(); + + // for each child of this dict node + for(unsigned i=0; iis_pair() ) + { + rc = cwLogError(kSyntaxErrorRC,"A non-pair element was encountered inside a dictionary."); + goto errLabel; + } + + if( (label = child->pair_label()) == nullptr ) + { + rc = cwLogError(kInvalidStateRC,"A blank label was encountered as a dictionary label."); + goto errLabel; + } + + // verify that this is a known label + // (all labels in the dictionary must be known - this prevents mispelled fields from being inadverently skipped during parsing) + for(; r!=nullptr; r=r->link) + if( strcmp(r->label,label) == 0 ) + break; + + if( r == nullptr ) + { + rc = cwLogError(kSyntaxErrorRC,"The unknown field '%s' was encountered.",cwStringNullGuard(label)); + goto errLabel; + } + + } + + errLabel: + return rc; + } + + // readv("label0",v0,"label1",v1, ... ) + template< typename T0, typename T1, typename... ARGS > + rc_t _readv( const read_t* list, T0 label, unsigned flags, T1& valRef, ARGS&&... args ) const + { + rc_t rc = read(label,flags,valRef); + + read_t r = { .label=label, .flags=flags, .link=list }; + + // if no error occurred .... + if( rc == kOkRC || (rc == kEleNotFoundRC && cwIsFlag(flags,kOptFl))) + rc = _readv(&r, std::forward(args)...); // ... recurse to find next label/value pair + else + rc = cwLogError(rc,"Object parse failed for the pair label:'%s'.",cwStringNullGuard(label)); + + return rc; + } + + + // readv("label0",flags0,v0,"label1",flags0,v1, ... ) + // Use kOptFl for optional fields. + // Use kListTId and kDictTId to validate the type of container fields. + // In general it should not be necessary to validate numeric and string types because + // they are validated by virtue of being converted to the returned value. + template< typename T0, typename T1, typename... ARGS > + rc_t readv( T0 label, unsigned flags, T1& valRef, ARGS&&... args ) const + { return _readv(nullptr, label,flags,valRef,args...); } + + + + + // Set flag 'kRecurseFl' to recurse into the object in search of the value. // Set flag 'kOptionalFl' if the label is optional and may not exist. template< typename T > @@ -344,6 +460,8 @@ namespace cw rc_t objectToFile( const char* fn, const object_t* obj ); + rc_t object_test( const test::test_args_t& args ); + } diff --git a/cwObjectTemplate.h b/cwObjectTemplate.h index 81027b4..7fca3ec 100644 --- a/cwObjectTemplate.h +++ b/cwObjectTemplate.h @@ -74,6 +74,12 @@ namespace cw return obj; } + template<> object_t* _objSetLeafValue( object_t* obj, float value ) + { + obj->u.f = value; + obj->type = _objIdToType(kFloatTId); + return obj; + } template<> object_t* _objSetLeafValue( object_t* obj, double value ) { diff --git a/cwPerfMeas.cpp b/cwPerfMeas.cpp index 999a355..392d1b9 100644 --- a/cwPerfMeas.cpp +++ b/cwPerfMeas.cpp @@ -1,6 +1,7 @@ #include "cwCommon.h" #include "cwLog.h" #include "cwCommonImpl.h" +#include "cwTest.h" #include "cwMem.h" #include "cwText.h" #include "cwFile.h" diff --git a/cwPianoScore.cpp b/cwPianoScore.cpp index 1fa8c24..9305f9e 100644 --- a/cwPianoScore.cpp +++ b/cwPianoScore.cpp @@ -1,6 +1,7 @@ #include "cwCommon.h" #include "cwLog.h" #include "cwCommonImpl.h" +#include "cwTest.h" #include "cwMem.h" #include "cwText.h" #include "cwObject.h" diff --git a/cwPresetSel.cpp b/cwPresetSel.cpp index 2a3d98f..1c75cd2 100644 --- a/cwPresetSel.cpp +++ b/cwPresetSel.cpp @@ -1,16 +1,18 @@ #include "cwCommon.h" #include "cwLog.h" #include "cwCommonImpl.h" +#include "cwTest.h" #include "cwMem.h" #include "cwText.h" #include "cwObject.h" #include "cwTime.h" #include "cwVectOps.h" +#include "cwMidi.h" +#include "cwMidiDecls.h" #include "cwFlowDecl.h" #include "cwPresetSel.h" #include "cwFile.h" -#include "cwMidi.h" #include "cwDynRefTbl.h" #include "cwScoreParse.h" #include "cwSfScore.h" @@ -104,38 +106,52 @@ namespace cw return kInvalidIdx; } - - rc_t _delete_fragment( preset_sel_t* p, unsigned fragId ) - { - frag_t* f0 = nullptr; - frag_t* f1 = p->fragL; - - for(; f1!=nullptr; f1=f1->link) - { - if( f1->fragId == fragId ) - { - if( f0 == nullptr ) - p->fragL = f1->link; - else - f0->link = f1->link; - for(unsigned i=0; ipresetN; ++i) - mem::release(f1->presetA[i].alt_str); + rc_t _delete_fragment( preset_sel_t* p, unsigned fragId ) + { + rc_t rc = kOkRC; + frag_t* f = p->fragL; + + for(; f!=nullptr; f=f->link) + { + if( f->fragId == fragId ) + { + // if this is the first frag in the list + if( f->prev == nullptr ) + p->fragL = f->link; + else + { + + // link the prev fragment to the next fragment + f->prev->link = f->link; + + // dur of prev frag now include the dur of the deleted frag + f->prev->endLoc = f->endLoc; + } + + // link the next fragment back to the previous fragment + if( f->link != nullptr ) + f->link->prev = f->prev; + + for(unsigned i=0; ipresetN; ++i) + mem::release(f->presetA[i].alt_str); // release the fragment - mem::release(f1->note); - mem::release(f1->presetA); - mem::release(f1); - - return kOkRC; + mem::release(f->note); + mem::release(f->presetA); + mem::release(f->altPresetIdxA); + //mem::release(f->multiPresetA); + mem::release(f); + goto errLabel; } - - f0 = f1; } - - return kOkRC; - } + rc = cwLogError(kEleNotFoundRC,"The fragment with id %i was not found.",fragId); + + errLabel: + return rc; + } + void _destroy_all_frags( preset_sel_t* p ) { while( p->fragL != nullptr) @@ -245,7 +261,7 @@ namespace cw else { unsigned alt_strN = textLength(alt_str); - char alt_str_buf[ alt_strN+1 ] = {0}; + char alt_str_buf[ alt_strN+1 ]; unsigned asi = 0; // clear the alt's pointing to the selected preset - because the 'alt_str' has changed @@ -260,6 +276,9 @@ namespace cw assert( asi < alt_strN ); alt_str_buf[ asi++ ] = alt_str[i]; } + + assert( asi <= alt_strN ); + alt_str_buf[asi] = 0; // store the preset's new alt str. f->presetA[ sel_preset_idx ].alt_str = mem::reallocStr(f->presetA[ sel_preset_idx ].alt_str, alt_str_buf); @@ -1209,33 +1228,7 @@ cw::rc_t cw::preset_sel::create_fragment( handle_t h, unsigned end_loc, time::sp cw::rc_t cw::preset_sel::delete_fragment( handle_t h, unsigned fragId ) { preset_sel_t* p = _handleToPtr(h); - frag_t* f = p->fragL; - - for(; f!=nullptr; f=f->link) - if( f->fragId == fragId ) - { - if( f->prev == nullptr ) - p->fragL = f->link; - else - { - // the previous fragment's end-loc become the - // endloc of the deleted fragment - f->prev->endLoc = f->endLoc; - f->prev->link = f->link; - } - - if( f->link != nullptr ) - f->link->prev = f->prev; - - // release the fragment - mem::release(f->presetA); - //mem::release(f->multiPresetA); - mem::release(f); - - return kOkRC; - } - - return cwLogError(kInvalidArgRC,"The fragment '%i' could not be found to delete.",fragId); + return _delete_fragment(p,fragId); } bool cw::preset_sel::is_fragment_end_loc( handle_t h, unsigned loc ) diff --git a/cwPvAudioFileProc.cpp b/cwPvAudioFileProc.cpp index ee37283..f4d54c3 100644 --- a/cwPvAudioFileProc.cpp +++ b/cwPvAudioFileProc.cpp @@ -1,6 +1,7 @@ #include "cwCommon.h" #include "cwLog.h" #include "cwCommonImpl.h" +#include "cwTest.h" #include "cwMem.h" #include "cwFile.h" #include "cwText.h" diff --git a/cwScoreFollowTest.cpp b/cwScoreFollowTest.cpp index da158ca..f3de48b 100644 --- a/cwScoreFollowTest.cpp +++ b/cwScoreFollowTest.cpp @@ -1,6 +1,7 @@ #include "cwCommon.h" #include "cwLog.h" #include "cwCommonImpl.h" +#include "cwTest.h" #include "cwMem.h" #include "cwText.h" #include "cwObject.h" diff --git a/cwScoreFollower.cpp b/cwScoreFollower.cpp index 7412a8e..975dbbe 100644 --- a/cwScoreFollower.cpp +++ b/cwScoreFollower.cpp @@ -1,6 +1,7 @@ #include "cwCommon.h" #include "cwLog.h" #include "cwCommonImpl.h" +#include "cwTest.h" #include "cwMem.h" #include "cwText.h" #include "cwObject.h" diff --git a/cwScoreParse.cpp b/cwScoreParse.cpp index f4158fd..0956166 100644 --- a/cwScoreParse.cpp +++ b/cwScoreParse.cpp @@ -1,6 +1,7 @@ #include "cwCommon.h" #include "cwLog.h" #include "cwCommonImpl.h" +#include "cwTest.h" #include "cwMem.h" #include "cwText.h" #include "cwObject.h" diff --git a/cwScoreTest.cpp b/cwScoreTest.cpp index 653cc25..e5964d2 100644 --- a/cwScoreTest.cpp +++ b/cwScoreTest.cpp @@ -1,6 +1,7 @@ #include "cwCommon.h" #include "cwLog.h" #include "cwCommonImpl.h" +#include "cwTest.h" #include "cwMem.h" #include "cwText.h" #include "cwObject.h" diff --git a/cwSfAnalysis.cpp b/cwSfAnalysis.cpp index 26044e8..4cf3866 100644 --- a/cwSfAnalysis.cpp +++ b/cwSfAnalysis.cpp @@ -1,6 +1,7 @@ #include "cwCommon.h" #include "cwLog.h" #include "cwCommonImpl.h" +#include "cwTest.h" #include "cwMem.h" #include "cwFile.h" #include "cwObject.h" diff --git a/cwSfMatch.cpp b/cwSfMatch.cpp index 89bf5b0..8428ec9 100644 --- a/cwSfMatch.cpp +++ b/cwSfMatch.cpp @@ -1,6 +1,7 @@ #include "cwCommon.h" #include "cwLog.h" #include "cwCommonImpl.h" +#include "cwTest.h" #include "cwMem.h" #include "cwText.h" #include "cwObject.h" diff --git a/cwSfScore.cpp b/cwSfScore.cpp index bb9119c..b868e20 100644 --- a/cwSfScore.cpp +++ b/cwSfScore.cpp @@ -1,6 +1,7 @@ #include "cwCommon.h" #include "cwLog.h" #include "cwCommonImpl.h" +#include "cwTest.h" #include "cwMem.h" #include "cwText.h" #include "cwObject.h" diff --git a/cwSfScoreParser.cpp b/cwSfScoreParser.cpp index cd11484..5b9f293 100644 --- a/cwSfScoreParser.cpp +++ b/cwSfScoreParser.cpp @@ -830,7 +830,7 @@ unsigned cw::sfscore::opcode_label_to_id( const char* label ) const char* cw::sfscore::opcode_id_to_label( unsigned opcode_id ) { const char* label; - if((label = idToLabel( _opcodeMapA, opcode_id, kInvalidEvtScId)) == nullptr ) + if((label = idToLabelNull( _opcodeMapA, opcode_id, kInvalidEvtScId)) == nullptr ) cwLogError(kInvalidArgRC,"The event opcode type id '%i' is not valid.",opcode_id); return label; diff --git a/cwSfTrack.cpp b/cwSfTrack.cpp index f718de7..1004147 100644 --- a/cwSfTrack.cpp +++ b/cwSfTrack.cpp @@ -1,6 +1,7 @@ #include "cwCommon.h" #include "cwLog.h" #include "cwCommonImpl.h" +#include "cwTest.h" #include "cwMem.h" #include "cwText.h" #include "cwObject.h" diff --git a/cwSvgMidi.cpp b/cwSvgMidi.cpp index 1c39877..33f5b83 100644 --- a/cwSvgMidi.cpp +++ b/cwSvgMidi.cpp @@ -1,6 +1,7 @@ #include "cwCommon.h" #include "cwLog.h" #include "cwCommonImpl.h" +#include "cwTest.h" #include "cwMem.h" #include "cwObject.h" #include "cwText.h" diff --git a/cwSvgScoreFollow.cpp b/cwSvgScoreFollow.cpp index 880ea7d..4266187 100644 --- a/cwSvgScoreFollow.cpp +++ b/cwSvgScoreFollow.cpp @@ -1,6 +1,7 @@ #include "cwCommon.h" #include "cwLog.h" #include "cwCommonImpl.h" +#include "cwTest.h" #include "cwMem.h" #include "cwObject.h" #include "cwText.h" diff --git a/cwTest.cpp b/cwTest.cpp new file mode 100644 index 0000000..b656db0 --- /dev/null +++ b/cwTest.cpp @@ -0,0 +1,540 @@ +#include "cwCommon.h" +#include "cwLog.h" +#include "cwCommonImpl.h" +#include "cwTest.h" +#include "cwMem.h" +#include "cwText.h" +#include "cwNumericConvert.h" +#include "cwObject.h" +#include "cwLex.h" + +#include "cwTime.h" +#include "cwFile.h" +#include "cwFileSys.h" +#include "cwVectOps.h" +#include "cwFlowTest.h" + + +namespace cw +{ + namespace test + { + typedef struct test_map_str + { + const char* module_name; + test_func_t test_func; + } test_map_t; + + test_map_t _test_map[] = { + { "/lex", lex::test }, + { "/filesys", filesys::test }, + { "/object", object_test }, + { "/vop", vop::test }, + { "/time", time::test }, + { "/flow", flow::test }, + { nullptr, nullptr }, + }; + + typedef struct test_str + { + int argc; // extra cmd line arguments to be passes to the test cases + const char** argv; + + const char* base_dir; // base test dictionary + const object_t* test_cfg; // top level test cfg. + + const char* rsrc_folder; // name of the 'rsrc' folder in the base dir + const char* out_folder; // name of the output folder in the base dir + const char* ref_folder; // name of the test reference folder in the base dir + + const char* sel_module_label; // selected module label + const char* sel_test_label; // selected test label + bool all_module_fl; // true if all modules should be run + bool all_test_fl; // true if all tests in the selected module should be run + bool compare_fl; // true if compare operation should be run + bool echo_fl; // echo test output to the console (false=write all output to log file only) + bool gen_report_fl; // print module/test names as the gen phase is executes + + void* logCbArg; // original log callback args + log::logOutputCbFunc_t logCbFunc; + + const char* cur_test_label; // current test label + file::handle_t cur_log_fileH; // current log file handle + + unsigned gen_cnt; // count of tests which generated output + unsigned compare_ok_cnt; // count of tests which passed the compare pass + unsigned compare_fail_cnt; // count of tests which failed the compare test + + } test_t; + + + rc_t _parse_args( test_t& test, const object_t* cfg, int argc, const char** argv ) + { + rc_t rc = kOkRC; + unsigned argN = 1; + char* out_dir = nullptr; + int argi = 1; + + test.all_module_fl = true; + test.all_test_fl = true; + + if( argc > 1 ) + { + test.sel_module_label = argv[1]; + test.all_module_fl = textIsEqual(test.sel_module_label,"all"); + argi += 1; + } + + if( argc > 2 ) + { + test.sel_test_label = argv[2]; + test.all_test_fl = textIsEqual(test.sel_test_label,"all"); + argi += 1; + } + + for(int i=argi; ireadv("base_dir",0,test.base_dir, + "test",kDictTId,test.test_cfg, + "resource_dir",0,test.rsrc_folder, + "output_dir",0,test.out_folder, + "ref_dir",0,test.ref_folder)) != kOkRC ) + { + goto errLabel; + } + + if((out_dir = filesys::makeFn(test.base_dir, nullptr, nullptr, test.out_folder, nullptr )) == nullptr ) + { + rc = cwLogError(kOpFailRC,"Output directory name formation failed."); + goto errLabel; + } + + if( !filesys::isDir(out_dir)) + { + if((rc = filesys::makeDir(out_dir)) != kOkRC ) + goto errLabel; + } + + errLabel: + mem::release(out_dir); + return rc; + } + + void _exec_test_log_cb( void* cbArg, unsigned level, const char* text ) + { + rc_t rc; + test_t* r = (test_t*)cbArg; + + if((rc = file::print(r->cur_log_fileH,text)) != kOkRC ) + cwLogError(rc,"Log file write failed for '%s'.",text); + + if( r->logCbFunc != nullptr && r->echo_fl ) + r->logCbFunc( r->logCbArg,level,text); + } + + rc_t _exec_dispatch( test_t& test, test_args_t& args ) + { + rc_t rc = kOkRC; + unsigned i = 0; + + // find the requested test function .... + for(; _test_map[i].module_name!=nullptr; ++i) + if( textIsEqual(_test_map[i].module_name,args.module_label) ) + { + rc = _test_map[i].test_func(args); //.... and call it + break; + } + + if( _test_map[i].module_name==nullptr ) + { + rc = cwLogError(kEleNotFoundRC,"The test function for module %s was not found.",cwStringNullGuard(args.module_label)); + goto errLabel; + } + + errLabel: + return rc; + } + + rc_t _compare_one_test( test_t& test, const char* ref_dir, const char* test_dir ) + { + rc_t rc = kOkRC; + unsigned testRefDirN = 0; + filesys::dirEntry_t* testRefDirA = nullptr; + bool ok_fl = true; + + // get the list of files in the test directory + if((testRefDirA = filesys::dirEntries( ref_dir, filesys::kFileFsFl, &testRefDirN )) == nullptr ) + { + rc = cwLogError(kOpFailRC,"An error occurred while attempting to read the directory file names from '%s'.",cwStringNullGuard(ref_dir)); + goto errLabel; + } + + // for each file + for(unsigned j=0; rc==kOkRC && jchild_count(); + + // for each test case + for(unsigned i=0; ichild_ele(i); + const char* test_label = test_pair->pair_label(); + + // apply filter/test label filter + if( (test.all_module_fl || textIsEqual(module_label,test.sel_module_label)) && + ( test.all_test_fl || textIsEqual(test_label,test.sel_test_label))) + { + if((rc = _exec_one_test(test, module_label, module_args, test_label, test_pair->pair_value() )) != kOkRC ) + { + goto errLabel; + } + } + } + + errLabel: + return rc; + } + + rc_t _exec_module( test_t& test, const char* module_label, const object_t* module_args, const object_t* module_cfg ) + { + rc_t rc = kOkRC; + + const object_t* cases_cfg = nullptr; + const object_t* mod_args_cfg = nullptr; + + if((rc = module_cfg->getv_opt("cases",cases_cfg, + "module_args",mod_args_cfg)) != kOkRC ) + { + rc = cwLogError(rc,"Parse failed on module fields in '%s'.",cwStringNullGuard(module_label)); + goto errLabel; + } + + if( cases_cfg == nullptr ) + cases_cfg = module_cfg; + + if( mod_args_cfg == nullptr ) + mod_args_cfg = module_args; + + if((rc = _exec_cases( test,module_label,mod_args_cfg,cases_cfg)) != kOkRC ) + goto errLabel; + + errLabel: + return rc; + } + + rc_t _proc_test_cfg( test_t& test, const char* module_label, const object_t* test_cfg ); + + rc_t _proc_test_from_file( test_t& test, const char* module_label, const char* fname ) + { + rc_t rc = kOkRC; + char* cfg_fname; + object_t* test_cases_cfg = nullptr; + const char* orig_base_dir = test.base_dir; + char* new_base_dir = nullptr; + + if((cfg_fname = filesys::makeFn(test.base_dir, fname, nullptr, nullptr )) == nullptr ) + { + rc = cwLogError(kOpFailRC,"The test cases file name for the module '%s' in '%s' / '%s' could not be formed.",cwStringNullGuard(module_label),cwStringNullGuard(test.base_dir),cwStringNullGuard(fname)); + goto errLabel; + } + + if((rc = objectFromFile( cfg_fname, test_cases_cfg )) != kOkRC ) + { + rc = cwLogError(kOpFailRC,"Parse failed on the test case module file '%s'.",cwStringNullGuard(cfg_fname)); + goto errLabel; + } + + if((new_base_dir = filesys::makeFn(test.base_dir, nullptr, nullptr, test.out_folder, module_label, nullptr )) == nullptr ) + { + rc = cwLogError(kOpFailRC,"The base test directory name for the module '%s' could not be formed in '%s'.",cwStringNullGuard(test.base_dir),cwStringNullGuard(module_label)); + goto errLabel; + } + + if( !filesys::isDir(new_base_dir)) + { + rc = cwLogError(kOpFailRC,"The base test directory '%s' for the module '%s' does not exist.",cwStringNullGuard(test.base_dir),cwStringNullGuard(module_label)); + goto errLabel; + } + + rc = _proc_test_cfg( test, module_label, test_cases_cfg ); + + errLabel: + mem::release(cfg_fname); + mem::release(new_base_dir); + + if( test_cases_cfg != nullptr ) + test_cases_cfg->free(); + + + return rc; + } + + rc_t _proc_module( test_t& test, const char* base_module_label, const char* module_label, const object_t* module_cfg ) + { + rc_t rc = kOkRC; + char* new_module_label = filesys::makeFn(base_module_label,nullptr,nullptr,module_label,nullptr ); + + switch( module_cfg->type_id() ) + { + case kStringTId: + { + const char* s = nullptr; + if((rc = module_cfg->value(s)) != kOkRC ) + { + rc = cwLogError(rc,"Parse failed on module filename in '%s'.",cwStringNullGuard(module_label)); + goto errLabel; + } + + rc = _proc_test_from_file(test, new_module_label, s ); + } + break; + + case kDictTId: + rc = _proc_test_cfg(test, new_module_label, module_cfg ); + break; + + default: + break; + } + + errLabel: + mem::release(new_module_label); + return rc; + } + + rc_t _proc_test_cfg( test_t& test, const char* module_label, const object_t* test_cfg ) + { + rc_t rc = kOkRC; + const object_t* module_args = nullptr; + const object_t* modules_cfg = nullptr; + const object_t* cases_cfg = nullptr; + + + if((rc = test_cfg->getv_opt("module_args",module_args, + "modules",modules_cfg, + "cases",cases_cfg )) != kOkRC ) + { + rc = cwLogError(rc,"The 'module_args' parse failed on module '%s'.",cwStringNullGuard(module_label)); + goto errLabel; + } + + // if a list of modules was given + if( modules_cfg != nullptr ) + { + unsigned modulesN = modules_cfg->child_count(); + for(unsigned i=0; ichild_ele(i); + + if((rc = _proc_module(test, module_label, mod_pair->pair_label(), mod_pair->pair_value() )) != kOkRC ) + goto errLabel; + + + } + } + + // if + if(module_args==nullptr && modules_cfg==nullptr && cases_cfg==nullptr ) + { + cases_cfg = test_cfg; + } + + // if a list of cases was given + if( cases_cfg != nullptr ) + { + if((rc = _exec_cases(test,module_label,module_args,cases_cfg)) != kOkRC ) + goto errLabel; + + } + + errLabel: + return rc; + } + + } +} + +cw::rc_t cw::test::test( const struct object_str* cfg, int argc, const char** argv ) +{ + test_t test = {}; + rc_t rc = kOkRC; + + if((rc = _parse_args(test, cfg, argc, argv )) != kOkRC ) + { + rc = cwLogError(rc,"Test arguments parse failed."); + goto errLabel; + } + + if((rc = _proc_test_cfg(test,"/",test.test_cfg)) != kOkRC ) + { + goto errLabel; + } + +errLabel: + + cwLogInfo("Test Gen Count:%i.",test.gen_cnt); + + if( test.compare_fl ) + cwLogInfo("Test Compare - ok:%i fail:%i.",test.compare_ok_cnt,test.compare_fail_cnt); + + if( rc != kOkRC ) + rc = cwLogError(rc,"Testing process failed."); + + + return rc; + +} + + diff --git a/cwTest.h b/cwTest.h new file mode 100644 index 0000000..f39538c --- /dev/null +++ b/cwTest.h @@ -0,0 +1,31 @@ +#ifndef cwTest_h +#define cwTest_h + +namespace cw +{ + struct object_str; + + namespace test + { + + typedef struct test_args_str + { + const char* module_label; // test module this test belongs to + const char* test_label; // test label + const struct object_str* module_args; // arguments for all tests in this module + const struct object_str* test_args; // arguments specific to this test + const char* rsrc_dir; // input data dir. for this test + const char* out_dir; // output data dir. for this test + int argc; // cmd line arg count + const char** argv; // cmd line arg's + + } test_args_t; + + typedef rc_t (*test_func_t)(const test_args_t& args); + + rc_t test( const struct object_str* cfg, int argc, const char** argv ); + + } +} + +#endif diff --git a/cwText.cpp b/cwText.cpp index f6b4e7a..2ae2f89 100644 --- a/cwText.cpp +++ b/cwText.cpp @@ -146,7 +146,7 @@ const char* cw::nextNonWhiteChar( const char* s ) const char* cw::nextNonWhiteCharEOS( const char* s ) { return _nextNonWhiteChar(s,true); } -const char* cw::firstMatchChar( const char* s, char c ) +char* cw::firstMatchChar( char* s, char c ) { if( s == nullptr ) return nullptr; @@ -157,7 +157,12 @@ const char* cw::firstMatchChar( const char* s, char c ) return nullptr; } -const char* cw::lastMatchChar( const char* s, char c ) +const char* cw::firstMatchChar( const char* s, char c ) +{ + return firstMatchChar((char*)s,c); +} + +char* cw::lastMatchChar( char* s, char c ) { unsigned sn; @@ -168,13 +173,18 @@ const char* cw::lastMatchChar( const char* s, char c ) if( sn == 0 ) return nullptr; - for(const char* s1=s+(sn-1); s<=s1; --s1) + for(char* s1=s+(sn-1); s<=s1; --s1) if( *s1 == c ) return s1; return nullptr; } +const char* cw::lastMatchChar( const char* s, char c ) +{ + return lastMatchChar((char*)s,c); +} + bool cw::isInteger( const char* s ) { for(; *s; ++s) diff --git a/cwText.h b/cwText.h index cc44f06..89a8bf1 100644 --- a/cwText.h +++ b/cwText.h @@ -8,7 +8,7 @@ namespace cw unsigned textLength( const char* s ); // If dst is non-null then dst is always 0-terminated. - // If src will be truncated if srcN > dstN-1. + // src will be truncated if srcN > dstN-1. // If dst is null then null is returned // if src is null then dst[0] = 0. // if srcN is 0 then textLength(src) is used for srcN @@ -65,8 +65,11 @@ namespace cw // Return a pointer to the first occurrence of 'c' in s[] or nullptr // if 'c' does not occur in s[] + char* firstMatchChar( char* s, char c ); const char* firstMatchChar( const char* s, char c ); + // Find the last occurrent of 'c' in s[]. + char* lastMatchChar( char* s, char c ); const char* lastMatchChar( const char* s, char c ); bool isInteger( const char* ); // text contains only [0-9] diff --git a/cwTime.cpp b/cwTime.cpp index ef7f211..7fd5658 100644 --- a/cwTime.cpp +++ b/cwTime.cpp @@ -1,6 +1,7 @@ #include "cwCommon.h" #include "cwLog.h" #include "cwCommonImpl.h" +#include "cwTest.h" #include "cwTime.h" #ifdef OS_OSX @@ -399,7 +400,7 @@ unsigned cw::time::formatDateTime( char* buffer, unsigned bufN, bool includeDate return (unsigned)n; } -cw::rc_t cw::time::test() +cw::rc_t cw::time::test(const test::test_args_t& test ) { spec_t t0,t1; @@ -410,15 +411,15 @@ cw::rc_t cw::time::test() unsigned dMs = elapsedMs(t0,t1); - printf("dMs:%i : GTE:%i LTE:%i\n",dMs, isGTE(t0,t1), isLTE(t0,t1) ); + cwLogPrint("dMs:%i : GTE:%i LTE:%i\n",dMs, isGTE(t0,t1), isLTE(t0,t1) ); microsecondsToSpec( t0, 2500000 ); // 2.5 seconds - printf("%li %li\n",t0.tv_sec,t0.tv_nsec); + cwLogPrint("%li %li\n",t0.tv_sec,t0.tv_nsec); subtractMicros( t0, 750000 ); // subtract .75 seconds - printf("%li %li\n",t0.tv_sec,t0.tv_nsec); + cwLogPrint("%li %li\n",t0.tv_sec,t0.tv_nsec); subtractMicros( t0, 500000 ); // subtract .5 seconds - printf("%li %li\n",t0.tv_sec,t0.tv_nsec); + cwLogPrint("%li %li\n",t0.tv_sec,t0.tv_nsec); time::get(t0); @@ -429,11 +430,11 @@ cw::rc_t cw::time::test() int usec = time::elapsedMicros(t0,t1); - printf("usec:%i\n",usec); + cwLogPrint("usec:%i\n",usec); t0 = current_time(); sleepMs(1000); - printf("sleep %i ms\n",elapsedMs(t0)); + cwLogPrint("sleep %i ms\n",elapsedMs(t0)); return kOkRC; diff --git a/cwTime.h b/cwTime.h index 7f1a3ed..5c4d9b7 100644 --- a/cwTime.h +++ b/cwTime.h @@ -85,7 +85,7 @@ namespace cw // Return count of bytes in in buf[] unsigned formatDateTime( char* buf, unsigned bufN, bool includeDateFl=false ); - rc_t test(); + rc_t test( const test::test_args_t& test ); //) diff --git a/cwUi.cpp b/cwUi.cpp index 1f636fc..455d118 100644 --- a/cwUi.cpp +++ b/cwUi.cpp @@ -1,6 +1,7 @@ #include "cwCommon.h" #include "cwLog.h" #include "cwCommonImpl.h" +#include "cwTest.h" #include "cwMem.h" #include "cwTime.h" #include "cwFileSys.h" @@ -117,7 +118,7 @@ namespace cw unsigned sentMsgN; unsigned recvMsgN; - bucket_t hashA[ hashN ]; + bucket_t hashA[ hashN+1 ]; } ui_t; @@ -129,7 +130,11 @@ namespace cw assert( parentUuId != kInvalidId && appId != kInvalidId ); unsigned hc = parentUuId + cwSwap32(appId); - return (unsigned short)(((hc & 0xffff0000)>>16) + (hc & 0x0000ffff)); + unsigned short hash_idx = (unsigned short)(((hc & 0xffff0000)>>16) + (hc & 0x0000ffff)); + + assert( hash_idx <= hashN ); + + return hash_idx; } @@ -162,7 +167,7 @@ namespace cw bucket_t* b = p->hashA + hash_idx; for(; b!=nullptr; b=b->link) - if( b->ele->appId==appId && b->ele->logical_parent->uuId==parentUuId && (chanId==kInvalidId || b->ele->chanId==chanId) ) + if( b->ele != nullptr && b->ele->logical_parent!=nullptr && b->ele->appId==appId && b->ele->logical_parent->uuId==parentUuId && (chanId==kInvalidId || b->ele->chanId==chanId) ) return b->ele->uuId; } @@ -225,6 +230,18 @@ namespace cw m = m0; } + // Note: hashA[] has hashN+1 elements + for(unsigned i=0; i<=hashN; ++i) + { + bucket_t* b = p->hashA[i].link; + while( b!=nullptr ) + { + bucket_t* b0 = b->link; + mem::release(b); + b = b0; + } + } + mem::release(p->sessA); mem::release(p->eleA); mem::release(p->buf); @@ -2219,6 +2236,7 @@ void cw::ui::realTimeReport( handle_t h ) { ui_t* p = _handleToPtr(h); printf("UI msg count: recv:%i send:%i\n",p->recvMsgN,p->sentMsgN); + } @@ -2235,7 +2253,6 @@ namespace cw void* cbArg; uiCallback_t uiCbFunc; websock::cbFunc_t wsCbFunc; - unsigned wsTimeOutMs; unsigned idleMsgPeriodMs; time::spec_t lastRecvMsgTimeStamp; } ui_ws_t; @@ -2289,7 +2306,7 @@ namespace cw ui_ws_t* p = static_cast(cbArg); return websock::send( p->wsH, kUiProtocolId, wsSessId, msg, msgByteN ); } - + } } } @@ -2317,13 +2334,18 @@ cw::rc_t cw::ui::ws::parseArgs( const object_t& o, args_t& args, const char* ob "rcvBufByteN", args.rcvBufByteN, "xmtBufByteN", args.xmtBufByteN, "fmtBufByteN", args.fmtBufByteN, - "websockTimeOutMs", args.wsTimeOutMs, "idleMsgPeriodMs", args.idleMsgPeriodMs, + "queueBlkCnt", args.queueBlkCnt, + "queueBlkByteCnt", args.queueBlkByteCnt, "uiCfgFn", uiCfgFn )) != kOkRC ) { rc = cwLogError(rc,"'ui' cfg. parse failed."); } + if( op->find_child("websockTimeOutMs") != nullptr ) + cwLogWarning("The UI parameter 'websockTimeOutMs' is obsolete and ignored."); + + // expand the physical root directory if((physRootDir = filesys::expandPath( args.physRootDir)) == nullptr ) { @@ -2374,11 +2396,12 @@ cw::rc_t cw::ui::ws::create( handle_t& h, appIdMapN, wsCbFunc, args.dfltPageFn, - args.wsTimeOutMs, args.idleMsgPeriodMs, args.rcvBufByteN, args.xmtBufByteN, - args.fmtBufByteN ); + args.fmtBufByteN, + args.queueBlkCnt, + args.queueBlkByteCnt); } @@ -2392,11 +2415,12 @@ cw::rc_t cw::ui::ws::create( handle_t& h, unsigned appIdMapN, websock::cbFunc_t wsCbFunc, const char* dfltPageFn, - unsigned websockTimeOutMs, unsigned idleMsgPeriodMs, unsigned rcvBufByteN, unsigned xmtBufByteN, - unsigned fmtBufByteN ) + unsigned fmtBufByteN, + unsigned queueBlkCnt, + unsigned queueBlkByteCnt ) { rc_t rc = kOkRC; @@ -2416,7 +2440,7 @@ cw::rc_t cw::ui::ws::create( handle_t& h, void* wsCbA = wsCbFunc==nullptr ? p : cbArg; // create the websocket - if((rc = websock::create(p->wsH, wsCbF, wsCbA, physRootDir, dfltPageFn, port, protocolA, protocolN )) != kOkRC ) + if((rc = websock::create(p->wsH, wsCbF, wsCbA, physRootDir, dfltPageFn, port, protocolA, protocolN, queueBlkCnt, queueBlkByteCnt )) != kOkRC ) { cwLogError(rc,"UI Websock create failed."); goto errLabel; @@ -2432,7 +2456,6 @@ cw::rc_t cw::ui::ws::create( handle_t& h, p->cbArg = cbArg; p->uiCbFunc = uiCbFunc; p->wsCbFunc = wsCbFunc; - p->wsTimeOutMs = websockTimeOutMs; p->idleMsgPeriodMs = idleMsgPeriodMs; // initialize the last received msg time::get(p->lastRecvMsgTimeStamp); @@ -2465,13 +2488,13 @@ cw::rc_t cw::ui::ws::destroy( handle_t& h ) return rc; } -cw::rc_t cw::ui::ws::exec( handle_t h ) +cw::rc_t cw::ui::ws::exec( handle_t h, unsigned wsTimeOutMs ) { - rc_t rc = kOkRC; ui_ws_t* p = _handleToPtr(h); + rc_t rc = kOkRC; time::spec_t t; - if((rc = websock::exec( p->wsH, p->wsTimeOutMs )) != kOkRC) + if((rc = websock::exec( p->wsH, wsTimeOutMs )) != kOkRC) cwLogError(rc,"The UI websock execution failed."); // make the idle callback @@ -2486,6 +2509,7 @@ cw::rc_t cw::ui::ws::exec( handle_t h ) return rc; } + cw::rc_t cw::ui::ws::onReceive( handle_t h, unsigned protocolId, unsigned sessionId, websock::msgTypeId_t msg_type, const void* msg, unsigned byteN ) { ui_ws_t* p = _handleToPtr(h); @@ -2508,6 +2532,13 @@ cw::ui::handle_t cw::ui::ws::uiHandle( handle_t h ) } +void cw::ui::ws::realTimeReport( handle_t h ) +{ + ui_ws_t* p = _handleToPtr(h); + report(p->wsH); + realTimeReport(p->uiH); +} + namespace cw { @@ -2519,6 +2550,7 @@ namespace cw { ws::handle_t wsUiH; thread::handle_t thH; + unsigned wsTimeOutMs; } ui_ws_srv_t; ui_ws_srv_t* _handleToPtr(handle_t h ) @@ -2543,7 +2575,7 @@ namespace cw ui_ws_srv_t* p = static_cast(arg); rc_t rc; - if((rc = ws::exec(p->wsUiH)) != kOkRC ) + if((rc = ws::exec(p->wsUiH,p->wsTimeOutMs)) != kOkRC ) { cwLogError(rc,"Websocket UI exec failed."); } @@ -2554,20 +2586,51 @@ namespace cw } } +cw::rc_t cw::ui::srv::create( handle_t& h, + const ws::args_t& args, + void* cbArg, + uiCallback_t uiCbFunc, + const appIdMap_t* appIdMapA, + unsigned appIdMapN, + unsigned wsTimeOutMs, + websock::cbFunc_t wsCbFunc ) +{ + return create(h, + args.port, + args.physRootDir, + cbArg, + uiCbFunc, + args.uiRsrc, + appIdMapA, + appIdMapN, + wsTimeOutMs, + wsCbFunc, + args.dfltPageFn, + args.idleMsgPeriodMs, + args.rcvBufByteN, + args.xmtBufByteN, + args.fmtBufByteN, + args.queueBlkCnt, + args.queueBlkByteCnt ); +} + cw::rc_t cw::ui::srv::create( handle_t& h, - unsigned port, - const char* physRootDir, - void* cbArg, - uiCallback_t uiCbFunc, - const object_t* uiRsrc, - const appIdMap_t* appIdMapA, - unsigned appIdMapN, - websock::cbFunc_t wsCbFunc, - const char* dfltPageFn, - unsigned websockTimeOutMs, - unsigned rcvBufByteN, - unsigned xmtBufByteN, - unsigned fmtBufByteN ) + unsigned port, + const char* physRootDir, + void* cbArg, + uiCallback_t uiCbFunc, + const object_t* uiRsrc, + const appIdMap_t* appIdMapA, + unsigned appIdMapN, + unsigned wsTimeOutMs, + websock::cbFunc_t wsCbFunc, + const char* dfltPageFn, + unsigned idleMsgPeriodMs, + unsigned rcvBufByteN, + unsigned xmtBufByteN, + unsigned fmtBufByteN, + unsigned queueBlkCnt, + unsigned queueBlkByteCnt ) { rc_t rc = kOkRC; if((rc = destroy(h)) != kOkRC ) @@ -2575,7 +2638,7 @@ cw::rc_t cw::ui::srv::create( handle_t& h, ui_ws_srv_t* p = mem::allocZ(); - if((rc = ws::create(p->wsUiH, port, physRootDir, cbArg, uiCbFunc, uiRsrc,appIdMapA, appIdMapN, wsCbFunc, dfltPageFn, websockTimeOutMs, rcvBufByteN, xmtBufByteN, fmtBufByteN )) != kOkRC ) + if((rc = ws::create(p->wsUiH, port, physRootDir, cbArg, uiCbFunc, uiRsrc,appIdMapA, appIdMapN, wsCbFunc, dfltPageFn, idleMsgPeriodMs, rcvBufByteN, xmtBufByteN, fmtBufByteN, queueBlkCnt, queueBlkByteCnt )) != kOkRC ) { cwLogError(rc,"The websock UI creationg failed."); goto errLabel; @@ -2587,6 +2650,8 @@ cw::rc_t cw::ui::srv::create( handle_t& h, goto errLabel; } + p->wsTimeOutMs = wsTimeOutMs; + h.set(p); errLabel: @@ -2596,32 +2661,6 @@ cw::rc_t cw::ui::srv::create( handle_t& h, return rc; } -cw::rc_t cw::ui::srv::create( handle_t& h, - const ws::args_t& args, - void* cbArg, - uiCallback_t uiCbFunc, - const appIdMap_t* appIdMapA, - unsigned appIdMapN, - websock::cbFunc_t wsCbFunc ) -{ - return create(h, - args.port, - args.physRootDir, - cbArg, - uiCbFunc, - args.uiRsrc, - appIdMapA, - appIdMapN, - wsCbFunc, - args.dfltPageFn, - args.wsTimeOutMs, - args.rcvBufByteN, - args.xmtBufByteN, - args.fmtBufByteN ); -} - - - cw::rc_t cw::ui::srv::destroy( handle_t& h ) { rc_t rc = kOkRC; @@ -2677,3 +2716,4 @@ cw::ui::handle_t cw::ui::srv::uiHandle( handle_t h ) ui_ws_srv_t* p = _handleToPtr(h); return ws::uiHandle(p->wsUiH); } + diff --git a/cwUi.h b/cwUi.h index 08b5bf7..30bdc49 100644 --- a/cwUi.h +++ b/cwUi.h @@ -192,8 +192,9 @@ namespace cw unsigned rcvBufByteN; unsigned xmtBufByteN; unsigned fmtBufByteN; - unsigned wsTimeOutMs; unsigned idleMsgPeriodMs; // min period without messages before an idle message is generated + unsigned queueBlkCnt; + unsigned queueBlkByteCnt; } args_t; rc_t parseArgs( const object_t& o, args_t& args, const char* object_label="ui" ); @@ -218,20 +219,20 @@ namespace cw unsigned appIdMapN = 0, websock::cbFunc_t wsCbFunc = nullptr, const char* dfltPageFn = "index.html", - unsigned websockTimeOutMs = 50, unsigned idleMsgPeriodMs = 50, unsigned rcvBufByteN = 1024, unsigned xmtBufByteN = 1024, - unsigned fmtBufByteN = 4096 - ); + unsigned fmtBufByteN = 4096, + unsigned queueBlkCnt = 4, + unsigned queueBlkByteCnt = 4096 ); rc_t destroy( handle_t& h ); // This function should be called periodically to send and receive // queued messages to and from the websocket. // Note that this call may block for up to 'wsTimeOutMs' milliseconds - // on the websocket handle. - rc_t exec( handle_t h ); + // while waiting for incoming websocket messages. + rc_t exec( handle_t h, unsigned wsTimeOutMs ); // This function executes the internal default websock callback function. // It is useful if the user provides a custom websock callback function @@ -240,6 +241,8 @@ namespace cw websock::handle_t websockHandle( handle_t h ); ui::handle_t uiHandle( handle_t h ); + void realTimeReport( handle_t h ); + } namespace srv @@ -248,27 +251,31 @@ namespace cw typedef handle handle_t; rc_t create( handle_t& h, - const ws::args_t& args, - void* cbArg, - uiCallback_t uiCbFunc, - const appIdMap_t* appIdMapA = nullptr, - unsigned appIdMapN = 0, - websock::cbFunc_t wsCbFunc = nullptr ); + const ws::args_t& args, + void* cbArg, + uiCallback_t uiCbFunc, + const appIdMap_t* appIdMapA = nullptr, + unsigned appIdMapN = 0, + unsigned wsTimeOutMs = 50, + websock::cbFunc_t wsCbFunc = nullptr ); rc_t create( handle_t& h, - unsigned port, - const char* physRootDir, - void* cbArg, - uiCallback_t uiCbFunc, - const object_t* uiRsrc = nullptr, - const appIdMap_t* appIdMapA = nullptr, - unsigned appIdMapN = 0, - websock::cbFunc_t wsCbFunc = nullptr, - const char* dfltPageFn = "index.html", - unsigned websockTimeOutMs = 50, - unsigned rcvBufByteN = 1024, - unsigned xmtBufByteN = 1024, - unsigned fmtBufByteN = 4096 ); + unsigned port, + const char* physRootDir, + void* cbArg, + uiCallback_t uiCbFunc, + const object_t* uiRsrc = nullptr, + const appIdMap_t* appIdMapA = nullptr, + unsigned appIdMapN = 0, + unsigned wsTimeOutMs = 50, + websock::cbFunc_t wsCbFunc = nullptr, + const char* dfltPageFn = "index.html", + unsigned idleMsgPeriodMs = 50, + unsigned rcvBufByteN = 1024, + unsigned xmtBufByteN = 1024, + unsigned fmtBufByteN = 4096, + unsigned queueBlkCnt = 4, + unsigned queueBlkByteCnt = 4096 ); rc_t destroy( handle_t& h ); diff --git a/cwUiTest.cpp b/cwUiTest.cpp index cefc571..b7da65c 100644 --- a/cwUiTest.cpp +++ b/cwUiTest.cpp @@ -1,6 +1,7 @@ #include "cwCommon.h" #include "cwLog.h" #include "cwCommonImpl.h" +#include "cwTest.h" #include "cwMem.h" #include "cwThread.h" #include "cwWebSock.h" @@ -378,8 +379,9 @@ namespace cw cw::rc_t cw::ui::test( const object_t* cfg ) { - rc_t rc = kOkRC; - ui::ws::args_t args = {}; + rc_t rc = kOkRC; + unsigned wsTimeOutMs = 50; + ui::ws::args_t args = {}; // Application Id's for the resource based UI elements. appIdMap_t mapA[] = @@ -437,7 +439,7 @@ cw::rc_t cw::ui::test( const object_t* cfg ) //app->uiCfgFn = "/home/kevin/src/cwtest/src/libcw/html/uiTest/ui.cfg"; // create the UI server - if((rc = srv::create(app->wsUiSrvH, args, app, _uiTestCallback, mapA, mapN, nullptr )) != kOkRC ) + if((rc = srv::create(app->wsUiSrvH, args, app, _uiTestCallback, mapA, mapN, wsTimeOutMs, nullptr )) != kOkRC ) return rc; if((rc = uiTestCreateUi( app )) != kOkRC ) diff --git a/cwVectOps.cpp b/cwVectOps.cpp new file mode 100644 index 0000000..c8288b8 --- /dev/null +++ b/cwVectOps.cpp @@ -0,0 +1,15 @@ +#include "cwCommon.h" +#include "cwLog.h" +#include "cwCommonImpl.h" +#include "cwTest.h" +#include "cwVectOps.h" + +cw::rc_t cw::vop::test( const test::test_args_t& args ) +{ + int v1[] = { 1,2,1,2,1,2,1,2,1,2 }; + int v0[ 10 ]; + + cw::vop::deinterleave( v0, v1, 5, 2 ); + cw::vop::print(v0,10,"%i "); + return cw::kOkRC; +} diff --git a/cwVectOps.h b/cwVectOps.h index 277ec11..bc8f087 100644 --- a/cwVectOps.h +++ b/cwVectOps.h @@ -17,10 +17,10 @@ namespace cw if( label != nullptr ) { - printf("%s : ",label); + cwLogPrint("%s : ",label); if( colN && n > colN ) { - printf("\n"); + cwLogPrint("\n"); newline_fl = true; } } @@ -30,19 +30,19 @@ namespace cw for(unsigned i=0; i +#include namespace cw { @@ -31,15 +34,18 @@ namespace cw // Internal outgoing msg structure. typedef struct msg_str { - unsigned protocolId; // Protocol associated with this msg. - unsigned sessionId; // Session Id that this msg should be sent to or kInvalidId if it should be sent to all sessions. - unsigned char* msg; // Msg data array. - unsigned msgByteN; // Count of bytes in msg[]. - unsigned msgId; // The msgId assigned when this msg is addded to the protocol state msg queue. - unsigned sessionN; // Count of sessions to which this msg has been sent. - struct msg_str* link; // Pointer to next message or nullptr if this is the last msg in the queue. + unsigned protocolId; // 0 Protocol associated with this msg. + unsigned sessionId; // 4 Session Id that this msg should be sent to or kInvalidId if it should be sent to all sessions. + unsigned char* msg; // 8 Msg data array. + unsigned msgByteN; // 16 Count of bytes in msg[]. + unsigned msgId; // 20 The msgId assigned when this msg is addded to the protocol state msg queue. + unsigned sessionN; // 24 Count of sessions to which this msg has been sent. + struct msg_str* link; // 28 Pointer to next message or nullptr if this is the last msg in the queue. + unsigned pad; // 36-40 (make size of msg_t a multiple of 8) } msg_t; + static_assert( sizeof(msg_t) % 8 == 0 ); + typedef struct websock_str { cbFunc_t _cbFunc; // @@ -50,7 +56,7 @@ namespace cw unsigned _nextSessionId = 0; // Next session id. unsigned _connSessionN = 0; // Count of connected sessions. struct lws_http_mount* _mount = nullptr; // - MpScNbQueue* _q; // Thread safe, non-blocking, protocol independent msg queue. + nbmpscq::handle_t _qH; // Thread safe, non-blocking, protocol independent msg queue. lws_pollfd* _pollfdA; // socket handle array used by poll() int _pollfdMaxN; @@ -186,14 +192,13 @@ namespace cw case LWS_CALLBACK_SERVER_WRITEABLE: { - msg_t* m1 = protoState->endMsg; - cwAssert(m1 != nullptr); + msg_t* m = protoState->endMsg; - // for each possible msg - while( m1->link != nullptr ) + // for each possible outgoing msg in this protocol state record + while( m != nullptr ) { - msg_t* m = m1->link; - + bool msg_written_fl = false; + // if this msg has not already been sent to this session if( m->msgId >= sess->nextMsgId ) { @@ -201,7 +206,8 @@ namespace cw // if the msg sessiond id is not valid or matches this session id ... if( m->sessionId == kInvalidId || m->sessionId == sess->id ) { - // ... then send the msg to this session + // ... then send the msg to this session + // Note: msgByteN should not include LWS_PRE int lws_result = lws_write(wsi, m->msg + LWS_PRE , m->msgByteN, LWS_WRITE_TEXT); @@ -211,17 +217,25 @@ namespace cw cwLogError(kWriteFailRC,"Websocket error: %d on write", lws_result); return -1; } + + msg_written_fl = true; } // at this point the write succeeded or this session was skipped sess->nextMsgId = m->msgId + 1; - m->sessionN += 1; - - break; + + // incr the msg session count - once m->sessionN >= protoState->sessionN this msg will be deleted in _cleanProtocoStateList() + m->sessionN += 1; + + // If a record was actually sent then we are done since only one + // call to lws_write() per LWS_CALLBACK_SERVER_WRITEABLE callback + // is permitted + if(msg_written_fl) + break; } - m1 = m1->link; + m = m->link; } } @@ -257,40 +271,41 @@ namespace cw return nullptr; } - - void _cleanProtocolStateList( protocolState_t* ps ) + // Remove message from the send queue (p->_qH), and the protocol msg list, + // which have already been sent to all relevant sessions. + rc_t _cleanProtocolStateList( websock_t* p, protocolState_t* ps ) { - msg_t* m0 = nullptr; - msg_t* m1 = ps->endMsg; + rc_t rc = kOkRC; + nbmpscq::blob_t b = get(p->_qH); - while( m1->link != nullptr ) + msg_t* oldest_msg = (msg_t*)b.blob; + + // if the last protocol end msg has been sent to all sessions ... + while( oldest_msg != nullptr && ps->endMsg != nullptr && ps->endMsg->sessionN >= ps->sessionN ) { - if( m1->link->sessionN >= ps->sessionN ) + // ... and this msg is also the oldest msg in the send queue + if( oldest_msg->msgId == ps->endMsg->msgId ) { - if( m0 == nullptr ) - ps->endMsg = m1->link; - else - m0->link = m1->link; - - msg_t* t = m1->link; - - //mem::free(m1->msg); - mem::free(m1); - - m1 = t; - - continue; + ps->endMsg = ps->endMsg->link; + b = advance(p->_qH); // ... then pop the msg off the send queue + oldest_msg = (msg_t*)b.blob; + } + else // ... otherwise the no further progress can be made + { + break; } - - m0 = m1; - m1 = m1->link; } + + if( ps->endMsg == nullptr ) + ps->begMsg = nullptr; + + return rc; } - rc_t _destroy( websock_t* p ) { - msg_t* m; + rc_t rc = kOkRC; + //msg_t* m; cwLogInfo("Websock: sent: msgs:%i largest msg:%i - recv: msgs:%i largest msg:%i - LWS_PRE:%i",p->_sendMsgCnt,p->_sendMaxByteN,p->_recvMsgCnt,p->_recvMaxByteN,LWS_PRE); cwLogInfo("Exec Time: %i avg ms, %i total ms, %i cnt", p->_execN==0 ? 0 : p->_execSumMs/p->_execN, p->_execSumMs, p->_execN ); @@ -300,39 +315,20 @@ namespace cw lws_context_destroy(p->_ctx); p->_ctx = nullptr; } - - if( p->_q != nullptr ) - { - while((m = p->_q->pop()) != nullptr) - { - //mem::free(m->msg); - mem::free(m); - } - - delete p->_q; - } - for(int i=0; p->_protocolA!=nullptr and p->_protocolA[i].callback != nullptr; ++i) { mem::free(const_cast(p->_protocolA[i].name)); // TODO: delete any msgs in the protocol state here auto ps = static_cast(p->_protocolA[i].user); - - m = ps->endMsg; - while( m != nullptr ) - { - msg_t* tmp = m->link; - - //mem::free(m->msg); - mem::free(m); - m = tmp; - } - + mem::free(ps); } - + + if((rc = nbmpscq::destroy(p->_qH)) != kOkRC ) + cwLogError(rc,"Websock queue destroy failed."); + mem::release(p->_protocolA); p->_protocolN = 0; @@ -466,12 +462,15 @@ namespace cw } - rc_t _exec( websock_t* p, unsigned timeOutMs ) + rc_t _exec( websock_t* p, unsigned timeOutMs, bool* msg_fl_ref=nullptr ) { rc_t rc = kOkRC; int adjTimeOut = lws_service_adjust_timeout(p->_ctx, timeOutMs, 0); int sysRC = 0; + if( msg_fl_ref != nullptr ) + *msg_fl_ref = false; + if( p->_pollfdN > 0 ) sysRC = poll(p->_pollfdA, p->_pollfdN, adjTimeOut); @@ -513,11 +512,76 @@ namespace cw { // check if it is an fd owned by the application } + + if( msg_fl_ref != nullptr ) + *msg_fl_ref = true; + } errLabel: return rc; } + + // Call _exec() repeatedly for at least timeOutMs-5 milliseconds. + rc_t _timed_exec_0( websock_t* p, unsigned timeOutMs ) + { + rc_t rc = kOkRC; + time::spec_t t0 = time::current_time(); + unsigned dMs = 0; + unsigned adjTimeOutMs; + + do { + + adjTimeOutMs = timeOutMs - dMs; + + if((rc = _exec(p,adjTimeOutMs)) != kOkRC ) + break; + + dMs = time::elapsedMs(t0); + + }while( dMs+5 < timeOutMs ); + + return rc; + } + + // Call _exec() as long as incoming messages are received. + rc_t _timed_exec_1( websock_t* p, unsigned timeOutMs ) + { + rc_t rc = kOkRC; + bool fl = false; + + do { + + fl = false; + if((rc = _exec(p,5,&fl)) != kOkRC ) + break; + + }while( fl ); + + return rc; + } + + // Call _exec() as long as incoming messages are received. + // or 'timeOutMs' time elapses. + rc_t _timed_exec_2( websock_t* p, unsigned timeOutMs ) + { + rc_t rc = kOkRC; + bool fl = false; + time::spec_t t0 = time::current_time(); + unsigned dMs = 0; + + do { + + fl = false; + if((rc = _exec(p,5,&fl)) != kOkRC ) + break; + + dMs = time::elapsedMs(t0); + + }while( fl && (dMs+5 < timeOutMs) ); + + return rc; + } } } @@ -530,7 +594,9 @@ cw::rc_t cw::websock::create( const char* dfltHtmlPageFn, int port, const protocol_t* protocolArgA, - unsigned protocolN ) + unsigned protocolN, + unsigned queueBlkCnt, + unsigned queueBlkByteCnt ) { rc_t rc; struct lws_context_creation_info info; @@ -575,11 +641,10 @@ cw::rc_t cw::websock::create( { // Allocate the application protocol state array where this application can keep protocol related info auto protocolState = mem::allocZ(1); - auto dummy = mem::allocZ(1); protocolState->thisPtr = p; - protocolState->begMsg = dummy; - protocolState->endMsg = dummy; + protocolState->begMsg = nullptr; + protocolState->endMsg = nullptr; // Setup the interal lws_protocols record struct lws_protocols* pr = p->_protocolA + i; @@ -609,7 +674,12 @@ cw::rc_t cw::websock::create( foreign_loops[0] = p; // pass in the custom poll object as the foreign loop object we will bind to info.foreign_loops = foreign_loops; - p->_q = new MpScNbQueue(); + if((rc = nbmpscq::create(p->_qH,queueBlkCnt,queueBlkByteCnt)) != kOkRC ) + { + rc = cwLogError(rc,"Websock queue create failed."); + goto errLabel; + } + p->_cbFunc = cbFunc; p->_cbArg = cbArg; @@ -653,23 +723,33 @@ cw::rc_t cw::websock::destroy( handle_t& h ) cw::rc_t cw::websock::send(handle_t h, unsigned protocolId, unsigned sessionId, const void* msg, unsigned byteN ) { rc_t rc = kOkRC; - - uint8_t* mem = mem::allocZ( sizeof(msg_t) + LWS_PRE + byteN ); - - msg_t* m = (msg_t*)mem; - m->msg = mem + sizeof(msg_t); - - memcpy(m->msg+LWS_PRE,msg,byteN); - m->msgByteN = byteN; - m->protocolId = protocolId; - m->sessionId = sessionId; - websock_t* p = _handleToPtr(h); - p->_q->push(m); + + unsigned dataByteN = sizeof(msg_t) + LWS_PRE + byteN; + uint8_t mem[ dataByteN ]; + msg_t* m = (msg_t*)mem; + + memcpy(mem + sizeof(msg_t) + LWS_PRE,msg,byteN); + m->protocolId = protocolId; + m->sessionId = sessionId; + m->msg = nullptr; + m->msgByteN = byteN; // length of msg w/o LWS_PRE + m->msgId = kInvalidId; + m->sessionN = 0; + m->link = nullptr; + + + // put the outgoing msgs on the queue + if((rc = nbmpscq::push(p->_qH,mem,dataByteN)) != kOkRC ) + { + rc = cwLogError(rc,"Websock queue push failed."); + goto errLabel; + } p->_sendMsgCnt += 1; p->_sendMaxByteN = std::max(p->_sendMaxByteN,byteN); +errLabel: return rc; } @@ -706,34 +786,74 @@ cw::rc_t cw::websock::exec( handle_t h, unsigned timeOutMs ) time::spec_t t0 = time::current_time(); - // service any pending websocket activity - with no-timeout - lws_service_tsi(p->_ctx, -1, 0 ); + // service any pending websocket activity - with no timeout + //_timed_exec_1(p,timeOutMs/2); + _exec(p,0); - msg_t* m; - // Get the next pending outgoing message. - while((m = p->_q->pop()) != nullptr ) + // clean already sent messages from each protocol outgoing msg list + for(unsigned i=0; i_protocolN-1; ++i) { - auto protocol = _idToProtocol(p,m->protocolId); + protocolState_t* ps = static_cast(p->_protocolA[i].user); + _cleanProtocolStateList( p, ps ); + } - // Get the application protcol record for this message + nbmpscq::peek_reset(p->_qH); + + // Get the next pending outgoing message. + while(1) + { + nbmpscq::blob_t b = nbmpscq::peek(p->_qH); + + // if the outgoing queue is empty + if( b.blob == nullptr ) + break; + + msg_t* m = (msg_t*)b.blob; + + // if msg data ptr is not null then this msg has been seen in an earlier call to this function. + if( m->msg != nullptr ) + break; + + m->msg = (unsigned char*)(m+1); + + // Get the protocol record for this msg. + struct lws_protocols* protocol = _idToProtocol(p,m->protocolId); + + // Get the application protcol state record from the protocol 'user' field protocolState_t* ps = static_cast(protocol->user); // remove messages from the protocol message queue which have already been sent - _cleanProtocolStateList( ps ); - - m->msgId = ps->nextNewMsgId; // set the msg id - ps->begMsg->link = m; // put the msg on the front of the outgoing queue - ps->begMsg = m; // - ps->nextNewMsgId += 1; - - lws_callback_on_writable_all_protocol(p->_ctx,protocol); + _cleanProtocolStateList( p, ps ); - lws_service_tsi(p->_ctx, -1, 0 ); + // Put the msg in the front of the protocal state list (msg's are removed from the back of the list) + m->msgId = ps->nextNewMsgId; // set the msg id + m->link = nullptr; + ps->nextNewMsgId += 1; + + if( ps->begMsg == nullptr ) + { + ps->begMsg = m; + ps->endMsg = m; + } + else + { + ps->begMsg->link = m; + ps->begMsg = m; + } + + // we want one callback for each session + for(unsigned i=0; isessionN; ++i) + { + lws_callback_on_writable_all_protocol(p->_ctx,protocol); + + _exec(p,0); + + } } // block waiting for incoming messages - _exec(p,timeOutMs); + _timed_exec_2(p,timeOutMs); p->_execSumMs += time::elapsedMs(t0); p->_execN += 1; @@ -741,3 +861,23 @@ cw::rc_t cw::websock::exec( handle_t h, unsigned timeOutMs ) return rc; } + +void cw::websock::report( handle_t h ) +{ + websock_t* p = _handleToPtr(h); + + printf("Websock: msgs sent:%i recvd:%i que count:%i\n",p->_sendMsgCnt,p->_recvMsgCnt,count(p->_qH)); + + // clean already sent messages from each protocol outgoing msg list + for(unsigned i=0; i_protocolN-1; ++i) + { + protocolState_t* ps = static_cast(p->_protocolA[i].user); + + const msg_t* m; + unsigned cnt = 0; + for(m=ps->endMsg; m!=nullptr; m=m->link) + cnt += 1; + + printf("Protocol:%i %i\n",i,cnt); + } +} diff --git a/cwWebSock.h b/cwWebSock.h index e87fb04..b7884ed 100644 --- a/cwWebSock.h +++ b/cwWebSock.h @@ -45,7 +45,9 @@ namespace cw const char* dfltHtmlPageFn, int port, const protocol_t* protocolA, - unsigned protocolN ); + unsigned protocolN, + unsigned queueBlkCnt, + unsigned queueBlkByteCnt ); rc_t destroy( handle_t& h ); @@ -56,6 +58,8 @@ namespace cw // Call periodically from the same thread to send/recv messages. rc_t exec( handle_t h, unsigned timeOutMs ); + + void report( handle_t h ); } diff --git a/cwWebSockSvr.cpp b/cwWebSockSvr.cpp index e55dbc4..3267d43 100644 --- a/cwWebSockSvr.cpp +++ b/cwWebSockSvr.cpp @@ -1,6 +1,7 @@ #include "cwCommon.h" #include "cwLog.h" #include "cwCommonImpl.h" +#include "cwTest.h" #include "cwMem.h" #include "cwWebSock.h" #include "cwThread.h" @@ -56,7 +57,9 @@ cw::rc_t cw::websockSrv::create( int port, const websock::protocol_t* protocolA, unsigned protocolN, - unsigned timeOutMs ) + unsigned timeOutMs, + unsigned queueBlkCnt, + unsigned queueBlkByteCnt ) { rc_t rc; if((rc = destroy(h)) != kOkRC ) @@ -64,7 +67,7 @@ cw::rc_t cw::websockSrv::create( websockSrv_t* p = mem::allocZ(); - if((rc = websock::create( p->_websockH, cbFunc, cbArg, physRootDir, dfltHtmlPageFn, port, protocolA, protocolN )) != kOkRC ) + if((rc = websock::create( p->_websockH, cbFunc, cbArg, physRootDir, dfltHtmlPageFn, port, protocolA, protocolN, queueBlkCnt, queueBlkByteCnt )) != kOkRC ) goto errLabel; @@ -176,6 +179,8 @@ cw::rc_t cw::websockSrvTest( const object_t* cfg ) int port = 5687; unsigned rcvBufByteN = 128; unsigned xmtBufByteN = 128; + unsigned queueBlkCnt = 3; + unsigned queueBlkByteCnt= 4096; appCtx_t appCtx; enum @@ -198,7 +203,7 @@ cw::rc_t cw::websockSrvTest( const object_t* cfg ) unsigned protocolN = sizeof(protocolA)/sizeof(protocolA[0]); - if((rc = websockSrv::create( h, websockCb, &appCtx, physRootDir, dfltHtmlPageFn, port, protocolA, protocolN, timeOutMs )) != kOkRC ) + if((rc = websockSrv::create( h, websockCb, &appCtx, physRootDir, dfltHtmlPageFn, port, protocolA, protocolN, timeOutMs, queueBlkCnt, queueBlkByteCnt )) != kOkRC ) return rc; appCtx.wsH = websockSrv::websockHandle(h); diff --git a/cwWebSockSvr.h b/cwWebSockSvr.h index fdab325..2be27e4 100644 --- a/cwWebSockSvr.h +++ b/cwWebSockSvr.h @@ -16,7 +16,9 @@ namespace cw { int port, const websock::protocol_t* protocolA, unsigned protocolN, - unsigned websockTimeOutMs ); + unsigned websockTimeOutMs, + unsigned queueBlkCnt, + unsigned queueBlkByteCnt); rc_t destroy( handle_t& h ); diff --git a/flow/flow_doc.md b/flow/flow_doc.md new file mode 100644 index 0000000..302a14d --- /dev/null +++ b/flow/flow_doc.md @@ -0,0 +1,434 @@ + + + + +## Proc Class Notation + +``` + : { + vars: { (:)* }, + presets: { (:)* }, + poly_limit_cnt: + } + + -> { type:, value:, doc:, flags:[ ] } + + -> { : } + + -> | NUMBER | STRING | CFG + + -> [ (value-literal)* ] + + +``` + +### Applying class presets during proc instantiaion + +The way class preset values get applied is a function of the type of the variable and the format of the value. +If a variable is not a _cfg_ type and the preset value is a list, or if the variable is a _cfg_ type +and the preset value is a list-of-containers then each each value in the list is assigned to successive +channels on the variable. This is shown as the __multi-chan__ algorithm in the table below. +In all other cases the the value is applied to the 'any' channel, which is applied to +all existing channels. + +| cfg | list | LoC | chan | algorithm | +| ----------- | ------- | --------------- | ---- | ---------------------------------------------- | +| no | no | no | any | single-chan | +| no | no | yes | n/a | Value can't be an l-of-c w/o also being a list.| +| no | yes | no | N | multi-chan | +| no | yes | yes | N | multi-chan | +| yes | no | no | any | single-chan | +| yes | no | yes | n/a | Value can't be an l-of-c w/o also being a list.| +| yes | yes | no | any | single-chan | +| yes | yes | yes | N | multi-chan | + +When the class preset is applied during proc instantiation all variables have +been instantiated with the base channel. This implies that the __single-chan__ +algorithm is only setting the value of the the existig base channel. +The __multi-chan__ algorithm however will instantiate any channels above the base channel +that do not already exist by duplicating the base channel and then assigning the preset value. + + +### Notes + +1. Legal `` values + +type | Description +---------|------------------------------------------------------------------------------------------------------------------- +string | +bool | `true` or `false` +int | `int32_t` +uint | `uint32_t`. The literal value must be suffixed with a 'u'. [See JSON Notes](#json-notes) +float | Single precision float. The literal value must be suffixed with an 'f'. [See JSON Notes](#json-notes) +double | Double precision float +cfg | JSON string +audio | +spectrum | +midi | +srate | (float) Sample rate type +sample | (float) Audio sample type +coeff | (float) Value that will be directly applied to a sample value (e.g added or multiplied) +ftime | (double) Fractional seconds +runtime | The type is left up to the processors custom 'create' function. These variables are not created automatically + | prior to calling the proc custom function. +all | +numeric | + + +See list in cwFlowTypes.cpp : typeLabelFlagsA[] + + +2. Attribute labels indicate properties of this variable. + +Attribute | Description +----------|-------------------------------------------------------------- +`src` | This variable must have a value or be connected to a source or + | the proc cannot be instantiated, and therefore the network cannot be instantiated. +`src_opt` | +`no_src` | This variable cannot be connected to a 'source' variable. Variables that are + | only used as output usually have this property. +`init` | This is an an initialization only variable. + | Changing the value during runtime will have no effect. +`mult` | This variable may be instantiated multiple times by the in-statement. + | Variables that do not have this property may only be instantiated + | once per proc-instance. +`out` | This is a subnet output variable. [See Subnet Implementation](#subnet-implementation) +`no_chan` | This variable cannot be 'channelized'. + +## Schema Notation + +1. The schemas all describe JSON like structures. For clarity the dictionary braces and +list brackets are shown. + +2. Variables are wrapped in `< >` markers. + +3. Entitities wrapped in `( )*` indicate that the wrapped structure may be repeated 0 or more times. +Entitities wrapped in `( )+` may be repeated 1 or more times. + +4. Variables with names ending in `label` indicate identifiers which are unique at least within +their local scope and possibly some greater scope. For example proc-class names must be +unique across all loaded proc-class modules. + +5. `` indicates arbitrary text. + +6. Entities are often optional. Rather than clutter the notation with additional +mark-up to indicate optional entitities this information is inclued in the notes +that accompany each schema. + + + + + +## JSON Notes + +The 'cfg' language is a JSON like with some added features: + +1. Comments can be inserted using C++ `\\` line comment and `\* *\` block comment syntax. + +2. Quotes are not necessary around strings that have no internal whitespace. + +3. Numeric literals without decimal places are interpretted as integers, numbers +with decimal places are interpretted as doubles. Unsigned and single precision float values +may be specified using suffix notation + +Type | Suffix | Example +---------|----------|-------------- +unsigned | u | 10u +float | f | 12.34f + + +# Flow Documentation: + +_flow_ is an experimental audio processing framework which supports +the execution of real-time and non-real-time music +and audio processing networks and the application of network state data. + +The framework is designed to easily build a certain flavor of +audio processing network which is defined by the ability +to arbitrarily and seemlessly update the state of the network in real-time. + + +A _flow_ program is a data flow network formed from a collection of +interconnected processing units. A processing unit is referered +to as a __proc__. Each processing unit has a function which +operates on a set of variables. The network is formed +by connecting processors together via their variables. + + +Fig. 1 shows a simple network non-real time network +where the output of a sine oscillator is written to an audio file. + + + + + + + + +## Building Neworks: + + +### A Simple Network. +``` +``` + +Use `$` prefix to a filename. Use proc_expand_filename() to prepend `proj_dir` to a file prefixed with `$`. + +### Polyphonic Network. + +### Network with sub-nets. + + +## Proc Class Descriptions + +``` +``` + + +## Proc Instance Syntax: + +``` +