diff --git a/.gitignore b/.gitignore index 31a0738..41fbfcb 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,4 @@ hold study/serial/serial cw_rt vg0.txt - +__pycache__ diff --git a/Makefile.am b/Makefile.am index ac568fd..df15925 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 @@ -28,14 +28,14 @@ libcwSRC += src/libcw/cwSpScBuf.cpp src/libcw/cwSpScQu libcwHDR += src/libcw/cwNbMpScQueue.h libcwSRC += src/libcw/cwNbMpScQueue.cpp -libcwHDR += src/libcw/cwAudioFile.h src/libcw/cwMidiFile.h -libcwSRC += src/libcw/cwAudioFile.cpp src/libcw/cwMidiFile.cpp +libcwHDR += src/libcw/cwAudioFile.h src/libcw/cwMidiFile.h src/libcw/cwWaveTableBank.h src/libcw/cwWaveTableNotes.h +libcwSRC += src/libcw/cwAudioFile.cpp src/libcw/cwMidiFile.cpp src/libcw/cwWaveTableBank.cpp src/libcw/cwWaveTableNotes.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 @@ -71,8 +71,8 @@ if cwWEBSOCK libcwHDR += src/libcw/cwIo.h src/libcw/cwIoTest.h src/libcw/cwIoMinTest.h src/libcw/cwIoSocketChat.h src/libcw/cwIoAudioPanel.h src/libcw/cwIoAudioMidi.h libcwSRC += src/libcw/cwIo.cpp src/libcw/cwIoTest.cpp src/libcw/cwIoMinTest.cpp src/libcw/cwIoSocketChat.cpp src/libcw/cwIoAudioPanel.cpp src/libcw/cwIoAudioMidi.cpp -libcwHDR += src/libcw/cwIoMidiRecordPlay.h src/libcw/cwIoAudioRecordPlay.h src/libcw/cwIoAudioMidiApp.h src/libcw/cwIoFlow.h -libcwSRC += src/libcw/cwIoMidiRecordPlay.cpp src/libcw/cwIoAudioRecordPlay.cpp src/libcw/cwIoAudioMidiApp.cpp src/libcw/cwIoFlow.cpp +libcwHDR += src/libcw/cwIoMidiRecordPlay.h src/libcw/cwIoAudioRecordPlay.h src/libcw/cwIoAudioMidiApp.h src/libcw/cwIoFlow.h src/libcw/cwIoFlowCtl.h +libcwSRC += src/libcw/cwIoMidiRecordPlay.cpp src/libcw/cwIoAudioRecordPlay.cpp src/libcw/cwIoAudioMidiApp.cpp src/libcw/cwIoFlow.cpp src/libcw/cwIoFlowCtl.cpp libcwHDR += src/libcw/cwIoPresetSelApp.h src/libcw/cwPianoScore.h src/libcw/cwPresetSel.h src/libcw/cwVelTableTuner.h src/libcw/cwGutimReg.h libcwSRC += src/libcw/cwIoPresetSelApp.cpp src/libcw/cwPianoScore.cpp src/libcw/cwPresetSel.cpp src/libcw/cwVelTableTuner.cpp src/libcw/cwGutimReg.cpp diff --git a/README.md b/README.md index 2a8f27c..0d0c8b2 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,38 @@ 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. -Note that all vars must be included in the class description. +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. + +Notes: +- All vars must be included in the class description. +- All vars have a 'kAnyChIdx' instantiation. The kAnyChIdx variable serves two purposes: + + Setting the value of kAnyChIdx automatically broadcasts the value to all other channels. + + kAnyChIdx acts as a template when variables are created by 'channelization'. + This allows the network designer to set the value of the kAnyIdx variable + and have that become the default value for all subsequent variables + which are created without an explicit value. (Note that his currently + works for variables created from within `proc_create()` (i.e. + where `sfx_id` == kBaseSfxId, but it doesn't work for mult + variables that are automatically created via `var_register()` + because `var_register()` does not have a value to assign to the + kAnyChIdx instance. In this case the variable get assigned + the class default value. The way around this is to explicitely + set the mult variable value in the 'in' stmt or the 'args' stmt.) + + + -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 +511,18 @@ 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 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. @@ -519,6 +542,757 @@ before registering the variable. access to registered variables. +# 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: + +- Eliminate the value() custom proc_t function and replace it by setting a 'delta flag' on the +variables that change. Optionally a linked list of changed variables could be implemented to +avoid having to search for changed variable values - although this list might have to be implemented as a thread safe linked list. + +- Allow proc's to send messages to the UI. Implementation: During exec() the proc builds a global list of variables whose values +should be passed to the UI. Adding to the list must be done atomically, but removing can be non-atomic because it will happen +at the end of the network 'exec' cycle when no proc's are being executed. See cwMpScNbQueue push() for an example of how to do this. + +- Allow min/max limits on numeric variables. + +- Add a 'doc' string-list to the class desc. + +- Try using adding 'time' type and 'cfg' types to a 'log:{...}' stmt. + +- print_network_fl and print_proc_dict_fl should be given from the command line. + (but it's ok to leave them as cfg flags also) + +- Add 'doc' strings to all proc classes. + +- Add 'doc' strings to user-defined proc data structure. + +- It is an error to specify a suffix_id on a poly network proc because the suffix_id's are generated automatically. + This error should be caught by the compiler. + +- How do user defined procedures handle suffix id's? + +- Add a 'preset' arg to 'poly' so that a preset can be selected via the owning network. + Currently it is not possible to select a preset for a poly. + +- Automatic assignment of sfx_id's should only occur when the network is a 'poly'. + This should be easy to detect. + +- When a var value is given to var_create() it does not appear to channelize the +var if value is a list. Is a value ever given directly to `var_create()`? +Look at all the places `var_create()` is called can the value arg. be removed? + +- var_channelize() should never be called at runtime. + +- Re-write the currawong circuit with caw. + +- Finish audio feedback example - this will probably involve writing an `audio_silence` class. + +- Issue a warning if memory is allocated during runtime. + +- String assignment is allocating memory: + See: `rc_t _val_set( value_t* val, const char* v ) cwFlowTypes.cpp line:464.` + +- cwMpScNbQueue is allocating memory. This makes it blocking. + +- 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 via the 'readv()' method. + +- Verify that all variables have been registered (have valid 'vid's) during post instantiation validation. + (this is apparently not currently happening) + +- How is the number of channels for a given variable determined? Is it the widest (max channel) preset + that is encountered during preset compilation? What if a variable has a wide preset but it is not + initially applied - does that mean an uninitialized channel is just sitting there? (... no i think + the previous channel is duplicated in var_channelize()) + +- UI Issues: + + When UI appIdMap[] labels do not match ui.cfg labels no error is generated. All appIdMap[] labels should be + validated to avoid this problem. + + + The reliance on using UUId to build UI's should be eliminated. It is very clunky. + + + UI elements should form proper tree's where elements know their children. As it is the links only go up the tree + from child to parent - searching down the tree is not possible. + + + Disabled "disp_str" should turn grey. + + +- Class presets cannot address 'mult' variables. Maybe this is ok since 'mult' variables are generally connected to a source? +... although 'gain' mult variables are not necessarily connected to a source see: `audio_split` or `audio_mix`. +Has this problem been addressed by allowing mult variables to be instantiated in the 'args' statement? + +- Documentation w/ examples. + + Write the rules for each implementing member functions. + +- 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. + + +- 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.) + +- How much of the proc initialization implementation can use the preset compile/apply code? + +- Reduce runtime overhead for var get/set operations. + +- Implement matrix types. + +- Should the `object_t` be used in place of `value_t`? + + +- 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 + +- 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. + +- Implement user-defined-proc preset application. + +- Implement the var attributes and attribute checking. + +- Port 'cm' and 'hum' processors. + +- Implement Linux audio plugins loading + +- Implement dynamic loading of procs. + +- Implement a debug mode to aid in building networks and user-defined-procs (or is logging good enough) + +- Implement multi-field messages. + +- Implement user defined data types. + +- 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? + + + +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 +---- +- DONE: Remove `preset_label` and `type_src_label` from `_var_channelize()` and report error +locations from the point of call. + +- DONE: Move proc_dict.cfg to libcw directory. + +- DONE: The proc inst 'args' should be able to create mult variables. The only way to instantiate +new mult variables now is via the 'in' stmt. + +- DONE: The `audio_merge` implementaiton is wrong. It should mimic `audio_mix` where all igain +coeff's are instantiated even if they are not referenced. + +- DONE: Add the `caw` examples to the test suite. + + +- DONE: Remove the multiple 'args' thing and and 'argsLabel'. 'args' should be a simple set of arg's. + +- 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 } ` + +- Complete user-def-procs: + + User-Def-Procs should have presets written in terms of the user-def-proc 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 user-def-proc implementation. + + + DONE: user-def-proc var desc's should be the same as non+user-def-proc vars but also include the 'proxy' field. + In particular they should get default values. + If a var desc is part of a user-def-proc then it must have a proxy. + The output variables of var desc's must have the 'out' attribute + + + + DONE: improve the user-def-proc creating code by using consistent naming + use proxy or wrap but not both + + + DONE: improve code comments on user-def-proc creation + + +- 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: +- DONE: The counter modulo mode is not working as expected. + + + +- 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 + + + +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. + +- 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 + +Proc instantiation +------------------ +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. + + +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 + + +caw w/ UI +--------- + +1. If no program is given, but a cfg file is given, then load a menu with the pgm's in the file. + Selecting a prg name then loads the pgm + Otherwise, if a pgm is given then the pgm is automatically loaded. + If the pgm does not set 'use_ui_fl' then it is automatically initialized and executed. + Otherwise goto step 2 + + + +2. Once a pgm is loaded then it can be queried for UI information. + - get basic information from proc dict + - get override information from the network + +3. + + + + + + + + + + + + + diff --git a/cwAudioBuf.cpp b/cwAudioBuf.cpp index e8dfdee..aef77db 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" @@ -991,7 +992,7 @@ void cw::audio::buf::report(handle_t h) /// [cwAudioBufExample] -cw::rc_t cw::audio::buf::test() +cw::rc_t cw::audio::buf::test(const test::test_args_t& args) { rc_t rc = kOkRC; unsigned devIdx = 0; @@ -1086,8 +1087,8 @@ cw::rc_t cw::audio::buf::test() } for(i=0; i wt{ + .tid = kLoopWtTId, + .cyc_per_loop = 0, + .aV = aV, + .aN = 16, + .rms = 0, + .hz = 1, + .srate = srate, + .pad_smpN = 1, + .posn_smp_idx = 0 + }; + + struct obj_str obj; + init(&obj,&wt); + + unsigned yN = (int)(srate*2); + sample_t yV[yN]; + unsigned actual = 0; + process(&obj, yV, yN,actual); + + vop::print( yV, yN, "%f"); + + return rc; + } + + } + + namespace wt_seq_osc + { + typedef float sample_t; + typedef float srate_t; + + rc_t test() + { + rc_t rc = kOkRC; + srate_t srate = 8; + sample_t aV[] = { 7,0,1,2,3,4,5,6,7,0,1,2,3,4,5,6,7,0 }; + + struct wt_osc::wt_str wt{ + .tid = wt_osc::kOneShotWtTId, + .cyc_per_loop = 0, + .aV = aV, + .aN = 16, + .rms = 0, + .hz = 1, + .srate = srate, + .pad_smpN = 1, + .posn_smp_idx = 0 + }; + + struct wt_osc::wt_str wt0 = wt; + struct wt_osc::wt_str wt1 = wt; + struct wt_osc::wt_str wt2 = wt; + + + wt1.tid = wt_osc::kLoopWtTId; + wt1.posn_smp_idx = 16; + wt2.tid = wt_osc::kLoopWtTId; + wt2.posn_smp_idx = 32; + + struct wt_osc::wt_str wtA[] = { + wt0, + wt1, + wt2 + }; + + struct wt_seq_osc::wt_seq_str wt_seq{ + .wtA = wtA, + .wtN = 3 + }; + + struct wt_seq_osc::obj_str obj; + init(&obj,&wt_seq); + + unsigned yN = (int)(srate*10); + sample_t yV[yN]; + unsigned actual = 0; + unsigned yi = 0; + unsigned yDspSmpCnt = 8; + while( yi < yN && is_init(&obj) ) + { + unsigned frmSmpN = std::min(yN-yi,yDspSmpCnt); + process(&obj, yV+yi, frmSmpN, actual); + assert( actual == frmSmpN ); + yi += actual; + } + + vop::print( yV, yi, "%5.3f",nullptr,8); + + return rc; + } + + } + + namespace multi_ch_wt_seq_osc + { + typedef float sample_t; + typedef float srate_t; + + rc_t test() + { + rc_t rc = kOkRC; + unsigned chN = 2; + srate_t srate = 8; + sample_t a0V[] = { 7,0,1,2,3,4,5,6,7,0,1,2,3,4,5,6,7,0 }; + sample_t a1V[] = { 17,10,11,12,13,14,15,16,17,10,11,12,13,14,15,16,17,10 }; + sample_t a2V[] = { 27,20,21,22,23,24,25,26,27,20,21,22,23,24,25,26,27,20 }; + + struct wt_osc::wt_str wt{ + .tid = wt_osc::kOneShotWtTId, + .cyc_per_loop = 0, + .aV = a0V, + .aN = 16, + .rms = 0, + .hz = 1, + .srate = srate, + .pad_smpN = 1, + .posn_smp_idx = 0 + }; + + struct wt_osc::wt_str wt0 = wt; + struct wt_osc::wt_str wt1 = wt; + struct wt_osc::wt_str wt2 = wt; + + + wt1.tid = wt_osc::kLoopWtTId; + wt1.posn_smp_idx = 16; + wt1.aV = a1V; + + wt2.tid = wt_osc::kLoopWtTId; + wt2.posn_smp_idx = 32; + wt2.aV = a2V; + + struct wt_osc::wt_str wtA[] = { + wt0, + wt1, + wt2 + }; + + struct wt_seq_osc::wt_seq_str wt_seq{ + .wtA = wtA, + .wtN = 3 + }; + + + struct wt_seq_osc::wt_seq_str chA[] = { + wt_seq, + wt_seq + }; + + struct multi_ch_wt_seq_str mcs = { + .chA = chA, + .chN = chN + }; + + + struct obj_str obj; + + if((rc = create(&obj,chN)) != kOkRC ) + goto errLabel; + + if((rc = setup(&obj,&mcs)) != kOkRC ) + goto errLabel; + else + { + unsigned yN = (int)(srate*10); + unsigned actual = 0; + unsigned yi = 0; + unsigned yDspSmpCnt = 8; + + + while( yi < yN && !is_done(&obj) ) + { + unsigned frmSmpN = std::min(yN-yi,yDspSmpCnt); + sample_t yV[frmSmpN*chN]; + + if((rc = process(&obj, yV, chN, frmSmpN, actual)) != kOkRC ) + goto errLabel; + + assert( actual == frmSmpN ); + + vop::print( yV, frmSmpN*chN, "%5.3f",nullptr,8); + + yi += actual; + } + } + + errLabel: + destroy(&obj); + + return rc; + } + + } + + + rc_t test( const test::test_args_t& args ) + { + rc_t rc = kOkRC; + + if( textIsEqual(args.test_label,"wnd_func") ) + { + rc = wnd_func::test(); + goto errLabel; + } + + if( textIsEqual(args.test_label,"ola") ) + { + rc = ola::test(); + goto errLabel; + } + + if( textIsEqual(args.test_label,"shift_buf") ) + { + rc = shift_buf::test(); + goto errLabel; + } + + if( textIsEqual(args.test_label,"wt_osc") ) + { + rc = wt_osc::test(); + goto errLabel; + } + + if( textIsEqual(args.test_label,"wt_seq_osc") ) + { + rc = wt_seq_osc::test(); + goto errLabel; + } + + if( textIsEqual(args.test_label,"multi_ch_wt_seq_osc") ) + { + rc = multi_ch_wt_seq_osc::test(); + goto errLabel; + } + + rc = cwLogError(kInvalidArgRC,"Unknown test case module:%s test:%s.",args.module_label,args.test_label); + errLabel: + return rc; } } diff --git a/cwAudioTransforms.h b/cwAudioTransforms.h index b3d2614..81dbbb4 100644 --- a/cwAudioTransforms.h +++ b/cwAudioTransforms.h @@ -779,9 +779,9 @@ namespace cw ifft::exec_polar( p->ft, magV, phsV ); - // convert double to float - T0 v[ p->ft->outN ]; - vop::copy( v, p->ft->outV, p->ft->outN ); + // convert double to float + T0 v[ p->ft->outN ]; + vop::copy( v, p->ft->outV, p->ft->outN ); ola::exec( p->ola, v, p->ft->outN ); @@ -1233,8 +1233,482 @@ namespace cw } + + //--------------------------------------------------------------------------------------------------------------------------------- + // wt_osc + // - rc_t test( const cw::object_t* args ); + namespace wt_osc + { + + typedef enum { + kInvalidWtTId, + kOneShotWtTId, + kLoopWtTId + } wt_tid_t; + + template< typename sample_t, typename srate_t > + struct wt_str + { + wt_tid_t tid; + unsigned cyc_per_loop; // count of cycles in the loop + sample_t* aV; // aV[ padN + aN + padN ] + unsigned aN; // Count of unique samples + double rms; + double hz; + srate_t srate; + unsigned pad_smpN; + unsigned posn_smp_idx; // The location of this sample in the original audio file. + }; + + template< typename sample_t > + sample_t table_read_2( const sample_t* tab, double frac ) + { + + unsigned i0 = floor(frac); + unsigned i1 = i0 + 1; + double f = frac - int(frac); + + sample_t r = (sample_t)(tab[i0] + (tab[i1] - tab[i0]) * f); + + //intf("r:%f frac:%f i0:%i f:%f\n",r,frac,i0,f); + return r; + } + + template< typename sample_t > + sample_t hann_read( double x, double N ) + { + while( x > N) + x -= N; + + x = x - (N/2) ; + + return (sample_t)(0.5 + 0.5 * cos(2*M_PI * x / N)); + } + + template< typename sample_t, typename srate_t > + struct obj_str + { + const wt_str* wt; + + double phs; // current fractional phase into wt->aV[] + double fsmp_per_wt; // + + }; + + template< typename sample_t, typename srate_t > + bool validate_srate(const struct obj_str* p, srate_t expected_srate) + { return p->wt != nullptr && p->wt->srate == expected_srate; } + + template< typename sample_t, typename srate_t > + bool is_init(const struct obj_str* p) + { return p->wt != nullptr; } + + template< typename sample_t, typename srate_t > + void init(struct obj_str* p, struct wt_str* wt) + { + if( wt == nullptr ) + p->wt = nullptr; + else + { + double fsmp_per_cyc = wt->srate/wt->hz; + p->fsmp_per_wt = fsmp_per_cyc * 2; // each wavetable contains 2 + + p->wt = wt; + p->phs = 0; + } + } + + template< typename sample_t, typename srate_t > + void _process_loop(struct obj_str* p, sample_t* aV, unsigned aN, unsigned& actual_Ref) + { + double phs0 = p->phs; + double phs1 = phs0 + p->fsmp_per_wt/2; + unsigned smp_per_wt = (int)floor(p->fsmp_per_wt); // + + while(phs1 >= smp_per_wt) + phs1 -= smp_per_wt; + + for(unsigned i=0; iwt->aV+p->wt->pad_smpN, phs0 ); + sample_t s1 = table_read_2( p->wt->aV+p->wt->pad_smpN, phs1 ); + + sample_t e0 = hann_read(phs0,p->fsmp_per_wt); + sample_t e1 = hann_read(phs1,p->fsmp_per_wt); + + aV[ i ] = e0*s0 + e1*s1; + + // advance the phases of the oscillators + phs0 += 1; + while(phs0 >= smp_per_wt) + phs0 -= smp_per_wt; + + phs1 += 1; + while(phs1 >= smp_per_wt) + phs1 -= smp_per_wt; + + } + + p->phs = phs0; + actual_Ref = aN; + } + + template< typename sample_t, typename srate_t > + void _process_one_shot(struct obj_str* p, sample_t* aV, unsigned aN, unsigned& actual_Ref) + { + unsigned phs = (unsigned)p->phs; + unsigned i; + for(i=0; iwt->aN; ++i,++phs) + aV[i] = p->wt->aV[ p->wt->pad_smpN + phs ]; + + p->phs = phs; + actual_Ref = i; + + } + + template< typename sample_t, typename srate_t > + void process(struct obj_str* p, sample_t* aV, unsigned aN, unsigned& actual_Ref) + { + actual_Ref = 0; + switch( p->wt->tid ) + { + case wt_osc::kLoopWtTId: + _process_loop(p,aV,aN,actual_Ref); + break; + + case wt_osc::kOneShotWtTId: + _process_one_shot(p,aV,aN,actual_Ref); + break; + + default: + assert(0); + } + + } + + rc_t test(); + + } // wt_osc + + + namespace wt_seq_osc + { + + template< typename sample_t, typename srate_t > + struct wt_seq_str + { + struct wt_osc::wt_str* wtA; + unsigned wtN; + }; + + template< typename sample_t, typename srate_t > + struct obj_str + { + struct wt_seq_osc::wt_seq_str* wt_seq; + struct wt_osc::obj_str osc0; + struct wt_osc::obj_str osc1; + + unsigned wt_idx; // index of wt0 in wt_seq->wtA[] + + + unsigned mix_interval_smp; // osc0/osc1 crossfade interval in samples + unsigned mix_phs; // current crossfade phase (0 <= mix_phs <= mix_interval_smp) + }; + + template< typename sample_t, typename srate_t > + rc_t _update_wt( struct obj_str* p, unsigned wt_idx ) + { + rc_t rc = kOkRC; + struct wt_osc::wt_str* wt0 = nullptr; + struct wt_osc::wt_str* wt1 = nullptr; + + p->mix_interval_smp = 0; + + if( wt_idx < p->wt_seq->wtN ) + wt0 = p->wt_seq->wtA + wt_idx; + + if( (wt_idx+1) < p->wt_seq->wtN ) + { + wt1 = p->wt_seq->wtA + (wt_idx+1); + + unsigned posn0_smp_idx = wt0->posn_smp_idx; + unsigned posn1_smp_idx = wt1->posn_smp_idx; + + if( posn1_smp_idx < posn0_smp_idx ) + { + rc = cwLogError(kInvalidStateRC,"The position of the wavetable at wt. seq index:%i must be greater than the position of the previous wt.",wt_idx+1); + + goto errLabel; + } + + p->mix_interval_smp = posn1_smp_idx - posn0_smp_idx; + } + + wt_osc::init(&p->osc0,wt0); + wt_osc::init(&p->osc1,wt1); + + p->wt_idx = wt_idx; + p->mix_phs = 0; + + errLabel: + return rc; + } + + template< typename sample_t, typename srate_t > + bool validate_srate(const struct obj_str* p, srate_t expected_srate) + { + if( p->wt_seq == nullptr ) + return false; + for(unsigned i=0; iwt_seq->wtN; ++i) + if( p->wt_seq->wtA[i].srate != expected_srate ) + return false; + + return true; + } + + template< typename sample_t, typename srate_t > + bool is_init( const struct obj_str* p ) + { + return is_init(&p->osc0); + } + + template< typename sample_t, typename srate_t > + rc_t init(struct obj_str* p, struct wt_seq_osc::wt_seq_str* wt_seq) + { + rc_t rc = kOkRC; + p->wt_seq = wt_seq; + p->wt_idx = 0; + + if((rc = _update_wt(p,0)) != kOkRC ) + goto errLabel; + + errLabel: + return rc; + } + + + template< typename sample_t, typename srate_t > + rc_t process(struct obj_str* p, sample_t* aV, unsigned aN, unsigned& actual_Ref) + { + actual_Ref = 0; + + rc_t rc = kOkRC; + unsigned actual; + bool atk_fl = p->wt_idx==0 && p->osc0.wt->tid == wt_osc::kOneShotWtTId; + + // if the osc is in the attack phase + if( atk_fl ) + { + // update aV[aN] from osc0 + wt_osc::process(&p->osc0,aV,aN,actual); + + actual_Ref = actual; + + // if all requested samples were generated we are done ... + if( actual >= aN ) + return rc; + + // otherwise all requested samples were not generated + // fill the rest of aV[] from the next one or two wave tables. + aN -= actual; + aV += actual; + + // initialize osc0 and osc1 + if((rc = _update_wt(p, 1)) != kOkRC ) + goto errLabel; + } + + wt_osc::process(&p->osc0,aV,aN,actual); + + // if the second oscillator is initialized + if( wt_osc::is_init(&p->osc1) ) + { + unsigned actual1 = 0; + sample_t tV[ aN ]; + // generate aN samples into tV[aN] + wt_osc::process(&p->osc1,tV,aN,actual1); + + assert( actual1 == actual ); + + + sample_t g = (sample_t)std::min(1.0,(double)p->mix_phs / p->mix_interval_smp); + + // mix the output of the second oscillator into the output signal + vop::scale_add(aV,aV,(1.0f-g),tV,g,actual1); + + p->mix_phs += actual; + + // if the osc0/osc1 xfade is complete ... + if( p->mix_phs >= p->mix_interval_smp ) + { + // ... then advance to the next set of wavetables + if((rc = _update_wt(p, p->wt_idx+1)) != kOkRC ) + goto errLabel; + } + + } + + actual_Ref += actual; + + errLabel: + return rc; + } + + rc_t test(); + + } // wt_seq_osc + + namespace multi_ch_wt_seq_osc + { + template< typename sample_t, typename srate_t > + struct multi_ch_wt_seq_str + { + struct wt_seq_osc::wt_seq_str* chA; + unsigned chN; + }; + + template< typename sample_t, typename srate_t > + struct obj_str + { + const struct multi_ch_wt_seq_str* mcs = nullptr; + struct wt_seq_osc::obj_str* chA = nullptr; + unsigned chAllocN = 0; + unsigned chN = 0; + bool done_fl = true; + }; + + + // if mcs != nullptr and expected_srate is non-zero then the expected_srate will be validated + template< typename sample_t, typename srate_t > + rc_t create(struct obj_str* p, unsigned maxChN, const struct multi_ch_wt_seq_str* mcs=nullptr, srate_t expected_srate=0 ) + { + rc_t rc = kOkRC; + + destroy(p); + + p->chA = mem::allocZ< struct wt_seq_osc::obj_str >(maxChN); + p->chAllocN = maxChN; + p->chN = 0; + p->done_fl = true; + + if( mcs != nullptr ) + setup(p,mcs); + + return rc; + } + + template< typename sample_t, typename srate_t > + rc_t destroy(struct obj_str* p ) + { + rc_t rc = kOkRC; + + mem::release(p->chA); + p->chAllocN = 0; + p->chN = 0; + p->done_fl = true; + return rc; + } + + // if mcs != nullptr and expected_srate is non-zero then the expected_srate will be validated + template< typename sample_t, typename srate_t > + rc_t setup( struct obj_str* p, const struct multi_ch_wt_seq_str* mcs, srate_t expected_srate=0 ) + { + rc_t rc = kOkRC; + + if( mcs->chN > p->chAllocN ) + { + rc = cwLogError(kInvalidArgRC,"Invalid multi-ch-wt-osc channel count. (%i > %i)",mcs->chN,p->chAllocN); + goto errLabel; + } + + p->mcs = mcs; + p->done_fl = false; + p->chN = mcs->chN; + for(unsigned i=0; ichN; ++i) + if((rc = wt_seq_osc::init(p->chA+i,mcs->chA + i)) != kOkRC ) + goto errLabel; + + if( mcs != nullptr && expected_srate != 0 ) + if( !validate_srate(p,expected_srate) ) + { + rc = cwLogError(kInvalidArgRC,"The srate is not valid. All wave tables do not share the same sample rate."); + goto errLabel; + } + + errLabel: + if( rc != kOkRC ) + rc = cwLogError(rc,"multi-ch-wt-osc setup failed."); + + return rc; + } + + template< typename sample_t, typename srate_t > + bool validate_srate(const struct obj_str* p, srate_t expected_srate) + { + if( p->chA == nullptr ) + return false; + + for(unsigned i=0; ichN; ++i) + if( !validate_srate(p->chA+i,expected_srate) ) + return false; + return true; + } + + + template< typename sample_t, typename srate_t > + rc_t is_done( struct obj_str* p ) + { return p->done_fl; } + + template< typename sample_t, typename srate_t > + rc_t process( struct obj_str* p, sample_t* aM, unsigned chN, unsigned frmN, unsigned& actual_Ref ) + { + rc_t rc = kOkRC; + unsigned actual = 0; + unsigned doneN = 0; + + for(unsigned i=0; ichN; ++i) + { + unsigned actual0 = 0; + sample_t* aV = aM + (i*frmN); + + if( !wt_seq_osc::is_init(p->chA + i) ) + { + vop::zero(aV,frmN); + actual0 = frmN; + doneN += 1; + } + else + { + if((rc = wt_seq_osc::process(p->chA + i, aV, frmN, actual0 )) != kOkRC ) + goto errLabel; + } + + if( i!=0 && actual0 != actual ) + { + rc = cwLogError(kInvalidStateRC,"An inconsistent sample count was generated across channels (%i != !i).",actual0,actual); + goto errLabel; + } + + actual = actual0; + } + + actual_Ref = actual; + p->done_fl = doneN == p->chN; + + errLabel: + if( rc != kOkRC ) + rc = cwLogError(rc,"multi-ch-wt-osc process failed."); + + return rc; + } + + rc_t test(); + + } //multi_ch_wt_seq_osc + + + rc_t test( const test::test_args_t& args ); } // dsp } // cw 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..65cbb80 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 ); assert(0); } 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..616c8f7 100644 --- a/cwCsv.cpp +++ b/cwCsv.cpp @@ -1,12 +1,14 @@ #include "cwCommon.h" #include "cwLog.h" #include "cwCommonImpl.h" +#include "cwTest.h" #include "cwMem.h" #include "cwText.h" #include "cwFile.h" #include "cwObject.h" #include "cwCsv.h" #include "cwNumericConvert.h" +#include namespace cw { @@ -262,6 +264,8 @@ namespace cw goto errLabel; } + + fieldStr_Ref = p->lineBuf + p->colA[colIdx].char_idx; errLabel: @@ -274,16 +278,28 @@ namespace cw { rc_t rc = kOkRC; const char* fieldStr = nullptr; - + if((rc = _get_field_str(p,colIdx,fieldStr)) != kOkRC ) goto errLabel; - if((rc = string_to_number(fieldStr,valueRef)) != kOkRC ) + if( fieldStr != nullptr ) { - rc = cwLogError(rc,"Numeric parse failed on column '%s' on line index:%i",cwStringNullGuard(p->colA[colIdx].title),p->curLineIdx); - goto errLabel; - } + // advance past white space + while( *fieldStr && isspace(*fieldStr) ) + ++fieldStr; + // the first char must be a number or decimal point + if( isdigit(*fieldStr) || (*fieldStr=='.' && std::is_floating_point()) ) + { + + if((rc = string_to_number(fieldStr,valueRef)) != kOkRC ) + { + rc = cwLogError(rc,"Numeric parse failed on column '%s' on line index:%i",cwStringNullGuard(p->colA[colIdx].title),p->curLineIdx); + goto errLabel; + } + } + } + errLabel: return rc; } 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..348bb75 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,15 +30,20 @@ namespace cw } library_t; library_t g_library[] = { + { "user_def_proc", &user_def_proc::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 }, { "audio_merge", &audio_merge::members }, { "audio_mix", &audio_mix::members }, + { "audio_silence", &audio_silence::members }, { "sine_tone", &sine_tone::members }, { "pv_analysis", &pv_analysis::members }, { "pv_synthesis", &pv_synthesis::members }, @@ -44,6 +55,24 @@ namespace cw { "balance", &balance::members }, { "audio_meter", &audio_meter::members }, { "audio_marker", &audio_marker::members }, + { "xfade_ctl", &xfade_ctl::members }, + { "poly_voice_ctl", &poly_voice_ctl::members }, + { "midi_voice", &midi_voice::members }, + { "piano_voice", &piano_voice::members }, + { "sample_hold", &sample_hold::members }, + { "number", &number::members }, + { "reg", ®::members }, + { "timer", &timer::members }, + { "counter", &counter::members }, + { "list", &list::members }, + { "add", &add::members }, + { "preset", &preset::members }, + { "print", &print::members }, + { "halt", &halt::members }, + { "midi_msg", &midi_msg::members }, + { "midi_split", &midi_split::members }, + { "midi_file", &midi_file::members }, + { "midi_merge", &midi_merge::members }, { nullptr, nullptr } }; @@ -85,6 +114,180 @@ namespace cw } return rc; } + + rc_t _parse_udp_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_udp_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 _create_class_ui_desc( class_desc_t* desc ) + { + desc->ui = mem::allocZ(); + desc->ui->label = desc->label; + for(class_preset_t* p0 = desc->presetL; p0!=nullptr; p0=p0->link) + desc->ui->presetN += 1; + + desc->ui->presetA = mem::allocZ(desc->ui->presetN); + + unsigned i=0; + for(class_preset_t* p0 = desc->presetL; iui->presetN; ++i,p0=p0->link) + { + desc->ui->presetA[i].label = p0->label; + desc->ui->presetA[i].preset_idx = i; + } + + return kOkRC; + } rc_t _parse_class_cfg(flow_t* p, const object_t* classCfg) { @@ -109,7 +312,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 +340,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(); @@ -144,6 +348,14 @@ namespace cw cd->presetL = preset; } } + + // create the class descripiton + if((rc = _create_class_ui_desc(cd)) != kOkRC ) + { + cwLogError(rc,"Class desc UI record create failed on '%s'.",cwStringNullGuard(cd->label)); + goto errLabel; + } + // parse the variable dictionary if( varD != nullptr ) @@ -157,1388 +369,422 @@ 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 UDP definition.",cwStringNullGuard(vd->label),cwStringNullGuard(cd->label)); + } + if( cwIsFlag(vd->flags,kUdpOutVarDescFl ) ) + { + cwLogWarning("The 'out' flag in the variable description '%s' on class description '%s' will be ignored because the variable is not part of a UDP definition.",cwStringNullGuard(vd->label),cwStringNullGuard(cd->label)); + } + vd->link = cd->varDescL; cd->varDescL = vd; } } - } errLabel: return rc; } - void _connect_vars( variable_t* src_var, variable_t* in_var ) + rc_t _find_udp_proc_class_desc( flow_t* p, const object_t* udpProcD, 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 UDP + if((procInstD = udpProcD->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 UDP.",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_udp_var_desc( flow_t* p, class_desc_t* udpClassDesc, const object_t* udpProcD, 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, udpClassDesc, 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(udpClassDesc->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 UDP variable description '%s' in the UDP '%s' must have a valid 'proxy' field.",cwStringNullGuard(var_desc->label),cwStringNullGuard(udpClassDesc->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_udp_proc_class_desc( p, udpProcD, 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 UDP proxied variable desc '%s.%s' could not be found in UDP '%s'.",cwStringNullGuard(var_desc->proxyProcLabel),cwStringNullGuard(var_desc->proxyVarLabel),cwStringNullGuard(udpClassDesc->label)); + goto errLabel; } + // get the UDP var_desc type from the proxied var_desc + var_desc->type = proxy_var_desc->type; + + // augment the udp var_desc flags from the proxied var_desc + var_desc->flags |= proxy_var_desc->flags; + + // if no default value was given to the UDP 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_udp_vars( flow_t* p, class_desc_t* class_desc, const object_t* udpProcD, 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 UDP 'vars' dictioanry + for(unsigned i=0; ichild_ele(i); + var_desc_t* var_desc = nullptr; + + if((rc = _create_udp_var_desc( p, class_desc, udpProcD, 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_udp_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* udpD = nullptr; + const object_t* udpProcD = nullptr; + const object_t* udpPresetD = nullptr; + const char* udpProcDescLabel = nullptr; + + // Validate the UDP proc desc label and value + if( class_obj==nullptr || !class_obj->is_pair() || class_obj->pair_value()==nullptr || !class_obj->pair_value()->is_dict() || (udpProcDescLabel = class_obj->pair_label()) == nullptr ) + { + rc = cwLogError(kInvalidArgRC,"An invalid UDP description '%s' was encountered.",cwStringNullGuard(udpProcDescLabel)); + goto errLabel; + } + + // verify that another UDP with the same name does not already exist + if( class_desc_find(p,udpProcDescLabel) != nullptr ) + { + rc = cwLogError(kInvalidStateRC,"A UDP named '%s' already exists.",udpProcDescLabel); + goto errLabel; + } + + class_desc->cfg = class_obj->pair_value(); + class_desc->label = class_obj->pair_label(); + + // get the 'UDP' members record + if((class_desc->members = _find_library_record("user_def_proc")) == nullptr ) + { + rc = cwLogError(kSyntaxErrorRC,"The 'UDP' class member function record could not be found." ); + goto errLabel; + } + + // get the variable description + if((rc = class_desc->cfg->getv_opt("vars", varD, + "network", udpD)) != kOkRC ) + { + rc = cwLogError(rc,"Parse failed while parsing UDP desc:'%s'", cwStringNullGuard(class_desc->label) ); + goto errLabel; + } + + // get the UDP proc and preset dictionaries + if((rc = udpD->getv("procs", udpProcD, + "presets", udpPresetD)) != kOkRC ) + { + rc = cwLogError(rc,"Parse failed on the 'network' element."); + goto errLabel; + } + + // fill class_desc.varDescL from the UDP vars dictionary + if((rc = _parse_udp_vars( p, class_desc, udpProcD, varD )) != kOkRC ) + { + rc = cwLogError(rc,"UDP 'vars' processing failed."); + goto errLabel; + } + + + errLabel: + if(rc != kOkRC ) + rc = cwLogError(rc,"'proc' class description creation failed for the UDP '%s'. ",cwStringNullGuard(udpProcDescLabel)); + + return rc; + } + + + rc_t _parse_udp_cfg(flow_t* p, const object_t* udpCfg) { 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( !udpCfg->is_dict() ) + return cwLogError(kSyntaxErrorRC,"The UDP 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 udpDescN = udpCfg->child_count(); + p->udpDescA = mem::allocZ( udpDescN ); + + // for each UDP description + for(unsigned i=0; iis_list() ) + const object_t* udp_obj = udpCfg->child_ele(i); + + if((rc = _create_udp_class_desc(p, udp_obj, p->udpDescA + i )) != kOkRC ) { - rc = _set_var_from_dual_preset_list_scalar( inst, var_label, value_0, value_1, coeff ); + rc = cwLogError(rc,"UDP class description create failed on the UDP 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 UDP class array + // as we go because we may want be able to search p->udpDescA[] + // aand to do that we must now the current length. + p->udpDescN += 1; } + assert( udpDescN == p->udpDescN ); 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 ); - - return rc; - } - - - rc_t _class_multi_preset_channelize_vars(instance_t* inst, 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 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++; - } - } - - // 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); + rc = cwLogError(rc,"UDP processing failed."); + } - - errLabel: return rc; - - } - - 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_t _parse_preset_array(flow_t* p, const object_t* netCfg ) + { + rc_t rc = kOkRC; + unsigned presetAllocN = 0; + const object_t* presetD = nullptr; + + if((rc = netCfg->getv_opt("presets",presetD)) != kOkRC ) { - rc = cwLogError(kInvalidIdRC,"The preset '%s' could not be found for the instance '%s'.", preset_label, inst->label); + rc = cwLogError(rc,"An error ocurred while locating the network 'presets' configuration."); 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 ) + // if this network does not have any presets + if( presetD == 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 ) + // the 'preset' cfg must be a dictionary + if( !presetD->is_dict() ) { - rc = cwLogError(kSyntaxErrorRC,"The instance cfg. is not a valid pair. No instance label could be parsed."); + rc = cwLogError(kSyntaxErrorRC,"The network preset list is not a dictionary."); 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; - } + presetAllocN = presetD->child_count(); + p->presetA = mem::allocZ(presetAllocN); + p->presetN = 0; - // if an argument dict was given in the instanec cfg - if( arg_dict != nullptr ) + // parse each preset_label pair + for(unsigned i=0; iis_dict() ) + const object_t* preset_pair_cfg = presetD->child_ele(i); + network_preset_t& network_preset = p->presetA[ p->presetN ]; + + // validate the network preset pair + if( preset_pair_cfg==nullptr || !preset_pair_cfg->is_pair() || preset_pair_cfg->pair_label()==nullptr || preset_pair_cfg->pair_value()==nullptr ) { - cwLogError(kSyntaxErrorRC,"The instance argument dictionary on instance '%s' is not a dictionary.",pvars.inst_label); + rc = cwLogError(kSyntaxErrorRC,"Invalid syntax encountered on a network preset."); 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 ) + // get the preset type id + switch( preset_pair_cfg->pair_value()->type_id() ) { + case kDictTId: // 'value-list' preset + network_preset.tid = kPresetVListTId; + break; - // 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); + case kListTId: // dual preset + network_preset.tid = kPresetDualTId; + break; + + default: + rc = cwLogError(kAssertFailRC,"Unknown preset type on network preset: '%s'.",cwStringNullGuard(network_preset.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; } + + network_preset.label = preset_pair_cfg->pair_label(); - // 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; - } - //} - } + p->presetN += 1; } - - // 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); - + if(rc != kOkRC ) + { + mem::release(p->presetA); + p->presetN = 0; + } + return rc; } + void _release_class_desc_array( class_desc_t*& classDescA, unsigned classDescN ) + { + // release the class records + for(unsigned i=0; inetwork_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->udpDescA,p->udpDescN); + mem::release(p->presetA); + p->presetN = 0; + p->classDescN = 0; + p->udpDescN = 0; + mem::release(p); return rc; } - const object_t* _find_network_preset( flow_t* p, const char* presetLabel ) + void _make_flow_to_ui_callback( flow_t* p ) { - const object_t* preset_value = nullptr; - if( p->presetCfg != nullptr ) + // There is no concurrent contention for the linked list when + // this function is called and so all accesses use relaxed memory order. + + // Get the first variable to send to the UI + variable_t* var = p->ui_var_tail->ui_var_link.load(std::memory_order_relaxed); + + while( var!=nullptr) { - rc_t rc; + // Send the var to the UI + if( p->ui_callback != nullptr ) + p->ui_callback( p->ui_callback_arg, var->ui_var ); + + // Get the next var to send to the UI + variable_t* var0 = var->ui_var_link.load(std::memory_order_relaxed); + + // Nullify the list links as they are used + var->ui_var_link.store(nullptr,std::memory_order_relaxed); - if((rc = p->presetCfg->getv_opt( presetLabel, preset_value )) != kOkRC ) - cwLogError(rc,"Search for network preset named '%s' failed.", cwStringNullGuard(presetLabel)); + var = var0; } - return preset_value; + // Empty the UI message list. + p->ui_var_head.store(&p->ui_var_stub,std::memory_order_relaxed); + p->ui_var_tail = &p->ui_var_stub; + p->ui_var_stub.ui_var_link.store(nullptr,std::memory_order_relaxed); } - - 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,85 +795,100 @@ 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); + cwLogPrint("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"); + cwLogPrint("\n"); } + cw::rc_t cw::flow::create( handle_t& hRef, - const object_t& classCfg, - const object_t& networkCfg, - external_device_t* deviceA, - unsigned deviceN ) + const object_t* classCfg, + const object_t* pgmCfg, + const object_t* udpCfg, + const char* proj_dir, + ui_callback_t ui_callback, + void* ui_callback_arg) { rc_t rc = kOkRC; - const object_t* network = nullptr; bool printClassDictFl = false; - bool printNetworkFl = false; + unsigned maxCycleCount = kInvalidCnt; + double durLimitSecs = 0; if(( rc = destroy(hRef)) != kOkRC ) return rc; flow_t* p = mem::allocZ(); - p->networkCfg = &networkCfg; // 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 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 ) + // parse the UDP descriptions + if( udpCfg != nullptr ) + if((rc = _parse_udp_cfg(p,udpCfg)) != kOkRC ) + { + rc = cwLogError(kSyntaxErrorRC,"Error parsing the UDP list."); + goto errLabel; + } + + + p->pgmCfg = pgmCfg; + p->framesPerCycle = kDefaultFramesPerCycle; + p->sample_rate = kDefaultSampleRate; + p->maxCycleCount = kInvalidCnt; + p->proj_dir = proj_dir; + p->printLogHdrFl = true; + p->ui_callback = ui_callback; + p->ui_callback_arg= ui_callback_arg; + p->ui_var_head.store(&p->ui_var_stub); + p->ui_var_tail = &p->ui_var_stub; + + // parse the optional args + if((rc = pgmCfg->readv("network", 0, p->networkCfg, + "non_real_time_fl", kOptFl, p->non_real_time_fl, + "frames_per_cycle", kOptFl, p->framesPerCycle, + "sample_rate", kOptFl, p->sample_rate, + "max_cycle_count", kOptFl, maxCycleCount, + "dur_limit_secs", kOptFl, durLimitSecs, + "preset", kOptFl, p->init_net_preset_label, + "print_class_dict_fl", kOptFl, printClassDictFl, + "print_network_fl", kOptFl, p->printNetworkFl, + "multiPriPresetProbFl", kOptFl, p->multiPriPresetProbFl, + "multiSecPresetProbFl", kOptFl, p->multiSecPresetProbFl, + "multiPresetInterpFl", kOptFl, p->multiPresetInterpFl)) != kOkRC ) { - rc = cwLogError(kSyntaxErrorRC,"Error parsing the required flow configuration parameters."); + rc = cwLogError(kSyntaxErrorRC,"Error parsing the network system parameters."); goto errLabel; } - // parse the optional args - if((rc = networkCfg.getv_opt("maxCycleCount", p->maxCycleCount, - "printClassDictFl", printClassDictFl, - "printNetworkFl", printNetworkFl, - "presets", p->presetCfg)) != kOkRC ) + if((rc = _parse_preset_array(p, p->networkCfg )) != kOkRC ) { - rc = cwLogError(kSyntaxErrorRC,"Error parsing the optional flow configuration parameters."); + rc = cwLogError(rc,"Preset dictionary parsing failed."); goto errLabel; } + + // if a maxCycle count was given + if( maxCycleCount != kInvalidCnt ) + p->maxCycleCount = maxCycleCount; + else + { + // if a durLimitSecs was given - use it to setMaxCycleCount + if( durLimitSecs != 0.0 ) + p->maxCycleCount = (unsigned)((durLimitSecs * p->sample_rate) / p->framesPerCycle); + } // print the class dict if( printClassDictFl ) class_dict_print( p ); - - // build the network - for(unsigned i=0; ichild_count(); ++i) - { - 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; - - } - } - - if( printNetworkFl ) - network_print(p); - + hRef.set(p); errLabel: - if( rc != kOkRC ) _destroy(p); @@ -1635,23 +896,44 @@ cw::rc_t cw::flow::create( handle_t& hRef, return rc; } -cw::rc_t cw::flow::destroy( handle_t& hRef ) +bool cw::flow::is_non_real_time( handle_t h ) { - rc_t rc = kOkRC; - flow_t* p = nullptr;; - - if( !hRef.isValid() ) - return rc; - - p = _handleToPtr(hRef); - - _destroy(p); - - hRef.clear(); - - return rc; + flow_t* p = _handleToPtr(h); + return p->non_real_time_fl; } +double cw::flow::sample_rate( handle_t h ) +{ + flow_t* p = _handleToPtr(h); + return p->sample_rate; +} + +unsigned cw::flow::frames_per_cycle( handle_t h ) +{ + flow_t* p = _handleToPtr(h); + return p->framesPerCycle; +} + +unsigned cw::flow::preset_count( handle_t h ) +{ + flow_t* p = _handleToPtr(h); + return p->presetN; +} + +const char* cw::flow::preset_label( handle_t h, unsigned preset_idx ) +{ + flow_t* p = _handleToPtr(h); + + if( preset_idx >= p->presetN ) + { + cwLogError(kInvalidArgRC,"The preset index %i is invalid.",preset_idx); + return nullptr; + } + + return p->presetA[ preset_idx ].label; +} + + unsigned cw::flow::preset_cfg_flags( handle_t h ) { flow_t* p = _handleToPtr(h); @@ -1669,453 +951,231 @@ unsigned cw::flow::preset_cfg_flags( handle_t h ) return flags; } +cw::rc_t cw::flow::initialize( handle_t h, + external_device_t* deviceA, + unsigned deviceN, + unsigned preset_idx ) +{ + rc_t rc = kOkRC; + variable_t* proxyVarL = nullptr; + flow_t* p = _handleToPtr(h); + + p->deviceA = deviceA; + p->deviceN = deviceN; + + 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); + } + + // if an initialization preset was given + if( preset_idx != kInvalidIdx ) + { + const char* preset_label_str; + if((preset_label_str = preset_label(h,preset_idx)) == nullptr ) + { + cwLogError(kInvalidArgRC,"The preset index '%i' could not be resolved to an existing preset."); + goto errLabel; + } + + // override the program assigned 'preset' + p->init_net_preset_label = preset_label_str; + } + + // instantiate the network + if((rc = network_create(p,&p->networkCfg,1,proxyVarL,1,p->init_net_preset_label,p->net)) != kOkRC ) + { + rc = cwLogError(rc,"Network creation failed."); + goto errLabel; + } + + if( p->printNetworkFl && p->net != nullptr ) + network_print(*p->net); + + // The network preset may have been applied as each proc. was instantiated. + // This way any 'init' only preset values will have been applied and the + // proc's custom create is more likely to see the values from the preset. + // Now that the network is fully instantiated however we will apply it again + // to be sure that the final state of the network is determined by selected preset. + if( p->init_net_preset_label != nullptr && p->net != nullptr ) + network_apply_preset( *p->net, p->init_net_preset_label ); + + // form the UI description + if((rc = create_net_ui_desc(p)) != kOkRC ) + { + rc = cwLogError(rc,"UI description formation failed."); + goto errLabel; + } + + p->isInRuntimeFl = true; + cwLogInfo("Entering runtime."); + + errLabel: + + return rc; +} + + +cw::rc_t cw::flow::destroy( handle_t& hRef ) +{ + rc_t rc = kOkRC; + flow_t* p = nullptr;; + + if( !hRef.isValid() ) + return rc; + + p = _handleToPtr(hRef); + + _destroy(p); + + hRef.clear(); + + return rc; +} + + +const cw::flow::ui_net_t* cw::flow::ui_net( handle_t h ) +{ + flow_t* p = _handleToPtr(h); + + if( p->net == nullptr ) + { + cwLogError(kInvalidStateRC,"No UI net exists because the no net exists."); + return nullptr; + } + + if( p->net->ui_net == nullptr ) + return nullptr; + + return p->net->ui_net; +} cw::rc_t cw::flow::exec_cycle( handle_t h ) { - return _exec_cycle(_handleToPtr(h)); + rc_t rc = kOkRC;; + flow_t* p = _handleToPtr(h); + + if( p->maxCycleCount!=kInvalidCnt && p->cycleIndex >= p->maxCycleCount ) + { + rc = kEofRC; + cwLogInfo("'maxCycleCnt' reached: %i. Shutting down flow.",p->maxCycleCount); + } + else + { + // Execute one cycle of the network + if((rc = exec_cycle(*p->net)) == kOkRC ) + { + // During network execution variables which need to update the UI + // are collected in a linked list based on p->ui_var_tail. + // Callback to the UI with those variables here. + _make_flow_to_ui_callback(p); + + } + + p->cycleIndex += 1; + } + + + return rc; } cw::rc_t cw::flow::exec( handle_t h ) { rc_t rc = kOkRC; - flow_t* p = _handleToPtr(h); - - while( true ) - { - rc = _exec_cycle(p); - - if( rc == kEofRC ) - { - rc = kOkRC; - break; - } - - p->cycleIndex += 1; - if( p->maxCycleCount > 0 && p->cycleIndex >= p->maxCycleCount ) - break; - } + //flow_t* p = _handleToPtr(h); + while( rc == kOkRC ) + rc = exec_cycle(h); + 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::set_variable_user_id( handle_t h, const ui_var_t* ui_var, unsigned user_id ) +{ return set_variable_user_id( *_handleToPtr(h)->net, ui_var, user_id ); } -/* -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 ); } +cw::rc_t cw::flow::set_variable_value( handle_t h, const ui_var_t* ui_var, bool value ) +{ return set_variable_value( *_handleToPtr(h)->net, ui_var, value ); } +cw::rc_t cw::flow::set_variable_value( handle_t h, const ui_var_t* ui_var, int value ) +{ return set_variable_value( *_handleToPtr(h)->net, ui_var, value ); } +cw::rc_t cw::flow::set_variable_value( handle_t h, const ui_var_t* ui_var, unsigned value ) +{ return set_variable_value( *_handleToPtr(h)->net, ui_var, value ); } +cw::rc_t cw::flow::set_variable_value( handle_t h, const ui_var_t* ui_var, float value ) +{ return set_variable_value( *_handleToPtr(h)->net, ui_var, value ); } +cw::rc_t cw::flow::set_variable_value( handle_t h, const ui_var_t* ui_var, double value ) +{ return set_variable_value( *_handleToPtr(h)->net, ui_var, value ); } +cw::rc_t cw::flow::set_variable_value( handle_t h, const ui_var_t* ui_var, const char* value ) +{ return set_variable_value( *_handleToPtr(h)->net, ui_var, value ); } + +cw::rc_t cw::flow::get_variable_value( handle_t h, const ui_var_t* ui_var, bool& value_ref ) +{ return get_variable_value( *_handleToPtr(h)->net, ui_var, value_ref ); } +cw::rc_t cw::flow::get_variable_value( handle_t h, const ui_var_t* ui_var, int& value_ref ) +{ return get_variable_value( *_handleToPtr(h)->net, ui_var, value_ref ); } +cw::rc_t cw::flow::get_variable_value( handle_t h, const ui_var_t* ui_var, unsigned& value_ref ) +{ return get_variable_value( *_handleToPtr(h)->net, ui_var, value_ref ); } +cw::rc_t cw::flow::get_variable_value( handle_t h, const ui_var_t* ui_var, float& value_ref ) +{ return get_variable_value( *_handleToPtr(h)->net, ui_var, value_ref ); } +cw::rc_t cw::flow::get_variable_value( handle_t h, const ui_var_t* ui_var, double& value_ref ) +{ return get_variable_value( *_handleToPtr(h)->net, ui_var, value_ref ); } +cw::rc_t cw::flow::get_variable_value( handle_t h, const ui_var_t* ui_var, const char*& value_ref ) +{ return get_variable_value( *_handleToPtr(h)->net, ui_var, value_ref ); } void cw::flow::print_class_list( handle_t h ) @@ -2131,56 +1191,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..1735d40 100644 --- a/cwFlow.h +++ b/cwFlow.h @@ -1,5 +1,5 @@ -#ifndef cwFlowSys_h -#define cwFlowSys_h +#ifndef cwFlow_h +#define cwFlow_h namespace cw { @@ -8,68 +8,52 @@ 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 ); + + // Parse the cfg's but don't yet instantiate the network. + // Upon completion of this function the caller can + // query the network for configuration information which can + // be used to setup the extern_device_t array. + rc_t create(handle_t& hRef, + const object_t* classCfg, // processor class dictionary + const object_t* pgmCfg, // top level program cfg + const object_t* udpCfg = nullptr, + const char* projDir = nullptr, + ui_callback_t ui_callback = nullptr, + void* ui_callback_arg = nullptr); + + // Network cfg. information which is available following create(). + bool is_non_real_time( handle_t h ); + double sample_rate( handle_t h ); + unsigned frames_per_cycle( handle_t h ); + unsigned preset_cfg_flags( handle_t h ); + + // Get the count and labels of the top level presets + unsigned preset_count( handle_t h ); + const char* preset_label( handle_t h, unsigned preset_idx ); + + // Instantiate the network and prepare for runtime. + // The UI is not available until after initialization. + rc_t initialize( handle_t handle, + external_device_t* deviceA = nullptr, + unsigned deviceN = 0, + unsigned preset_idx = kInvalidIdx); - - rc_t create( handle_t& hRef, - const object_t& classCfg, - const object_t& networkCfg, - external_device_t* deviceA = nullptr, - unsigned deviceN = 0); - rc_t destroy( handle_t& hRef ); - unsigned preset_cfg_flags( handle_t h ); + // The ui_net() is not available until the network has been initialized. + const ui_net_t* ui_net( handle_t h ); // Run one cycle of the network. rc_t exec_cycle( handle_t h ); - // Run the network to completion. - rc_t exec( handle_t h ); + // Run a non-real-time program to completion. + 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 ); @@ -83,11 +67,29 @@ namespace cw rc_t get_variable_value( handle_t h, const char* inst_label, const char* var_label, unsigned chIdx, unsigned& valueRef ); rc_t get_variable_value( handle_t h, const char* inst_label, const char* var_label, unsigned chIdx, float& valueRef ); rc_t get_variable_value( handle_t h, const char* inst_label, const char* var_label, unsigned chIdx, double& valueRef ); + + + // The 'user_id' shows up as the 'user_id' in the ui_var field. + rc_t set_variable_user_id( handle_t h, const ui_var_t* ui_var, unsigned user_id ); + + rc_t set_variable_value( handle_t h, const ui_var_t* ui_var, bool value ); + rc_t set_variable_value( handle_t h, const ui_var_t* ui_var, int value ); + rc_t set_variable_value( handle_t h, const ui_var_t* ui_var, unsigned value ); + rc_t set_variable_value( handle_t h, const ui_var_t* ui_var, float value ); + rc_t set_variable_value( handle_t h, const ui_var_t* ui_var, double value ); + rc_t set_variable_value( handle_t h, const ui_var_t* ui_var, const char* value ); + + rc_t get_variable_value( handle_t h, const ui_var_t* ui_var, bool& value_ref ); + rc_t get_variable_value( handle_t h, const ui_var_t* ui_var, int& value_ref ); + rc_t get_variable_value( handle_t h, const ui_var_t* ui_var, unsigned& value_ref ); + rc_t get_variable_value( handle_t h, const ui_var_t* ui_var, float& value_ref ); + rc_t get_variable_value( handle_t h, const ui_var_t* ui_var, double& value_ref ); + rc_t get_variable_value( handle_t h, const ui_var_t* ui_var, const char*& value_ref ); + 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..49c29f2 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; ideviceN = 0; net->stateId = net_idx == 0 ? kActiveStateId : kInactiveStateId; net->net_idx = net_idx; + + if((rc = flow::create( net->flowH, &classCfg, &networkCfg, nullptr, nullptr )) == kOkRC ) + { + rc = cwLogError(rc,"Flow cross index %i network create failed.",net_idx); + } - if((rc = flow::create( net->flowH, classCfg, networkCfg, net->deviceA, deviceN )) == kOkRC ) + if((rc = flow::initialize( net->flowH, net->deviceA, deviceN )) == kOkRC ) net->deviceN = deviceN; else { - cwLogError(rc,"Flow cross index %i network created failed.",net_idx); + cwLogError(rc,"Flow cross index %i network initialize failed.",net_idx); goto errLabel; } @@ -203,9 +225,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 +249,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 +412,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..6ec73fb 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, @@ -31,6 +92,82 @@ namespace cw const preset_order_t* presetA; unsigned presetN; } multi_preset_selector_t; + + + typedef struct ui_preset_str + { + const char* label; + unsigned preset_idx; + } ui_preset_t; + + typedef struct ui_proc_desc_str + { + const char* label; // class label + ui_preset_t* presetA; // presetA[ presetN ] + unsigned presetN; + } ui_proc_desc_t; + + struct ui_proc_str; + + typedef struct ui_var_str + { + struct ui_proc_str* ui_proc; // owning proc + + const char* label; // flow::variable_t::label + unsigned label_sfx_id; // flow::variable_t::label_sfx_id + + const object_t* desc_cfg; // var desc cfg from flow::var_desc_t + unsigned desc_flags; // flow::var_desc_t::flags + + bool has_source_fl; // true if this var is connected to a source var + unsigned value_tid; // flow::variable_t::type + unsigned vid; // flow::variable_t::vid + unsigned ch_idx; // flow::variable_t::chIdx + unsigned ch_cnt; // 0=kAnyChIdx only, kInvalidCnt=no channels, 1=mono, 2=stereo, ... + + unsigned user_id; // uuId of the UI element that represents this var + } ui_var_t; + + struct proc_str; + + typedef struct ui_proc_str + { + const struct ui_net_str* ui_net; + + struct proc_str* proc; + + const ui_proc_desc_t* desc; + const object_t* cfg; // complete proc inst. cfg + + const char* label; // flow::proc_t::label + unsigned label_sfx_id; // + + ui_var_t* varA; // varA[varN] + unsigned varN; // + + struct ui_net_str* internal_net; + + } ui_proc_t; + + struct network_str; + + typedef struct ui_net_str + { + struct network_str* net; + + ui_proc_t* procA; // procA[procN] + unsigned procN; + + ui_preset_t* presetA; // presetA[presetN] network presets + unsigned presetN; + + struct ui_net_str* poly_link; + unsigned poly_idx; + + } ui_net_t; + + typedef rc_t (*ui_callback_t)( void* arg, const ui_var_t* ui_var ); + } } diff --git a/cwFlowNet.cpp b/cwFlowNet.cpp new file mode 100644 index 0000000..06aec55 --- /dev/null +++ b/cwFlowNet.cpp @@ -0,0 +1,3516 @@ +#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 + { + + + + //======================================================================================================= + // + // 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; + + 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; iprocN; ++i) + { + ui_net_t* iun = ui_net->procA[i].internal_net; + while( iun!=nullptr ) + { + ui_net_t* iun0 = iun->poly_link; + _destroy_ui_net(iun); + iun = iun0; + } + + mem::release(ui_net->procA[i].varA); + + } + + mem::release(ui_net->procA); + mem::release(ui_net->presetA); + mem::release(ui_net); + return rc; + } + + + rc_t _network_destroy_one( network_t*& net ) + { + rc_t rc = kOkRC; + + if( net == nullptr ) + return rc; + + for(unsigned i=0; iprocN; ++i) + proc_destroy(net->procA[i]); + + mem::release(net->procA); + net->procN = 0; + + _network_preset_array_destroy(*net); + + mem::release(net->preset_pairA); + net->preset_pairN = 0; + + net_global_var_t* gv=net->globalVarL; + while( gv != nullptr ) + { + net_global_var_t* gv0 = gv->link; + mem::release(gv->var_label); + mem::release(gv->blob); + mem::release(gv); + gv = gv0; + } + + _destroy_ui_net(net->ui_net); + + mem::release(net); + + return rc; + } + + rc_t _network_destroy( network_t*& net ) + { + rc_t rc = kOkRC; + + while( net != nullptr ) + { + network_t* n0 = net->poly_link; + rc_t rc0; + if((rc0 = _network_destroy_one(net)) != kOkRC ) + rc = cwLogError(rc0,"A network destroy failed."); + net = n0; + } + + return rc; + } + + 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); + } + } + + } + + if( labeled_net == nullptr && net.poly_link != nullptr ) + labeled_net = _io_stmt_find_labeled_network(*net.poly_link,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; + } + + if( proc_ele.sfx_id_count == kInvalidCnt ) + cnt_ref = n; + else + { + if( proc_ele.sfx_id_count > n ) + { + rc = cwLogError(kSyntaxErrorRC,"The given literal sfx-id count %i execeeds the maximimum possible value of %i on %s-proc '%s:%i' .",proc_ele.sfx_id_count,n,in_or_src_label,cwStringNullGuard(proc_ele.label),sfx_id); + goto errLabel; + } + + cnt_ref = proc_ele.sfx_id_count; + } + + + 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; + + cnt_ref = 0; + + proc_t* proc = nullptr; + unsigned proc_sfx_id = proc_ele.base_sfx_id==kInvalidCnt ? kBaseSfxId : proc_ele.base_sfx_id; + + // 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; + } + + + if( var_ele.sfx_id_count == kInvalidCnt ) + cnt_ref = n; + else + { + if( var_ele.sfx_id_count > n ) + { + rc = cwLogError(kSyntaxErrorRC,"The given literal sfx-id count %i execeeds the maximimum possible value of %i on %s-var '%s:%i-%s:%i' .",var_ele.sfx_id_count,n,in_or_src_label,cwStringNullGuard(proc_ele.label),proc_ele.sfx_id,cwStringNullGuard(var_ele.label),sfx_id); + goto errLabel; + } + + cnt_ref = var_ele.sfx_id_count; + } + + } + + 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 (type:0x%x) %s:%s:%i.%s:%i (type:0x%x).", local_label,local_var_label, local_var_sfx_id, local_var->varDesc->type, remote_label, remote_proc_label, remote_proc_sfx_id, remote_var_label, remote_var_sfx_id, remote_var->varDesc->type); + 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,kUdpOutVarDescFl) ) + 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 = nullptr; + + // determine the max variable vid and max channel index value among all variables + for(var=proc->varL; 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; + } + + + //============================================================================================================================================ + // + // Class Preset and Arg Value application + // + + rc_t _var_channelize( proc_t* proc, const char* preset_label, const char* var_label, unsigned label_sfx_id, const object_t* value ) + { + rc_t rc = kOkRC; + + variable_t* dummy = nullptr; + var_desc_t* vd = nullptr; + + // verify that a valid value exists + if( value == nullptr ) + { + rc = cwLogError(kSyntaxErrorRC,"Unexpected missig value on preset '%s' proc instance '%s:%i-%s:%i'.", preset_label, proc->label, proc->label_sfx_id, cwStringNullGuard(var_label), label_sfx_id ); + 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, label_sfx_id, chIdx, value->child_ele(chIdx), kInvalidId, dummy )) != kOkRC ) + goto errLabel; + } + else // otherwise a single value was given + { + if((rc = var_channelize( proc, var_label, label_sfx_id, kAnyChIdx, value, kInvalidId, dummy )) != kOkRC ) + goto errLabel; + } + } + + errLabel: + return rc; + } + + rc_t _preset_channelize_vars( proc_t* proc, 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' is not a dictionary.", preset_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, var_label, kBaseSfxId, value )) != kOkRC ) + goto errLabel; + + + } + + errLabel: + if( rc != kOkRC ) + rc = cwLogError(rc,"Apply preset failed on proc instance:%s:%i class:%s preset:%s.", proc->label, proc->label_sfx_id, proc->class_desc->label, preset_label ); + + return rc; + } + + + 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:%i'.", preset_label, proc->label, proc->label_sfx_id); + goto errLabel; + } + + rc = _preset_channelize_vars( proc, 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; + } + + + rc_t _process_args_stmt( proc_t* proc, const object_t* arg_cfg ) + { + rc_t rc = kOkRC; + + if( arg_cfg == nullptr ) + return rc; + + unsigned argN = arg_cfg->child_count(); + + for(unsigned i=0; ichild_ele(i); + io_ele_t r; + + // validate the arg pair + if( arg_pair==nullptr || !arg_pair->is_pair() || arg_pair->pair_label()==nullptr || arg_pair->pair_value()==nullptr ) + { + rc = cwLogError(kSyntaxErrorRC,"Invalid 'arg' pair."); + goto errLabel; + } + + // parse the var label string + if((rc = _io_stmt_parse_ele( arg_pair->pair_label(), r )) != kOkRC ) + { + goto errLabel; + } + + // if the arg expr is not iterating then set the iter count to 1 + if( r.sfx_id_count == kInvalidCnt ) + r.sfx_id_count = 1; + + // if no base sfx id was given then set the base sfx id to kBaseSfxId + if( r.base_sfx_id == kInvalidId ) + r.base_sfx_id = kBaseSfxId; + + // + for(unsigned sfx_id=r.base_sfx_id; sfx_id< r.base_sfx_id + r.sfx_id_count; ++sfx_id) + { + // if this var has not been created yet - then create it + if( !var_exists(proc, r.label,sfx_id, kAnyChIdx) ) + { + variable_t* dum = nullptr; + + if((rc = var_create( proc, + r.label, + sfx_id, + kInvalidId, + kAnyChIdx, + nullptr, + kInvalidTFl, + dum )) != kOkRC ) + { + rc = cwLogError(rc,"Variable create failed on '%s %s:%i'.",cwStringNullGuard(proc->label),cwStringNullGuard(r.label),sfx_id); + goto errLabel; + } + } + + if((rc= _var_channelize( proc, "args", r.label, sfx_id, arg_pair->pair_value() )) != kOkRC ) + { + rc = cwLogError(rc,"Channeliize failed on '%s %s:%i'.",cwStringNullGuard(proc->label),cwStringNullGuard(r.label),sfx_id); + goto errLabel; + } + } + + mem::release(r.label); + } + + + errLabel: + if(rc != kOkRC ) + rc = cwLogError(rc,"'args' processing failed on proc '%s'.",cwStringNullGuard(proc->label)); + + return rc; + } + + + rc_t _process_net_preset(proc_t* proc, const object_t* net_preset_cfgD) + { + rc_t rc = kOkRC; + const object_t* proc_preset_cfg; + + // if no network preset dict exists or if this proc. is not mentioned in it then there is nothing to do + if( net_preset_cfgD==nullptr || (proc_preset_cfg=net_preset_cfgD->find_child(proc->label)) == nullptr ) + return rc; + + switch( proc_preset_cfg->type_id() ) + { + case kDictTId: + if((rc = _process_args_stmt(proc,proc_preset_cfg)) != kOkRC ) + goto errLabel; + break; + + case kListTId: + case kStringTId: + if((rc = _class_apply_presets(proc,proc_preset_cfg)) != kOkRC ) + goto errLabel; + break; + + default: + rc = cwLogError(kInvalidStateRC,"A network preset must be either a dictionary, list or string."); + goto errLabel; + break; + } + + errLabel: + if(rc != kOkRC ) + rc = cwLogError(rc,"Network preset application failed for proc instance:%s.",cwStringNullGuard(proc->label)); + + 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, + const object_t* net_preset_cfgD, + proc_t*& proc_ref ) + { + rc_t rc = kOkRC; + proc_inst_parse_state_t pstate = {}; + proc_t* proc = nullptr; + class_desc_t* class_desc = nullptr; + const object_t* proc_preset_cfg = 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->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 = _process_args_stmt(proc,pstate.arg_cfg)) != kOkRC ) + goto errLabel; + } + + // If the the network preset holds a preset for this proc + if( net_preset_cfgD!=nullptr && (proc_preset_cfg=net_preset_cfgD->find_child(proc->label)) != nullptr ) + { + if((rc = _process_net_preset(proc,net_preset_cfgD)) != 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_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; + } + + // Given a network preset label return the dictionary of proc presets that is associated with it. + rc_t _get_network_preset_cfg( const object_t* presetsCfg, const char* preset_label, const object_t*& preset_ref ) + { + rc_t rc = kOkRC; + preset_ref = nullptr; + + if( preset_label == nullptr ) + return rc; + + if( presetsCfg == nullptr ) + { + rc = cwLogError(kInvalidArgRC,"The network preset '%s' could not be found because there is no network 'presets' dictionary.",cwStringNullGuard(preset_label)); + goto errLabel; + } + + if((preset_ref = presetsCfg->find_child( preset_label)) == nullptr ) + { + rc = cwLogError(kEleNotFoundRC,"The network preset '%s' was not found in the 'presets' dictionary."); + goto errLabel; + } + + switch( preset_ref->type_id() ) + { + case kDictTId: // this is a dictionary of proc presets + break; + + case kListTId: // this is a 'dual' preset - skip it + preset_ref = nullptr; + break; + + default: + rc = cwLogError(kSyntaxErrorRC,"Network presets must be either dictionaries or lists. Preset '%s' is neither.",cwStringNullGuard(preset_label)); + preset_ref = nullptr; + } + + errLabel: + 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; + } + + + //================================================================================================================== + // + // Presets - Probabilistic Selection + // + + 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; i(); + + // if the top level network has not been set then set it here. + // (this is necessary so that proc's later in the exec order + // can locate proc's earlier in the exec order) + if(p->net == nullptr ) + p->net = net; + + 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; + } + + // locate the requested network preset cfg + if((rc = _get_network_preset_cfg(net->presetsCfg,preset_label,netPresetCfg)) != kOkRC ) + { + rc = cwLogError(rc,"Network create failed because the network preset resolution failed."); + goto errLabel; + } + + procN = net->procsCfg->child_count(); + net->procA = mem::allocZ(procN); + + // for each proc in the network + for(unsigned j=0; jprocsCfg->child_ele(j); + + // create the proc inst instance + if( (rc= _proc_create( p, proc_cfg, sfx_id, *net, proxyVarL, netPresetCfg, net->procA[j] ) ) != kOkRC ) + { + rc = cwLogError(rc,"The processor instantiation at proc index %i failed.",j); + goto errLabel; + } + + net->procN += 1; + } + + + if((rc = _network_preset_pair_create_table(*net)) != kOkRC ) + goto errLabel; + + // parse the network presets but do not apply thme + _network_preset_parse_dict(p, *net, net->presetsCfg ); + + net_ref = net; + errLabel: + + if( rc != kOkRC ) + { + if( p->net == net ) + p->net = nullptr; + + net_ref = nullptr; + + _network_destroy(net); + + + } + + return rc; + } + + rc_t _form_net_ui_desc( const flow_t* p, network_t& net, ui_net_t*& ui_net_ref ); + + + rc_t _fill_net_ui_proc_and_preset_arrays( const flow_t* p, network_t& net, ui_net_t*& ui_net_ref ) + { + rc_t rc = kOkRC; + + ui_net_ref->procA = mem::allocZ(net.procN); + ui_net_ref->procN = net.procN; + + for(unsigned i=0; iprocN; ++i) + { + ui_proc_t* ui_proc = ui_net_ref->procA + i; + ui_proc->ui_net = ui_net_ref; + ui_proc->label = net.procA[i]->label; + ui_proc->label_sfx_id = net.procA[i]->label_sfx_id; + ui_proc->desc = net.procA[i]->class_desc->ui; + ui_proc->cfg = net.procA[i]->proc_cfg; + ui_proc->varN = 0; + ui_proc->varA = mem::allocZ(net.procA[i]->varMapN); + + proc_t* proc_ptr = proc_find(net,ui_proc->label,ui_proc->label_sfx_id ); + assert(proc_ptr != nullptr ); + ui_proc->proc = proc_ptr; + + + for(unsigned j=0; jvarMapN; ++j) + { + variable_t* var = net.procA[i]->varMapA[j]; + + // all slots in the varMapA[] are not used + if( var != nullptr ) + { + ui_var_t* ui_var = ui_proc->varA + ui_proc->varN++; + + ui_var->ui_proc = ui_proc; + ui_var->label = var->label; + ui_var->label_sfx_id = var->label_sfx_id; + ui_var->has_source_fl= is_connected_to_source( var ); + ui_var->vid = var->vid; + ui_var->ch_cnt = var_channel_count( net.procA[i], var->label, var->label_sfx_id ); + ui_var->ch_idx = var->chIdx; + ui_var->value_tid = var->type; + ui_var->desc_flags = var->varDesc->flags; + ui_var->desc_cfg = var->varDesc->cfg; + ui_var->user_id = kInvalidId; + + var->ui_var = ui_var; + } + } + + if( net.procA[i]->internal_net != nullptr ) + { + if((rc = _form_net_ui_desc(p, *net.procA[i]->internal_net, ui_proc->internal_net )) != kOkRC ) + goto errLabel; + } + + } + + ui_net_ref->presetA = mem::allocZ(net.presetN); + ui_net_ref->presetN = net.presetN; + + for(unsigned i=0; ipresetN; ++i) + { + ui_net_ref->presetA[i].label = net.presetA[i].label; + ui_net_ref->presetA[i].preset_idx = i; + } + + errLabel: + return rc; + } + + rc_t _form_net_ui_desc( const flow_t* p, network_t& net, ui_net_t*& ui_net_ref ) + { + rc_t rc = kOkRC; + ui_net_ref = mem::allocZ(); + + if((rc = _fill_net_ui_proc_and_preset_arrays(p,net,ui_net_ref)) != kOkRC ) + goto errLabel; + + ui_net_ref->poly_idx = net.poly_idx; + + if( net.poly_link != nullptr ) + _form_net_ui_desc(p,*net.poly_link,ui_net_ref->poly_link); + + errLabel: + return rc; + } + + + + } +} + + + +cw::rc_t cw::flow::network_create( flow_t* p, + const object_t* const * netCfgA, + unsigned netCfgN, + variable_t* proxyVarL, + unsigned polyCnt, + const char* preset_label, + network_t*& net_ref ) +{ + rc_t rc = kOkRC; + network_t* n0 = nullptr; + + net_ref = nullptr; + + if( !(netCfgN==1 || netCfgN==polyCnt )) + { + cwLogError(kInvalidArgRC,"The count of network cfg's must be one, or must match the 'poly count'."); + goto errLabel; + } + + for(unsigned i=0; i1 ? i : kInvalidId; + + const object_t* netCfg = i < netCfgN ? netCfgA[i] : netCfgA[0]; + + // create the network + if((rc = _network_create(p, netCfg, sfx_id, proxyVarL, preset_label, net)) != kOkRC ) + { + rc = cwLogError(rc,"Network create failed on poly index %i.",i); + goto errLabel; + } + + // The returned net is always the first in a poly chain + if( net_ref == nullptr ) + net_ref = net; + + net->poly_idx = i; + + if( n0 != nullptr ) + n0->poly_link = net; + + n0 = net; + + } + +errLabel: + if( rc != kOkRC && net_ref != nullptr ) + _network_destroy(net_ref); + + 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::create_net_ui_desc( flow_t* p ) +{ + rc_t rc = kOkRC; + + if( p->net == nullptr ) + { + rc = cwLogError(kInvalidStateRC,"The UI description could not be formed because the network is not valid."); + goto errLabel; + } + + if((rc = _form_net_ui_desc(p, *p->net, p->net->ui_net )) != kOkRC ) + { + rc = cwLogError(rc,"The UI description creation failed."); + goto errLabel; + } + +errLabel: + + return rc; +} + +cw::rc_t cw::flow::exec_cycle( network_t& net ) +{ + rc_t rc = kOkRC; + bool halt_fl = false; + + for(unsigned i=0; iclass_desc->members->exec(net.procA[i])) != kOkRC ) + { + if( rc == kEofRC ) + halt_fl = true; + else + { + rc = cwLogError(rc,"Execution failed on the proc:%s:%i.",cwStringNullGuard(net.procA[i]->label),net.procA[i]->label_sfx_id); + break; + } + } + } + + return halt_fl ? kEofRC : 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::set_variable_user_id( network_t&net, const ui_var_t* ui_var, unsigned user_id ) +{ + rc_t rc = kOkRC; + variable_t* var = nullptr; + + if((rc = var_find( ui_var->ui_proc->proc, ui_var->vid, ui_var->ch_idx, var )) != kOkRC ) + { + rc = cwLogError(rc,"User-Id assigned failed on '%s:%i' because the variable was not found.",cwStringNullGuard(ui_var->label),ui_var->label_sfx_id); + goto errLabel; + } + + var->ui_var->user_id = user_id; + +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; +} + + +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; + } + + template< typename T > + rc_t set_variable_value( network_t& net, const ui_var_t* ui_var, T value ) + { + rc_t rc = kOkRC; + + // set the variable value + if((rc = var_set( ui_var->ui_proc->proc, ui_var->vid, ui_var->ch_idx, value )) != kOkRC ) + { + rc = cwLogError(kOpFailRC,"The variable set failed on instance:'%s:%i' variable:'%s:%i'.",cwStringNullGuard(ui_var->ui_proc->proc->label),ui_var->ui_proc->proc->label_sfx_id,cwStringNullGuard(ui_var->label),ui_var->label_sfx_id); + goto errLabel; + } + + errLabel: + return rc; + } + + template< typename T > + rc_t get_variable_value( network_t& net, const ui_var_t* ui_var, T& valueRef ) + { + rc_t rc = kOkRC; + + // get the variable value + if((rc = var_get( ui_var->ui_proc->proc, ui_var->vid, ui_var->ch_idx, valueRef )) != kOkRC ) + { + rc = cwLogError(kOpFailRC,"The variable get failed on instance:'%s:%i' variable:'%s:%i'.",cwStringNullGuard(ui_var->ui_proc->proc->label),ui_var->ui_proc->proc->label_sfx_id,cwStringNullGuard(ui_var->label),ui_var->label_sfx_id); + 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..75df7ac 100644 --- a/cwFlowProc.cpp +++ b/cwFlowProc.cpp @@ -1,16 +1,25 @@ #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 "cwMidi.h" +#include "cwMidiFile.h" + #include "cwFlowDecl.h" #include "cwFlow.h" #include "cwFlowTypes.h" +#include "cwFlowNet.h" #include "cwFlowProc.h" #include "cwFile.h" @@ -18,12 +27,51 @@ #include "cwDsp.h" #include "cwAudioTransforms.h" #include "cwDspTransforms.h" +#include "cwMidiDecls.h" + +#include "cwWaveTableBank.h" + +#include "cwThread.h" +#include "cwThreadMach.h" 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 +84,343 @@ 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 + // Notes: + // 1. var_register_*() will automatically create any variables that don't already exist. + // therefore var_create() should never have to be called directly. + // 2. The variable 'vid' value must be unique across all ('label','sfx-id') pairs but not across channels. + 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 + }; + + } + + //------------------------------------------------------------------------------------------------------------------ + // + // user_def_proc + // + namespace user_def_proc + { + 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 UDP 'network' cfg. was not found."); + goto errLabel; + } + + if((rc = network_create(proc->ctx,&networkCfg,1,proc->varL,1,nullptr,p->net)) != 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(p->net != nullptr ) + 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 + { + kParallelFlPId, + kCountPId, + }; + + typedef struct voice_str + { + unsigned voice_idx; + struct network_str* net; + } voice_t; + + typedef struct + { + unsigned count; // count of subnets in 'net' + //network_t* net; // internal network containing 'count' duplicate sub-nets + bool parallel_fl; // true if the subnets should be executed in parallel + thread_tasks::handle_t threadTasksH; // + thread_tasks::task_t* taskA; // taskA[ count ] + voice_t* voiceA; // voiceA[ count ] + + } inst_t; + + rc_t _voice_thread_func( void* arg ) + { + rc_t rc = kOkRC; + voice_t* v = (voice_t*)arg; + + if((rc = exec_cycle(*v->net)) != kOkRC ) + { + rc = cwLogError(rc,"Parallel subnet exec failed on voice %i.",v->voice_idx); + goto errLabel; + } + + errLabel: + return rc; + } + + + rc_t create( proc_t* proc ) + { + rc_t rc = kOkRC; + inst_t* inst = mem::allocZ(); + const object_t* networkCfg = nullptr; + variable_t* proxyVarL = nullptr; + const object_t** networkCfgA = nullptr; + unsigned networkCfgN = 1; + network_t* internal_net = nullptr; + + proc->userPtr = inst; + + // get the network cfg + if((rc = proc->proc_cfg->getv("network",networkCfg)) != kOkRC ) + { + rc = cwLogError(rc,"The 'network' cfg. was not found."); + goto errLabel; + } + + // get the 'parallel flag' + if((rc = var_register_and_get( proc, kAnyChIdx,kParallelFlPId, "parallel_fl", kBaseSfxId, inst->parallel_fl )) != kOkRC ) + { + goto errLabel; + } + + // if the network is a list of cfgs + if( networkCfg->is_list() ) + { + inst->count = networkCfg->child_count(); + networkCfgN = inst->count; + } + else + { + // otherwise multiple networks use the same cfg + if((rc = var_register_and_get( proc, kAnyChIdx,kCountPId,"count", kBaseSfxId, inst->count )) != kOkRC ) + { + goto errLabel; + } + } + + // the network cannot be empty + if( inst->count == 0 ) + { + cwLogWarning("The 'poly' %s:%i was given a count of 0.",proc->label,proc->label_sfx_id); + goto errLabel; + } + + // allocate the network cfg array + networkCfgA = mem::allocZ(inst->count); + + // by default there is only one cfg. + networkCfgA[0] = networkCfg; + + // ... but if there are more than one cfg ... + if( networkCfg->is_list() ) + { + // ... fill the network cfg array + for(unsigned i=0; icount; ++i) + { + networkCfgA[i] = networkCfg->child_ele(i); + + if( !networkCfgA[i]->is_dict() ) + { + cwLogError(kSyntaxErrorRC,"The network cfg. for the network index %i is not a dictionary.",i); + goto errLabel; + } + } + } + + + // create the network object - which will hold 'count' subnets - each a duplicate of the + // network described by 'networkCfg'. + if((rc = network_create(proc->ctx,networkCfgA,networkCfgN,proxyVarL,inst->count,nullptr,internal_net)) != kOkRC ) + { + rc = cwLogError(rc,"Creation failed on the internal network."); + goto errLabel; + } + + if( inst->parallel_fl ) + { + network_t* net = internal_net; + + // create a thread_tasks object + if((rc = thread_tasks::create( inst->threadTasksH, inst->count )) != kOkRC ) + { + rc = cwLogError(rc,"Thread machine create failed."); + goto errLabel; + } + + // the taskA[] array is needed to hold voice specific info. for the call to thread_tasks::run() + inst->taskA = mem::allocZ(inst->count); + inst->voiceA = mem::allocZ(inst->count); + + for(unsigned i=0; net !=nullptr; ++i) + { + assert(icount); + + inst->voiceA[i].voice_idx = i; + inst->voiceA[i].net = net; + + inst->taskA[i].func = _voice_thread_func; + inst->taskA[i].arg = inst->voiceA + i; + + net = net->poly_link; + } + } + + + // Set the internal net pointer in the base proc instance + // so that network based utilities can scan it + proc->internal_net = internal_net; + + errLabel: + mem::release(networkCfgA); + + return rc; + } + + rc_t destroy( proc_t* proc ) + { + inst_t* p = (inst_t*)proc->userPtr; + + if( proc->internal_net != nullptr ) + network_destroy(proc->internal_net); + + thread_tasks::destroy(p->threadTasksH); + mem::release( p->taskA); + mem::release( p->voiceA); + 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( p->parallel_fl ) + { + if((rc = thread_tasks::run(p->threadTasksH,p->taskA,p->count)) != kOkRC ) + { + rc = cwLogError(rc,"poly internal network parallel exec failed."); + } + } + else + { + if((rc = exec_cycle(*proc->internal_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 +440,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 +464,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 +496,270 @@ 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(port_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 MIDI 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 + { + mbuf->msgA = nullptr; + mbuf->msgN = 0; + + // 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 + { + kDevLabelPId, + kPortLabelPId, + kBufMsgCntPId, + kInPId, + }; + + typedef struct + { + external_device_t* ext_dev; + + unsigned inVarN; + + unsigned msgN; + midi::ch_msg_t* msgA; + unsigned msg_idx; + } inst_t; + + rc_t _create( proc_t* proc, inst_t* p ) + { + rc_t rc = kOkRC; // + const char* dev_label = nullptr; + const char* port_label = nullptr; + unsigned inVarN = var_mult_count(proc,"in"); + mbuf_t* mbuf = nullptr; + unsigned sfxIdA[ inVarN ]; + + // get the the sfx_id's of the input audio variables + if((rc = var_mult_sfx_id_array(proc, "in", sfxIdA, inVarN, p->inVarN )) != kOkRC ) + goto errLabel; + + std::sort(sfxIdA, sfxIdA + p->inVarN, [](unsigned& a,unsigned& b){ return amsgN )) != kOkRC ) + { + goto errLabel; + } + + + if((p->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; + } + + p->msgA = mem::allocZ(p->msgN); + + errLabel: + return rc; + } + + rc_t _destroy( proc_t* proc, inst_t* p ) + { + mem::release(p->msgA); + return kOkRC; + } + + rc_t _value( proc_t* proc, inst_t* p, variable_t* var ) + { + return kOkRC; + } + + rc_t _exec( proc_t* proc, inst_t* p ) + { + rc_t rc = kOkRC; + 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; + p->ext_dev->u.m.sendTripleFunc( p->ext_dev, m->ch, m->status, m->d0, m->d1 ); + } + } + + 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 + }; + + } + //------------------------------------------------------------------------------------------------------------------ // @@ -184,21 +780,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 +802,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 +878,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 +903,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 +954,10 @@ namespace cw //------------------------------------------------------------------------------------------------------------------ // - // AudioFileIn + // audio_file_in // - namespace audioFileIn + namespace audio_file_in { enum { @@ -376,31 +972,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 +1022,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 +1065,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 +1120,10 @@ namespace cw //------------------------------------------------------------------------------------------------------------------ // - // AudioFileOut + // audio_file_out // - namespace audioFileOut + namespace audio_file_out { enum { @@ -530,27 +1135,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 +1176,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 +1188,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 +1217,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 +1257,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 +1327,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 +1336,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); } } @@ -746,141 +1363,252 @@ namespace cw // namespace audio_split { + enum { kInPId, kSelectPId, - kGainPId, - kOutPId, + kInGainPId, + kOutGainPId }; + + typedef struct out_var_str + { + unsigned audioChN; + coeff_t* ogainV; // ogainV[ audioChN ] + unsigned* iChIdxV; // inChIdx[ audioChN ] + } out_var_t; typedef struct { - bool* chSelMap; // [ inChCnt ] selected channel map - unsigned outChN; + + unsigned oVarN; // count of output variables + out_var_t* oVarA; // oVarA[ oVarN ] + + unsigned baseOutPId; + + unsigned iChN; // count of input audio channels + coeff_t* igainV; // igainV[ inChN ] input ch. gain coeff's + } inst_t; - rc_t create( instance_t* ctx ) + rc_t _create( proc_t* proc, inst_t* p ) { - rc_t rc = kOkRC; - const abuf_t* abuf = nullptr; // - inst_t* inst = mem::allocZ(); + rc_t rc = kOkRC; + const abuf_t* abuf = nullptr; // + const object_t* selList = nullptr; + unsigned selListN = 0; + unsigned* oVarSelMap = nullptr; + - ctx->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, + kSelectPId,"select",kBaseSfxId, selList )) != kOkRC ) + { + goto errLabel; + } + + // the input must not have 0 channels + if( abuf->chN == 0 ) goto errLabel; - if( abuf->chN ) + // validate the channel input->output map + if( selList == nullptr || !selList->is_list() ) { - unsigned selChN = 0; + rc = cwLogError(kSyntaxErrorRC,"The 'audio_split' 'select' list has invalid syntax."); + goto errLabel; + } + + // there must be one in->out map for each input channel + if(( selListN = selList->child_count()) != abuf->chN ) + { + rc = cwLogError(kInvalidArgRC,"The 'audio_split' selection list must be the same length as the count of input channels:%i.",abuf->chN); + goto errLabel; + } + + // convert in->out map to an array + oVarSelMap = mem::allocZ(selListN); + + // determine the count of output variables + for(unsigned i = 0; ichild_ele(i); - inst->chSelMap = mem::allocZ(abuf->chN); - - if((rc = var_channel_count(ctx,"select",selChN)) != kOkRC ) - goto errLabel; - - // register the gain - for(unsigned i=0; ichN; ++i) + if( listEle == nullptr || (rc = listEle->value(oVarIdx)) != kOkRC ) { - if( i < selChN ) - if((rc = var_register_and_get( ctx, i, kSelectPId, "select", inst->chSelMap[i] )) != kOkRC ) - goto errLabel; + rc = cwLogError(kInvalidArgRC,"The 'audio_split' selection list element at index %i is not a valid integer.",i); + goto errLabel; + } - if( inst->chSelMap[i] ) - { - // register an output gain control - if((rc = var_register( ctx, inst->outChN, kGainPId, "gain")) != kOkRC ) - goto errLabel; + // count the number of outputs + if( oVarIdx+1 > p->oVarN ) + p->oVarN = oVarIdx+1; - // count the number of selected channels to determine the count of output channels - inst->outChN += 1; - } + oVarSelMap[i] = oVarIdx; + } + + p->oVarA = mem::allocZ(p->oVarN); + p->baseOutPId = kOutGainPId + p->oVarN; + + // fill p->oVar[].audioChN with the count of channels for each output variable + for(unsigned i=0; ichild_ele(i)->value(oVarIdx); + p->oVarA[oVarIdx].audioChN += 1; + } + + // for each output variable + for(unsigned i=0; ioVarN; ++i) + { + out_var_t* ov = p->oVarA + i; + + if( ov->audioChN == 0 ) + { + cwLogWarning("No channels have been assigned to 'audio_split' output index %i on '%s:%i'.",i,cwStringNullGuard(proc->label),proc->label_sfx_id); + continue; } - // create the output audio buffer - if( inst->outChN == 0 ) - cwLogWarning("The audio split instance '%s' has no selected channels.",ctx->label); - else - rc = var_register_and_set( ctx, "out", kOutPId, kAnyChIdx, abuf->srate, inst->outChN, abuf->frameN ); + ov->ogainV = mem::allocZ( ov->audioChN ); + ov->iChIdxV = mem::allocZ(ov->audioChN ); + + // create the output signal variable + if((rc = var_register_and_set( proc, "out", kBaseSfxId + i, p->baseOutPId+i, kAnyChIdx, abuf->srate, ov->audioChN, abuf->frameN )) != kOkRC ) + goto errLabel; + + // register the ogain variables for this output and store the current gain values + for(unsigned j=0; jaudioChN; ++j) + if((rc = var_register_and_get(proc, j, kOutGainPId + i,"ogain", kBaseSfxId + i, ov->ogainV[j])) != kOkRC ) + goto errLabel; + + // fill ov->iChIdV[] with the input channels that are mapped to this output var + for(unsigned iChIdx=0,k=0; iChIdxaudioChN); + ov->iChIdxV[k++] = iChIdx; + } + } + + + p->iChN = abuf->chN; + p->igainV = mem::allocZ(abuf->chN); + + // register the input gain variables and store the current gain values + for(unsigned i=0; ichN; ++i) + if((rc = var_register_and_get( proc, i, kInGainPId, "igain", kBaseSfxId, p->igainV[i] )) != kOkRC ) + goto errLabel; + errLabel: + mem::release(oVarSelMap); 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; - - mem::release(inst->chSelMap); - - mem::release(inst); + for(unsigned i=0; ioVarN; ++i) + { + mem::release(p->oVarA[i].ogainV); + mem::release(p->oVarA[i].iChIdxV); + } + mem::release(p->oVarA); + mem::release(p->igainV); return rc; } - rc_t value( instance_t* ctx, variable_t* var ) + rc_t _value( proc_t* proc, inst_t* p, variable_t* var ) { rc_t rc = kOkRC; + + switch( var->vid ) + { + case kInPId: + break; + case kSelectPId: + break; + + case kInGainPId: + // skip kAnyChIdx because individual channels exist will be sent + if(var->chIdx != kAnyChIdx ) + rc = var_get(var,p->igainV[ var->chIdx ]); + break; + + + + default: + + // skip kAnyChIdx because individual channels exist and will be sent + if( kOutGainPId <= var->vid && var->vid < kOutGainPId + p->oVarN && var->chIdx != kAnyChIdx ) + { + unsigned oVarIdx = var->label_sfx_id - kBaseSfxId; + assert( oVarIdx < p->oVarN && var->chIdx < p->oVarA[oVarIdx].audioChN ); + rc = var_get(var,p->oVarA[ oVarIdx ].ogainV[var->chIdx]); + } + break; + + break; + } + return rc; } - rc_t exec( instance_t* ctx ) + rc_t _exec( proc_t* proc, inst_t* p ) { rc_t rc = kOkRC; - const abuf_t* ibuf = nullptr; - abuf_t* obuf = nullptr; - inst_t* inst = (inst_t*)ctx->userPtr; - unsigned outChIdx = 0; - - if( inst->outChN ) - { - // get the src buffer - if((rc = var_get(ctx,kInPId, kAnyChIdx, ibuf )) != kOkRC ) - goto errLabel; - // get the dst buffer - if((rc = var_get(ctx,kOutPId, kAnyChIdx, obuf)) != kOkRC ) - goto errLabel; - - // for each channel - for(unsigned i=0; ichN && outChIdxchN; ++i) - if( inst->chSelMap[i] ) - { - - sample_t* isig = ibuf->buf + i * ibuf->frameN; - sample_t* osig = obuf->buf + outChIdx * obuf->frameN; - sample_t gain = 1; + const abuf_t* ibuf = nullptr; - var_get(ctx,kGainPId,outChIdx,gain); + // get the input audio buffer + if((rc = var_get(proc,kInPId, kAnyChIdx, ibuf )) != kOkRC ) + goto errLabel; - // apply the gain - for(unsigned j=0; jframeN; ++j) - osig[j] = gain * isig[j]; + for(unsigned i=0; ioVarN; ++i) + { + abuf_t* obuf = nullptr; + + // get the ith output buffer + if((rc = var_get(proc, p->baseOutPId +i, kAnyChIdx, obuf)) != kOkRC ) + goto errLabel; - outChIdx += 1; - } + for( unsigned oChIdx=0; oChIdxchN; ++oChIdx) + { + unsigned iChIdx = p->oVarA[i].iChIdxV[oChIdx]; + coeff_t ogain = p->oVarA[i].ogainV[oChIdx]; + coeff_t igain = p->igainV[iChIdx]; + coeff_t gain = igain * ogain; + sample_t* isig = ibuf->buf + iChIdx * ibuf->frameN; + sample_t* osig = obuf->buf + oChIdx * obuf->frameN; + + for(unsigned j=0; jframeN; ++j) + osig[j] = gain * isig[j]; + } + } errLabel: return rc; } + rc_t _report( proc_t* proc, inst_t* p ) + { return kOkRC; } + class_members_t members = { - .create = create, - .destroy = destroy, - .value = value, - .exec = exec, - .report = nullptr + .create = std_create, + .destroy = std_destroy, + .value = std_value, + .exec = std_exec, + .report = std_report }; } - + //------------------------------------------------------------------------------------------------------------------ // // audio_duplicate @@ -902,16 +1630,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 +1649,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 +1665,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 +1687,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 +1717,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 ) { @@ -1017,187 +1745,266 @@ namespace cw }; } - - //------------------------------------------------------------------------------------------------------------------ - // - // audio_merge - // - namespace audio_merge - { - enum { - kGainPId, - kOutPId, - kInBasePId, - }; - - typedef struct - { - unsigned srcN; - } inst_t; - rc_t create( instance_t* ctx ) - { - rc_t rc = kOkRC; - unsigned outChN = 0; - unsigned frameN = 0; - srate_t srate = 0; - - inst_t* inst = mem::allocZ(); - - ctx->userPtr = inst; - - for(unsigned i=0; 1; ++i) - { - const abuf_t* abuf = nullptr; // - - char label[32]; - snprintf(label,31,"in%i",i); - label[31] = 0; - - // TODO: allow non-contiguous source labels - - // the source labels must be contiguous - if( !var_has_value( ctx, label, kAnyChIdx ) ) - break; - - // get the source audio buffer - if((rc = var_register_and_get(ctx, kAnyChIdx,kInBasePId+i,label,abuf )) != kOkRC ) - { - goto errLabel; - } - - if( i == 0 ) - { - frameN = abuf->frameN; - srate = abuf->srate; - } - else - { - // TODO: check srate and frameN are same as first src - assert( abuf->frameN == frameN ); - assert( abuf->srate == srate ); - } - - inst->srcN += 1; - outChN += abuf->chN; - - } - - // register the gain - for(unsigned i=0; iuserPtr; - - mem::release(inst); - - return kOkRC; - } - - rc_t value( instance_t* ctx, variable_t* var ) - { return kOkRC; } - - unsigned _exec( instance_t* ctx, const abuf_t* ibuf, abuf_t* obuf, unsigned outChIdx ) - { - // 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; - sample_t gain = 1; - - var_get(ctx,kGainPId,outChIdx,gain); - - // apply the gain - for(unsigned j=0; jframeN; ++j) - osig[j] = gain * isig[j]; - - outChIdx += 1; - } - - return outChIdx; - } - - /* - rc_t exec( instance_t* ctx ) - { - rc_t rc = kOkRC; - const abuf_t* ibuf0 = nullptr; - const abuf_t* ibuf1 = nullptr; - abuf_t* obuf = nullptr; - unsigned oChIdx = 0; - - if((rc = var_get(ctx,kIn0PId, kAnyChIdx, ibuf0 )) != kOkRC ) - goto errLabel; - - if((rc = var_get(ctx,kIn1PId, kAnyChIdx, ibuf1 )) != kOkRC ) - goto errLabel; - - if((rc = var_get(ctx,kOutPId, kAnyChIdx, obuf)) != kOkRC ) - goto errLabel; - - oChIdx = _exec( ctx, ibuf0, obuf, oChIdx ); - oChIdx = _exec( ctx, ibuf1, obuf, oChIdx ); - - assert( oChIdx == obuf->chN ); - - errLabel: - return rc; - } - */ - - rc_t exec( instance_t* ctx ) - { - rc_t rc = kOkRC; - inst_t* inst = (inst_t*)ctx->userPtr; - abuf_t* obuf = nullptr; - unsigned oChIdx = 0; - - if((rc = var_get(ctx,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 ) - goto errLabel; - - oChIdx = _exec( ctx, ibuf, obuf, oChIdx ); - } - - errLabel: - return rc; - } - - class_members_t members = { - .create = create, - .destroy = destroy, - .value = value, - .exec = exec, - .report = nullptr - }; - - } - - //------------------------------------------------------------------------------------------------------------------ // // audio_mix // namespace audio_mix + { + enum { + kOutPId, + kOutGainPId, + kInBasePId, + }; + + typedef struct audio_gain_str + { + unsigned sfx_id; // sfx_id of both the audio and gain var's + unsigned audioChN; // count of audio channels + unsigned aVId; // (there can only be one audio var.) + unsigned gVId; // (there is one gain var. per audio channel) + coeff_t* gainV; // gainV[ audioChN ] + } audio_gain_t; + + typedef struct + { + unsigned baseInGainPId; + unsigned baseOutGainPId; + + unsigned inAudioVarCnt; + + audio_gain_t oag; + audio_gain_t* iagV; // iagV[ inAudioVarCnt ] + + } inst_t; + + + // Mix the the first N channels of the input audio signal from iag->aVId + // into the first N channels of the output signal. + rc_t _mix( proc_t* proc, audio_gain_t* iag, audio_gain_t* oag, abuf_t* obuf ) + { + rc_t rc = kOkRC; + const abuf_t* ibuf = nullptr; + unsigned chN; + + // get the input audio buffer + if((rc = var_get(proc, iag->aVId, kAnyChIdx, ibuf )) != kOkRC ) + goto errLabel; + + chN = std::min(ibuf->chN,obuf->chN); + + for(unsigned i=0; ibuf + i*ibuf->frameN; + sample_t* osig = obuf->buf + i*obuf->frameN; + coeff_t gain = iag->gainV[i] * oag->gainV[i]; + + for(unsigned j=0; jframeN; ++j) + osig[j] += gain * isig[j]; + } + + errLabel: + return rc; + } + + // Be sure that there is a gain channel instantiated for every audio channel + // and fill ag->gainV[] with the current value of each pre-created gain variable. + // or the class default gain value if no pre-created gain variable exists. + rc_t _setup_gain( proc_t* proc, const char* var_label, unsigned sfx_id, unsigned a_vid, unsigned g_vid, unsigned audioChN, audio_gain_t* ag ) + { + rc_t rc = kOkRC; + + // setup the gain control record + ag->audioChN = audioChN; + ag->gainV = mem::allocZ(audioChN); + ag->sfx_id = sfx_id; + ag->aVId = a_vid; + ag->gVId = g_vid; + + vop::fill(ag->gainV,audioChN,1); + + // register audio gain variables for each channel of the audio signal represented by ag + for(unsigned i=0; iaudioChN; ++i) + if((rc = var_register_and_get(proc, i, g_vid, var_label, sfx_id, ag->gainV[i])) != kOkRC ) + goto errLabel; + + + errLabel: + return rc; + } + + rc_t _create( proc_t* proc, inst_t* p ) + { + rc_t rc = kOkRC; + + unsigned audioFrameN = 0; + unsigned maxInAudioChCnt = 0; + srate_t srate = 0; + unsigned aSfxIdAllocN = var_mult_count(proc,"in"); + unsigned aSfxIdA[ aSfxIdAllocN ]; + + // get the the sfx_id's of the input audio variables + if((rc = var_mult_sfx_id_array(proc, "in", aSfxIdA, aSfxIdAllocN, p->inAudioVarCnt )) != kOkRC ) + goto errLabel; + + p->iagV = mem::allocZ(p->inAudioVarCnt); + + // set the baseInGainPId + p->baseInGainPId = kInBasePId + p->inAudioVarCnt; + + // for each audio input var + 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",aSfxIdA[i],abuf)) != kOkRC ) + goto errLabel; + + // the sample rate of the input audio signals must be the same + if( i != 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; + + // track the max channel count among all audio input variables + if( abuf->chN > maxInAudioChCnt ) + maxInAudioChCnt = abuf->chN; + + // setup the audio_gain record for this input audio variable + if((rc= _setup_gain(proc, "igain", aSfxIdA[i], kInBasePId+i, p->baseInGainPId+i, abuf->chN, p->iagV + i )) != kOkRC ) + goto errLabel; + } + + if((rc = var_register_and_set( proc, "out", kBaseSfxId, kOutPId, kAnyChIdx, srate, maxInAudioChCnt, audioFrameN )) != kOkRC ) + { + goto errLabel; + } + + // setup the audio_gain record for the output gains + if((rc= _setup_gain(proc, "ogain", kBaseSfxId, kOutPId, kOutGainPId, maxInAudioChCnt, &p->oag )) != kOkRC ) + goto errLabel; + + + errLabel: + return rc; + } + + rc_t _destroy( proc_t* proc, inst_t* p ) + { + rc_t rc = kOkRC; + + mem::release(p->oag.gainV); + + for(unsigned i=0; iinAudioVarCnt; ++i) + mem::release(p->iagV[i].gainV); + + mem::release(p->iagV); + + return rc; + } + + rc_t _value( proc_t* proc, inst_t* p, variable_t* var ) + { + rc_t rc = kOkRC; + + switch( var->vid ) + { + + case kOutPId: + break; + + case kOutGainPId: + assert( var->chIdx == kAnyChIdx || var->chIdx < p->oag.audioChN); + + // (we skip kAnyChIdx because individual channels will follow) + if( var->chIdx != kAnyChIdx ) + var_get(var,p->oag.gainV[var->chIdx] ); // ... update the associated gainV[] value + break; + + default: + + // if this is an in-gain value + if( p->baseInGainPId <= var->vid && var->vid < p->baseInGainPId + p->inAudioVarCnt ) + { + // determine which in-gain variable this var is associated with var->vid + for(unsigned i=0; iinAudioVarCnt; ++i) + if( p->iagV[i].gVId == var->vid ) + { + assert( var->chIdx == kAnyChIdx || var->chIdx < p->iagV[i].audioChN); + + // ... and update the associated gainV[] value + // (we skip kAnyChIdx because individual channels will follow) + if( var->chIdx != kAnyChIdx ) + var_get(var,p->iagV[i].gainV[var->chIdx]); + break; + } + + } + else + { + assert(kInBasePId <= var->vid && var->vid < kInBasePId + p->inAudioVarCnt); + } + + } + + + return rc; + } + + rc_t _exec( proc_t* proc, inst_t* p ) + { + rc_t rc = kOkRC; + abuf_t* obuf = nullptr; + + // get the output audio buffer + if((rc = var_get(proc,kOutPId, kAnyChIdx, obuf)) != kOkRC ) + goto errLabel; + + // zero the output buffer + vop::zero(obuf->buf, obuf->frameN*obuf->chN ); + + // mix each input channel into the output buffer + for(unsigned i=0; iinAudioVarCnt; ++i) + if((rc =_mix(proc, p->iagV + i, &p->oag, obuf )) != kOkRC ) + goto errLabel; + + 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 + }; + + } + + //------------------------------------------------------------------------------------------------------------------ + // + // audio_mix + // + namespace audio_mix_0 { enum { kIn0PId, @@ -1212,7 +2019,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 +2028,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 +2040,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 +2073,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 +2088,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; @@ -1316,6 +2123,169 @@ namespace cw } + //------------------------------------------------------------------------------------------------------------------ + // + // audio_marker + // + namespace audio_marker + { + enum + { + kInPId, + kTriggerPId, + kOutPId + }; + + typedef struct inst_str + { + bool trig_fl; + } inst_t; + + rc_t _create( proc_t* proc, inst_t* p ) + { + rc_t rc = kOkRC; + const abuf_t* abuf = nullptr; // + proc->userPtr = mem::allocZ(); + + // get the source audio buffer + if((rc = var_register_and_get(proc, kAnyChIdx,kInPId,"in",kBaseSfxId,abuf )) != kOkRC ) + goto errLabel; + + // register the marker input + if((rc = var_register( proc, kAnyChIdx, kTriggerPId, "trigger", kBaseSfxId )) != kOkRC ) + goto errLabel; + + // create the output audio buffer + rc = var_register_and_set( proc, "out", kBaseSfxId, kOutPId, kAnyChIdx, abuf->srate, abuf->chN, abuf->frameN ); + + 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 ) + { + if( var->vid == kTriggerPId ) + p->trig_fl = true; + + return kOkRC; + } + + rc_t _exec( proc_t* proc, inst_t* p ) + { + rc_t rc = kOkRC; + const abuf_t* ibuf = nullptr; + abuf_t* obuf = nullptr; + sample_t mark = p->trig_fl ? 1 : 0; + + p->trig_fl = false; + + // get the src buffer + if((rc = var_get(proc,kInPId, kAnyChIdx, ibuf )) != kOkRC ) + goto errLabel; + + // get the dst buffer + if((rc = var_get(proc,kOutPId, kAnyChIdx, obuf)) != kOkRC ) + goto errLabel; + + // for each channel + for(unsigned i=0; ichN; ++i) + { + sample_t* isig = ibuf->buf + i*ibuf->frameN; + sample_t* osig = obuf->buf + i*obuf->frameN; + + // apply the marker + for(unsigned j=0; jframeN; ++j) + osig[j] = mark + isig[j]; + } + + 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 + }; + + } + + //------------------------------------------------------------------------------------------------------------------ + // + // audio_silence + // + namespace audio_silence + { + enum { + kSratePId, + kChCntPId, + kOutPId + }; + + typedef struct + { + + } inst_t; + + + rc_t _create( proc_t* proc, inst_t* p ) + { + rc_t rc = kOkRC; + srate_t srate = 0; + unsigned ch_cnt = 1; + + + if((rc = var_register_and_get(proc, kAnyChIdx, + kSratePId,"srate",kBaseSfxId,srate, + kChCntPId,"ch_cnt",kBaseSfxId,ch_cnt)) != kOkRC ) + { + goto errLabel; + } + + if( srate == 0 ) + srate = proc->ctx->sample_rate; + + + // create the output audio buffer + rc = var_register_and_set( proc, "out", kBaseSfxId, kOutPId, kAnyChIdx, srate, ch_cnt, proc->ctx->framesPerCycle ); + + 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; } + + 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 + }; + + } + //------------------------------------------------------------------------------------------------------------------ // @@ -1329,93 +2299,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, "ch_cnt", 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 ); - sample_t* v = abuf->buf + (i*abuf->frameN); + 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 +2443,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 +2465,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 +2483,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 +2499,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 +2525,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 +2536,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 +2551,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 +2575,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 +2631,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 +2649,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 +2672,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 +2704,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 +2757,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 +2780,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 +2800,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 +2812,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 +2823,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 +2838,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 +2846,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 +2860,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 +2881,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 +2891,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 +2968,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 +2991,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 +3014,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 +3029,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 +3043,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 +3065,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 +3075,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 +3105,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 +3157,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 +3180,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 +3197,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 +3212,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 +3226,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 +3244,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 +3254,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 +3284,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 +3329,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 +3350,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 +3372,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 +3391,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 +3425,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 +3521,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 +3544,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 +3559,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 +3574,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 +3588,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 +3625,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; @@ -2654,11 +3660,13 @@ namespace cw { kInPId, kDbFlPId, + kConsoleFlPId, kWndMsPId, kPeakDbPId, kOutPId, kPeakFlPId, - kClipFlPId + kClipFlPId, + kRptPeriodMsPId }; @@ -2668,25 +3676,32 @@ namespace cw { audio_meter_t** mtrA; unsigned mtrN; + unsigned rptPeriodSmpN; + unsigned rptPhase; } 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(); + rc_t rc = kOkRC; + const abuf_t* srcBuf = nullptr; // + inst_t* inst = mem::allocZ(); + unsigned rptPeriodMs = 0; - 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, + kRptPeriodMsPId,"rpt_ms",kBaseSfxId,rptPeriodMs)) != 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 { + inst->rptPeriodSmpN = (unsigned)(proc->ctx->sample_rate * rptPeriodMs/1000.0); + // allocate channel array inst->mtrN = srcBuf->chN; inst->mtrA = mem::allocZ( inst->mtrN ); @@ -2694,33 +3709,36 @@ namespace cw // create a audio_meter object for each input channel for(unsigned i=0; ichN; ++i) { - real_t wndMs, peakThreshDb; - bool dbFl; - + ftime_t wndMs; + coeff_t peakThreshDb; + bool dbFl; + bool consoleFl; + // 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, + kConsoleFlPId,"consoleFl", kBaseSfxId, consoleFl, + 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 +3750,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 +3764,25 @@ 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; + + bool rptFl = inst->rptPeriodSmpN != 0 && inst->rptPhase >= inst->rptPeriodSmpN; + bool consoleFl = false; + + var_get(proc,kConsoleFlPId, kAnyChIdx, consoleFl); // 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 +3790,41 @@ 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 ); + if( rptFl ) + { + var_send_to_ui( proc, kOutPId, i ); + if( consoleFl ) + cwLogPrint("%6.2f ",inst->mtrA[i]->outDb); + } + } + + if(rptFl) + { + if( consoleFl ) + cwLogPrint("\n"); + inst->rptPhase -= inst->rptPeriodSmpN; + } + + inst->rptPhase += srcBuf->frameN; + + 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; @@ -2801,87 +3841,1429 @@ namespace cw //------------------------------------------------------------------------------------------------------------------ // - // audio_marker + // xfade_ctl // - namespace audio_marker + namespace xfade_ctl { - enum - { - kInPId, - kMarkPId, - kOutPId + enum { + kNetLabelPId, + kNetLabelSfxPId, + kSrateRefPId, + kDurMsPId, + kTriggerPId, + kPresetPId, + kGainPId, }; - typedef struct inst_str + typedef struct poly_ch_str { - real_t mark; + 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->procN/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; - rc_t create( instance_t* ctx ) + void _trigger_xfade( inst_t* p ) { - rc_t rc = kOkRC; - const abuf_t* abuf = nullptr; // - ctx->userPtr = mem::allocZ(); + // 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; - // get the source audio buffer - if((rc = var_register_and_get(ctx, kAnyChIdx,kInPId,"in",abuf )) != kOkRC ) - goto errLabel; + // 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; - // register the marker input - if((rc = var_register_and_set( ctx, kAnyChIdx, kMarkPId, "mark", 0.0f )) != kOkRC ) + // 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; + unsigned poly_cnt = 0; + network_t* net = 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; + } + + poly_cnt = p->net_proc->internal_net==nullptr ? 0 : network_poly_count(*p->net_proc->internal_net); + + if( poly_cnt < 3 ) + { + cwLogError(rc,"The xfade_ctl source network must have at least 3 poly channels. %i < 3",poly_cnt); + goto errLabel; + } + + p->poly_ch_cnt = poly_cnt; + + net = p->net_proc->internal_net; + + // 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->procN / 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,net=net->poly_link) + { + assert(net != nullptr ); - // create the output audio buffer - rc = var_register_and_set( ctx, "out", kOutPId, kAnyChIdx, abuf->srate, abuf->chN, abuf->frameN ); + p->netA[i].net.procN = net->procN; + p->netA[i].net.procA = mem::allocZ(p->netA[i].net.procN); + p->netA[i].net.presetsCfg = net->presetsCfg; + + p->netA[i].net.presetA = net->presetA; + p->netA[i].net.presetN = net->presetN; + + p->netA[i].net.preset_pairA = net->preset_pairA; + p->netA[i].net.preset_pairN = net->preset_pairN; + + for(unsigned j=0,k=0; jprocN; ++j) + if( net->procA[j]->label_sfx_id == i ) + { + assert( k < p->net_proc_cnt ); + p->netA[i].net.procA[k++] = net->procA[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.procA); + + 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; ipoly_ch_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 Voice Control + // + namespace poly_voice_ctl + { + enum { + kInPId, + kVoiceCntPId, + kBaseOutPId, + }; + + enum { + kVoiceMsgN = 16, + kGlobalMsgN = 256, + }; + + typedef struct voice_str + { + bool activeFl; // true if this voice is currently active + unsigned pitch; // pitch associated with this voice + unsigned age; // age of this voice in exec() cycles. + + midi::ch_msg_t* msgA; // msgA[ msgN ] msg buffer for this voice + unsigned msgN; // + unsigned msg_idx; // current count of msg's in msgA[] + + mbuf_t* mbuf; // cached mbuf for this output variable + + } voice_t; + + typedef struct + { + unsigned baseDoneFlPId; + + unsigned voiceN; // voiceA[ voiceN ] + voice_t* voiceA; + + // sizeof of each voice msgA[] (same as voice_t.msgN) + unsigned voiceMsgN; + + } inst_t; + + + rc_t _create( proc_t* proc, inst_t* p ) + { + rc_t rc = kOkRC; + mbuf_t* mbuf = nullptr; + + if((rc = var_register_and_get(proc,kAnyChIdx, + kInPId, "in", kBaseSfxId, mbuf, + kVoiceCntPId, "voice_cnt", kBaseSfxId, p->voiceN)) != kOkRC ) + { + goto errLabel; + } + + if( p->voiceN == 0 ) + { + rc = cwLogError(kInvalidArgRC,"The poly_voice_ctl '%s:%i' has 0 voices.",proc->label,proc->label_sfx_id ); + goto errLabel; + } + + p->baseDoneFlPId = kBaseOutPId + p->voiceN; + p->voiceMsgN = kVoiceMsgN; + p->voiceA = mem::allocZ(p->voiceN); + + for(unsigned i=0; ivoiceN; ++i) + { + // create one output MIDI variable per voice + if((rc = var_register_and_set( proc, "out", i, kBaseOutPId + i, kAnyChIdx, nullptr, 0 )) != kOkRC ) + goto errLabel; + + // create one 'done_fl' variable per voice + if((rc = var_register_and_set( proc, kAnyChIdx, p->baseDoneFlPId + i, "done_fl", i, false )) != kOkRC ) + goto errLabel; + + p->voiceA[i].msgA = mem::allocZ(p->voiceMsgN); + p->voiceA[i].msgN = p->voiceMsgN; + + // cache a pointer to each output variables mbuf (because we know these won't change) + if((rc = var_get(proc,kBaseOutPId+i, kAnyChIdx, p->voiceA[i].mbuf )) != kOkRC ) + goto errLabel; + } + + errLabel: + return rc; + } + + rc_t _destroy( proc_t* proc, inst_t* p ) + { + rc_t rc = kOkRC; + + for(unsigned i=0; ivoiceN; ++i) + mem::release(p->voiceA[i].msgA); + + mem::release( p->voiceA ); + p->voiceN = 0; + return rc; + } + + rc_t _value( proc_t* proc, inst_t* p, variable_t* var ) + { + rc_t rc = kOkRC; + + if( p->baseDoneFlPId <= var->vid && var->vid < p->baseDoneFlPId + p->voiceN ) + { + p->voiceA[ var->vid - p->baseDoneFlPId ].activeFl = false; + } + + return rc; + } + + unsigned _get_next_avail_voice( inst_t* p ) + { + unsigned max_age_idx = 0; + for(unsigned i=0; ivoiceN; ++i) + { + if( p->voiceA[i].activeFl == false ) + return i; + + if( p->voiceA[i].age > p->voiceA[ max_age_idx].age ) + max_age_idx = i; + } + + return max_age_idx; + } + + unsigned _pitch_to_voice( inst_t* p, unsigned pitch ) + { + for(unsigned i=0; ivoiceN; ++i) + if( p->voiceA[i].activeFl && p->voiceA[i].pitch == pitch ) + return i; + return kInvalidIdx; + } + + rc_t _update_voice_msg( proc_t* proc, inst_t* p, unsigned voice_idx, const midi::ch_msg_t* m ) + { + rc_t rc = kOkRC; + voice_t* v = p->voiceA + voice_idx; + + if( v->msg_idx >= v->msgN ) + { + cwLogError(kBufTooSmallRC,"The voice MIDI buffer on ch:%i is full on '%s:%i'",voice_idx,cwStringNullGuard(proc->label),proc->label_sfx_id); + goto errLabel; + } + else + { + v->msgA[ v->msg_idx++ ] = *m; + v->mbuf->msgA = v->msgA; + v->mbuf->msgN = v->msg_idx; + + //printf("vctl:%i : st:%i %i %i\n",voice_idx,m->status,m->d0,m->d1); + } + + errLabel: + return rc; + } + + rc_t _on_note_on( proc_t* proc, inst_t* p, const midi::ch_msg_t* m ) + { + rc_t rc = kOkRC; + unsigned voice_idx = _get_next_avail_voice(p); + + assert( voice_idx <= p->voiceN); + + voice_t* v = p->voiceA + voice_idx; + + v->age = 0; + v->activeFl = true; + v->pitch = m->d0; + + rc = _update_voice_msg(proc,p,voice_idx,m); + + return rc; + } + + rc_t _on_note_off( proc_t* proc, inst_t* p, const midi::ch_msg_t* m ) + { + rc_t rc = kOkRC; + unsigned voice_idx; + if((voice_idx = _pitch_to_voice(p,m->d0)) == kInvalidIdx ) + { + cwLogWarning("Voice not found for note:%i.",m->d0); + goto errLabel; + } + + assert( voice_idx <= p->voiceN); + + rc = _update_voice_msg(proc,p,voice_idx,m); errLabel: return rc; } - rc_t destroy( instance_t* ctx ) + rc_t _send_to_all_voices( proc_t* proc, inst_t*p, const midi::ch_msg_t* m ) { - inst_t* inst = (inst_t*)(ctx->userPtr); - mem::release(inst); - return kOkRC; + rc_t rc = kOkRC; + + if( midi::isChStatus( m->status ) ) + for(unsigned i=0; ivoiceN; ++i) + if((rc = _update_voice_msg(proc,p,i,m)) != kOkRC ) + goto errLabel; + + errLabel: + return rc; } - rc_t value( instance_t* ctx, variable_t* var ) + rc_t _exec( proc_t* proc, inst_t* p ) + { + rc_t rc = kOkRC; + mbuf_t* mbuf = nullptr; + + // update the voice array + for(unsigned i=0; ivoiceN; ++i) + { + if( p->voiceA[i].activeFl ) + p->voiceA[i].age += 1; + + p->voiceA[i].msg_idx = 0; + p->voiceA[i].mbuf->msgN = 0; + p->voiceA[i].mbuf->msgA = nullptr; + } + + // get the input MIDI buffer + if((rc = var_get(proc,kInPId,kAnyChIdx,mbuf)) != kOkRC ) + goto errLabel; + + // process the incoming MIDI messages + for(unsigned i=0; imsgN; ++i) + { + const midi::ch_msg_t* m = mbuf->msgA + i; + + switch( m->status ) + { + case midi::kNoteOnMdId: + if( m->d1 == 0 ) + rc = _on_note_off(proc,p,m); + else + rc = _on_note_on(proc,p,m); + break; + + case midi::kNoteOffMdId: + rc = _on_note_off(proc,p,m); + break; + + default: + rc = _send_to_all_voices(proc,p,m); + break; + } + } + + + 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 + }; + + } + + //------------------------------------------------------------------------------------------------------------------ + // + // midi_voice + // + namespace midi_voice + { + enum { + kInPId, + kOutPId, + kDoneFlPId + }; + + typedef struct + { + + unsigned wtAllocN; // wtAlloc[ wtAllocN ] + sample_t* wtAllocA; // total allocated WT space with extra leading and trailing samples + + unsigned wtN; // wtA[ wtA ] + sample_t* wtA; // actual WT space which sits inside of wtAllocA[] + + double wtPhase; // current WT phase + + unsigned cur_vel; // current MIDI velocity + double cur_hz; // current fund. frequency + double cur_pbend; // current pitch bend factor + + unsigned hzN; + double* hzA; // hzA[128] - midi to Hz lookup table. + + bool done_fl; + coeff_t gain; + coeff_t gain_coeff; + coeff_t gain_thresh; + + } inst_t; + + + rc_t _create( proc_t* proc, inst_t* p ) + { + rc_t rc = kOkRC; + mbuf_t* mbuf = nullptr; + srate_t srate = proc->ctx->sample_rate; + const unsigned ch_cnt = 1; + bool done_fl = false; + + // get the MIDI input variable + if((rc = var_register_and_get( proc, kAnyChIdx, + kInPId, "in", kBaseSfxId, mbuf, + kDoneFlPId, "done_fl", kBaseSfxId, done_fl)) != kOkRC ) + goto errLabel; + + // create one output audio buffer + if((rc = var_register_and_set( proc, "out", kBaseSfxId,kOutPId, kAnyChIdx, srate, ch_cnt, proc->ctx->framesPerCycle )) != kOkRC ) + goto errLabel; + + // create the wave table + p->wtN = srate; + p->wtAllocN = p->wtN + 2; + p->wtAllocA = mem::allocZ(p->wtAllocN); + p->wtA = p->wtAllocA + 1; + + vop::sine( p->wtA, p->wtN, srate, 1); + p->wtAllocA[0] = p->wtA[p->wtN-1]; + p->wtAllocA[p->wtAllocN-1] = p->wtA[0]; + + // create the MIDI pitch to hertz + p->hzN = midi::kMidiNoteCnt; + p->hzA = mem::allocZ(p->hzN); + + for(unsigned i=0; ihzA[i] = midi_to_hz(i); + + p->done_fl = true; + + errLabel: + return rc; + } + + rc_t _destroy( proc_t* proc, inst_t* p ) + { + rc_t rc = kOkRC; + + mem::release(p->wtAllocA); + mem::release(p->hzA); + + return rc; + } + + rc_t _value( proc_t* proc, inst_t* p, variable_t* var ) + { + rc_t rc = kOkRC; + return rc; + } + + void _on_note_off( inst_t* p ) + { + p->gain_coeff = 0.9; + } + + rc_t _exec( proc_t* proc, inst_t* p ) + { + rc_t rc = kOkRC; + abuf_t* abuf = nullptr; + mbuf_t* mbuf = nullptr; + + // get the input MIDI buffer + if((rc = var_get(proc,kInPId,kAnyChIdx,mbuf)) != kOkRC ) + goto errLabel; + + // get the output audio buffer + if((rc = var_get(proc,kOutPId,kAnyChIdx,abuf)) != kOkRC ) + goto errLabel; + + // if there are MIDI messages - update cur_hz and cur_vel + for(unsigned i=0; imsgN; ++i) + { + const midi::ch_msg_t* m = mbuf->msgA + i; + switch( m->status ) + { + case midi::kNoteOnMdId: + p->cur_hz = p->hzA[ m->d0 ]; + p->cur_vel = m->d1; + + if( m->d1 == 0 ) + _on_note_off(p); + else + { + p->done_fl = false; + p->gain = (coeff_t)p->cur_vel / 127; + p->gain_coeff = 1.0; + p->gain_thresh = 0.001; + } + break; + + case midi::kNoteOffMdId: + _on_note_off(p); + break; + + case midi::kPbendMdId: + p->cur_pbend = midi::toPbend(m->d0,m->d1) / 8192.0; + break; + + default: + break; + } + } + + // if the voice is off then zero the audio buffer + if( p->done_fl ) + { + vop::zero(abuf->buf,abuf->frameN); + } + else + { + + // fill in the audio buffer + for(unsigned i=0; iframeN; ++i) + { + unsigned j = (unsigned)floor(p->wtPhase); + double frac = p->wtPhase - j; + sample_t smp = p->wtA[j] + (p->wtA[j+1] - p->wtA[j]) * frac; + + abuf->buf[i] = p->gain*smp; + + p->wtPhase += p->cur_hz + (p->cur_hz * p->cur_pbend); + if( p->wtPhase >= p->wtN ) + p->wtPhase -= p->wtN; + } + + p->gain *= p->gain_coeff; + + if( p->gain < p->gain_thresh ) + { + var_set(proc,kDoneFlPId,kAnyChIdx,true); + p->done_fl = true; + } + + } + + 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 + }; + + } + + + //------------------------------------------------------------------------------------------------------------------ + // + // piano_voice + // + namespace piano_voice + { + enum { + kWtbFnPId, + kWtbInstrPId, + kInPId, + kOutPId, + kDoneFlPId, + kTestPitchPId, + kKeyPitchPId, + }; + + enum { + kChCnt=2 + }; + + typedef struct + { + wt_bank::handle_t* wtbH_ptr; + unsigned wtb_instr_idx; + + // multi-channel wave table oscillator + struct dsp::multi_ch_wt_seq_osc::obj_str osc; + + unsigned test_pitch; // Pitch under test or 0 if not on test mode + unsigned test_key_pitch; // Key associated with lowest velocity when in test mode. + unsigned test_pitchN; // Count of valid velocities for test_pitch + unsigned* test_pitch_map; // test_pitch_map[ test_pitch_N ] + + bool done_fl; + coeff_t gain; + coeff_t gain_coeff; + coeff_t kReleaseGain; + coeff_t kGainThreshold; + + } inst_t; + + rc_t _load_wtb(proc_t* proc, inst_t* p, const char* wtb_fname) + { + rc_t rc = kOkRC; + const char* wtb_var_label = "wtb"; + char* exp_wtb_fname = nullptr; + unsigned padSmpN = 1; + + // if the global wave table bank has not yet been created + if((p->wtbH_ptr = (wt_bank::handle_t*)network_global_var(proc, wtb_var_label )) == nullptr ) + { + wt_bank::handle_t wtbH; + + if((exp_wtb_fname = proc_expand_filename(proc,wtb_fname)) == nullptr ) + { + rc = cwLogError(kOpFailRC,"The wave-table bank directory expansion failed."); + goto errLabel; + } + + // create the wave table bank + if((rc = create( wtbH, padSmpN, exp_wtb_fname)) != kOkRC ) + { + rc = cwLogError(rc,"The wave table bank global variable creation failed."); + goto errLabel; + } + + // store the wave table bank global var + if((rc = network_global_var_alloc(proc, wtb_var_label, &wtbH, sizeof(wtbH) )) != kOkRC ) + { + rc = cwLogError(rc,"The wave table bank global variable allocation failed."); + goto errLabel; + } + + if((p->wtbH_ptr = (wt_bank::handle_t*)network_global_var(proc, wtb_var_label )) == nullptr ) + { + rc = cwLogError(rc,"The wave table bank global variable store failed."); + goto errLabel; + } + + } + + errLabel: + if( rc != kOkRC ) + rc = cwLogError(rc,"Wave table bank load failed on '%s'.",cwStringNullGuard(wtb_fname)); + + mem::release(exp_wtb_fname); + return rc; + + } + + rc_t _create_test_pitch_map( proc_t* proc, inst_t* p ) + { + rc_t rc = kOkRC; + unsigned velA[ midi::kMidiVelCnt ]; + + if((rc = instr_pitch_velocities(*p->wtbH_ptr, p->wtb_instr_idx, p->test_pitch, velA, midi::kMidiVelCnt, p->test_pitchN )) != kOkRC ) + { + rc = cwLogError(rc,"Sampled velocity access failed on '%s'.",cwStringNullGuard(proc->label)); + goto errLabel; + } + + p->test_pitch_map = mem::allocZ(p->test_pitchN); + + for(unsigned i=0; itest_pitchN; ++i) + p->test_pitch_map[i] = velA[i]; + + errLabel: + return rc; + } + + + rc_t _create( proc_t* proc, inst_t* p ) + { + rc_t rc = kOkRC; + const char* wtb_fname = nullptr; + const char* wtb_instr = nullptr; + mbuf_t* mbuf = nullptr; + bool done_fl = false; + srate_t srate = proc->ctx->sample_rate; + + // get the MIDI input variable + if((rc = var_register_and_get( proc, kAnyChIdx, + kWtbFnPId, "wtb_fname", kBaseSfxId, wtb_fname, + kWtbInstrPId, "wtb_instr", kBaseSfxId, wtb_instr, + kInPId, "in", kBaseSfxId, mbuf, + kDoneFlPId, "done_fl", kBaseSfxId, done_fl, + kTestPitchPId, "test_pitch",kBaseSfxId, p->test_pitch, + kKeyPitchPId, "test_key_pitch", kBaseSfxId, p->test_key_pitch)) != kOkRC ) + { + goto errLabel; + } + + // get the wave table bank handle (p->wtbH_ptr( + if((rc = _load_wtb(proc, p, wtb_fname)) != kOkRC ) + { + goto errLabel; + } + + + // create one output audio buffer + if((rc = var_register_and_set( proc, "out", kBaseSfxId,kOutPId, kAnyChIdx, srate, kChCnt, proc->ctx->framesPerCycle )) != kOkRC ) + { + goto errLabel; + } + + if((p->wtb_instr_idx = wt_bank::instr_index( *p->wtbH_ptr, wtb_instr )) == kInvalidIdx ) + { + rc = cwLogError(rc,"The wave table bank instrument '%s' could not be found.",cwStringNullGuard(wtb_instr)); + goto errLabel; + } + + // if we are running in 'test-pitch' mode + if( p->test_pitch != 0 ) + { + cwLogInfo("%s is in test-pitch mode",proc->label); + if((rc = _create_test_pitch_map(proc,p)) != kOkRC ) + goto errLabel; + } + + // allocate,setup and validate the expected srate of the oscillator + if((rc = create(&p->osc,kChCnt)) != kOkRC ) + { + rc = cwLogError(rc,"multi-ch-wt-seq-osc create failed."); + goto errLabel; + } + + + p->done_fl = true; + + errLabel: + + return rc; + } + + rc_t _on_note_on( proc_t* proc, inst_t* p, unsigned d0, unsigned d1 ) + { + rc_t rc = kOkRC; + const struct dsp::multi_ch_wt_seq_osc::multi_ch_wt_seq_str* mcs = nullptr; + + // if in voice test mode + if( p->test_pitch_map != nullptr ) + { + // if the the pitch is inside the test range + if( d0 < p->test_key_pitch || p->test_key_pitch + p->test_pitchN <= d0 ) + goto errLabel; + + // ... then the velocity is mapped to a vel for which there is a known vel in the wt-bank + // Performed pitches above the test pitch trigger increasing velocities ... + d1 = p->test_pitch_map[ d0 - p->test_key_pitch ]; + + // ... and the pitch is set to the test pitch + d0 = p->test_pitch; + } + + printf("%i %i\n",d0,d1); + + // get the wave-table associated with the pitch and velocity + if((rc = get_wave_table( *p->wtbH_ptr, p->wtb_instr_idx, d0, d1, mcs)) != kOkRC ) + { + rc = cwLogError(rc,"No piano voice for pitch:%i vel:%i",d0,d1); + goto errLabel; + } + + // setup the oscillator with a new wave table + if((rc = setup(&p->osc,mcs)) != kOkRC ) + { + rc = cwLogError(rc,"Oscilllator setup error on instr:%i pitch:%i vel:%i.",p->wtb_instr_idx,d0,d1); + goto errLabel; + } + + p->done_fl = false; + p->kGainThreshold = 0.01; + p->kReleaseGain = 0.9; + p->gain = 1.0; + p->gain_coeff = 1.0; + + errLabel: + return rc; + } + + void _on_note_off( inst_t* p ) + { + p->gain_coeff = p->kReleaseGain; + //printf("%i nof: %i %i\n",proc->label_sfx_id,m->d0,m->d1); + + } + + + rc_t _destroy( proc_t* proc, inst_t* p ) + { + rc_t rc = kOkRC; + + if( p->wtbH_ptr ) + destroy(*p->wtbH_ptr); + + mem::release(p->test_pitch_map); + + 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; + abuf_t* abuf = nullptr; + mbuf_t* mbuf = nullptr; + unsigned actualFrmN = 0; + + // get the input MIDI buffer + if((rc = var_get(proc,kInPId,kAnyChIdx,mbuf)) != kOkRC ) + { + goto errLabel; + } + + // get the output audio buffer + if((rc = var_get(proc,kOutPId,kAnyChIdx,abuf)) != kOkRC ) + { + goto errLabel; + } + + // if there are MIDI messages - update the wavetable oscillators + for(unsigned i=0; imsgN; ++i) + { + const midi::ch_msg_t* m = mbuf->msgA + i; + switch( m->status ) + { + case midi::kNoteOnMdId: + if( m->d1 > 0 ) + rc = _on_note_on(proc,p,m->d0,m->d1); + else + _on_note_off(p); + break; + + case midi::kNoteOffMdId: + _on_note_off(p); + break; + + case midi::kPbendMdId: + break; + + default: + break; + } + } + + if((rc = process( &p->osc, abuf->buf, abuf->chN, abuf->frameN, actualFrmN )) != kOkRC ) + { + goto errLabel; + } + + vop::mul(abuf->buf, p->gain, abuf->chN * abuf->frameN); + + p->gain *= p->gain_coeff; + + if( (p->gain < p->kGainThreshold && !p->done_fl) || (actualFrmN < abuf->frameN) ) + { + var_set(proc,kDoneFlPId,kAnyChIdx,true); + p->done_fl = true; + } + + 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 + }; + + } + + //------------------------------------------------------------------------------------------------------------------ + // + // audio_merge + // + + namespace audio_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( i != 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 or there must be exactly 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 exec( instance_t* ctx ) + 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; - abuf_t* obuf = nullptr; - //inst_t* inst = (inst_t*)(ctx->userPtr); - sample_t mark = 1; + 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(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 ) - goto errLabel; - - - var_get(ctx,kMarkPId,kAnyChIdx,mark); + //chN = std::min(ibuf->chN,p->chN); - // for each channel + // Copy samples into buf. for(unsigned i=0; ichN; ++i) { sample_t* isig = ibuf->buf + i*ibuf->frameN; - sample_t* osig = obuf->buf + i*obuf->frameN; - - // apply the marker + sample_t* obuf = p->buf[i]; + unsigned k = p->ii; + for(unsigned j=0; jframeN; ++j) - osig[j] = mark + isig[j]; + { + obuf[k++] = isig[j]; + if( k>= p->bufAllocFrmN ) + k -= p->bufAllocFrmN; + } } - var_set(ctx,kMarkPId,kAnyChIdx,0.0f); + // 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; } @@ -2896,6 +5278,2444 @@ namespace cw }; } + + //------------------------------------------------------------------------------------------------------------------ + // + // Number + // + namespace number + { + enum { + kTriggerPId, + kOutTypePId, + kOutPId, + kInPId, + }; + + typedef struct + { + unsigned inVarN; + unsigned store_vid; + bool send_fl; + } inst_t; + + rc_t _create( proc_t* proc, inst_t* p ) + { + rc_t rc = kOkRC; + const char* out_type_label = nullptr; + unsigned out_type_fl = kInvalidTFl; + variable_t* first_in_var = nullptr; + unsigned inVarN = var_mult_count(proc,"in"); + unsigned inSfxIdA[ inVarN ]; + + if((rc = var_register(proc,kAnyChIdx,kTriggerPId,"trigger",kBaseSfxId)) != kOkRC ) + goto errLabel; + + + // if there are no inputs + if( inVarN == 0 ) + { + rc = cwLogError(rc,"The 'number' unit '%s' does not have any inputs.",cwStringNullGuard(proc->label)); + goto errLabel; + } + + // get the the sfx_id's of the 'in' variables + if((rc = var_mult_sfx_id_array(proc, "in", inSfxIdA, inVarN, p->inVarN )) != kOkRC ) + goto errLabel; + + // sort the input id's in ascending order + std::sort(inSfxIdA, inSfxIdA + p->inVarN, [](unsigned& a,unsigned& b){ return ainVarN; ++i) + { + variable_t* foo; + if((rc = var_register(proc, "in", inSfxIdA[i], kInPId+i, kAnyChIdx, nullptr, foo )) != kOkRC ) + { + rc = cwLogError(rc,"Variable registration failed for the variable 'in:%i'.",inSfxIdA[i]);; + goto errLabel; + } + + if( i==0 ) + first_in_var = foo; + } + + + // Get the output type label as a string + if((rc = var_register_and_get(proc,kAnyChIdx,kOutTypePId,"out_type",kBaseSfxId,out_type_label)) != kOkRC ) + { + rc = cwLogError(rc,"Variable registration failed for the variable 'otype:0'.");; + goto errLabel; + } + + + // if an explicit output type was not given ... + if( textIsEqual(out_type_label,"") ) + { + // ... then get the type of the first input variable + if((rc = var_find(proc, kInPId, kAnyChIdx, first_in_var )) != kOkRC ) + { + goto errLabel; + } + + // if the first input variable's type has a valid type is not included in the + if( first_in_var->value != nullptr ) + out_type_fl = (first_in_var->value->tflag & kTypeMask) ; + } + else + { + out_type_fl = value_type_label_to_flag( out_type_label ); + } + + if(out_type_fl == kInvalidTFl ) + { + rc = cwLogError(kInvalidArgRC,"The output type '%s' is not a valid type.",cwStringNullGuard(out_type_label)); + goto errLabel; + } + + // Create the output variable + if((rc = var_create( proc, "out", kBaseSfxId, kOutPId, kAnyChIdx, nullptr, out_type_fl, first_in_var )) != kOkRC ) + { + goto errLabel; + } + + + p->store_vid = kInvalidId; + + 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 ) + { + if( var->vid == kTriggerPId ) + { + p->store_vid = kOutPId; + } + else + { + if( kInPId <= var->vid && var->vid < kInPId + p->inVarN ) + { + if( proc->ctx->isInRuntimeFl ) + { + p->store_vid = var->vid; + } + else + { + // This call to set the a variable is safe because + // we are in init-time not runtime and therefore single threaded + var_set( proc, kOutPId, kAnyChIdx, var->value ); + } + } + } + + return kOkRC; + } + + rc_t _exec( proc_t* proc, inst_t* p ) + { + rc_t rc = kOkRC; + + if( p->store_vid != kInvalidIdx ) + { + variable_t* var = nullptr; + + // 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, p->store_vid, kAnyChIdx, var )) == kOkRC && var->value != nullptr /*&& is_connected_to_source(var)*/ ) + { + rc = var_set(proc,kOutPId,kAnyChIdx,var->value); + } + + p->store_vid = kInvalidIdx; + } + + 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 + }; + + } + +#ifdef NOT_DEF + //------------------------------------------------------------------------------------------------------------------ + // + // 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 + }; + + } +#endif + + //------------------------------------------------------------------------------------------------------------------ + // + // Register + // + namespace reg + { + enum { + kInPId, + kStorePId, + kOutPId, + }; + + typedef struct + { + value_t value; + bool store_fl; + } inst_t; + + rc_t _set_stored_value( proc_t* proc, inst_t* p, const variable_t* var ) + { + rc_t rc = kOkRC; + + if( var->value == nullptr ) + { + rc = cwLogError(kInvalidStateRC,"The incoming register value is NULL."); + goto errLabel; + } + + value_duplicate(p->value,*var->value); + p->store_fl = true; + + errLabel: + return rc; + } + + rc_t _create( proc_t* proc, inst_t* p ) + { + rc_t rc = kOkRC; + const variable_t* in_var = nullptr; + variable_t* out_var = nullptr; + variable_t* store_var = nullptr; + + if((rc = var_register(proc, kAnyChIdx, + kInPId, "in", kBaseSfxId, + kStorePId, "store", kBaseSfxId)) != kOkRC ) + { + goto errLabel; + } + + if((rc = var_find(proc,"in",kBaseSfxId,kAnyChIdx,in_var )) != kOkRC ) + { + goto errLabel; + } + + if((rc = _set_stored_value(proc,p,in_var)) != kOkRC ) + { + goto errLabel; + } + + // Create the output var + if((rc = var_create( proc, "out", kBaseSfxId, kOutPId, kAnyChIdx, nullptr, in_var->value->tflag, out_var )) != kOkRC ) + { + rc = cwLogError(rc,"The output variable create failed."); + goto errLabel; + } + + + if((rc = var_find(proc,"store",kBaseSfxId,kAnyChIdx,store_var )) != kOkRC ) + { + goto errLabel; + } + + if((rc = var_set(store_var,&p->value)) != kOkRC ) + goto errLabel; + + if((rc = var_set(out_var,&p->value)) != kOkRC ) + goto errLabel; + + //store_var->value = &p->value; + //out_var->value = &p->value; + + + + 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 ) + { + switch( var->vid ) + { + case kInPId: + case kStorePId: + if( var->value != nullptr ) + _set_stored_value(proc,p,var); + break; + + case kOutPId: + break; + + default: + assert(0); + } + + return kOkRC; + } + + rc_t _exec( proc_t* proc, inst_t* p ) + { + rc_t rc = kOkRC; + + if( p->store_fl ) + { + rc = var_set(proc,kOutPId,kAnyChIdx,&p->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, + kDelayMsPId, + kOutPId, + }; + + typedef struct + { + bool delayFl; + unsigned delayFrmN; + 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; + ftime_t delayMs = 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, + kDelayMsPId, "delay_ms", kBaseSfxId,delayMs, + 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); + p->delayFrmN = _period_ms_to_frame_count(proc,p,srate,delayMs); + p->delayFl = true; + + 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; + + + case kDelayMsPId: + { + double delayMs; + srate_t srate; + inst_t* p = (inst_t*)(proc->userPtr); + + var_get(proc,kSratePId,kAnyChIdx,srate); + + if((rc = var_get(var,delayMs)) == kOkRC ) + p->delayFrmN = _period_ms_to_frame_count( proc, p, srate, delayMs ); + } + 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; + + if( p->delayFl ) + { + if( p->periodPhase >= p->delayFrmN ) + { + p->periodPhase -= p->delayFrmN; + p->delayFl = false; + } + } + + //printf("%i %i\n",p->periodPhase,p->periodFrmN); + + if( p->delayFl==false && 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, + kUprLimPId, + kLwrLimPId, + kLimitPId, + kOutPId + }; + + enum { + kModuloModeId, + kReverseModeId, + kClipModeId, + kInvalidModeId + }; + + typedef struct + { + unsigned mode_id; + + bool trig_val; + bool delta_fl; + + bool done_fl; + unsigned iter_cnt; + + 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, + 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, + kResetPId, "reset", kBaseSfxId, + kUprLimPId, "upr_lim", kBaseSfxId, + kLwrLimPId, "lwr_lim", kBaseSfxId, + kLimitPId, "limit", 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, init_val )) != 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; + p->iter_cnt = 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; + } + */ + p->delta_fl = true; + + } + break; + + case kModePId: + { + const char* s; + if((rc = var_get(var,s)) == kOkRC ) + rc = _string_to_mode_id(s,p->mode_id); + } + break; + + case kResetPId: + p->iter_cnt = 0; + p->dir = 1.0; + p->delta_fl = false; + p->done_fl = false; + break; + + } + + return rc; + } + + rc_t exec( proc_t* proc ) + { + rc_t rc = kOkRC; + inst_t* p = (inst_t*)proc->userPtr; + bool trig_upr_fl = false; + bool trig_lwr_fl = false; + 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; + + p->iter_cnt += 1; + /* + if( p->iter_cnt == 1 ) + { + var_get(proc,kInitPId,kAnyChIdx,cnt); + } + else + &*/ + if(1) + { + 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; + + //printf("%f %f %f\n",minv,cnt,maxv); + + if( minv > cnt || cnt >= maxv ) + { + bool repeat_fl; + var_get(proc,kRepeatPId,kAnyChIdx,repeat_fl); + + trig_upr_fl = cnt >= maxv; + trig_lwr_fl = cnt < minv; + + 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); + + if( trig_upr_fl ) + if((rc = var_set(proc, kUprLimPId, kAnyChIdx, true )) != kOkRC ) + goto errLabel; + + if( trig_lwr_fl ) + if((rc = var_set(proc, kLwrLimPId, kAnyChIdx, true )) != kOkRC ) + goto errLabel; + + if( trig_upr_fl || trig_lwr_fl ) + if((rc = var_set(proc, kLimitPId, kAnyChIdx, true )) != kOkRC ) + goto errLabel; + + errLabel: + return rc; + } + + class_members_t members = { + .create = create, + .destroy = destroy, + .value = value, + .exec = exec, + .report = nullptr + }; + + } + + //------------------------------------------------------------------------------------------------------------------ + // + // List + // + namespace list + { + enum + { + kInPId, + kCfgFnamePId, + 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; + object_t* file_list; + } 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; + const char* cfg_fname = nullptr; + char* exp_cfg_fname = nullptr; + unsigned index = kInvalidIdx; + const object_t* list_arg = nullptr; + + inst_t* p = mem::allocZ(); + proc->userPtr = p; + + variable_t* dum = nullptr; + + p->index = kInvalidIdx; + p->typeFl = kInvalidTFl; + p->deltaFl = false; + + if((rc = var_register_and_get(proc, kAnyChIdx, + kCfgFnamePId, "cfg_fname", kBaseSfxId, cfg_fname, + kInPId, "in", kBaseSfxId, index, + kListPId, "list", kBaseSfxId, list_arg)) != kOkRC ) + { + goto errLabel; + } + + if( cfg_fname != nullptr && textLength(cfg_fname)!=0 ) + { + + if((exp_cfg_fname = proc_expand_filename(proc,cfg_fname)) == nullptr ) + { + rc = cwLogError(kInvalidArgRC,"The list cfg filename could not be formed."); + goto errLabel; + + } + + if((rc = objectFromFile(exp_cfg_fname,p->file_list)) != kOkRC ) + { + rc = cwLogError(rc,"The list configuration file '%s' could not be parsed.",cwStringNullGuard(exp_cfg_fname)); + goto errLabel; + } + + if((rc = p->file_list->getv("list",p->list)) != kOkRC ) + { + rc = cwLogError(rc,"The list configuration file '%s' does not have a 'list' field.",cwStringNullGuard(exp_cfg_fname)); + goto errLabel; + } + + } + else + { + p->list = list_arg; + } + + 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: + mem::release(exp_cfg_fname); + return rc; + } + + rc_t destroy( proc_t* proc ) + { + rc_t rc = kOkRC; + + inst_t* p = (inst_t*)proc->userPtr; + + if(p->file_list != nullptr ) + p->file_list->free(); + + 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); + } + } + */ + + if( var->vid == p->eolPId ) + { + for(unsigned vid = kBaseInPId; vidinVarN; vid+=1 ) + { + variable_t* v = nullptr; + + if(var_find(proc, vid, kAnyChIdx, v) != kOkRC ) + continue; + + _print_field(proc, p, vid - kBaseInPId, v->value); + } + + _print_field(proc,p,p->inVarN,nullptr); + + } + + } + + // 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 + }; + + } + + + //------------------------------------------------------------------------------------------------------------------ + // + // Halt + // + namespace halt + { + enum { + kInPId + }; + + typedef struct + { + bool halt_fl; + } inst_t; + + + rc_t _create( proc_t* proc, inst_t* p ) + { + return var_register(proc,kAnyChIdx,kInPId,"in",kBaseSfxId); + } + + 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( proc->ctx->isInRuntimeFl && var->vid == kInPId ) + { + p->halt_fl = true; + } + + return rc; + } + + rc_t _exec( proc_t* proc, inst_t* p ) + { + return p->halt_fl ? kEofRC : 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 + }; + + } + + + //------------------------------------------------------------------------------------------------------------------ + // + // midi_msg + // + namespace midi_msg + { + enum { + kChPId, + kStatusPId, + kD0_PId, + kD1_PId, + kTriggerPId, + kBufCntPId, + kOutPId + }; + + typedef struct + { + midi::ch_msg_t* msgA; + unsigned msgN; + unsigned msg_idx; + + uint8_t ch; + uint8_t status; + uint8_t d0; + uint8_t d1; + + } inst_t; + + + rc_t _create( proc_t* proc, inst_t* p ) + { + rc_t rc = kOkRC; + + if((rc = var_register(proc,kAnyChIdx, + kChPId,"ch",kBaseSfxId, + kStatusPId,"status",kBaseSfxId, + kD0_PId,"d0",kBaseSfxId, + kD1_PId,"d1",kBaseSfxId, + kTriggerPId,"trigger",kBaseSfxId)) != kOkRC ) + { + goto errLabel; + } + + if((rc = var_register_and_get(proc,kAnyChIdx,kBufCntPId,"buf_cnt",kBaseSfxId,p->msgN)) != kOkRC ) + { + goto errLabel; + } + + p->msgA = mem::allocZ(p->msgN); + + // create one output MIDI buffer + rc = var_register_and_set( proc, "out", kBaseSfxId, kOutPId, kAnyChIdx, nullptr, 0 ); + + + errLabel: + return rc; + } + + rc_t _destroy( proc_t* proc, inst_t* p ) + { + mem::release(p->msgA); + return kOkRC; + } + + rc_t _set_midi_byte_value( proc_t* proc, inst_t* p, unsigned vid, const char* label, uint8_t max_val, uint8_t& midi_byte_ref ) + { + rc_t rc; + unsigned v; + + if((rc = var_get(proc,vid,kAnyChIdx,v)) != kOkRC ) + goto errLabel; + + if( 0 <= v && v <= max_val ) + midi_byte_ref = (uint8_t)v; + else + { + rc = cwLogError(kInvalidArgRC,"MIDI %s value (%i) is out of range 0-%i.",label,v,max_val); + goto errLabel; + } + + + errLabel: + return rc; + } + + rc_t _store_msg( proc_t* proc, inst_t* p ) + { + rc_t rc = kOkRC; + + if( p->msg_idx >= p->msgN ) + rc = cwLogError(kBufTooSmallRC,"MIDI buffer overflow."); + else + { + midi::ch_msg_t* m = p->msgA + p->msg_idx; + time::now(m->timeStamp); + m->devIdx = kInvalidIdx; + m->portIdx = 0; + m->uid = 0; + m->ch = p->ch; + m->status = p->status; + m->d0 = p->d0; + m->d1 = p->d1; + + p->msg_idx += 1; + } + + return rc; + } + + rc_t _value( proc_t* proc, inst_t* p, variable_t* var ) + { + rc_t rc = kOkRC; + + switch( var->vid ) + { + case kChPId: + rc = _set_midi_byte_value(proc,p,kChPId,"channel",midi::kMidiChCnt,p->ch); + break; + + case kStatusPId: + rc = _set_midi_byte_value(proc,p,kStatusPId,"status",255,p->status); + break; + + case kD0_PId: + rc = _set_midi_byte_value(proc,p,kD0_PId,"d0",127,p->d0); + break; + + case kD1_PId: + rc = _set_midi_byte_value(proc,p,kD1_PId,"d1",127,p->d1); + break; + + case kTriggerPId: + rc = _store_msg(proc,p); + break; + } + + return rc; + } + + rc_t _exec( proc_t* proc, inst_t* p ) + { + rc_t rc = kOkRC; + mbuf_t* mbuf = nullptr; + + // get the output variable + if((rc = var_get(proc,kOutPId,kAnyChIdx,mbuf)) != kOkRC ) + rc = cwLogError(kInvalidStateRC,"The MIDI msg. instance '%s' does not have a valid MIDI output buffer.",proc->label); + else + { + mbuf->msgN = p->msg_idx; + mbuf->msgA = p->msg_idx > 0 ? p->msgA : nullptr; + } + + p->msg_idx = 0; + + 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 + }; + + } + + //------------------------------------------------------------------------------------------------------------------ + // + // midi_split + // + namespace midi_split + { + enum { + kInPId, + kChPId, + kStatusPId, + kD0PId, + kD1PId, + kBufCntPId + }; + + typedef struct + { + midi::ch_msg_t* msgA; + unsigned msgN; + unsigned msg_idx; + } inst_t; + + + rc_t _create( proc_t* proc, inst_t* p ) + { + rc_t rc = kOkRC; + + if((rc = var_register(proc,kAnyChIdx, + kInPId,"in",kBaseSfxId, + kChPId,"ch",kBaseSfxId, + kStatusPId,"status",kBaseSfxId, + kD0PId,"d0",kBaseSfxId, + kD1PId,"d1",kBaseSfxId)) != kOkRC ) + { + goto errLabel; + } + + if((rc = var_register_and_get(proc,kAnyChIdx,kBufCntPId,"buf_cnt",kBaseSfxId,p->msgN)) != kOkRC ) + { + goto errLabel; + } + + p->msgA = mem::allocZ(p->msgN); + + errLabel: + return rc; + } + + rc_t _destroy( proc_t* proc, inst_t* p ) + { + rc_t rc = kOkRC; + + mem::release(p->msgA); + + 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; + mbuf_t* mbuf = nullptr; + + if((rc = var_get(proc,kInPId,kAnyChIdx,mbuf)) != kOkRC ) + goto errLabel; + + + for(unsigned i=0; imsgN; ++i) + { + var_set(proc, kChPId, kAnyChIdx, mbuf->msgA[i].ch); + var_set(proc, kStatusPId, kAnyChIdx, mbuf->msgA[i].status); + var_set(proc, kD0PId, kAnyChIdx, mbuf->msgA[i].d0); + var_set(proc, kD1PId, kAnyChIdx, mbuf->msgA[i].d1); + } + + 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 + }; + + } + + //------------------------------------------------------------------------------------------------------------------ + // + // midi_file + // + namespace midi_file + { + enum { + kMidiFileNamePId, + kCsvFileNamePId, + kDoneFlPId, + kOutPId + }; + + typedef struct msg_str + { + unsigned sample_idx; + midi::ch_msg_t* m; + } msg_t; + + typedef struct + { + midi::file::handle_t mfH; + msg_t* msgA; + midi::ch_msg_t* chMsgA; + unsigned msgN; + unsigned msg_idx; + unsigned sample_idx; + + char* midi_fname; + char* csv_fname; + } inst_t; + + + rc_t _create( proc_t* proc, inst_t* p ) + { + rc_t rc = kOkRC; + const char* midi_fname = nullptr; + const char* csv_fname = nullptr; + const midi::file::trackMsg_t** tmA = nullptr; + unsigned msgAllocN = 0; + bool done_fl = false; + time::spec_t asecs; + + time::setZero(asecs); + + if((rc = var_register_and_get(proc,kAnyChIdx, + kMidiFileNamePId, "fname", kBaseSfxId, midi_fname, + kCsvFileNamePId, "csv_fname", kBaseSfxId, csv_fname, + kDoneFlPId, "done_fl", kBaseSfxId, done_fl)) != kOkRC ) + { + goto errLabel; + } + + if( csv_fname != nullptr && textLength(csv_fname)>0 ) + if((p->csv_fname = proc_expand_filename(proc,csv_fname)) == nullptr ) + { + rc = cwLogError(kInvalidArgRC,"The MIDI CSV filename could not be formed."); + goto errLabel; + } + + if( midi_fname != nullptr && textLength(midi_fname)>0 ) + if((p->midi_fname = proc_expand_filename(proc,midi_fname)) == nullptr ) + { + rc = cwLogError(kInvalidArgRC,"The MIDI filename could not be formed."); + goto errLabel; + } + + + if( p->midi_fname != nullptr && textLength(p->midi_fname) > 0 ) + { + if((rc = midi::file::open(p->mfH,p->midi_fname)) != kOkRC ) + goto errLabel; + } + else + { + if( p->csv_fname != nullptr && textLength(p->csv_fname)>0 ) + { + if((rc = midi::file::open_csv(p->mfH,p->csv_fname)) != kOkRC ) + goto errLabel; + } + else + { + rc = cwLogError(kOpenFailRC,"No MIDI or CSV filename was given."); + } + } + + // create one output MIDI buffer + rc = var_register_and_set( proc, "out", kBaseSfxId, kOutPId, kAnyChIdx, nullptr, 0 ); + + tmA = msgArray(p->mfH); + + msgAllocN = msgCount(p->mfH); + p->msgA = mem::allocZ(msgAllocN); + p->chMsgA = mem::allocZ(msgAllocN); + p->msg_idx = 0; + + + for(unsigned i=0; imsgA + p->msg_idx; + + m->m = p->chMsgA + p->msg_idx; + + time::microsecondsToSpec( m->m->timeStamp, tmA[i]->amicro ); + + m->sample_idx = (unsigned)(proc->ctx->sample_rate * time::specToSeconds(m->m->timeStamp)); + + m->m->devIdx = 0; + m->m->portIdx = 0; + m->m->uid = tmA[i]->uid; + + if( midi::isChStatus(tm->status) ) + { + m->m->status = tmA[i]->status & 0xf0; + m->m->ch = tmA[i]->u.chMsgPtr->ch; + m->m->d0 = tmA[i]->u.chMsgPtr->d0; + m->m->d1 = tmA[i]->u.chMsgPtr->d1; + + //printf("%lli %f %f %i %i ch:%i st:%i d0:%i d1:%i\n",tmA[i]->amicro/1000,secs,m->sample_idx/proc->ctx->sample_rate, p->msg_idx, m->m->uid, m->m->ch, m->m->status, m->m->d0, m->m->d1); + + p->msg_idx += 1; + } + } + + p->msgN = p->msg_idx; + p->msg_idx = 0; + + errLabel: + return rc; + } + + rc_t _destroy( proc_t* proc, inst_t* p ) + { + rc_t rc = kOkRC; + + mem::release(p->midi_fname); + mem::release(p->csv_fname); + close(p->mfH); + + 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; + mbuf_t* mbuf = nullptr; + bool done_fl = false; + + p->sample_idx += proc->ctx->framesPerCycle; + + // 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 + { + mbuf->msgA = nullptr; + mbuf->msgN = 0; + + while( p->msg_idx < p->msgN && p->sample_idx >= p->msgA[p->msg_idx].sample_idx ) + { + if( mbuf->msgA == nullptr ) + mbuf->msgA = p->msgA[p->msg_idx].m; + + mbuf->msgN += 1; + + p->msg_idx += 1; + + //printf("si:%i next:%i mi:%i\n",p->sample_idx,p->msgA[p->msg_idx].sample_idx,p->msg_idx); + + done_fl = p->msg_idx == p->msgN; + + } + + if( done_fl ) + var_set(proc, kDoneFlPId, kAnyChIdx, true ); + + } + + 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 + }; + + } + + //------------------------------------------------------------------------------------------------------------------ + // + // midi_merge + // + namespace midi_merge + { + enum + { + kOutPId, + kBufMsgCntPId, + kBaseInPId, + }; + + typedef struct + { + external_device_t* ext_dev; + + unsigned inVarN; + + unsigned msgN; + midi::ch_msg_t* msgA; + unsigned msg_idx; + } inst_t; + + rc_t _create( proc_t* proc, inst_t* p ) + { + rc_t rc = kOkRC; // + unsigned inVarN = var_mult_count(proc,"in"); + + unsigned sfxIdA[ inVarN ]; + + // get the the sfx_id's of the input audio variables + if((rc = var_mult_sfx_id_array(proc, "in", sfxIdA, inVarN, p->inVarN )) != kOkRC ) + goto errLabel; + + std::sort(sfxIdA, sfxIdA + p->inVarN, [](unsigned& a,unsigned& b){ return amsgN )) != kOkRC ) + { + goto errLabel; + } + + // Register each input var + for(unsigned i=0; iinVarN; ++i) + if((rc = var_register( proc, kAnyChIdx, kBaseInPId+i, "in", sfxIdA[i] )) != kOkRC ) + goto errLabel; + + + // create one output MIDI buffer + if((rc = var_register_and_set( proc, "out", kBaseSfxId, kOutPId, kAnyChIdx, nullptr, 0 )) != kOkRC ) + { + goto errLabel; + } + + p->msgA = mem::allocZ(p->msgN); + + errLabel: + return rc; + } + + rc_t _destroy( proc_t* proc, inst_t* p ) + { + mem::release(p->msgA); + return kOkRC; + } + + 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; + mbuf_t* out_mbuf = nullptr; + unsigned mbufN = 0; + mbuf_t* mbufA[ p->inVarN ]; + + + // get the output buffer + if((rc = var_get(proc,kOutPId,kAnyChIdx,out_mbuf)) != kOkRC ) + { + rc = cwLogError(kInvalidStateRC,"The MIDI merge instance '%s' does not have a valid input connection.",proc->label); + goto errLabel; + } + + // get the mbuf from each input + for(unsigned i=0; iinVarN; ++i) + { + mbuf_t* mbuf = nullptr; + if((rc = var_get(proc, kBaseInPId+i, kAnyChIdx, mbuf)) != kOkRC ) + goto errLabel; + + // ... only store buffers that have events + if( mbuf->msgN ) + mbufA[ mbufN++ ] = mbuf; + + assert( mbufN <= p->inVarN ); + } + + switch( mbufN ) + { + case 0: + // no midi events arrived + out_mbuf->msgA = nullptr; + out_mbuf->msgN = 0; + break; + + case 1: + // exactly one full midi buffer was found + out_mbuf->msgA = mbufA[0]->msgA; + out_mbuf->msgN = mbufA[0]->msgN; + break; + + default: + // multiple full midi buffers were found + { + unsigned i,j,k; + for(i=0,j=0; imsgN; ++i) + for(k=0; jmsgN && kmsgN; ++k) + p->msgA[j++] = mbufA[i]->msgA[k]; + + std::sort(p->msgA, p->msgA + j, [](const midi::ch_msg_t& a, const midi::ch_msg_t& b){ return time::isLTE(a.timeStamp,b.timeStamp); } ); + + out_mbuf->msgA = p->msg_idx > 0 ? p->msgA : nullptr; + out_mbuf->msgN = p->msg_idx; + } + } + + p->msg_idx = 0; + + 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 + }; + + } + } // flow diff --git a/cwFlowProc.h b/cwFlowProc.h index 15fee86..3ec8641 100644 --- a/cwFlowProc.h +++ b/cwFlowProc.h @@ -2,15 +2,21 @@ namespace cw { namespace flow { + namespace user_def_proc { 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; } namespace audio_duplicate { extern class_members_t members; } namespace audio_mix { extern class_members_t members; } + namespace audio_marker { extern class_members_t members; } + namespace audio_silence { extern class_members_t members; } namespace sine_tone { extern class_members_t members; } namespace pv_analysis { extern class_members_t members; } namespace pv_synthesis { extern class_members_t members; } @@ -22,5 +28,24 @@ 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 midi_voice { extern class_members_t members; } + namespace piano_voice { extern class_members_t members; } + namespace poly_voice_ctl { extern class_members_t members; } + namespace sample_hold { extern class_members_t members; } + namespace number { extern class_members_t members; } + namespace reg { 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; } + namespace halt { extern class_members_t members; } + namespace midi_msg { extern class_members_t members; } + namespace midi_split { extern class_members_t members; } + namespace midi_file { extern class_members_t members; } + namespace midi_merge { extern class_members_t members; } + } } diff --git a/cwFlowTest.cpp b/cwFlowTest.cpp new file mode 100644 index 0000000..811f309 --- /dev/null +++ b/cwFlowTest.cpp @@ -0,0 +1,88 @@ +#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 configure failed."); + goto errLabel; + } + + // create the flow object + if((rc = initialize( flowH )) != 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..4994730 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" }, + { kUdpOutVarDescFl, "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; @@ -116,15 +148,19 @@ namespace cw case kABufTFl: - dst.u.abuf = src.u.abuf == nullptr ? nullptr : abuf_duplicate(src.u.abuf); - dst.flags = src.flags; + dst.u.abuf = src.u.abuf == nullptr ? nullptr : abuf_duplicate(dst.u.abuf,src.u.abuf); + 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.u.fbuf = src.u.fbuf == nullptr ? nullptr : fbuf_duplicate(dst.u.fbuf,src.u.fbuf); + 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,95 @@ 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: + cwLogPrint(""); + 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 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 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 +291,70 @@ namespace cw break; case kStringTFl: - printf("%s ", v->u.s); + cwLogPrint("s:%s ", v->u.s); break; case kTimeTFl: assert(0); break; + case kCfgTFl: + cwLogPrint("c:"); + 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 +362,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 +401,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 +442,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 +481,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 +565,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 +609,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 +720,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 +741,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(" %12s:%3i vid:%3i 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 +923,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 +969,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 +995,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 +1032,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 +1058,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 static (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,107 +1094,121 @@ 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; + + } // 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_duplicate( value_t& dst, const value_t& src ) +{ + _value_duplicate(dst,src); +} + +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 ) { - abuf_t* a = mem::allocZ(); - a->srate = srate; - a->chN = chN; - a->frameN = frameN; - a->buf = mem::allocZ( chN*frameN ); + if( chN*frameN == 0 ) + { + cwLogError(kInvalidArgRC,"The %s audio signal parameter cannot be zero.", chN==0 ? "channel count" : "frame count"); + return nullptr; + } + + abuf_t* a = mem::allocZ(); + a->srate = srate; + a->chN = chN; + a->frameN = frameN; + a->bufAllocSmpN = chN*frameN; + + + a->buf = mem::allocZ(a->bufAllocSmpN); return a; } @@ -877,9 +1222,22 @@ void cw::flow::abuf_destroy( abuf_t*& abuf ) mem::release(abuf); } -cw::flow::abuf_t* cw::flow::abuf_duplicate( const abuf_t* src ) +cw::flow::abuf_t* cw::flow::abuf_duplicate( abuf_t* dst, const abuf_t* src ) { - return abuf_create( src->srate, src->chN, src->frameN ); + abuf_t* abuf = nullptr; + + if( dst != nullptr && dst->bufAllocSmpN < src->bufAllocSmpN ) + mem::release(dst->buf); + + if( dst == nullptr || dst->buf == nullptr ) + abuf = abuf_create( src->srate, src->chN, src->frameN ); + else + abuf = dst; + + if( abuf != nullptr ) + vop::copy(abuf->buf,src->buf,src->chN*src->frameN); + + return abuf; } @@ -904,7 +1262,80 @@ 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] ) + { + cwLogError(kInvalidArgRC,"A channel bin count (%i) execeeds the max bin count (%i).",binN_V[i],maxBinN_V[i]); + return nullptr;; + } + + fbuf_t* f = mem::allocZ(); + + bool proxy_fl = magV != nullptr || phsV != nullptr || hzV != nullptr; + + // Calculate the total count of bins for each data vector. + unsigned maxTotalBinN = proxy_fl ? 0 : vop::sum(maxBinN_V, chN); + + // calc the total size of memory required for all internal data structures + f->memByteN + = sizeof(unsigned) * chN*kFbufVectN // maxBinN_V[],binN_V[],hopSmpN_V[] + + sizeof(fd_sample_t*) * chN*kFbufVectN // magV[],phsV[],hzV[] (pointer to bin buffers) + + sizeof(bool) * chN*1 // readyFlV[] + + sizeof(fd_sample_t) * maxTotalBinN*kFbufVectN; // bin buffer memory + + // allocate mory + f->mem = mem::allocZ(f->memByteN); + + unsigned* base_maxBinV = (unsigned*)f->mem; + fd_sample_t** base_bufV = (fd_sample_t**)(base_maxBinV + kFbufVectN * chN); + bool* base_boolV = (bool*)(base_bufV + kFbufVectN * chN); + fd_sample_t* base_buf = (fd_sample_t*)(base_boolV + chN); + + + f->srate = srate; + f->chN = chN; + f->maxBinN_V = base_maxBinV; + f->binN_V = f->maxBinN_V + chN; + f->hopSmpN_V = f->binN_V + chN; + f->magV = base_bufV; + f->phsV = f->magV + chN; + f->hzV = f->phsV + chN; + f->readyFlV = base_boolV; + + vop::copy( f->binN_V, binN_V, chN ); + vop::copy( f->maxBinN_V, maxBinN_V, chN ); + vop::copy( f->hopSmpN_V, hopSmpN_V, chN ); + + if( proxy_fl ) + { + for(unsigned chIdx=0; chIdxmagV[ chIdx ] = (fd_sample_t*)magV[chIdx]; + f->phsV[ chIdx ] = (fd_sample_t*)phsV[chIdx]; + f->hzV[ chIdx ] = (fd_sample_t*)hzV[chIdx]; + } + } + else + { + fd_sample_t* m = base_buf; + for(unsigned chIdx=0; chIdxmagV[chIdx] = m + 0 * f->binN_V[chIdx]; + f->phsV[chIdx] = m + 1 * f->binN_V[chIdx]; + f->hzV[ chIdx] = m + 2 * f->binN_V[chIdx]; + m += f->maxBinN_V[chIdx]; + assert( m <= base_buf + kFbufVectN * maxTotalBinN ); + } + } + + return f; +} + +/* +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 +1351,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 +1364,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]; @@ -959,9 +1390,9 @@ 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,30 +1402,28 @@ 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 ) return; - mem::release( fbuf->maxBinN_V ); - mem::release( fbuf->binN_V ); - mem::release( fbuf->hopSmpN_V); - mem::release( fbuf->magV); - mem::release( fbuf->phsV); - mem::release( fbuf->hzV); - mem::release( fbuf->buf); - mem::release( fbuf->readyFlV); + mem::release( fbuf->mem); mem::release( fbuf); } -cw::flow::fbuf_t* cw::flow::fbuf_duplicate( const fbuf_t* src ) +cw::flow::fbuf_t* cw::flow::fbuf_duplicate( fbuf_t* dst, const fbuf_t* src ) { - fbuf_t* fbuf = fbuf_create( src->srate, src->chN, src->maxBinN_V, src->binN_V, src->hopSmpN_V ); + fbuf_t* fbuf = nullptr; + + if( dst != nullptr && dst->memByteN < src->memByteN ) + fbuf_destroy(dst); + + if( dst == nullptr ) + fbuf = fbuf_create( src->srate, src->chN, src->maxBinN_V, src->binN_V, src->hopSmpN_V ); + else + fbuf = dst; for(unsigned i=0; ichN; ++i) { @@ -1010,27 +1439,116 @@ 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; + } + + if( class_desc->ui != nullptr ) + { + mem::release(class_desc->ui->presetA); + mem::release(class_desc->ui); + } + +} 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; iudpDescN; ++i) + if( textIsEqual(p->udpDescA[i].label,label)) + return p->udpDescA + 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 +1556,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 +1568,278 @@ 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; iudpDescN; ++i) + _class_desc_print(p->udpDescA+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.procA[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 ) +void* cw::flow::network_global_var( proc_t* proc, const char* var_label ) { - for(instance_t* inst = p->network_head; inst!=nullptr; inst=inst->link) - instance_print(inst); + net_global_var_t* gv; + + assert( proc->net != nullptr ); + + for(gv=proc->net->globalVarL; gv!=nullptr; gv=gv->link ) + if( textIsEqual(proc->class_desc->label,gv->class_label) && textIsEqual(gv->var_label,var_label) ) + return gv->blob; + + return nullptr; } -cw::flow::instance_t* cw::flow::instance_find( flow_t* p, const char* inst_label ) +cw::rc_t cw::flow::network_global_var_alloc( proc_t* proc, const char* var_label, const void* blob, unsigned blobByteN ) { - for(instance_t* inst = p->network_head; inst!=nullptr; inst=inst->link ) - if( textCompare(inst_label,inst->label) == 0 ) - return inst; + rc_t rc = kOkRC; + net_global_var_t* gv; + void* v; + + unsigned allocWordN = 0; + + if((v = network_global_var(proc,var_label)) != nullptr ) + { + rc = cwLogError(kInvalidArgRC,"The global variable '%s:%s' already exists.",cwStringNullGuard(proc->class_desc->label),cwStringNullGuard(var_label)); + goto errLabel; + } + + gv = mem::allocZ(); + + allocWordN = std::max(blobByteN/sizeof(unsigned),1ul); + + gv->class_label = proc->class_desc->label; + gv->var_label = mem::duplStr(var_label); + gv->blob = mem::allocZ(allocWordN); + gv->blobByteN = blobByteN; + memcpy(gv->blob,blob,blobByteN); + + gv->link = proc->net->globalVarL; + proc->net->globalVarL = gv; + +errLabel: + return rc; +} + + +const cw::flow::network_preset_t* cw::flow::network_preset_from_label( const network_t& net, const char* preset_label ) +{ + 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.procA[i]->label_sfx_id; + multN += 1; + } + + idN_ref = multN; + +errLabel: + + return rc; +} + +unsigned cw::flow::network_poly_count( const network_t& net ) +{ + unsigned n =1; + for(const network_t* n0 = net.poly_link; n0!=nullptr; n0=n0->poly_link) + ++n; + + return n; +} + +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.procA[i]->label) ) + return net.procA[i]; + } + + if( net.poly_link != nullptr ) + return proc_find(*net.poly_link, proc_label, sfx_id ); 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 +1847,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.", cwStringNullGuard(proc->label), proc->label_sfx_id, cwStringNullGuard(var_label), sfx_id, chIdx ); } } @@ -1198,27 +2021,159 @@ 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; } +unsigned cw::flow::var_channel_count( proc_t* proc, const char* var_label, unsigned sfx_id ) +{ + rc_t rc = kOkRC; + unsigned chN = kInvalidCnt; + variable_t* base_var = nullptr; -bool cw::flow::var_has_value( instance_t* inst, const char* label, unsigned chIdx ) + if((rc = _var_find_on_label_and_ch( proc, var_label, sfx_id, kAnyChIdx, base_var)) != kOkRC || base_var==nullptr) + { + cwLogError(rc,"Var. channel count calc failed."); + goto errLabel; + } + + chN = 0; + for(base_var=base_var->ch_link; base_var!=nullptr; base_var=base_var->ch_link) + if( base_var->chIdx+1 > chN ) + chN = base_var->chIdx + 1; + + +errLabel: + return chN; +} + + +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 ) + { + + if( var->proc->ctx->printLogHdrFl ) + { + cwLogPrint("%s","exe cycle: process: id: variable: id vid ch : : : type:value : destination\n"); + cwLogPrint("%s","---------- ----------- ----- --------------- -- --- ----- ------------: -------------\n"); + //: 0 : a: 0: out: 0 vid: 2 ch: -1 : : : : + var->proc->ctx->printLogHdrFl = false; + } + + cwLogPrint("%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; + +} + +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 +2181,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 +2208,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 +2248,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 +2263,257 @@ 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 ) - goto errLabel; + + // if the kAnyChIdx has not been created for this variable ... + if((var = _var_find_on_label_and_ch(proc,var_label,sfx_id,kAnyChIdx)) == nullptr ) + { + variable_t* dum = nullptr; + + // ... then create it here + if((rc = var_create( proc, var_label, sfx_id, kInvalidId, kAnyChIdx, nullptr, kInvalidTFl, dum )) != kOkRC ) + { + rc = cwLogError(rc,"An attempt to create the 'any-channel' for '%s:%i' failed.",cwStringNullGuard(var_label),sfx_id); + goto errLabel; + } + + // if the var being registered is on channel kAnyChIdx + if( chIdx == kAnyChIdx ) + var = dum; + + } + + // don't channelize kAnyChIdx because it must already exist + if( chIdx != kAnyChIdx ) + if((rc = var_channelize(proc,var_label,sfx_id,chIdx,value_cfg,vid,var)) != kOkRC ) + goto errLabel; } + assert( var != nullptr ); + var->vid = vid; varRef = var; - if((var = _var_find_on_label_and_ch(inst,var_label,kAnyChIdx)) != nullptr ) - var->vid = vid; + // The kAnyChIdx shares the 'vid' with channelized variables - this is by design (vids are unique across variables, but shared across channels) + if((var = _var_find_on_label_and_ch(proc,var_label,sfx_id,kAnyChIdx)) == nullptr ) + 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); else - rc = cwLogError(kInvalidStateRC,"The variable '%s' instance '%s' has no base channel.", var_label, inst->label, chIdx); + { + var->vid = vid; // ... this guarantee's that the kAnyChIdx variable has a valid vid + } 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_send_to_ui( variable_t* var ) +{ + + // var->ui_var_link is set to null when the var is removed from the list + + // 1. Atomically set _head to the new node and return 'old-head' + variable_t* prev = var->proc->ctx->ui_var_head.exchange(var,std::memory_order_acq_rel); + + // Note that at this point only the new node may have the 'old-head' as it's predecssor. + // Other threads may therefore safely interrupt at this point. + + // 2. Set the old-head next pointer to the new node (thereby adding the new node to the list) + prev->ui_var_link.store(var,std::memory_order_release); // RELEASE 'next' to consumer + + return kOkRC; +} + +cw::rc_t cw::flow::var_send_to_ui( proc_t* proc, unsigned vid, unsigned chIdx ) +{ + rc_t rc = kOkRC; + variable_t* var = nullptr; + + if((rc = var_find(proc, vid, chIdx, var )) == kOkRC ) + rc = var_send_to_ui(var); + + 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 +2521,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 +2555,255 @@ 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, abuf_t* val ) +{ + rc_t rc = kOkRC; + variable_t* var = nullptr; + + if((rc = var_find(proc, vid, chIdx, var )) == kOkRC ) + rc = _var_set_driver(var,kABufTFl,val); + + return rc; +} + +cw::rc_t cw::flow::var_set( proc_t* proc, unsigned vid, unsigned chIdx, fbuf_t* val ) +{ + rc_t rc = kOkRC; + variable_t* var = nullptr; + + if((rc = var_find(proc, vid, chIdx, var )) == kOkRC ) + rc = _var_set_driver(var,kFBufTFl,val); + + return rc; +} + +cw::rc_t cw::flow::var_set( proc_t* proc, unsigned vid, unsigned chIdx, mbuf_t* val ) +{ + rc_t rc = kOkRC; + variable_t* var = nullptr; + + if((rc = var_find(proc, vid, chIdx, var )) == kOkRC ) + rc = _var_set_driver(var,kMBufTFl,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..68216dd 100644 --- a/cwFlowTypes.h +++ b/cwFlowTypes.h @@ -3,49 +3,59 @@ 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 - sample_t* buf; // buf[ chN ][ frameN ] + srate_t srate; // Signal sample rate + unsigned chN; // Count of channels + unsigned frameN; // Count of sample frames per channel + unsigned bufAllocSmpN; // Size of allocated buf[] in samples. + sample_t* buf; // buf[ chN ][ frameN ] } 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; + { + unsigned memByteN; // Count of bytes in mem[]. + void* mem; // mem[ memByteN ] All dynamically allocated memory used by this fbuf. + 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* maxBinN_V; // maxBinN_V[chN] 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) } fbuf_t; + typedef struct mbuf_str + { + const midi::ch_msg_t* msgA; + unsigned msgN; + } mbuf_t; + enum { kInvalidTFl = 0x00000000, @@ -58,17 +68,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 +92,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 +99,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 +124,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, + kUdpOutVarDescFl = 0x20 }; typedef struct class_members_str @@ -139,57 +160,87 @@ 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; // preset linked list + 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 + ui_proc_desc_t* ui; } 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; // See kLogVarFl, kProxiedVarFl, etc + 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. + + ui_var_t* ui_var; // this variables UI description + std::atomic ui_var_link; // UI update var link based on flow_t ui_var_head; } 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 char* arg_label; // optional args label - const object_t* arg_cfg; // optional args configuration - + const object_t* proc_cfg; // instance configuration + void* userPtr; // instance state variable_t* varL; // linked list of all variables on this instance @@ -198,86 +249,260 @@ 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 net_global_var_str + { + const char* class_label; + char* var_label; + void* blob; + unsigned blobByteN; + + struct net_global_var_str* link; + + } net_global_var_t; + + typedef struct network_str + { + const object_t* procsCfg; // network proc list + const object_t* presetsCfg; // presets designed for this network + + struct proc_str** procA; + unsigned procN; + + network_preset_t* presetA; + unsigned presetN; + + // Preset pair table used by network_apply_dual_preset() + network_preset_pair_t* preset_pairA; + unsigned preset_pairN; + + net_global_var_t* globalVarL; + + struct network_str* poly_link; + unsigned poly_idx; + + ui_net_t* ui_net; + + } network_t; + + typedef struct flow_str { - const object_t* networkCfg; // complete cfg used to create this network + const object_t* pgmCfg; // complete program cfg + const object_t* networkCfg; // 'network' cfg from pgmCfg - const object_t* presetCfg; // presets designed for this network + bool printNetworkFl; + bool non_real_time_fl; // set if this is a non-real-time program + 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 + + bool printLogHdrFl; - 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* udpDescA; // + unsigned udpDescN; // - 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 + + // Top-level preset list. + network_preset_t* presetA; // presetA[presetN] partial (label and tid only) parsing of the network presets + unsigned presetN; // + + network_t* net; // The root of the network instance + + ui_callback_t ui_callback; + void* ui_callback_arg; + + std::atomic ui_var_head; // Linked lists of var's to send to the UI + variable_t ui_var_stub; + variable_t* ui_var_tail; + } 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 ); - abuf_t* abuf_duplicate( const abuf_t* src ); + + // If 'dst' is null then a new abuf is allocated, filled with the contents of 'src'. + // If 'dst' is non-null and there is enough space for the contents of 'src' then only a copy is executed. + // If there is not enough space then dst is reallocated. + abuf_t* abuf_duplicate( abuf_t* dst, const abuf_t* src ); 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 ); + + // Memory allocation will only occur if dst is null, or the size of dst's internal buffer are too small. + fbuf_t* fbuf_duplicate( fbuf_t* dst, 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_duplicate( value_t& dst, const value_t& src ); + + 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 ); + // Access a blob stored via network_global_var() + void* network_global_var( proc_t* proc, const char* var_label ); + + // Copy a named blob into the network global variable space. + rc_t network_global_var_alloc( proc_t* proc, const char* var_label, const void* blob, unsigned blobByteN ); + + + 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 ); + + unsigned network_poly_count( const network_t& net ); + + //------------------------------------------------------------------------------------------------------------------------ // - // 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 +511,78 @@ 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 ); + // automatically generated variable whose channel index is set to 'kAnyChIdx'. + 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 ); + + // Get the count of channels attached to var_label:sfx_id:kAnyChIdx. + // Returns 0 if only kAnyChIdx exists, + // Returns kInvalidCnt if var_label:sfx_id does not exist. + // Otherwise returns count of channels no including kAnyChIdx. (e.g. mono=1, stereo=2, quad=4 ...) + unsigned var_channel_count( proc_t* proc, const char* var_label, unsigned sfx_id ); + + // 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 ); + + // Send a variable value to the UI + rc_t var_send_to_ui( variable_t* var ); + rc_t var_send_to_ui( proc_t* proc, unsigned vid, unsigned chIdx ); //----------------- // // 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 +591,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 +620,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 +633,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 +662,98 @@ 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, mbuf_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 68d39a7..daf382d 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" @@ -95,6 +96,8 @@ namespace cw audioGroup_t* oGroup; // audio_group_dev_t* iagd; // Audio group device record assoc'd with this device audio_group_dev_t* oagd; // + unsigned cycleCnt; + unsigned framesPerCycle; struct audioDev_str* clockInList; // List of devices sync'd to this devices input clock struct audioDev_str* clockOutList; // List of devices sync'd to this devices output clock @@ -611,7 +614,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; @@ -975,7 +978,6 @@ namespace cw goto errLabel; } - if((rc = audio::device::alsa::destroy(p->alsaH)) != kOkRC ) { rc = cwLogError(rc,"ALSA sub-system shutdown failed."); @@ -987,7 +989,6 @@ namespace cw rc = cwLogError(rc,"Audio device file sub-system shutdown failed."); goto errLabel; } - if((rc = audio::device::destroy(p->audioH)) != kOkRC ) { @@ -1637,6 +1638,85 @@ namespace cw errLabel: return rc; } + + rc_t _audioDeviceConfigure( io_t* p, audioDev_t* ad, audioGroup_t* iag, audioGroup_t* oag, unsigned cycleCnt, unsigned framesPerCycle ) + { + rc_t rc = kOkRC; + + double israte = 0; + double osrate = 0; + double srate = 0; + + unsigned iDspFrameCnt = 0; + unsigned oDspFrameCnt = 0; + unsigned dspFrameCnt = 0; + + unsigned iChCnt = 0; + unsigned oChCnt = 0; + + const char* inGroupLabel = iag==nullptr || iag->msg.label==nullptr ? "" : iag->msg.label; + const char* outGroupLabel = oag==nullptr || oag->msg.label==nullptr ? "" : oag->msg.label; + + + // get the ingroup + if( iag != nullptr ) + { + israte = iag->msg.srate; + iDspFrameCnt = iag->msg.dspFrameCnt; + } + + // get the outgroup + if( oag != nullptr ) + { + osrate = oag->msg.srate; + oDspFrameCnt = oag->msg.dspFrameCnt; + } + + // in-srate and out-srate must be equal or one must be 0 + if( osrate==0 || israte==0 || osrate==israte ) + { + // the true sample rate is the non-zero sample rate + srate = std::max(israte,osrate); + } + else + { + rc = cwLogError(kInvalidArgRC,"The device '%s' belongs to two groups (%s and %s) at different sample rates (%f != %f).", cwStringNullGuard(ad->devName), cwStringNullGuard(inGroupLabel), cwStringNullGuard(outGroupLabel), israte, osrate ); + goto errLabel; + } + + // in-dspFrameCnt an out-dspFrameCnt must be equal or one must be 0 + if( oDspFrameCnt==0 || iDspFrameCnt==0 || oDspFrameCnt==iDspFrameCnt) + { + // the true sample rate is the non-zero sample rate + dspFrameCnt = std::max(iDspFrameCnt,oDspFrameCnt); + } + else + { + rc = cwLogError(kInvalidArgRC,"The device '%s' belongs to two groups (%s and %s) width different dspFrameCnt values (%i != %i).", cwStringNullGuard(ad->devName), cwStringNullGuard(inGroupLabel), cwStringNullGuard(outGroupLabel), iDspFrameCnt, oDspFrameCnt ); + goto errLabel; + } + + // setup the device based on the configuration + if((rc = audio::device::setup(p->audioH, ad->devIdx, srate, framesPerCycle, _audioDeviceCallback, p)) != kOkRC ) + { + rc = cwLogError(rc,"Unable to setup the audio hardware device:'%s'.", ad->devName); + goto errLabel; + } + + // get the device channel counts + iChCnt = audio::device::channelCount(p->audioH,ad->devIdx,true); + oChCnt = audio::device::channelCount(p->audioH,ad->devIdx,false); + + // initialize the audio bufer for this device + if((rc = audio::buf::setup( p->audioBufH, ad->devIdx, srate, dspFrameCnt, cycleCnt, iChCnt, framesPerCycle, oChCnt, framesPerCycle )) != kOkRC ) + { + rc = cwLogError(rc,"Audio device buffer channel setup failed."); + goto errLabel; + } + + errLabel: + return rc; + } // Create the audio device records by parsing the cfg audio.deviceL[] list. rc_t _audioDeviceParseAudioDeviceList( io_t* p, const object_t* cfg ) @@ -1666,7 +1746,7 @@ namespace cw // fill in the audio device cfg list for(unsigned i=0; ichild_count(); ++i) { - audioDev_t* ad = nullptr; //p->audioDevA + i; + audioDev_t* ad = nullptr; bool activeFl = false; bool meterFl = false; char* userLabel = nullptr; @@ -1677,7 +1757,7 @@ namespace cw audioGroup_t* iag = nullptr; audioGroup_t* oag = nullptr; - +/* double israte = 0; double osrate = 0; double srate = 0; @@ -1685,7 +1765,7 @@ namespace cw unsigned iDspFrameCnt = 0; unsigned oDspFrameCnt = 0; unsigned dspFrameCnt = 0; - +*/ char* inGroupLabel = nullptr; char* outGroupLabel = nullptr; @@ -1738,12 +1818,18 @@ namespace cw rc = cwLogError(rc,"Unable to locate the audio hardware device:'%s'.", cwStringNullGuard(ad->devName)); goto errLabel; } - - // get the device channel counts - unsigned iChCnt = 0; //audio::device::channelCount(p->audioH,ad->devIdx,true); - unsigned oChCnt = 0; //audio::device::channelCount(p->audioH,ad->devIdx,false); + if( inGroupLabel != nullptr ) + iag = _audioGroupFromLabel(p, inGroupLabel ); + // get the outgroup + if( outGroupLabel != nullptr ) + oag = _audioGroupFromLabel(p, outGroupLabel); + + if((rc = _audioDeviceConfigure(p, ad, iag, oag, cycleCnt, framesPerCycle )) != kOkRC ) + goto errLabel; + + /* // get the ingroup if( inGroupLabel != nullptr ) if((iag = _audioGroupFromLabel(p, inGroupLabel )) != nullptr ) @@ -1760,7 +1846,7 @@ namespace cw oDspFrameCnt = oag->msg.dspFrameCnt; } - // in-srate an out-srate must be equal or one must be 0 + // in-srate and out-srate must be equal or one must be 0 if( osrate==0 || israte==0 || osrate==israte ) { // the true sample rate is the non-zero sample rate @@ -1792,8 +1878,8 @@ namespace cw } // get the device channel counts - iChCnt = audio::device::channelCount(p->audioH,ad->devIdx,true); - oChCnt = audio::device::channelCount(p->audioH,ad->devIdx,false); + unsigned iChCnt = audio::device::channelCount(p->audioH,ad->devIdx,true); + unsigned oChCnt = audio::device::channelCount(p->audioH,ad->devIdx,false); // initialize the audio bufer for this device @@ -1802,7 +1888,12 @@ namespace cw rc = cwLogError(rc,"Audio device buffer channel setup failed."); goto errLabel; } + */ + unsigned iChCnt = audio::device::channelCount(p->audioH,ad->devIdx,true); + unsigned oChCnt = audio::device::channelCount(p->audioH,ad->devIdx,false); + + // if an input group was assigned to this device then create a assoc'd audio_group_dev_t if( iag != nullptr ) { @@ -1832,6 +1923,8 @@ namespace cw ad->userId = userId; ad->iGroup = iag; ad->oGroup = oag; + ad->cycleCnt = cycleCnt; + ad->framesPerCycle = framesPerCycle; } } @@ -2114,10 +2207,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 +2534,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 +2543,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 +2587,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); } @@ -2655,6 +2761,11 @@ cw::rc_t cw::io::timerStop( handle_t h, unsigned timerIdx ) // // Serial // +bool cw::io::serialIsEnabled( handle_t h ) +{ + io_t* p = _handleToPtr(h); + return p->serialN != 0; +} unsigned cw::io::serialDeviceCount( handle_t h ) { @@ -2719,9 +2830,18 @@ errLabel: // MIDI // +bool cw::io::midiIsEnabled( handle_t h ) +{ + io_t* p = _handleToPtr(h); + return p->midiH.isValid(); +} + unsigned cw::io::midiDeviceCount( handle_t h ) { io_t* p = _handleToPtr(h); + if( !p->midiH.isValid() ) + return 0; + return midi::device::count(p->midiH); } @@ -2761,6 +2881,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 ); @@ -3158,6 +3296,64 @@ unsigned cw::io::audioGroupDspFrameCount( handle_t h, unsigned groupIdx ) return 0; } +cw::rc_t cw::io::audioGroupReconfigure( handle_t h, unsigned groupIdx, double srate, unsigned dspFrameN ) +{ + rc_t rc = kOkRC; + audioGroup_t* ag = nullptr; + + io_t* p = _handleToPtr(h); + + // locate the group record + if((ag = _audioGroupFromIndex( p, groupIdx )) == nullptr ) + goto errLabel; + + // if the parameters are not changing then there is nothing to do + if( ag->msg.dspFrameCnt == dspFrameN && ag->msg.srate == srate ) + goto errLabel; + + // change the parameters in the group record + ag->msg.dspFrameCnt = dspFrameN; + ag->msg.srate = srate; + + // stop the audio sub-system + if((rc = _audioDeviceStartStop(p,false)) != kOkRC ) + goto errLabel; + + // TODO: be sure the audio subsystem is really stopped + + // for each audio device + for(unsigned i=0; iaudioDevN; ++i) + { + audioDev_t* ad = p->audioDevA + i; + + // if this devices in-group/out-group was reconfigured + bool iGroupFl = ad->iGroup != nullptr && ad->iGroup->msg.groupIndex == groupIdx; + bool oGroupFl = ad->oGroup != nullptr && ad->oGroup->msg.groupIndex == groupIdx; + + if( iGroupFl || oGroupFl ) + { + // reconfigure the device with the updated srate and framesPerCycle + if((rc = _audioDeviceConfigure(p, ad, ad->oGroup, ad->oGroup, ad->cycleCnt, ad->framesPerCycle )) != kOkRC ) + goto errLabel; + + cwLogInfo("The audio device: '%s' was reconfigured srate=%f dspFrameCnt:%i.",cwStringNullGuard(ad->label), srate,dspFrameN); + + + } + } + + // restart the audio sub-system + if((rc = _audioDeviceStartStop(p,true)) != kOkRC ) + goto errLabel; + +errLabel: + if( rc != kOkRC ) + rc = cwLogError(rc,"Audio group reconfiguration failed."); + + return rc; +} + + unsigned cw::io::audioGroupDeviceCount( handle_t h, unsigned groupIdx, unsigned inOrOutFl ) { audioGroup_t* ag; @@ -3204,6 +3400,11 @@ unsigned cw::io::audioGroupDeviceIndex( handle_t h, unsigned groupIdx, unsigned // Socket // +bool cw::io::socketIsEnabled( handle_t h ) +{ + io_t* p = _handleToPtr(h); + return p->sockN != 0; +} unsigned cw::io::socketCount( handle_t h ) { @@ -3351,6 +3552,13 @@ cw::rc_t cw::io::socketSend( handle_t h, unsigned sockIdx, const void* data, // // UI // + +bool cw::io::uiIsEnabled( handle_t h ) +{ + io_t* p = _handleToPtr(h); + return p->wsUiH.isValid(); +} + unsigned cw::io::parentAndNameToAppId( handle_t h, unsigned parentAppId, const char* eleName ) { rc_t rc; @@ -3623,6 +3831,24 @@ cw::rc_t cw::io::uiCreateLog( handle_t h, unsigned& uuIdRef, unsigned pare return rc; } +cw::rc_t cw::io::uiCreateVList( handle_t h, unsigned& uuIdRef, unsigned parentUuId, const char* eleName, unsigned appId, unsigned chanId, const char* clas, const char* title ) +{ + rc_t rc; + ui::handle_t uiH; + if((rc = _handleToUiHandle(h,uiH)) == kOkRC ) + rc = ui::createVList(uiH,uuIdRef,parentUuId,eleName,appId,chanId,clas,title); + return rc; +} + +cw::rc_t cw::io::uiCreateHList( handle_t h, unsigned& uuIdRef, unsigned parentUuId, const char* eleName, unsigned appId, unsigned chanId, const char* clas, const char* title ) +{ + rc_t rc; + ui::handle_t uiH; + if((rc = _handleToUiHandle(h,uiH)) == kOkRC ) + rc = ui::createHList(uiH,uuIdRef,parentUuId,eleName,appId,chanId,clas,title); + return rc; +} + cw::rc_t cw::io::uiSetNumbRange( handle_t h, unsigned uuId, double minValue, double maxValue, double stepValue, unsigned decPl, double value ) { rc_t rc; @@ -3649,6 +3875,16 @@ cw::rc_t cw::io::uiSetLogLine( handle_t h, unsigned uuId, const char* text ) rc = ui::setLogLine(uiH,uuId,text); return rc; } + +cw::rc_t cw::io::uiEmptyParent( handle_t h, unsigned uuId) +{ + rc_t rc; + ui::handle_t uiH; + if((rc = _handleToUiHandle(h,uiH)) == kOkRC ) + rc = ui::emptyParent(uiH,uuId); + return rc; +} + cw::rc_t cw::io::uiSetClickable( handle_t h, unsigned uuId, bool clickableFl ) { @@ -3788,6 +4024,14 @@ cw::rc_t cw::io::uiSetScrollTop( handle_t h, unsigned uuId ) return rc; } +cw::rc_t cw::io::uiSetTitle( handle_t h, unsigned uuId, const char* title ) +{ + rc_t rc; + ui::handle_t uiH; + if((rc = _handleToUiHandle(h,uiH)) == kOkRC ) + rc = ui::setTitle(uiH,uuId,title); + return rc; +} cw::rc_t cw::io::uiSetBlob( handle_t h, unsigned uuId, const void* blob, unsigned blobByteN ) { @@ -3808,6 +4052,21 @@ const void* cw::io::uiGetBlob( handle_t h, unsigned uuId, unsigned& blobByteN_ return nullptr; } +cw::rc_t cw::io::uiGetBlob( handle_t h, unsigned uuId, void* buf, unsigned& bufByteN_Ref ) +{ + unsigned bN = 0; + const void* b = uiGetBlob(h,uuId,bN); + if( bN > bufByteN_Ref ) + { + bufByteN_Ref = 0; + return cwLogError(kBufTooSmallRC,"UI blob buffer is too small."); + } + memcpy(buf,b,bN); + bufByteN_Ref = bN; + return kOkRC; +} + + cw::rc_t cw::io::uiClearBlob( handle_t h, unsigned uuId ) { rc_t rc; @@ -3981,8 +4240,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..525579b 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 ); + //---------------------------------------------------------------------------------------------------------- // @@ -208,6 +211,7 @@ namespace cw // Serial // + bool serialIsEnabled( handle_t h ); unsigned serialDeviceCount( handle_t h ); unsigned serialDeviceIndex( handle_t h, const char* label ); const char* serialDeviceLabel( handle_t h, unsigned devIdx ); @@ -220,7 +224,8 @@ namespace cw // // MIDI // - + + bool midiIsEnabled( handle_t h ); unsigned midiDeviceCount( handle_t h ); const char* midiDeviceName( handle_t h, unsigned devIdx ); unsigned midiDeviceIndex( handle_t h, const char* devName ); @@ -228,6 +233,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 @@ -275,6 +285,7 @@ namespace cw rc_t audioGroupSetUserId( handle_t h, unsigned groupIdx, unsigned userId ); double audioGroupSampleRate( handle_t h, unsigned groupIdx ); unsigned audioGroupDspFrameCount( handle_t h, unsigned groupIdx ); + rc_t audioGroupReconfigure( handle_t h, unsigned groupIdx, double srate, unsigned dspFrameN ); // Get the count of in or out devices assigned to this group. unsigned audioGroupDeviceCount( handle_t h, unsigned groupIdx, unsigned inOrOutFl ); @@ -286,7 +297,8 @@ namespace cw // // Socket // - + + bool socketIsEnabled( handle_t h ); unsigned socketCount( handle_t h ); unsigned socketLabelToIndex( handle_t h, const char* label ); unsigned socketUserId( handle_t h, unsigned sockIdx ); @@ -320,7 +332,8 @@ namespace cw // UI // - + bool uiIsEnabled( handle_t h ); + // Find id's associated with elements. unsigned parentAndNameToAppId( handle_t h, unsigned parentAppId, const char* eleName ); unsigned parentAndNameToUuId( handle_t h, unsigned parentAppId, const char* eleName ); @@ -370,10 +383,16 @@ namespace cw rc_t uiCreateLog( handle_t h, unsigned& uuIdRef, unsigned parentUuId, const char* eleName, unsigned appId, unsigned chanId, const char* clas, const char* title ); + rc_t uiCreateVList( handle_t h, unsigned& uuIdRef, unsigned parentUuId, const char* eleName, unsigned appId, unsigned chanId, const char* clas, const char* title ); + rc_t uiCreateHList( handle_t h, unsigned& uuIdRef, unsigned parentUuId, const char* eleName, unsigned appId, unsigned chanId, const char* clas, const char* title ); + + + rc_t uiSetTitle( handle_t h, unsigned uuId, const char* title ); rc_t uiSetNumbRange( handle_t h, unsigned uuId, double minValue, double maxValue, double stepValue, unsigned decPl, double value ); rc_t uiSetProgRange( handle_t h, unsigned uuId, double minValue, double maxValue, double value ); - rc_t uiSetLogLine( handle_t h, unsigned uuId, const char* text ); + rc_t uiSetLogLine( handle_t h, unsigned uuId, const char* text ); + rc_t uiEmptyParent( handle_t h, unsigned uuId); // empty a list or selection menu of all children rc_t uiSetClickable( handle_t h, unsigned uuId, bool clickableFl=true ); rc_t uiClearClickable( handle_t h, unsigned uuId ); @@ -395,9 +414,14 @@ namespace cw int uiGetOrderKey( handle_t h, unsigned uuId ); rc_t uiSetScrollTop( handle_t h, unsigned uuId ); - + + // uiSetBlob() allocates internal memory and copies the contents of blob[blobByeN] rc_t uiSetBlob( handle_t h, unsigned uuId, const void* blob, unsigned blobByteN ); const void* uiGetBlob( handle_t h, unsigned uuId, unsigned& blobByteN_Ref ); + + // On call bufByteN_Ref holds the size of buf in bytes, on return it is set to the count of bytes in buf[]. + // If buf[] is not large enough to hold all bytes kBufTooSmallRC is returned. + rc_t uiGetBlob( handle_t h, unsigned uuId, void* buf, unsigned& bufByteN_Ref ); rc_t uiClearBlob( handle_t h, unsigned uuId ); // Register parent/child/name app id's 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/cwIoFlowCtl.cpp b/cwIoFlowCtl.cpp new file mode 100644 index 0000000..f626e8f --- /dev/null +++ b/cwIoFlowCtl.cpp @@ -0,0 +1,929 @@ +#include "cwCommon.h" +#include "cwLog.h" +#include "cwCommonImpl.h" +#include "cwTest.h" +#include "cwMem.h" +#include "cwText.h" +#include "cwObject.h" +#include "cwFileSys.h" +#include "cwFile.h" +#include "cwTime.h" +#include "cwVectOps.h" +#include "cwMtx.h" +#include "cwTime.h" +#include "cwMidiDecls.h" + +#include "cwTime.h" +#include "cwMidiDecls.h" + +#include "cwDspTypes.h" +#include "cwFlowDecl.h" +#include "cwFlow.h" +#include "cwFlowTypes.h" + +#include "cwIo.h" + +#include "cwIoFlowCtl.h" + + +namespace cw +{ + namespace io_flow_ctl + { + + // An audio_dev_t record exists for each possible input or output device. + typedef struct audio_dev_str + { + unsigned ioDevIdx; // device index in the io:: API + unsigned ioDevId; // device id in the io:: API + flow::abuf_t abuf; // src/dst buffer for incoming/outgoing (record/play) samples used by flow proc 'audio_in' and 'audio_out'. + } audio_dev_t; + + typedef struct audio_group_str + { + double srate; + unsigned dspFrameCnt; + unsigned ioGroupIdx; + + audio_dev_t* iDeviceA; + unsigned iDeviceN; + + audio_dev_t* oDeviceA; + unsigned oDeviceN; + + } audio_group_t; + + typedef struct pgm_str + { + const char* label; + const object_t* cfg; + } pgm_t; + + typedef struct io_flow_ctl_str + { + const char* base_dir; + object_t* proc_class_dict_cfg; + object_t* udp_dict_cfg; + + io::handle_t ioH; + + flow::external_device_t* deviceA; // Array of generic device descriptions used by the ioFlow controller + unsigned deviceN; // (This array must exist for the life of ioFlow controller) + + audio_group_t* audioGroupA; // Array of real time audio device control records. + unsigned audioGroupN; // + + pgm_t* pgmA; // pgmA[ pgmN ] + unsigned pgmN; + + unsigned pgm_idx; // current program index + flow::handle_t flowH; // + char* proj_dir; // current project directory + + bool init_fl; + bool done_fl; + } io_flow_ctl_t; + + io_flow_ctl_t* _handleToPtr( handle_t h ) + { return handleToPtr(h); } + + void _destroy_device_setup( io_flow_ctl_t* p ) + { + mem::release(p->deviceA); + p->deviceN = 0; + + for(unsigned gi=0; giaudioGroupN; ++gi) + { + audio_group_t* ag = p->audioGroupA + gi; + for(unsigned di=0; diiDeviceN; ++di) + mem::release( ag->iDeviceA[di].abuf.buf ); + + for(unsigned di=0; dioDeviceN; ++di) + mem::release( ag->oDeviceA[di].abuf.buf ); + + mem::release( ag->iDeviceA); + mem::release( ag->oDeviceA); + } + + mem::release(p->audioGroupA); + p->audioGroupN = 0; + } + + rc_t _program_unload( io_flow_ctl_t* p ) + { + rc_t rc; + if((rc = destroy(p->flowH)) != kOkRC ) + { + rc = cwLogError(rc,"Program unload failed."); + goto errLabel; + } + + _destroy_device_setup(p); + + mem::release(p->proj_dir); + p->pgm_idx = kInvalidIdx; + p->done_fl = true; + p->init_fl = false; + + errLabel: + return rc; + } + + + rc_t _destroy( io_flow_ctl_t* p ) + { + rc_t rc = kOkRC; + + destroy( p->flowH ); + + + if( p->proc_class_dict_cfg != nullptr ) + p->proc_class_dict_cfg->free(); + + if( p->udp_dict_cfg != nullptr ) + p->udp_dict_cfg->free(); + + _program_unload(p); + p->pgmN = 0; + mem::release(p->pgmA); + mem::release(p); + return rc; + } + + rc_t _validate_pgm_idx( io_flow_ctl_t* p, unsigned pgm_idx ) + { + rc_t rc = kOkRC; + + if( pgm_idx == kInvalidIdx || pgm_idx >= p->pgmN ) + { + rc = cwLogError(kInvalidArgRC,"The program index '%i' is invalid. Program count=%i.",pgm_idx,p->pgmN); + goto errLabel; + } + + errLabel: + return rc; + } + + rc_t _parse_cfg( io_flow_ctl_t* p, const object_t* cfg ) + { + rc_t rc = kOkRC; + const char* proc_cfg_fname = nullptr; + const char* udp_cfg_fname = nullptr; + const char* io_cfg_fname = nullptr; + const object_t* pgmL = nullptr; + + // parse the cfg parameters + if((rc = cfg->readv("base_dir", kReqFl, p->base_dir, + "proc_dict", kReqFl, proc_cfg_fname, + "udp_dict", kReqFl, udp_cfg_fname, + "io_dict", kOptFl, io_cfg_fname, + "programs", kDictTId, pgmL)) != kOkRC ) + { + rc = cwLogError(rc,"'caw' system parameter processing failed."); + goto errLabel; + } + + // parse the proc dict. file + if((rc = objectFromFile(proc_cfg_fname,p->proc_class_dict_cfg)) != kOkRC ) + { + rc = cwLogError(rc,"The flow proc dictionary could not be read from '%s'.",cwStringNullGuard(proc_cfg_fname)); + goto errLabel; + } + + // parse the udp dict file + if((rc = objectFromFile(udp_cfg_fname,p->udp_dict_cfg)) != kOkRC ) + { + rc = cwLogError(rc,"The flow user-defined-proc dictionary could not be read from '%s'.",cwStringNullGuard(udp_cfg_fname)); + goto errLabel; + } + + p->pgmN = pgmL->child_count(); + p->pgmA = mem::allocZ(p->pgmN); + + // find the parameters for the requested program + for(unsigned i=0; ipgmN; i++) + { + const object_t* pgm = pgmL->child_ele(i); + + if( pgm->pair_label()==nullptr || pgm->pair_value()==nullptr || !pgm->pair_value()->is_dict() ) + { + rc = cwLogError(kSyntaxErrorRC,"The program at index %i has a syntax error.",i); + goto errLabel; + } + + p->pgmA[i].label = pgm->pair_label(); + p->pgmA[i].cfg = pgm->pair_value(); + + + } + + errLabel: + return rc; + } + + unsigned _calc_device_count(io_flow_ctl_t* p) + { + unsigned devN = 0; + + 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; + + return devN; + } + + void _setup_audio_device( io_flow_ctl_t* p,audio_dev_t* dev, unsigned inOrOutFl, unsigned ioDevIdx, unsigned dspFrameCnt ) + { + dev->ioDevIdx = ioDevIdx; + dev->ioDevId = audioDeviceUserId( p->ioH, ioDevIdx ); + dev->abuf.srate = audioDeviceSampleRate( p->ioH, ioDevIdx ); + dev->abuf.chN = audioDeviceChannelCount( p->ioH, ioDevIdx, inOrOutFl ); + dev->abuf.frameN = dspFrameCnt; + dev->abuf.buf = mem::allocZ< flow::sample_t >( dev->abuf.chN * dev->abuf.frameN ); + + //printf("%i %s\n", dev->abuf.chN, audioDeviceLabel( p->ioH, ioDevIdx ) ); + + } + + rc_t _setup_audio_groups( io_flow_ctl_t* p, double srate, unsigned dspFrameN ) + { + rc_t rc = kOkRC; + p->audioGroupN = audioGroupCount( p->ioH ); + p->audioGroupA = mem::allocZ( p->audioGroupN ); + + for(unsigned gi=0; giioH); ++gi) + { + audio_group_t* ag = p->audioGroupA + gi; + + if((rc = audioGroupReconfigure(p->ioH, gi, srate, dspFrameN )) != kOkRC ) + { + rc = cwLogError(rc,"Audio group reconfiguration to srate=%f dspFrameN:%i failed.",srate,dspFrameN); + goto errLabel; + } + + + ag->srate = audioGroupSampleRate( p->ioH, gi ); + ag->dspFrameCnt = audioGroupDspFrameCount( p->ioH, gi ); + ag->ioGroupIdx = gi; + + ag->iDeviceN = audioGroupDeviceCount( p->ioH, gi, io::kInFl ); + ag->iDeviceA = mem::allocZ< audio_dev_t >( ag->iDeviceN ); + + for(unsigned gdi=0; gdiiDeviceN; ++gdi) + _setup_audio_device( p, ag->iDeviceA + gdi, io::kInFl, audioGroupDeviceIndex( p->ioH, gi, io::kInFl, gdi), ag->dspFrameCnt ); + + ag->oDeviceN = audioGroupDeviceCount( p->ioH, gi, io::kOutFl ); + ag->oDeviceA = mem::allocZ< audio_dev_t >( ag->oDeviceN ); + + for(unsigned gdi=0; gdioDeviceN; ++gdi) + _setup_audio_device( p, ag->oDeviceA + gdi, io::kOutFl, audioGroupDeviceIndex( p->ioH, gi, io::kOutFl, gdi), ag->dspFrameCnt ); + + } + + errLabel: + return rc; + } + + rc_t _send_midi_triple( flow::external_device_t* dev, uint8_t ch, uint8_t status, uint8_t d0, uint8_t d1 ) + { + return midiDeviceSend(((io_flow_ctl_t*)dev->reserved)->ioH, dev->ioDevIdx, dev->ioPortIdx, status |= ch, d0, d1); + } + + void _setup_device_cfg( io_flow_ctl_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_ctl_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_ctl_t* p, flow::external_device_t* d, audio_group_t* ag, audio_dev_t* ad, unsigned 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_ctl_t* p ) + { + unsigned i = 0; + + // allocate the generic device control records + p->deviceN = _calc_device_count(p); + p->deviceA = mem::allocZ( p->deviceN ); + + + // get serial devices + for(unsigned di=0; ideviceN && diioH); ++di,++i) + _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, 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) + { + audio_group_t* ag = p->audioGroupA + gi; + + for(unsigned di=0; ideviceN && diiDeviceN; ++di,++i) + _setup_audio_device_cfg( p, p->deviceA + i, ag, ag->iDeviceA + di, flow::kInFl ); + + for(unsigned di=0; ideviceN && dioDeviceN; ++di,++i) + _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_ctl_t* p, unsigned ioGroupIdx, unsigned ioDevIdx, unsigned inOrOutFl, flow::abuf_t*& abuf_ref ) + { + + rc_t rc = kOkRC; + + for(unsigned gi=0; giaudioGroupN; ++gi) + if( p->audioGroupA[gi].ioGroupIdx == ioGroupIdx ) + { + audio_dev_t* adA = inOrOutFl == flow::kInFl ? p->audioGroupA[gi].iDeviceA : p->audioGroupA[gi].oDeviceA; + unsigned adN = inOrOutFl == flow::kInFl ? p->audioGroupA[gi].iDeviceN : p->audioGroupA[gi].oDeviceN; + + for(unsigned di=0; dibuf + (i*dst_abuf->frameN); + memcpy(dst,src,dst_abuf->frameN*sizeof(flow::sample_t)); + } + } + + void _zero_output_buffer( flow::abuf_t* dst_abuf ) + { + memset(dst_abuf->buf,0, dst_abuf->chN*dst_abuf->frameN*sizeof(flow::sample_t)); + } + + void _fill_output_buffer( const flow::abuf_t* src_abuf, flow::sample_t** bufChArray, unsigned bufChArrayN ) + { + for(unsigned i=0; ichN; ++i) + { + const flow::sample_t* src = src_abuf->buf + (i*src_abuf->frameN); + flow::sample_t* dst = bufChArray[i]; + memcpy(dst,src,src_abuf->frameN*sizeof(flow::sample_t)); + } + } + + rc_t _audio_callback( io_flow_ctl_t* p, io::audio_msg_t& m ) + { + 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); + + if( p->done_fl ) + { + rc = cwLogError(kInvalidStateRC,"Cannot execute an already completed program."); + goto errLabel; + } + + + // 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 ) + { + unsigned chIdx = 0; + + // for each input device in this group + for(io::audio_group_dev_t* agd = m.iDevL; agd!=nullptr; agd=agd->link) + { + // get the abuf associated with each device in this group + if((rc = _device_index_to_abuf( p, m.groupIndex, agd->devIdx, flow::kInFl, abuf )) != kOkRC ) + goto errLabel; + + // fill the input audio buf from the the external audio device + _fill_input_buffer( m.iBufArray + chIdx, agd->chCnt, abuf ); + + chIdx += agd->chCnt; + } + + } + + // if there are empty output (playback) buffers + if( m.oBufChCnt > 0 ) + { + + // for each output device in this group + for(io::audio_group_dev_t* agd=m.oDevL; agd!=nullptr; agd=agd->link) + { + // get the output audio buf associated with this external audio device + if((rc = _device_index_to_abuf( p, m.groupIndex, agd->devIdx, flow::kOutFl, abuf )) != kOkRC ) + goto errLabel; + + // zerot the output buffer + _zero_output_buffer( abuf ); + } + } + + + // update the flow network - this will generate audio into the output audio buffers + if((rc = flow::exec_cycle(p->flowH)) != kOkRC ) + { + if( rc == kEofRC ) + { + p->done_fl = true; + p->init_fl = false; + rc = kOkRC; + } + } + + + // if there are empty output (playback) buffers + if( m.oBufChCnt > 0 ) + { + + unsigned chIdx = 0; + + // for each output device in this group + for(io::audio_group_dev_t* agd=m.oDevL; agd!=nullptr; agd=agd->link) + { + // get the output audio buf associated with this external audio device + if((rc = _device_index_to_abuf( p, m.groupIndex, agd->devIdx, flow::kOutFl, abuf )) != kOkRC ) + goto errLabel; + + // copy the samples from the flow 'audio_out' buffers to the outgoing buffer passed from the device driver + _fill_output_buffer( abuf, m.oBufArray + chIdx, agd->chCnt ); + + chIdx += agd->chCnt; + + } + + } + + errLabel: + // Drop the MIDI messages that were processed on this call. + midiDeviceClearBuffer(p->ioH,midiBufMsgCnt); + + return rc; + } + + template< typename T > + rc_t _ui_callback_tpl( io_flow_ctl_t* p, const flow::ui_var_t* ui_var ) + { + rc_t rc; + + T value; + if((rc = get_variable_value(p->flowH,ui_var,value)) != kOkRC ) + { + rc = cwLogError(rc,"The variable value could not be read."); + goto errLabel; + } + + if((rc = uiSendValue(p->ioH,ui_var->user_id,value)) != kOkRC ) + { + rc = cwLogError(rc,"UI element data transmission failed."); + goto errLabel; + } + + errLabel: + return rc; + } + + // This function is called with messages for the UI from the flow proc instances + rc_t _ui_callback( void* arg, const flow::ui_var_t* ui_var ) + { + rc_t rc = kOkRC; + + io_flow_ctl_t* p = (io_flow_ctl_t*)arg; + + if( ui_var->user_id == kInvalidId ) + { + rc = cwLogError(kInvalidArgRC,"The user_id (uuid) of the variable was not set."); + goto errLabel; + } + + switch( ui_var->value_tid & flow::kTypeMask ) + { + case flow::kBoolTFl: + rc = _ui_callback_tpl(p,ui_var); + break; + + case flow::kIntTFl: + rc = _ui_callback_tpl(p,ui_var); + break; + + case flow::kUIntTFl: + rc = _ui_callback_tpl(p,ui_var); + break; + + case flow::kFloatTFl: + rc = _ui_callback_tpl(p,ui_var); + break; + + case flow::kDoubleTFl: + rc = _ui_callback_tpl(p,ui_var); + break; + + case flow::kStringTFl: + rc = _ui_callback_tpl(p,ui_var); + break; + + } + + errLabel: + if( rc != kOkRC ) + rc = cwLogError(rc,"Update of UI element of '%s:%i-%s:%i' failed.",cwStringNullGuard(ui_var->ui_proc->label),ui_var->ui_proc->label_sfx_id,cwStringNullGuard(ui_var->label),ui_var->label_sfx_id); + + return rc; + + } + + } +} + + +cw::rc_t cw::io_flow_ctl::create( handle_t& hRef, io::handle_t ioH, const object_t* flow_cfg ) +{ + rc_t rc = kOkRC; + + if((rc = destroy(hRef)) != kOkRC ) + return rc; + + io_flow_ctl_t* p = mem::allocZ(); + p->pgm_idx = kInvalidIdx; + p->ioH = ioH; + + if((rc = _parse_cfg(p,flow_cfg)) != kOkRC ) + goto errLabel; + + hRef.set(p); + +errLabel: + if( rc != kOkRC ) + { + rc = cwLogError(rc,"io_flow create failed."); + _destroy(p); + } + return rc; +} + +cw::rc_t cw::io_flow_ctl::destroy( handle_t& hRef ) +{ + rc_t rc = kOkRC; + io_flow_ctl_t* p = nullptr; + + if(!hRef.isValid()) + return rc; + + p = _handleToPtr(hRef); + + if((rc = _destroy(p)) != kOkRC ) + goto errLabel; + + + hRef.clear(); + +errLabel: + return rc; +} + +unsigned cw::io_flow_ctl::program_count(handle_t h) +{ + io_flow_ctl_t* p = _handleToPtr(h); + return p->pgmN; +} + +const char* cw::io_flow_ctl::program_title( handle_t h, unsigned pgm_idx ) +{ + + io_flow_ctl_t* p = _handleToPtr(h); + const char* pgm_title = nullptr; + + if(_validate_pgm_idx(p,pgm_idx) != kOkRC ) + goto errLabel; + + pgm_title = p->pgmA[pgm_idx].label; + +errLabel: + return pgm_title; +} + + +unsigned cw::io_flow_ctl::program_index( handle_t h, const char* pgm_title ) +{ + io_flow_ctl_t* p = _handleToPtr(h); + for(unsigned i=0; ipgmN; ++i) + if( textIsEqual(pgm_title,p->pgmA[i].label) ) + return i; + + return kInvalidIdx; +} + +cw::rc_t cw::io_flow_ctl::program_load( handle_t h, unsigned pgm_idx ) +{ + rc_t rc = kOkRC; + io_flow_ctl_t* p = _handleToPtr(h); + + if((rc = _validate_pgm_idx(p,pgm_idx)) != kOkRC ) + goto errLabel; + + if((rc = _program_unload(p)) != kOkRC ) + goto errLabel; + + // form the program project directory + if((p->proj_dir = filesys::makeFn(p->base_dir,nullptr,nullptr,p->pgmA[pgm_idx].label,nullptr)) == nullptr ) + { + rc = cwLogError(kOpFailRC,"The project directory formation failed."); + goto errLabel; + } + + // create the project directory if it doesn't already exist + if( !filesys::isDir(p->proj_dir) ) + if((rc = filesys::makeDir(p->proj_dir)) != kOkRC ) + goto errLabel; + + // configure the flow network + if((rc = create( p->flowH, + p->proc_class_dict_cfg, + p->pgmA[ pgm_idx ].cfg, + p->udp_dict_cfg, + p->proj_dir, + _ui_callback, + p)) != kOkRC ) + { + rc = cwLogError(rc,"Network configuration failed."); + goto errLabel; + } + + // allocate p->audioGroupA[] and create the audio input/output buffers associated with each audio device + _setup_audio_groups(p, sample_rate(p->flowH), frames_per_cycle(p->flowH) ); + + // setup the control record for each external device known to the IO interface + _setup_generic_device_array(p); + + p->pgm_idx = pgm_idx; + p->done_fl = false; + p->init_fl = false; + +errLabel: + return rc; +} + +unsigned cw::io_flow_ctl::program_current_index( handle_t h ) +{ + io_flow_ctl_t* p = _handleToPtr(h); + return p->pgm_idx; +} + +bool cw::io_flow_ctl::is_program_nrt( handle_t h ) +{ + io_flow_ctl_t* p = _handleToPtr(h); + bool nrt_fl = false; + + if( !p->flowH.isValid() ) + { + cwLogWarning("No program is loaded."); + goto errLabel; + } + + if(_validate_pgm_idx(p,p->pgm_idx) != kOkRC ) + goto errLabel; + + nrt_fl = is_non_real_time(p->flowH); + +errLabel: + return nrt_fl; +} + +unsigned cw::io_flow_ctl::program_preset_count( handle_t h ) +{ + io_flow_ctl_t* p = _handleToPtr(h); + + if(!p->flowH.isValid() || _validate_pgm_idx(p,p->pgm_idx) != kOkRC ) + return 0; + + return preset_count(p->flowH); +} + +const char* cw::io_flow_ctl::program_preset_title( handle_t h, unsigned preset_idx ) +{ + io_flow_ctl_t* p = _handleToPtr(h); + + if(!p->flowH.isValid() || _validate_pgm_idx(p,p->pgm_idx) != kOkRC ) + return nullptr; + return preset_label(p->flowH,preset_idx); +} + +cw::rc_t cw::io_flow_ctl::program_initialize( handle_t h, unsigned preset_idx ) +{ + rc_t rc = kOkRC; + io_flow_ctl_t* p = _handleToPtr(h); + + if( p->pgm_idx == kInvalidIdx || p->init_fl || p->done_fl || !p->flowH.isValid() ) + { + cwLogError(kInvalidStateRC,"A valid pre-initialized program is not loaded."); + goto errLabel; + } + + // create the flow network + if((rc = initialize( p->flowH, + p->deviceA, + p->deviceN, + preset_idx )) != kOkRC ) + { + rc = cwLogError(rc,"Network create failed."); + goto errLabel; + } + + p->init_fl = true; +errLabel: + return rc; +} + +bool cw::io_flow_ctl::program_is_initialized( handle_t h ) +{ + io_flow_ctl_t* p = _handleToPtr(h); + return p->init_fl; +} + +const cw::flow::ui_net_t* cw::io_flow_ctl::program_ui_net( handle_t h ) +{ + io_flow_ctl_t* p = _handleToPtr(h); + return ui_net(p->flowH); +} + + +cw::rc_t cw::io_flow_ctl::exec_nrt( handle_t h ) +{ + rc_t rc = kOkRC; + io_flow_ctl_t* p = _handleToPtr(h); + + if( p->pgm_idx == kInvalidIdx ) + { + rc = cwLogWarning("No program is loaded."); + goto errLabel; + } + + if( p->done_fl ) + { + rc = cwLogError(kInvalidStateRC,"Cannot execute an already completed program."); + goto errLabel; + } + + if((rc = exec( p->flowH )) != kOkRC ) + { + if(rc == kEofRC ) + { + p->done_fl = true; + rc = kOkRC; + } + else + { + rc = cwLogError(rc,"%s execution failed.", cwStringNullGuard(p->pgmA[p->pgm_idx].label)); + goto errLabel; + } + } + +errLabel: + return rc; +} + +cw::rc_t cw::io_flow_ctl::exec( handle_t h, const io::msg_t& msg ) +{ + rc_t rc = kOkRC; + io_flow_ctl_t* p = _handleToPtr(h); + + switch( msg.tid ) + { + case io::kAudioTId: + if( msg.u.audio != nullptr ) + rc = _audio_callback(p,*msg.u.audio); + break; + + default: + rc = kOkRC; + + } + + return rc; +} + +bool cw::io_flow_ctl::is_executable( handle_t h ) +{ + io_flow_ctl_t* p = _handleToPtr(h); + + // A program must be loaded, initialized and execution cannot be complete + return p->pgm_idx != kInvalidIdx && p->init_fl && p->done_fl==false; +} + +bool cw::io_flow_ctl::is_exec_complete( handle_t h ) +{ + io_flow_ctl_t* p = _handleToPtr(h); + + return p->done_fl; +} + + +cw::rc_t cw::io_flow_ctl::get_variable_value( handle_t h, const flow::ui_var_t* ui_var, bool& value_ref ) +{ return get_variable_value( _handleToPtr(h)->flowH, ui_var, value_ref ); } +cw::rc_t cw::io_flow_ctl::get_variable_value( handle_t h, const flow::ui_var_t* ui_var, int& value_ref ) +{ return get_variable_value( _handleToPtr(h)->flowH, ui_var, value_ref ); } +cw::rc_t cw::io_flow_ctl::get_variable_value( handle_t h, const flow::ui_var_t* ui_var, unsigned& value_ref ) +{ return get_variable_value( _handleToPtr(h)->flowH, ui_var, value_ref ); } +cw::rc_t cw::io_flow_ctl::get_variable_value( handle_t h, const flow::ui_var_t* ui_var, float& value_ref ) +{ return get_variable_value( _handleToPtr(h)->flowH, ui_var, value_ref ); } +cw::rc_t cw::io_flow_ctl::get_variable_value( handle_t h, const flow::ui_var_t* ui_var, double& value_ref ) +{ return get_variable_value( _handleToPtr(h)->flowH, ui_var, value_ref ); } +cw::rc_t cw::io_flow_ctl::get_variable_value( handle_t h, const flow::ui_var_t* ui_var, const char*& value_ref ) +{ return get_variable_value( _handleToPtr(h)->flowH, ui_var, value_ref ); } + +cw::rc_t cw::io_flow_ctl::set_variable_user_id( handle_t h, const flow::ui_var_t* ui_var, unsigned user_id ) +{ return set_variable_user_id( _handleToPtr(h)->flowH, ui_var, user_id ); } + +cw::rc_t cw::io_flow_ctl::set_variable_value( handle_t h, const flow::ui_var_t* ui_var, bool value ) +{ return set_variable_value( _handleToPtr(h)->flowH, ui_var, value ); } +cw::rc_t cw::io_flow_ctl::set_variable_value( handle_t h, const flow::ui_var_t* ui_var, int value ) +{ return set_variable_value( _handleToPtr(h)->flowH, ui_var, value ); } +cw::rc_t cw::io_flow_ctl::set_variable_value( handle_t h, const flow::ui_var_t* ui_var, unsigned value ) +{ return set_variable_value( _handleToPtr(h)->flowH, ui_var, value ); } +cw::rc_t cw::io_flow_ctl::set_variable_value( handle_t h, const flow::ui_var_t* ui_var, float value ) +{ return set_variable_value( _handleToPtr(h)->flowH, ui_var, value ); } +cw::rc_t cw::io_flow_ctl::set_variable_value( handle_t h, const flow::ui_var_t* ui_var, double value ) +{ return set_variable_value( _handleToPtr(h)->flowH, ui_var, value ); } +cw::rc_t cw::io_flow_ctl::set_variable_value( handle_t h, const flow::ui_var_t* ui_var, const char* value ) +{ return set_variable_value( _handleToPtr(h)->flowH, ui_var, value ); } + + +void cw::io_flow_ctl::report( handle_t h ) +{ + io_flow_ctl_t* p = _handleToPtr(h); + if( p->flowH.isValid() ) + print_network(p->flowH); +} + +void cw::io_flow_ctl::print_network( handle_t h ) +{ + io_flow_ctl_t* p = _handleToPtr(h); + if( p->flowH.isValid() ) + print_network(p->flowH); +} + + + + diff --git a/cwIoFlowCtl.h b/cwIoFlowCtl.h new file mode 100644 index 0000000..40ccf3e --- /dev/null +++ b/cwIoFlowCtl.h @@ -0,0 +1,87 @@ +#ifndef cwIoFlowCtl_H +#define cwIoFlowCtl_H + +namespace cw +{ + namespace io_flow_ctl + { + + typedef handle< struct io_flow_ctl_str > handle_t; + + rc_t create( handle_t& hRef, io::handle_t ioH, const char* flow_cfg_fn ); + rc_t create( handle_t& hRef, io::handle_t ioH, const object_t* flow_cfg ); + rc_t destroy( handle_t& hRef ); + + // Query the available programs from the 'flow_cfg' file. + unsigned program_count( handle_t h); + const char* program_title( handle_t h, unsigned pgm_idx ); + unsigned program_index( handle_t h, const char* pgm_title); + + // Create the parse the program but do not instantiate the network. + rc_t program_load( handle_t h, unsigned pgm_idx ); + + // Return the index of the currently loaded program or kInvalidIdx if no program is loaded. + unsigned program_current_index( handle_t h ); + + // Is the currently loaded program in non-real-time mode + bool is_program_nrt( handle_t h ); + + // Return the count of network presets and the associated labels for the currently loaded program. + unsigned program_preset_count( handle_t h ); + const char* program_preset_title( handle_t h, unsigned preset_idx ); + + // Create the network and prepare to enter runtime. + rc_t program_initialize( handle_t h, unsigned preset_idx=kInvalidIdx ); + bool program_is_initialized( handle_t h ); + + // Get the UI description data structures for the current program. + const flow::ui_net_t* program_ui_net( handle_t h ); + + // Execute the currently loaded non-real-time program to completion. + rc_t exec_nrt( handle_t h ); + + // Handle an incoming IO msg. This is the main point of entry for executing + // real-time programs. + rc_t exec( handle_t h, const io::msg_t& msg ); + + // Is the current program loaded, initialized and not yet complete. + bool is_executable( handle_t h ); + + // The current program has completed. + bool is_exec_complete( handle_t h ); + + rc_t get_variable_value( handle_t h, const flow::ui_var_t* ui_var, bool& value_ref ); + rc_t get_variable_value( handle_t h, const flow::ui_var_t* ui_var, int& value_ref ); + rc_t get_variable_value( handle_t h, const flow::ui_var_t* ui_var, unsigned& value_ref ); + rc_t get_variable_value( handle_t h, const flow::ui_var_t* ui_var, float& value_ref ); + rc_t get_variable_value( handle_t h, const flow::ui_var_t* ui_var, double& value_ref ); + rc_t get_variable_value( handle_t h, const flow::ui_var_t* ui_var, const char*& value_ref ); + + template< typename T > + rc_t get_variable( handle_t h, const flow::ui_var_t* ui_var, T& value_ref ) + { return get_variable_value(h,ui_var,value_ref); } + + rc_t set_variable_user_id( handle_t h, const flow::ui_var_t* ui_var, unsigned user_id ); + + rc_t set_variable_value( handle_t h, const flow::ui_var_t* ui_var, bool value ); + rc_t set_variable_value( handle_t h, const flow::ui_var_t* ui_var, int value ); + rc_t set_variable_value( handle_t h, const flow::ui_var_t* ui_var, unsigned value ); + rc_t set_variable_value( handle_t h, const flow::ui_var_t* ui_var, float value ); + rc_t set_variable_value( handle_t h, const flow::ui_var_t* ui_var, double value ); + rc_t set_variable_value( handle_t h, const flow::ui_var_t* ui_var, const char* value ); + + template< typename T > + rc_t set_variable( handle_t h, const flow::ui_var_t* ui_var, T value ) + { return set_variable_value(h,ui_var,value); } + + void report( handle_t h ); + void print_network( handle_t h ); + + + } + +} + + + +#endif 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/cwMidi.h b/cwMidi.h index 62f1f9c..adbe3ec 100644 --- a/cwMidi.h +++ b/cwMidi.h @@ -161,6 +161,10 @@ namespace cw // of this range will be returned as kInvalidMidiPitch. uint8_t sciPitchToMidi( const char* sciPitchStr ); + +#define midi_to_hz( midi_pitch ) ((13.75 * std::pow(2,(-9.0/12.0))) * std::pow(2.0,(midi_pitch / 12.0))) + + } } diff --git a/cwMidiAlsa.cpp b/cwMidiAlsa.cpp index cc969a7..321a358 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" @@ -53,7 +54,8 @@ namespace cw { unsigned devCnt; // MIDI devices attached to this computer dev_t* devArray; - cbFunc_t cbFunc; // MIDI input application callback + cbFunc_t cbFunc; // MIDI input application callback + bool filterRtSenseFl; void* cbDataPtr; snd_seq_t* h; // ALSA system sequencer handle snd_seq_addr_t alsa_addr; // ALSA client/port address representing the application @@ -71,6 +73,7 @@ namespace cw bool latency_meas_enable_in_fl; bool latency_meas_enable_out_fl; latency_meas_result_t latency_meas_result; + } alsa_device_t; @@ -253,7 +256,7 @@ namespace cw case SND_SEQ_EVENT_START: status = kSysRtStartMdId; break; case SND_SEQ_EVENT_CONTINUE: status = kSysRtContMdId; break; case SND_SEQ_EVENT_STOP: status = kSysRtStopMdId; break; - case SND_SEQ_EVENT_SENSING: status = kSysRtSenseMdId; break; + case SND_SEQ_EVENT_SENSING: status = p->filterRtSenseFl ? 0 : kSysRtSenseMdId; break; case SND_SEQ_EVENT_RESET: status = kSysRtResetMdId; break; case SND_SEQ_EVENT_SYSEX: @@ -283,6 +286,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 +443,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 +465,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 +479,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: @@ -592,7 +601,12 @@ namespace cw } // cw -cw::rc_t cw::midi::device::alsa::create( handle_t& h, cbFunc_t cbFunc, void* cbArg, unsigned parserBufByteCnt, const char* appNameStr ) +cw::rc_t cw::midi::device::alsa::create( handle_t& h, + cbFunc_t cbFunc, + void* cbArg, + unsigned parserBufByteCnt, + const char* appNameStr, + bool filterRtSenseFl ) { rc_t rc = kOkRC; int arc = 0; @@ -624,8 +638,9 @@ cw::rc_t cw::midi::device::alsa::create( handle_t& h, cbFunc_t cbFunc, void* cb p->alsa_fd = mem::allocZ(p->alsa_fdCnt); snd_seq_poll_descriptors(p->h, p->alsa_fd, p->alsa_fdCnt, POLLIN); - p->cbFunc = cbFunc; - p->cbDataPtr = cbArg; + p->cbFunc = cbFunc; + p->cbDataPtr = cbArg; + p->filterRtSenseFl = filterRtSenseFl; // start the sequencer queue if((arc = snd_seq_start_queue(p->h, p->alsa_queue, NULL)) < 0 ) @@ -736,7 +751,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 +797,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 +811,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 +872,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/cwMidiAlsa.h b/cwMidiAlsa.h index 4c74913..fd6f9f6 100644 --- a/cwMidiAlsa.h +++ b/cwMidiAlsa.h @@ -9,7 +9,13 @@ namespace cw typedef handle< struct alsa_device_str> handle_t; - rc_t create( handle_t& h, cbFunc_t cbFunc, void* cbDataPtr, unsigned parserBufByteCnt, const char* appNameStr ); + rc_t create( handle_t& h, + cbFunc_t cbFunc, + void* cbDataPtr, + unsigned parserBufByteCnt, + const char* appNameStr, + bool filterRtSenseFl ); + rc_t destroy( handle_t& h); bool isInitialized( handle_t h ); 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..ef484d9 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,13 @@ 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; + + bool filterRtSenseFl; } device_t; @@ -101,9 +109,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 +184,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 +238,10 @@ 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, + bool filterRtSenseFl ) { rc_t rc = kOkRC; rc_t rc1 = kOkRC; @@ -198,7 +251,12 @@ 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, + filterRtSenseFl)) != kOkRC ) { rc = cwLogError(rc,"ALSA MIDI device create failed."); goto errLabel; @@ -206,16 +264,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,14 +328,20 @@ 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; + bool filterRtSenseFl = true;; if((rc = args->getv("appNameStr",appNameStr, "fileDevName",fileDevName, "fileDevReadAheadMicros",fileDevReadAheadMicros, "parseBufByteCnt",parseBufByteCnt, - "file_ports",file_ports)) != kOkRC ) + "enableBufFl",enableBufFl, + "bufferMsgCnt",bufMsgCnt, + "file_ports",file_ports, + "filterRtSenseFl",filterRtSenseFl)) != kOkRC ) { rc = cwLogError(rc,"MIDI port parse args. failed."); } @@ -290,7 +367,18 @@ 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, + filterRtSenseFl); } @@ -437,15 +525,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 +661,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 +679,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 +793,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..a633cd6 100644 --- a/cwMidiDevice.h +++ b/cwMidiDevice.h @@ -30,7 +30,10 @@ 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. + bool filterRtSenseFl = true); rc_t create( handle_t& h, cbFunc_t cbFunc, @@ -47,7 +50,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 +60,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..f018cf9 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" @@ -8,6 +9,7 @@ #include "cwMidi.h" #include "cwMidiFile.h" #include "cwText.h" +#include "cwCsv.h" #ifdef cwBIG_ENDIAN #define mfSwap16(v) (v) @@ -500,9 +502,8 @@ namespace cw trackMsg_t* nextTrkMsg[ p->trkN ]; // next msg in each track unsigned long long atick = 0; unsigned i; - bool fl = true; - // iniitalize nextTrkTick[] and nextTrkMsg[] to the first msg in each track + // iniitalize nextTrkMsg[] to the first msg in each track for(i=0; itrkN; ++i) if((nextTrkMsg[i] = p->trkV[i].base) != NULL ) nextTrkMsg[i]->atick = nextTrkMsg[i]->dtick; @@ -511,7 +512,7 @@ namespace cw { unsigned k = kInvalidIdx; - // find the trk which has the next msg (min atick time) + // find the index of the track in nextTrkMsg[] which has the min atick for(i=0; itrkN; ++i) if( nextTrkMsg[i]!=NULL && (k==kInvalidIdx || nextTrkMsg[i]->atick < nextTrkMsg[k]->atick) ) k = i; @@ -520,13 +521,6 @@ namespace cw if( k == kInvalidIdx ) break; - if( fl && nextTrkMsg[k]->dtick > 0 ) - { - fl = false; - nextTrkMsg[k]->dtick = 1; - nextTrkMsg[k]->atick = 1; - } - // store the current atick atick = nextTrkMsg[k]->atick; @@ -542,20 +536,20 @@ namespace cw void _setAbsoluteTime( file_t* mfp ) { const trackMsg_t** msgV = _msgArray(mfp); - double microsPerQN = 60000000/120; // default tempo; - double microsPerTick = microsPerQN / mfp->ticksPerQN; - unsigned long long amicro = 0; - unsigned i; + double microsPerQN = 60000000.0/120.0; // default tempo; + double microsPerTick = microsPerQN / mfp->ticksPerQN; + unsigned long long amicro = 0; + unsigned i; for(i=0; imsgN; ++i) { trackMsg_t* mp = (trackMsg_t*)msgV[i]; // cast away const - unsigned dtick = 0; + unsigned dtick = 0; if( i > 0 ) { - // atick must have already been set and sorted + // atick must have already been set and sortedh assert( mp->atick >= msgV[i-1]->atick ); dtick = mp->atick - msgV[i-1]->atick; } @@ -566,7 +560,7 @@ namespace cw // track tempo changes if( mp->status == kMetaStId && mp->metaId == kTempoMdId ) - microsPerTick = mp->u.iVal / mfp->ticksPerQN; + microsPerTick = (double)mp->u.iVal / mfp->ticksPerQN; } } @@ -683,6 +677,14 @@ namespace cw } + void _init( file_t* p, unsigned trkN, unsigned ticksPerQN ) + { + p->ticksPerQN = ticksPerQN; + p->fmtId = 1; + p->trkN = trkN; + p->trkV = mem::allocZ(p->trkN); + } + rc_t _write8( file_t* mfp, unsigned char v ) { rc_t rc = kOkRC; @@ -1146,7 +1148,7 @@ namespace cw return rc; } - rc_t _testCsv( const object_t* cfg ) + rc_t _testGenCsv( const object_t* cfg ) { rc_t rc = kOkRC; const char* midiFn = nullptr; @@ -1164,6 +1166,30 @@ namespace cw return rc; } + rc_t _testOpenCsv( const object_t* cfg ) + { + rc_t rc = kOkRC; + rc_t rc1 = kOkRC; + midi::file::handle_t mfH; + const char* csvFn = nullptr; + + if((rc = cfg->getv("csvFn",csvFn)) != kOkRC ) + return cwLogError(kSyntaxErrorRC,"Invalid parameter to MIDI to CSV file conversion."); + + if(( rc = midi::file::open_csv(mfH,csvFn)) != kOkRC ) + goto errLabel; + + midi::file::printMsgs(mfH,log::globalHandle()); + + + errLabel: + + if((rc1 = close(mfH)) != kOkRC ) + rc1 = cwLogError(rc1,"MIDI file close failed on '%s'.",cwStringNullGuard(csvFn)); + + return rcSelect(rc,rc1); + } + rc_t _testBatchConvert( const object_t* cfg ) { @@ -1308,19 +1334,117 @@ cw::rc_t cw::midi::file::open( handle_t& hRef, const char* fn ){ return rc; } +cw::rc_t cw::midi::file::open_csv( handle_t& hRef, const char* csv_fname ) +{ + rc_t rc = kOkRC; + csv::handle_t csvH; + + const char* titleA[] = { "uid","tpQN","bpm","dticks","ch","status","d0","d1" }; + unsigned titleN = sizeof(titleA)/sizeof(titleA[0]); + + unsigned TpQN = 1260; + unsigned BpM = 60; + unsigned lineN = 0; + unsigned line_idx = 0; + + //double asecs = 0; + + unsigned uid = kInvalidId; + unsigned dtick = 0; + unsigned ch = 0; + unsigned status = 0; + unsigned d0 = 0; + unsigned d1 = 0; + file_t* p = nullptr; + + unsigned aticks = 0; + + if((rc = _create(hRef)) != kOkRC ) + goto errLabel; + + if((p = _handleToPtr(hRef)) == nullptr ) + goto errLabel; + + if((rc = csv::create(csvH,csv_fname,titleA,titleN)) != kOkRC ) + { + rc = cwLogError(rc,"MIDI CSV file open failed."); + goto errLabel; + } + + if((rc = line_count(csvH,lineN)) != kOkRC ) + { + rc = cwLogError(rc,"MIDI CSV line count access failed."); + goto errLabel; + } + + + for(; (rc = next_line(csvH)) == kOkRC; ++line_idx ) + { + if((rc = getv(csvH,"uid",uid,"tpQN",TpQN,"bpm",BpM,"dticks",dtick,"ch",ch,"status",status,"d0",d0,"d1",d1)) != kOkRC ) + { + cwLogError(rc,"Error reading CSV line %i.",line_idx+1); + goto errLabel; + } + + //printf("%i %i tpqn:%i bpm:%i dtick:%i ch:%i st:%i d0:%i d1:%i\n",line_idx,uid,TpQN,BpM,dtick,ch,status,d0,d1); + + //double ticks_per_sec = TpQN * BpM / 60; + //double dsecs = dtick * ticks_per_sec; + + //asecs += dsecs; + + if( line_idx == 0 ) + _init(p,1,TpQN); + + aticks += dtick; + + if( BpM != 0 ) + { + if((rc = insertTrackTempoMsg(hRef, 0, aticks, BpM )) != kOkRC ) + { + rc = cwLogError(rc,"BPM insert failed."); + goto errLabel; + } + } + + if( status != 0 ) + { + if((rc = insertTrackChMsg(hRef, 0, aticks, ch+status, d0, d1 )) != kOkRC ) + { + rc = cwLogError(rc,"Channel msg insert failed."); + goto errLabel; + } + } + + TpQN = 0; + BpM = 0; + status = 0; + dtick = 0; + } + + if( rc == kEofRC ) + rc = kOkRC; + +errLabel: + + if( rc != kOkRC ) + close(hRef); + + destroy(csvH); + return rc; +} + + cw::rc_t cw::midi::file::create( handle_t& hRef, unsigned trkN, unsigned ticksPerQN ) { rc_t rc = kOkRC; if((rc = _create(hRef)) != kOkRC ) return rc; - + file_t* p = _handleToPtr(hRef); - p->ticksPerQN = ticksPerQN; - p->fmtId = 1; - p->trkN = trkN; - p->trkV = mem::allocZ(p->trkN); + _init(p,trkN,ticksPerQN); return rc; } @@ -2539,14 +2663,18 @@ cw::rc_t cw::midi::file::test( const object_t* cfg ) if( textIsEqual(o->pair_label(),"rpt") ) rc = _testReport(o->pair_value()); - if( textIsEqual(o->pair_label(),"csv") ) - rc = _testCsv(o->pair_value()); + if( textIsEqual(o->pair_label(),"gen_csv") ) + rc = _testGenCsv(o->pair_value()); + + if( textIsEqual(o->pair_label(),"open_csv") ) + rc = _testOpenCsv(o->pair_value()); if( textIsEqual(o->pair_label(),"batch_convert") ) rc = _testBatchConvert(o->pair_value()); if( textIsEqual(o->pair_label(),"rpt_beg_end") ) rc = _testRptBeginEnd(o->pair_value()); + } } diff --git a/cwMidiFile.h b/cwMidiFile.h index 602f03c..67bf2ec 100644 --- a/cwMidiFile.h +++ b/cwMidiFile.h @@ -114,6 +114,12 @@ namespace cw // Read a MIDI file. rc_t open( handle_t& hRef, const char* fn ); + // Read from a CSV. + // Columns: "uid","tpQN","bpm","dticks","ch","status","d0","d1" + // tpQN = ticks per quarter note should be given on the first line. (Defaults to 1260). + // bpm = beats per minute should be given on the first line. (Defaults to 60). + rc_t open_csv( handle_t& hRef, const char* csv_fname ); + // Create an empty MIDI file object. rc_t create( handle_t& hRef, unsigned trkN, unsigned ticksPerQN ); 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/cwMpScNbQueue.h b/cwMpScNbQueue.h index 0e481de..2e86c73 100644 --- a/cwMpScNbQueue.h +++ b/cwMpScNbQueue.h @@ -32,6 +32,7 @@ namespace cw void push( T* payload ) { + // BUG: malloc() isn't non-blocking node_t* new_node = mem::allocZ(1); new_node->payload = payload; @@ -47,7 +48,11 @@ namespace cw // 2. Set the old-head next pointer to the new node (thereby adding the new node to the list) prev->next.store(new_node,std::memory_order_release); // RELEASE 'next' to consumer - + + // After the first insertion: + // tail -> stub + // stub.next -> new_node + // head -> new_node } @@ -60,7 +65,7 @@ namespace cw { _tail = next; payload = next->payload; - mem::free(t); + mem::free(t); // BUG: free() isn't non-blocking } return payload; diff --git a/cwMtx.cpp b/cwMtx.cpp index 30eb58e..59e74bc 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" @@ -88,9 +89,10 @@ namespace cw -cw::rc_t cw::mtx::test( const object_t* cfg ) +cw::rc_t cw::mtx::test( const test::test_args_t& args ) { rc_t rc = kOkRC; + const object_t* cfg = args.test_args; d_t* mtx0 = nullptr; d_t* mtx1 = nullptr; @@ -134,7 +136,7 @@ cw::rc_t cw::mtx::test( const object_t* cfg ) mtx_y1 = allocCfg(y1); unsigned n = offset(*mtx1,1,1); - printf("offset: %i\n",n); + cwLogPrint("offset: %i\n",n); report(*mtx0,"m0"); diff --git a/cwMtx.h b/cwMtx.h index 4121e13..d364b5e 100644 --- a/cwMtx.h +++ b/cwMtx.h @@ -732,7 +732,7 @@ namespace cw double v = ele( m, idxV ); // print the value - printf("%*.*f ",colWidth,decPl,v); + cwLogPrint("%*.*f ",colWidth,decPl,v); } else { @@ -742,11 +742,11 @@ namespace cw { // print the dimension index for matrices with 3+ dim's if( i > 0 && j == 0 ) - printf("%i\n",idxV[i-1]); + cwLogPrint("%i\n",idxV[i-1]); // print the row index for matrices with 2+ dim's if( m.dimN>1 ) - printf("%i | ",j); + cwLogPrint("%i | ",j); } idxV[i] = j; @@ -755,7 +755,7 @@ namespace cw // prevent multiple newlines on last printed line if( m.dimN==1 || (m.dimN>=2 && i > m.dimN-2) ) - printf("\n"); + cwLogPrint("\n"); } } @@ -774,10 +774,10 @@ namespace cw template< typename T > void report( const struct mtx_str& m, const char* label, unsigned decPl=3, unsigned colWidth=10 ) { - printf("%s :",label); + cwLogPrint("%s :",label); for(unsigned i=0; i f_t; typedef struct mtx_str d_t; - rc_t test( const struct object_str* cfg ); + rc_t test( const test::test_args_t& args ); } 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/cwMutex.h b/cwMutex.h index 15a4143..d05e147 100644 --- a/cwMutex.h +++ b/cwMutex.h @@ -15,6 +15,8 @@ namespace cw rc_t lock( handle_t h ); rc_t unlock( handle_t h ); + // Set timeOutMs to 0 to wait forever. + // // Set 'lockThenWaitFl' if the function should lock the mutex prior to waiting. // If 'lockThenWaitFl' is false then the function assumes the mutex is already locked // and directly waits. If 'lockThenWaitFl' is set and the mutex is not already locked 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..6285432 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 ); } @@ -930,10 +937,14 @@ cw::rc_t cw::objectFromString( const char* s, object_t*& objRef ) // then make the parent 'object' the current node if( cnp->is_pair() && cnp->child_count()==2 ) cnp = cnp->parent; - - } + if( lexId == lex::kErrorLexTId ) + { + rc = cwLogError(kSyntaxErrorRC,"A lexical element was not recognized."); + goto errLabel; + } + // if the root has only one child then make the child the root if( root != nullptr && root->child_count() == 1 ) { @@ -1007,4 +1018,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..d58ee20 100644 --- a/cwObject.h +++ b/cwObject.h @@ -31,7 +31,10 @@ namespace cw kRootTId = 0x00100000, kHexFl = 0x10000000, - kIdentFl = 0x20000000 + kIdentFl = 0x20000000, + + kOptFl = 0x40000000, + kReqFl = 0x00000000, }; @@ -135,12 +138,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 +209,118 @@ 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 kReqFl/kOptFl for required/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. + // Note that readv() assumes that the list of possible fields given as input is complete + // and any fields that it finds which are not in the list are not valid. This + // validity check is the main difference between readv() and getv()/getv_opt(). + 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 +465,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/cwSpScBuf.cpp b/cwSpScBuf.cpp index a7a0992..dca6dd4 100644 --- a/cwSpScBuf.cpp +++ b/cwSpScBuf.cpp @@ -1,6 +1,7 @@ #include "cwCommon.h" #include "cwLog.h" #include "cwCommonImpl.h" +#include "cwTest.h" #include "cwMem.h" #include "cwSpScBuf.h" #include "cwThread.h" diff --git a/cwSpScQueueTmpl.cpp b/cwSpScQueueTmpl.cpp index c11b2df..62bf385 100644 --- a/cwSpScQueueTmpl.cpp +++ b/cwSpScQueueTmpl.cpp @@ -1,6 +1,7 @@ #include "cwCommon.h" #include "cwLog.h" #include "cwCommonImpl.h" +#include "cwTest.h" #include "cwMem.h" #include "cwThread.h" #include "cwThreadMach.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..c5c7706 --- /dev/null +++ b/cwTest.cpp @@ -0,0 +1,610 @@ +#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 "cwTextBuf.h" + +#include "cwAudioDevice.h" +#include "cwAudioBufDecls.h" +#include "cwAudioBuf.h" +#include "cwMtx.h" + +#include "cwAudioFile.h" + +#include "cwDspTypes.h" +#include "cwMath.h" +#include "cwDsp.h" +#include "cwAudioTransforms.h" +#include "cwWaveTableBank.h" +#include "cwWaveTableNotes.h" + +#include "cwFlowTest.h" + +#include "cwThread.h" +#include "cwThreadMach.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 }, + { "/textBuf", textBuf::test }, + { "/audioBuf",audio::buf::test }, + { "/mtx", mtx::test }, + { "/wt_bank", wt_bank::test }, + { "/audio_transform", dsp::test }, + { "/wt_note", wt_note::test }, + { "/thread_tasks", thread_tasks::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; + 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 ); + + // form the module output directory + char* out_dir = filesys::makeFn(test.base_dir,nullptr,nullptr,test.out_folder,new_module_label,nullptr); + + // verify that the the module output directory exists + if( !filesys::isDir(out_dir) ) + { + if((rc = filesys::makeDir(out_dir)) != kOkRC ) + { + rc = cwLogError(rc,"The module output directory '%s' create failed.",cwStringNullGuard(out_dir)); + goto errLabel; + } + } + + switch( module_cfg->type_id() ) + { + case kStringTId: // an external module file was given + { + 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: // a nested module dict or case dict was given + rc = _proc_test_cfg(test, new_module_label, module_cfg ); + break; + + default: + break; + } + + errLabel: + mem::release(out_dir); + 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 no keywords were found then the dictionary must be a list of cases + 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; + +} + +cw::rc_t cw::test::test( const char* fname, int argc, const char** argv ) +{ + rc_t rc = kOkRC; + object_t* cfg = nullptr; + const object_t* test_dict = nullptr; + const object_t* test_test_dict = nullptr; + + if((rc = objectFromFile(fname,cfg)) != kOkRC ) + { + rc = cwLogError(rc,"Parsing failed on the test cfg file '%s'.",cwStringNullGuard(fname)); + goto errLabel; + } + + if((rc = cfg->get("test",test_dict)) != kOkRC ) + { + goto errLabel; + } + + if((rc = test_dict->get("test",test_test_dict)) != kOkRC ) + { + goto errLabel; + } + + rc = test(test_test_dict,argc,argv); + +errLabel: + + if( cfg != nullptr ) + cfg->free(); + + return rc; +} + diff --git a/cwTest.h b/cwTest.h new file mode 100644 index 0000000..2f46b9f --- /dev/null +++ b/cwTest.h @@ -0,0 +1,32 @@ +#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 ); + rc_t test( const char* cfg_fname, int argc, const char** argv ); + + } +} + +#endif diff --git a/cwText.cpp b/cwText.cpp index f6b4e7a..811a8de 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,42 @@ 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); +} + +char* cw::removeTrailingWhitespace( char* s ) +{ + char* s0; + unsigned sn; + + if( s == nullptr ) + return nullptr; + + if((sn = textLength(s)) == 0 ) + return s; + + s0 = s + (sn-1); + + for(; s0>=s; --s0) + { + if( !isspace(*s0) ) + break; + *s0 = 0; + } + + return s; +} + + bool cw::isInteger( const char* s ) { for(; *s; ++s) diff --git a/cwText.h b/cwText.h index cc44f06..f749882 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,9 +65,14 @@ 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[]. - const char* lastMatchChar( const char* s, char c ); + char* lastMatchChar( char* s, char c ); + const char* lastMatchChar( const char* s, char c ); + + char* removeTrailingWhitespace( char* s ); bool isInteger( const char* ); // text contains only [0-9] bool isReal( const char* ); // text contains only [0-9] with one decimal place diff --git a/cwTextBuf.cpp b/cwTextBuf.cpp index df1c60c..9a25c74 100644 --- a/cwTextBuf.cpp +++ b/cwTextBuf.cpp @@ -1,6 +1,7 @@ #include "cwCommon.h" #include "cwLog.h" #include "cwCommonImpl.h" +#include "cwTest.h" #include "cwMem.h" #include "cwTextBuf.h" @@ -164,7 +165,7 @@ cw::rc_t cw::textBuf::setFloatFormat( handle_t h, unsigned width, unsigned decPl return kOkRC; } -cw::rc_t cw::textBuf::test() +cw::rc_t cw::textBuf::test( const test::test_args_t& args ) { handle_t h; rc_t rc; @@ -175,7 +176,7 @@ cw::rc_t cw::textBuf::test() print(h,"Hello\n"); print(h,"foo\n"); - printf("%s", text(h) ); + cwLogPrint("%s", text(h) ); return destroy(h); } diff --git a/cwTextBuf.h b/cwTextBuf.h index 936aaf4..9bf899e 100644 --- a/cwTextBuf.h +++ b/cwTextBuf.h @@ -28,7 +28,7 @@ namespace cw rc_t setIntFormat( handle_t h, unsigned width, unsigned flags ); rc_t setFloatFormat( handle_t h, unsigned width, unsigned decPlN ); - rc_t test(); + rc_t test( const test::test_args_t& args); } } diff --git a/cwThread.cpp b/cwThread.cpp index f900e2a..8582c04 100644 --- a/cwThread.cpp +++ b/cwThread.cpp @@ -3,6 +3,9 @@ #include "cwCommonImpl.h" #include "cwMem.h" #include "cwThread.h" +#include "cwMutex.h" +#include "cwTest.h" +#include "cwTime.h" #include @@ -25,13 +28,18 @@ namespace cw std::atomic stateId; std::atomic doFlags; - cbFunc_t func; - void* funcArg; - unsigned stateMicros; - unsigned pauseMicros; - unsigned sleepMicros; + cbFunc_t func; + void* funcArg; + unsigned stateMicros; + unsigned pauseMicros; + unsigned waitMicros; pthread_attr_t attr; - char* label; + char* label; + + mutex::handle_t mutexH; + unsigned cycleIdx; // current cycle phase + unsigned cycleCnt; // cycle phase limit + unsigned execCnt; } thread_t; @@ -50,9 +58,9 @@ namespace cw if(curStateId == stateId ) break; - sleepUs( p->sleepMicros ); + sleepUs( p->waitMicros ); - waitTimeMicroSecs += p->sleepMicros; + waitTimeMicroSecs += p->waitMicros; }while( waitTimeMicroSecs < p->stateMicros ); @@ -83,15 +91,43 @@ namespace cw // if we are in the pause state if( curStateId == kPausedThId ) { - - sleepUs( p->pauseMicros ); + // unlock mutex and block on cond. var. for pauseMicros or until signaled + rc_t rc = waitOnCondVar(p->mutexH, false, p->pauseMicros/1000 ); + + switch(rc) + { + case kTimeOutRC: + // the mutex is not locked + break; + + case kOkRC: + // the cond. var. was signaled and the mutex is locked + break; + + default: + // mutex is not locked + rc = cwLogError(rc,"Condition variable wait failed."); + } + curDoFlags = p->doFlags.load(std::memory_order_acquire); - // check if we have been requested to leave the pause state - if( cwIsFlag(curDoFlags,kDoRunThFl) ) + // if exit was requested - and the mutex is unlocked + if( cwIsFlag(curDoFlags,kDoExitThFl) && rc != kOkRC ) { - p->stateId.store(kRunningThId,std::memory_order_release); + // this will cause the waitOnCondVar() to + // immediately return at the top of the loop + signalCondVar(p->mutexH); + //mutex::lock(p->mutexH); + } + else + { + // check if we have been requested to leave the pause state + if( cwIsFlag(curDoFlags,kDoRunThFl) ) + { + p->cycleIdx = 0; + p->stateId.store(kRunningThId,std::memory_order_release); + } } } else // ... we are in running state @@ -101,12 +137,22 @@ namespace cw break; curDoFlags = p->doFlags.load(std::memory_order_acquire); - - // check if we have been requested to enter the pause state - if( cwIsFlag(curDoFlags,kDoPauseThFl) ) + + if( cwIsNotFlag(curDoFlags,kDoExitThFl) ) { - p->stateId.store(kPausedThId,std::memory_order_release); + p->cycleIdx += 1; + + // if a cycle limit was set then check if the limit was reached + bool cycles_done_fl = p->cycleCnt > 0 && p->cycleIdx >= p->cycleCnt; + + // check if we have been requested to enter the pause state + if( (cwIsFlag(curDoFlags,kDoPauseThFl) || cycles_done_fl) ) + { + p->stateId.store(kPausedThId,std::memory_order_release); + p->doFlags.store(0,std::memory_order_release); + } } + } }while( cwIsFlag(curDoFlags,kDoExitThFl) == false ); @@ -125,6 +171,7 @@ cw::rc_t cw::thread::create( handle_t& hRef, cbFunc_t func, void* funcArg, const { rc_t rc; int sysRC; + bool mutex_is_locked_fl = false; if((rc = destroy(hRef)) != kOkRC ) return rc; @@ -136,7 +183,7 @@ cw::rc_t cw::thread::create( handle_t& hRef, cbFunc_t func, void* funcArg, const p->stateMicros = stateMicros; p->pauseMicros = pauseMicros; p->stateId = kPausedThId; - p->sleepMicros = 15000; + p->waitMicros = 15000; p->label = mem::duplStr(label); if((sysRC = pthread_attr_init(&p->attr)) != 0) @@ -146,6 +193,7 @@ cw::rc_t cw::thread::create( handle_t& hRef, cbFunc_t func, void* funcArg, const } else { + /* // Creating the thread in a detached state should prevent it from leaking memory when @@ -158,11 +206,29 @@ cw::rc_t cw::thread::create( handle_t& hRef, cbFunc_t func, void* funcArg, const } else */ - if((sysRC = pthread_create(&p->pThreadH, &p->attr, _threadCallback, (void*)p )) != 0 ) - { - p->stateId = kNotInitThId; - rc = cwLogSysError(kOpFailRC,sysRC,"Thread create failed."); - } + + // Create the cond. var mutex + if((rc = mutex::create(p->mutexH )) != kOkRC ) + { + rc = cwLogError(rc,"Thread signal condition mutex create failed."); + goto errLabel; + } + + // Lock the mutex so that it is already locked prior to the first call to waitOnCondVar() + if((rc = mutex::lock(p->mutexH)) != kOkRC ) + { + rc = cwLogError(rc,"Thread signal condition mutex lock failed."); + goto errLabel; + } + + mutex_is_locked_fl = true; + + // create the thread - in paused state + if((sysRC = pthread_create(&p->pThreadH, &p->attr, _threadCallback, (void*)p )) != 0 ) + { + p->stateId = kNotInitThId; + rc = cwLogSysError(kOpFailRC,sysRC,"Thread create failed."); + } } if( label != nullptr ) @@ -174,6 +240,16 @@ cw::rc_t cw::thread::create( handle_t& hRef, cbFunc_t func, void* funcArg, const cwLogInfo("Thread %s id:%p created.",cwStringNullGuard(label), p->pThreadH); +errLabel: + + if( rc != kOkRC && p->mutexH.isValid() ) + { + if( mutex_is_locked_fl ) + mutex::unlock(p->mutexH); + + mutex::destroy(p->mutexH); + } + return rc; } @@ -200,7 +276,12 @@ cw::rc_t cw::thread::destroy( handle_t& hRef ) //if( pthread_attr_destroy(&p->attr) != 0 ) // rc = cwLogError(kOpFailRC,"Thread attribute destroy failed."); - + + if( p->mutexH.isValid() ) + { + mutex::unlock(p->mutexH); + mutex::destroy(p->mutexH); + } mem::release(p->label); mem::release(p); hRef.clear(); @@ -209,7 +290,7 @@ cw::rc_t cw::thread::destroy( handle_t& hRef ) } -cw::rc_t cw::thread::pause( handle_t h, unsigned cmdFlags ) +cw::rc_t cw::thread::pause( handle_t h, unsigned cmdFlags, unsigned cycleCnt ) { rc_t rc = kOkRC; bool pauseFl = cwIsFlag(cmdFlags,kPauseFl); @@ -218,7 +299,9 @@ cw::rc_t cw::thread::pause( handle_t h, unsigned cmdFlags ) stateId_t curStateId = p->stateId.load(std::memory_order_acquire); bool isPausedFl = curStateId == kPausedThId; stateId_t waitId; - + + p->cycleCnt = cycleCnt; + if( isPausedFl == pauseFl ) return kOkRC; @@ -231,20 +314,26 @@ cw::rc_t cw::thread::pause( handle_t h, unsigned cmdFlags ) { p->doFlags.store(kDoRunThFl,std::memory_order_release); waitId = kRunningThId; + if((rc = signalCondVar(p->mutexH)) != kOkRC ) + { + cwLogError(rc,"Cond. var. signalling failed."); + goto errLabel; + } } if( waitFl ) rc = _waitForState(p,waitId); +errLabel: if( rc != kOkRC ) - cwLogError(rc,"Thread '%s' timed out waiting for '%s'. pauseMicros:%i stateMicros:%i sleepMicros:%i", p->label, pauseFl ? "pause" : "un-pause",p->pauseMicros,p->stateMicros,p->sleepMicros); + cwLogError(rc,"Thread '%s' timed out waiting for '%s'. pauseMicros:%i stateMicros:%i waitMicros:%i", p->label, pauseFl ? "pause" : "un-pause",p->pauseMicros,p->stateMicros,p->waitMicros); return rc; } -cw::rc_t cw::thread::unpause( handle_t h ) -{ return pause( h, kWaitFl); } +cw::rc_t cw::thread::unpause( handle_t h, unsigned cycleCnt ) +{ return pause( h, kWaitFl, cycleCnt); } cw::thread::stateId_t cw::thread::state( handle_t h ) { @@ -290,9 +379,22 @@ unsigned cw::thread::pauseMicros( handle_t h ) namespace cw { + time::spec_t g_t0 = {0,0}; + time_t g_micros = 0; + unsigned g_n = 0; + bool _threadTestCb( void* p ) { - unsigned* ip = (unsigned*)p; + if( g_t0.tv_nsec != 0 ) + { + time::spec_t t1; + time::get(t1); + g_micros += time::elapsedMicros(g_t0,t1); + g_n += 1; + g_t0.tv_nsec = 0; + } + + unsigned* ip = (unsigned*)p; ip[0]++; return true; } @@ -304,11 +406,14 @@ cw::rc_t cw::threadTest() unsigned val = 0; rc_t rc; char c = 0; + unsigned cycleCnt = 0; + // create the thread if((rc = thread::create(h,_threadTestCb,&val,"thread_test")) != kOkRC ) return rc; - - if((rc = thread::pause(h,0)) != kOkRC ) + + // start the thread + if((rc = thread::pause(h,0,cycleCnt)) != kOkRC ) goto errLabel; @@ -323,7 +428,7 @@ cw::rc_t cw::threadTest() switch(c) { case 'o': - cwLogInfo("val: 0x%x\n",val); + cwLogInfo("val: 0x%x %i\n",val,val); break; case 's': @@ -333,7 +438,14 @@ cw::rc_t cw::threadTest() case 'p': { if( thread::state(h) == thread::kPausedThId ) - rc = thread::pause(h,thread::kWaitFl); + { + time::get(g_t0); + // We don't set kWaitFl w/ cycleCnt>0 because we are running very + // few cycles - the cycles will run and the + // state of the thread will return to 'paused' + // before _waitForState() can notice the 'running' state. + rc = thread::pause(h, cycleCnt==0 ? thread::kWaitFl : 0,cycleCnt); + } else rc = thread::pause(h,thread::kPauseFl|thread::kWaitFl); @@ -349,6 +461,7 @@ cw::rc_t cw::threadTest() break; case 'q': + printf("wakeup micros:%li cnt:%i avg:%li\n",g_micros,g_n,g_n>0 ? g_micros/g_n : 0); break; //default: diff --git a/cwThread.h b/cwThread.h index 4260bd1..4263b4a 100644 --- a/cwThread.h +++ b/cwThread.h @@ -34,10 +34,12 @@ namespace cw rc_t destroy( handle_t& hRef ); - + + // 'cycleCnt' gives a limit on the count of time the thread function should be called + // when the thread exits the pause state. It is ignored if kPauseFl is set in 'cmdFlags' enum { kPauseFl=0x01, kWaitFl=0x02 }; - rc_t pause( handle_t h, unsigned cmdFlags = kWaitFl ); - rc_t unpause( handle_t h ); // same as threadPause(h,kWaitFl) + rc_t pause( handle_t h, unsigned cmdFlags = kWaitFl, unsigned cycleCnt=0 ); + rc_t unpause( handle_t h, unsigned cycleCnt=0 ); // same as threadPause(h,kWaitFl) stateId_t state( handle_t h ); diff --git a/cwThreadMach.cpp b/cwThreadMach.cpp index b35401f..4bac3e7 100644 --- a/cwThreadMach.cpp +++ b/cwThreadMach.cpp @@ -2,9 +2,12 @@ #include "cwLog.h" #include "cwCommonImpl.h" #include "cwMem.h" +#include "cwMutex.h" #include "cwThread.h" +#include "cwTest.h" #include "cwThreadMach.h" + namespace cw { namespace thread_mach @@ -159,3 +162,356 @@ bool cw::thread_mach::is_shutdown( handle_t h ) return true; } + +//--------------------------------------------------------------------------------------------------- +// thread_tasks +// + + +namespace cw +{ + namespace thread_tasks + { + struct thread_tasks_str; + + typedef struct task_thread_str + { + thread::handle_t threadH; + struct thread_tasks_str* owner; + unsigned threadId; + } task_thread_t; + + typedef struct thread_tasks_str + { + task_thread_t* threadA; // threadA[ threadN ] - arg. records for call to _threadFunc + unsigned threadN; + + task_t* taskA; // taskA[ taskN ] - list of user provided callbacks set by run + unsigned taskN; + + mutex::handle_t mutexH; + bool mutexLockFl; + + std::atomic next_task_idx; + std::atomic done_cnt; + + } thread_tasks_t; + + thread_tasks_t* _handleToPtr( handle_t h ) + { return handleToPtr(h); } + + rc_t _destroy( thread_tasks_t* p ) + { + rc_t rc = kOkRC; + if( p->threadN > 0 && p->threadA != nullptr ) + { + for(unsigned i=0; ithreadN; ++i) + { + rc_t rc0; + if((rc0 = thread::destroy(p->threadA[i].threadH)) != kOkRC ) + { + cwLogError(rc0,"Task thread %i destroy failed.",i); + } + } + + if( p->mutexLockFl ) + { + if((rc = mutex::unlock(p->mutexH)) != kOkRC ) + rc = cwLogError(rc,"Mutex unlock on thread tasks destroy failed."); + else + p->mutexLockFl = false; + } + + if((rc = mutex::destroy(p->mutexH)) != kOkRC ) + rc = cwLogError(rc,"Thread tasks mutex destroy failed."); + } + + mem::release(p->threadA); + mem::release(p); + return rc; + } + + bool _threadFunc( void* arg ) + { + rc_t rc = kOkRC; + task_thread_t* task_thread = (task_thread_t*)arg; + thread_tasks_t* p = task_thread->owner; + + // get the next available task + unsigned nti = p->next_task_idx.fetch_add(1, std::memory_order_acq_rel); + + // if nti is a valid task index ... + if( nti < p->taskN ) + { + // ... then execute the task + task_t* task = p->taskA + nti; + task->rc = task->func( task->arg ); + + // + unsigned done_cnt = p->done_cnt.fetch_add(1, std::memory_order_acq_rel); + + // if the last task is done + if( done_cnt + 1 == p->taskN ) + { + // By taking the lock here we guarantee that the the main thread is + // waiting on the cond. var.. Without doing this we might get here + // before the cond. var. is setup and the main thread will miss the signal. + if((rc = mutex::lock(p->mutexH)) != kOkRC ) + cwLogError(rc,"Last task mutex lock failed."); + else + { + mutex::unlock(p->mutexH); + + // signal the main thread that all tasks are done + if((rc = signalCondVar( p->mutexH )) != kOkRC ) + rc = cwLogError(rc,"Thread tasks signal cond var failed."); + } + + // all tasks are done - pause this thread + thread::pause(task_thread->threadH,thread::kPauseFl); + } + + } + else // ... otherwise pause the thread + { + // you are in the thread callback and so you can't wait - just signal the thread to pause + thread::pause(task_thread->threadH,thread::kPauseFl); + + } + + return true; + } + } +} + + + +cw::rc_t cw::thread_tasks::create( handle_t& hRef, unsigned threadN ) +{ + rc_t rc; + if((rc = destroy(hRef)) != kOkRC ) + return rc; + + thread_tasks_t* p = mem::allocZ(); + const unsigned labelCharN = 255; + char label[ labelCharN + 1 ]; + + p->threadN = threadN; + p->threadA = mem::allocZ(threadN); + + // Create a mutex for the run() blocking cond. var + if((rc = mutex::create( p->mutexH )) != kOkRC ) + { + rc = cwLogError(rc,"Thread tasks mutex failed."); + goto errLabel; + } + + // Lock the mutex so that it is locked on the first call to waitOnCondVar() + if((rc = mutex::lock(p->mutexH)) != kOkRC ) + { + rc = cwLogError(rc,"Thread tasks initial mutex lock failed."); + goto errLabel; + } + + p->mutexLockFl = true; + + for(unsigned i=0; ithreadA[i].owner = p; + p->threadA[i].threadId = i; + + // Threads are created in 'paused' mode + if((rc = thread::create( p->threadA[i].threadH, _threadFunc, p->threadA + i, label )) != kOkRC ) + { + rc = cwLogError(rc,"Task thread create %i failed.",i); + goto errLabel; + } + } + + hRef.set(p); + +errLabel: + if( rc != kOkRC ) + _destroy(p); + + return rc; +} + +cw::rc_t cw::thread_tasks::destroy( handle_t& hRef ) +{ + rc_t rc = kOkRC; + if( !hRef.isValid() ) + return rc; + + thread_tasks_t* p = _handleToPtr(hRef); + + if((rc = _destroy(p)) != kOkRC ) + return rc; + + hRef.clear(); + + return rc; +} + +cw::rc_t cw::thread_tasks::run( handle_t h, task_t* taskA, unsigned taskN, unsigned timeOutMs ) +{ + rc_t rc = kOkRC; + thread_tasks_t* p = _handleToPtr(h); + unsigned activeThreadN = std::min(p->threadN,taskN); + p->taskA = taskA; + p->taskN = taskN; + + p->done_cnt.store(0,std::memory_order_release); + p->next_task_idx.store(0,std::memory_order_release); + + for(unsigned i=0; ithreadA[i].threadH,0)) != kOkRC ) + { + rc = cwLogError(rc,"Task thread %i start failed.",i); + goto errLabel; + } + } + + /* + This spinlock works and is very simple - but it uses up a core which + may be assigned to one of the worker threads. + If threads were given core affinities to avoid this scenario then + the spin lock might be a better solution then the cond. var signaling. + + // block waiting for tasks to complete + while(1) + { + // spin on done_cnt + unsigned done_cnt = p->done_cnt.load(std::memory_order_acquire); + if( done_cnt >= taskN ) + { + //printf("DONE\n"); + break; + } + } + */ + + // block waiting for the the tasks to complete + rc = waitOnCondVar(p->mutexH, false, timeOutMs ); + + switch(rc) + { + case kOkRC: + // mutex is locked + p->mutexLockFl = true; + break; + + case kTimeOutRC: + // mutex is unlocked + p->mutexLockFl = false; + cwLogWarning("Thread tasks timed out."); + break; + + default: + // mutex is unlocked + p->mutexLockFl = false; + rc = cwLogError(rc,"Thread tasks run error."); + } + + /* + // pause all the threads + for(unsigned i=0; ithreadA[i].threadH,thread::kPauseFl | thread::kWaitFl)) != kOkRC ) + { + rc = cwLogError(rc,"Task thread %i post run pause failed.",i); + goto errLabel; + } + } + */ + + // if run failed then pause to threads + if( rc != kOkRC ) + { + // lock the mutex (if it isn't already) + if( !p->mutexLockFl ) + { + if((rc = mutex::lock(p->mutexH)) != kOkRC ) + { + rc = cwLogError(rc,"Thread task lock mutex on error cleanup failed."); + goto errLabel; + } + p->mutexLockFl = true; + } + } + +errLabel: + return rc; +} + + +namespace cw +{ + namespace thread_tasks + { + typedef struct test_task_str + { + std::atomic cnt; + } test_task_t; + + rc_t testThreadFunc( void* arg ) + { + test_task_t* t = (test_task_t*)arg; + + t->cnt.fetch_add(1,std::memory_order_relaxed); + return kOkRC; + } + + } +} + +cw::rc_t cw::thread_tasks::test( const test::test_args_t& args ) +{ + rc_t rc = kOkRC; + const unsigned threadN = 15; + const unsigned taskN = 10; + const unsigned execN = 10; + handle_t ttH; + + test_task_t* test_taskA = mem::allocZ(taskN); + task_t* taskA = mem::allocZ(taskN); + + for(unsigned i=0; i handle_t; + typedef thread::cbFunc_t threadFunc_t; + + // Create a thread tasks machine with threadN records + rc_t create( handle_t& hRef, unsigned threadN ); + rc_t destroy( handle_t& hRef ); + + typedef struct task_str + { + rc_t (*func)(void* arg); + void* arg; + rc_t rc; + } task_t; + + // timeOutMs is the count of milliseconds run will block while waiting + // for all the tasks to complete. + rc_t run( handle_t h, task_t* taskA, unsigned taskN, unsigned timeOutMs=100 ); + + rc_t test( const test::test_args_t& args ); + + } } #endif diff --git a/cwTime.cpp b/cwTime.cpp index ef7f211..fef2586 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 @@ -261,15 +262,12 @@ void cw::time::subtractMicros( spec_t& ts, unsigned micros ) void cw::time::advanceMicros( spec_t& ts, unsigned us ) { - const unsigned us_per_sec = 1000000; const unsigned ns_per_sec = 1000000000; - - unsigned sec = us / us_per_sec; - ts.tv_sec += sec; - ts.tv_nsec += (us - sec*us_per_sec)*1000; + ts.tv_nsec += us * 1000; // convert us to nano's - sec = ts.tv_nsec / ns_per_sec; + // check if nano's now have more than ns_pser_sec + time_t sec = ts.tv_nsec / ns_per_sec; ts.tv_sec += sec; ts.tv_nsec -= sec * ns_per_sec; @@ -280,18 +278,14 @@ void cw::time::advanceMicros( spec_t& ts, unsigned us ) void cw::time::advanceMs( spec_t& ts, unsigned ms ) { - const unsigned ms_per_sec = 1000; const unsigned ns_per_sec = 1000000000; - - unsigned sec = ms / ms_per_sec; - - ts.tv_sec += sec; - ts.tv_nsec += (ms - (sec*ms_per_sec)) * 1000000; - - sec = ts.tv_nsec / ns_per_sec; + + ts.tv_nsec += ms * 1000000; + time_t sec = ts.tv_nsec / ns_per_sec; ts.tv_sec += sec; ts.tv_nsec -= sec * ns_per_sec; + } cw::rc_t cw::time::futureMs( spec_t& ts, unsigned ms ) @@ -303,6 +297,13 @@ cw::rc_t cw::time::futureMs( spec_t& ts, unsigned ms ) return rc; } +void cw::time::fracSecondsToSpec( spec_t& ts, double sec ) +{ + const unsigned long long ns_per_sec = 1000000000; + ts.tv_sec = (unsigned long long)sec; + ts.tv_nsec = (sec - ts.tv_sec) * ns_per_sec; +} + void cw::time::secondsToSpec( spec_t& ts, unsigned sec ) { ts.tv_sec = sec; @@ -311,12 +312,13 @@ void cw::time::secondsToSpec( spec_t& ts, unsigned sec ) double cw::time::specToSeconds( const spec_t& t ) { + const long long ns_per_sec = 1000000000; spec_t ts = t; double sec = ts.tv_sec; - while( ts.tv_nsec >= 1000000000 ) + while( ts.tv_nsec >= ns_per_sec ) { sec += 1.0; - ts.tv_nsec -= 1000000000; + ts.tv_nsec -= ns_per_sec; } return sec + ((double)ts.tv_nsec)/1e9; @@ -399,7 +401,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 +412,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 +431,18 @@ 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)); + + + + cw::time::fracSecondsToSpec( t0, 12.34567 ); + double sec = specToSeconds( t0 ); + cwLogPrint("fsecs: %f\n",sec); + return kOkRC; diff --git a/cwTime.h b/cwTime.h index 7f1a3ed..ed7dba6 100644 --- a/cwTime.h +++ b/cwTime.h @@ -73,6 +73,7 @@ namespace cw // Advance the current time by 'ms' milliseconds; rc_t futureMs( spec_t& ts, unsigned ms ); + void fracSecondsToSpec( spec_t& ts, double sec ); void secondsToSpec( spec_t& ts, unsigned sec ); double specToSeconds( const spec_t& ts ); @@ -85,7 +86,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..c50073f 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" @@ -46,7 +47,8 @@ namespace cw { "number", false }, { "progress", false }, { "log", false }, - { "list", false }, + { "vlist", false }, + { "hlist", false }, { nullptr, false }, }; @@ -117,7 +119,7 @@ namespace cw unsigned sentMsgN; unsigned recvMsgN; - bucket_t hashA[ hashN ]; + bucket_t hashA[ hashN+1 ]; } ui_t; @@ -129,24 +131,42 @@ 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; } void _store_ele_in_hash_table( ui_t* p, ele_t* e ) { - unsigned parentUuId = e->logical_parent == nullptr ? kInvalidIdx : e->logical_parent->uuId; + unsigned parentUuId = e->logical_parent == nullptr ? kInvalidId : e->logical_parent->uuId; if( parentUuId == kInvalidId || e->appId == kInvalidId ) return; unsigned short hash_idx = _gen_hash_index( parentUuId, e->appId ); - + + // if the first bucket is empty ... if( p->hashA[ hash_idx ].ele == nullptr ) - p->hashA[ hash_idx ].ele = e; + p->hashA[ hash_idx ].ele = e; // ... then fill it else { - bucket_t* b = mem::allocZ(); + // otherwise look for an empty bucket + bucket_t* b = p->hashA[ hash_idx ].link; + + for(; b!=nullptr; b=b->link) + if( b->ele == nullptr ) + { + b->ele = e; // an empty bucket was found - fill it + return; + } + + // create a new bucket + b = mem::allocZ(); + + // and insert it as the second bucket b->link = p->hashA[ hash_idx ].link; b->ele = e; p->hashA[hash_idx].link = b; @@ -162,14 +182,59 @@ 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; } - return kInvalidId; - + return kInvalidId; } + void _remove_ele_from_hash_table_0( ui_t* p, const ele_t* e ) + { + // Note: hashA[] has hashN+1 elements + for(unsigned i=0; i<=hashN; ++i) + { + bucket_t* b = p->hashA + i; + for(; b!= nullptr; b=b->link) + if( b->ele == e ) + { + b->ele = nullptr; + break; + } + } + } + + void _remove_ele_from_hash_table( ui_t* p, const ele_t* e ) + { + if( e == nullptr ) + return; + + unsigned parentUuId = e->logical_parent == nullptr ? kInvalidId : e->logical_parent->uuId; + unsigned appId = e->appId; + + if( parentUuId == kInvalidId || appId == kInvalidId ) + { + //_remove_ele_from_hash_table_0(p,e); + return; + } + + unsigned hash_idx = _gen_hash_index(parentUuId,appId); + bucket_t* b = p->hashA + hash_idx; + + // locate the bucket to delete + for(; b!=nullptr; b=b->link) + { + if( b->ele == e ) + { + b->ele = nullptr; + return; + } + } + + _remove_ele_from_hash_table_0(p,e); + } + + void _print_eles( ui_t* p ) { for(unsigned i=0; ieleN; ++i) @@ -193,14 +258,14 @@ namespace cw return _is_child_of( parent, ele->phys_parent ) || _is_child_of( parent, ele->logical_parent ); } - void _destroy_element( ele_t* e ) + void _destroy_element( ui_t* p, ele_t* e ) { if( e == nullptr ) return; - + if( e->attr != nullptr ) e->attr->free(); - + mem::release(e->eleName); mem::release(e->blob); mem::release(e); @@ -213,9 +278,11 @@ namespace cw // free each element if( p->eleA != nullptr ) for(unsigned i=0; ieleN; ++i) - _destroy_element( p->eleA[i] ); + { + _destroy_element( p, p->eleA[i] ); + p->eleA[i] = nullptr; + } - appIdMapRecd_t* m = p->appIdMap; while( m!=nullptr ) { @@ -225,6 +292,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); @@ -693,7 +772,7 @@ namespace cw // if the given appId was not valid ... - if( appId == kInvalidId && parent != nullptr ) + if( appId == kInvalidId && parent != nullptr && eleName != nullptr ) { appIdMapRecd_t* m; @@ -831,7 +910,7 @@ namespace cw - // 'od' is an object dictionary where each pair in the dictionary has + // 'po' is an object dictionary where each pair in the dictionary has // the form: 'eleType':{ } rc_t _createElementsFromChildList( ui_t* p, const object_t* po, unsigned wsSessId, ele_t* parentEle, unsigned chanId ) { @@ -860,7 +939,7 @@ namespace cw return rc; } - // This functions assumes that the cfg object 'o' contains a field named: 'parent' + // This functions assumes that parentUuId is valid or the cfg object 'o' contains a field named: 'parent' // which contains the element name of the parent node. rc_t _createFromObj( ui_t* p, const object_t* o, unsigned wsSessId, unsigned parentUuId, unsigned chanId ) { @@ -1839,9 +1918,30 @@ cw::rc_t cw::ui::createProg( handle_t h, unsigned& uuIdRef, unsigned parentUuId cw::rc_t cw::ui::createLog( handle_t h, unsigned& uuIdRef, unsigned parentUuId, const char* eleName, unsigned appId, unsigned chanId, const char* clas, const char* title ) { return _createOneEle( _handleToPtr(h), uuIdRef, "log", kInvalidId, parentUuId, eleName, appId, chanId, clas, title); } -cw::rc_t cw::ui::createList( handle_t h, unsigned& uuIdRef, unsigned parentUuId, const char* eleName, unsigned appId, unsigned chanId, const char* clas, const char* title ) +cw::rc_t cw::ui::createVList( handle_t h, unsigned& uuIdRef, unsigned parentUuId, const char* eleName, unsigned appId, unsigned chanId, const char* clas, const char* title ) { return _createOneEle( _handleToPtr(h), uuIdRef, "list", kInvalidId, parentUuId, eleName, appId, chanId, clas, title); } +cw::rc_t cw::ui::createHList( handle_t h, unsigned& uuIdRef, unsigned parentUuId, const char* eleName, unsigned appId, unsigned chanId, const char* clas, const char* title ) +{ return _createOneEle( _handleToPtr(h), uuIdRef, "hlist", kInvalidId, parentUuId, eleName, appId, chanId, clas, title); } + +cw::rc_t cw::ui::setTitle( handle_t h, unsigned uuId, const char* title ) +{ + rc_t rc = kOkRC; + ui_t* p = _handleToPtr(h); + + const char* mFmt = "{ \"op\":\"set\", \"type\":\"title\", \"uuId\":%i, \"value\":\"%s\" }"; + const int mbufN = 256; + char mbuf[mbufN]; + + if( snprintf(mbuf,mbufN,mFmt,uuId,title) >= mbufN-1 ) + return cwLogError(kBufTooSmallRC,"The msg buffer is too small."); + + rc = _websockSend(p,kInvalidId,mbuf); + + return rc; +} + + cw::rc_t cw::ui::setNumbRange( handle_t h, unsigned uuId, double minValue, double maxValue, double stepValue, unsigned decPl, double value ) { rc_t rc = kOkRC; @@ -1934,6 +2034,53 @@ cw::rc_t cw::ui::setLogLine( handle_t h, unsigned uuId, const char* text ) return rc; } +cw::rc_t cw::ui::emptyParent( handle_t h, unsigned uuId ) +{ + rc_t rc = kOkRC; + unsigned childN = elementPhysChildCount(h,uuId); + unsigned* childUuIdA = nullptr; + + if( childN > 0 ) + { + childUuIdA = mem::alloc(childN); + unsigned actualChildN = 0; + if((rc = elementPhysChildUuId(h,uuId,childUuIdA,childN,actualChildN)) != kOkRC ) + goto errLabel; + + assert( actualChildN == childN ); + + for(unsigned i=0; i= mbufN-1 ) + { + rc = cwLogError(kBufTooSmallRC,"The msg buffer is too small for 'empty' msg."); + goto errLabel; + } + + if((rc = _websockSend(p,kInvalidId,mbuf)) != kOkRC ) + { + cwLogError(rc,"'empty' msg transmit failed."); + goto errLabel; + } + */ + +errLabel: + mem::release(childUuIdA); + return rc; +} + + cw::rc_t cw::ui::setClickable( handle_t h, unsigned uuId, bool clickableFl ) { return _setPropertyFlag( h, UI_CLICKABLE_LABEL, uuId, clickableFl ); } @@ -2068,6 +2215,87 @@ cw::rc_t cw::ui::clearBlob( handle_t h, unsigned uuId ) return kOkRC; } +unsigned cw::ui::elementChildCount( handle_t h, unsigned uuId ) +{ + ui_t* p = _handleToPtr(h); + unsigned n = 0; + ele_t* ele; + + if((ele = _uuIdToEle( p, uuId)) != nullptr ) + for(unsigned i=0; ieleN; ++i) + if( p->eleA[i] != nullptr && _is_child_of(ele, p->eleA[i] )) + ++n; + + return n; +} + +cw::rc_t cw::ui::elementChildUuId( handle_t h, unsigned uuId, unsigned* bufA, unsigned bufN, unsigned& actualN ) +{ + rc_t rc = kOkRC; + ui_t* p = _handleToPtr(h); + unsigned n = 0; + ele_t* ele; + + if((ele = _uuIdToEle( p, uuId)) != nullptr ) + for(unsigned i=0; ieleN; ++i) + if( p->eleA[i] != nullptr && _is_child_of(ele, p->eleA[i] )) + { + if( n >= bufN ) + { + rc = cwLogError(kBufTooSmallRC,"The child ele. id buffer is too small."); + goto errLabel; + } + + bufA[n++] = p->eleA[i]->uuId; + + } + + actualN = n; +errLabel: + return rc; +} + +unsigned cw::ui::elementPhysChildCount( handle_t h, unsigned uuId ) +{ + ui_t* p = _handleToPtr(h); + unsigned n = 0; + ele_t* ele; + + if((ele = _uuIdToEle( p, uuId)) != nullptr ) + for(unsigned i=0; ieleN; ++i) + if( p->eleA[i] != nullptr && p->eleA[i]->phys_parent == ele ) + ++n; + + return n; +} + +cw::rc_t cw::ui::elementPhysChildUuId( handle_t h, unsigned uuId, unsigned* bufA, unsigned bufN, unsigned& actualN ) +{ + rc_t rc = kOkRC; + ui_t* p = _handleToPtr(h); + unsigned n = 0; + ele_t* ele; + + actualN = 0; + + if((ele = _uuIdToEle( p, uuId)) != nullptr ) + for(unsigned i=0; ieleN; ++i) + if( p->eleA[i] != nullptr && p->eleA[i]->phys_parent == ele ) + { + if( n >= bufN ) + { + rc = cwLogError(kBufTooSmallRC,"The child ele. id buffer is too small."); + goto errLabel; + } + + bufA[n++] = p->eleA[i]->uuId; + + } + + actualN = n; +errLabel: + return rc; +} cw::rc_t cw::ui::destroyElement( handle_t h, unsigned uuId ) { @@ -2086,27 +2314,36 @@ cw::rc_t cw::ui::destroyElement( handle_t h, unsigned uuId ) // mark the element for deletion del_ele->destroyFl = true; + _remove_ele_from_hash_table(p, del_ele); - // mark all child elements of 'del_ele' for deletion + + // mark all child elements of 'del_ele' for deletion and remove them from the hash table for(unsigned i=0; ieleN; ++i) - if( p->eleA[i] != nullptr ) - p->eleA[i]->destroyFl = _is_child_of(del_ele, p->eleA[i] ); + if( p->eleA[i] != nullptr && p->eleA[i] != del_ele ) + if((p->eleA[i]->destroyFl = _is_child_of(del_ele, p->eleA[i] )) == true ) + _remove_ele_from_hash_table(p, p->eleA[i]); + + // Note that all ele's that are going to be deleted + // must be first removed from the hash table. + // The ele's can't be removed from the hash table + // as they are deleted because the removal fromo the hash table + // requires accessing the logical parent - which may have already been deleted. // release all elements that are marked for deletion for(unsigned i=0; ieleN; ++i) if( p->eleA[i] != nullptr && p->eleA[i]->destroyFl ) { - _destroy_element( p->eleA[i] ); - + _destroy_element( p, p->eleA[i] ); p->eleA[i] = nullptr; } - snprintf(mbuf,mbufN, "{ \"op\":\"destroy\", \"uuId\":%i }", del_ele->uuId ); + + snprintf(mbuf,mbufN, "{ \"op\":\"destroy\", \"uuId\":%i }", uuId ); _websockSend(p,kInvalidId,mbuf); errLabel: if(rc != kOkRC ) - rc = cwLogError(rc,"Element delete failed."); + rc = cwLogError(rc,"Element delete failed: uuid:%i.",uuId); return rc; } @@ -2219,6 +2456,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 +2473,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 +2526,7 @@ namespace cw ui_ws_t* p = static_cast(cbArg); return websock::send( p->wsH, kUiProtocolId, wsSessId, msg, msgByteN ); } - + } } } @@ -2317,13 +2554,19 @@ 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, + "extraLogsFl", args.extraLogsFl, "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 +2617,13 @@ 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, + args.extraLogsFl); } @@ -2392,11 +2637,13 @@ 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, + bool extraLogsFl) { rc_t rc = kOkRC; @@ -2416,7 +2663,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, extraLogsFl )) != kOkRC ) { cwLogError(rc,"UI Websock create failed."); goto errLabel; @@ -2432,7 +2679,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 +2711,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 +2732,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 +2755,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 +2773,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 +2798,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 +2809,52 @@ 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, + bool extraLogsFl ) { rc_t rc = kOkRC; if((rc = destroy(h)) != kOkRC ) @@ -2575,7 +2862,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 +2874,8 @@ cw::rc_t cw::ui::srv::create( handle_t& h, goto errLabel; } + p->wsTimeOutMs = wsTimeOutMs; + h.set(p); errLabel: @@ -2596,32 +2885,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 +2940,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..fd5e24e 100644 --- a/cwUi.h +++ b/cwUi.h @@ -125,11 +125,15 @@ namespace cw rc_t createProg( handle_t h, unsigned& uuIdRef, unsigned parentUuId, const char* eleName, unsigned appId, unsigned chanId, const char* clas, const char* title, double minValue, double maxValue ); rc_t createProg( handle_t h, unsigned& uuIdRef, unsigned parentUuId, const char* eleName, unsigned appId, unsigned chanId, const char* clas, const char* title, double minValue, double maxValue, double value ); rc_t createLog( handle_t h, unsigned& uuIdRef, unsigned parentUuId, const char* eleName, unsigned appId, unsigned chanId, const char* clas, const char* title ); - rc_t createList( handle_t h, unsigned& uuIdRef, unsigned parentUuId, const char* eleName, unsigned appId, unsigned chanId, const char* clas, const char* title ); + rc_t createVList( handle_t h, unsigned& uuIdRef, unsigned parentUuId, const char* eleName, unsigned appId, unsigned chanId, const char* clas, const char* title ); + rc_t createHList( handle_t h, unsigned& uuIdRef, unsigned parentUuId, const char* eleName, unsigned appId, unsigned chanId, const char* clas, const char* title ); + rc_t setTitle( handle_t h, unsigned uuId, const char* title ); + rc_t setNumbRange( handle_t h, unsigned uuId, double minValue, double maxValue, double stepValue, unsigned decPl, double value ); rc_t setProgRange( handle_t h, unsigned uuId, double minValue, double maxValue, double value ); rc_t setLogLine( handle_t h, unsigned uuId, const char* text ); + rc_t emptyParent( handle_t h, unsigned uuId ); rc_t setClickable( handle_t h, unsigned uuId, bool clickableFl=true ); rc_t clearClickable( handle_t h, unsigned uuId ); @@ -154,6 +158,7 @@ namespace cw // (uuId must identify a list element whose parent is a 'uiList') rc_t setScrollTop( handle_t h, unsigned uuId ); + // setBlob() allocates internal memeory and copies the contents of blob[blobByeN] rc_t setBlob( handle_t h, unsigned uuId, const void* blob, unsigned blobByteN ); const void* getBlob( handle_t h, unsigned uuId, unsigned& blobByteN_Ref ); rc_t clearBlob( handle_t h, unsigned uuId ); @@ -161,6 +166,13 @@ namespace cw // Register parent/child/name app id's rc_t registerAppIdMap( handle_t h, const appIdMap_t* map, unsigned mapN ); + unsigned elementChildCount( handle_t h, unsigned uuId ); + rc_t elementChildUuId( handle_t h, unsigned uuId, unsigned* bufA, unsigned bufN, unsigned& actualN ); + + unsigned elementPhysChildCount( handle_t h, unsigned uuId ); + rc_t elementPhysChildUuId( handle_t h, unsigned uuId, unsigned* bufA, unsigned bufN, unsigned& actualN ); + + // Release an element an all of it's children. rc_t destroyElement( handle_t h, unsigned uuId ); @@ -192,8 +204,10 @@ 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; + bool extraLogsFl; // Report the websock LLL_NOTICE logs } args_t; rc_t parseArgs( const object_t& o, args_t& args, const char* object_label="ui" ); @@ -218,20 +232,21 @@ 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, + bool extraLogsFl = false ); 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 +255,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 +265,32 @@ 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, + bool extraLogsFl = false ); 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..41f73e0 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 + void ones( T* v, unsigned n ) + { fill(v,n,1); } //================================================================================================================== // Compare @@ -169,6 +172,30 @@ namespace cw return v0; } + + + //================================================================================================================== + // find, count + // + + template< typename T0, typename T1 > + unsigned find( const T0* v, unsigned n, const T1& m ) + { + for(unsigned i=0; i + unsigned count( const T0* v, unsigned n, const T1& m ) + { + unsigned cnt = 0; + for(unsigned i=0; i + void sub( const T0& scalar, T1* v0, unsigned n ) + { + for(unsigned i=0; i void sub( T0* y0, const T0* v0, const T1& scalar, unsigned n ) { @@ -432,8 +466,8 @@ namespace cw - template< typename T > - unsigned phasor( T* y, unsigned n, T srate, T hz, unsigned init_idx=0 ) + template< typename T0, typename T1, typename T2 > + unsigned phasor( T0* y, unsigned n, T1 srate, T2 hz, unsigned init_idx=0 ) { for(unsigned i=init_idx; i - unsigned sine( T* y, unsigned n, T srate, T hz, unsigned init_idx=0 ) + template< typename T0, typename T1, typename T2 > + unsigned sine( T0* y, unsigned n, T1 srate, T2 hz, unsigned init_idx=0 ) { init_idx = phasor(y,n,srate,hz,init_idx); @@ -545,8 +579,7 @@ namespace cw } - - + rc_t test( const test::test_args_t& args ); } } diff --git a/cwVelTableTuner.cpp b/cwVelTableTuner.cpp index 6df6938..cabe9af 100644 --- a/cwVelTableTuner.cpp +++ b/cwVelTableTuner.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/cwWaveTableBank.cpp b/cwWaveTableBank.cpp new file mode 100644 index 0000000..11b8dbd --- /dev/null +++ b/cwWaveTableBank.cpp @@ -0,0 +1,632 @@ +#include "cwCommon.h" +#include "cwLog.h" +#include "cwCommonImpl.h" +#include "cwTest.h" +#include "cwMem.h" +#include "cwFile.h" +#include "cwText.h" +#include "cwObject.h" +#include "cwFileSys.h" +#include "cwAudioFile.h" +#include "cwMath.h" +#include "cwVectOps.h" +#include "cwDspTypes.h" +#include "cwDsp.h" +#include "cwAudioTransforms.h" +#include "cwWaveTableBank.h" +#include "cwMidi.h" +#include "cwTest.h" + +namespace cw +{ + namespace wt_bank + { + typedef struct vel_str + { + unsigned vel; + unsigned bsi; + multi_ch_wt_seq_t mc_seq; + } vel_t; + + typedef struct pitch_str + { + unsigned midi_pitch; + vel_t* velA; + unsigned velN; + } pitch_t; + + typedef struct instr_str + { + char* label; + pitch_t* pitchA; + unsigned pitchN; + + multi_ch_wt_seq_t** pvM; // pgM[128x128] pgmM[pitch][vel] // each row holds the vel's for a given pitch + struct instr_str* link; + } instr_t; + + typedef struct wt_bank_str + { + unsigned allocAudioBytesN; + unsigned padSmpN; + + unsigned instrN; + instr_t** instrA; + } wt_bank_t; + + typedef struct audio_buf_str + { + audiofile::handle_t afH; + + unsigned allocFrmN = 0; + unsigned allocChN = 0; + unsigned chN = 0; + unsigned frmN = 0; + sample_t** ch_buf = nullptr; // chBuf[chN][frmN] + } audio_buf_t; + + wt_bank_t* _handleToPtr( handle_t h ) + { return handleToPtr(h); } + + void _audio_buf_free( audio_buf_t& ab ) + { + audiofile::close(ab.afH); + + for(unsigned i=0; i ab.allocChN ) + { + ab.ch_buf = mem::resizeZ( ab.ch_buf, info.chCnt ); + ab.allocChN = info.chCnt; + } + + for(unsigned i=0; i ab.allocFrmN ) + { + ab.ch_buf[i] = mem::resizeZ( ab.ch_buf[i], info.frameCnt ); + ab.allocFrmN = info.frameCnt; + } + } + + if((rc = audiofile::readFloat(ab.afH, info.frameCnt, 0, info.chCnt, ab.ch_buf, &actualFrmCnt )) != kOkRC ) + { + rc = cwLogError(rc,"The instrument audio file read failed."); + goto errLabel; + } + + assert( info.frameCnt == actualFrmCnt ); + + ab.chN = info.chCnt; + ab.frmN = actualFrmCnt; + + errLabel: + return rc; + } + + + + void _destroy_instr( instr_t* instr ) + { + mem::release(instr->label); + for(unsigned i=0; ipitchN; ++i) + { + pitch_t* pr = instr->pitchA + i; + for(unsigned j=0; jvelN; ++j) + { + vel_t* vr = pr->velA + j; + for(unsigned ch_idx=0; ch_idxmc_seq.chN; ++ch_idx) + { + wt_seq_t* wts = vr->mc_seq.chA + ch_idx; + for(unsigned wti=0; wtiwtN; ++wti) + { + wt_t* wt = wts->wtA + wti; + mem::release(wt->aV); + } + mem::release(wts->wtA); + } + mem::release(vr->mc_seq.chA); + } + mem::release(pr->velA); + } + mem::release(instr->pitchA); + mem::release(instr->pvM); + mem::release(instr); + } + + rc_t _destroy( wt_bank_t* p ) + { + rc_t rc = kOkRC; + + for(unsigned i=0; iinstrN; ++i ) + _destroy_instr(p->instrA[i]); + + mem::release(p); + return rc; + } + + void _alloc_wt( wt_bank_t* p, + wt_t* wt, + wt_tid_t tid, + srate_t srate, + const sample_t* aV, + unsigned posn_smp_idx, + unsigned aN, + double hz, + double rms) + { + wt->tid = tid; + wt->aN = aN; + wt->hz = hz; + wt->rms = rms; + wt->cyc_per_loop = 1; + wt->srate = srate; + wt->pad_smpN = p->padSmpN; + wt->posn_smp_idx = posn_smp_idx; + + unsigned allocSmpCnt = p->padSmpN + wt->aN + p->padSmpN; + p->allocAudioBytesN += allocSmpCnt * sizeof(sample_t); + + // allocate the wavetable audio buffer + wt->aV = mem::allocZ( allocSmpCnt ); + + // fill the wavetable from the audio file + vop::copy(wt->aV+p->padSmpN, aV + posn_smp_idx, wt->aN); + + // fill the wavetable prefix + vop::copy(wt->aV, wt->aV + p->padSmpN + (wt->aN-p->padSmpN), p->padSmpN ); + + // fill the wavetable suffix + vop::copy(wt->aV + p->padSmpN + wt->aN, wt->aV + p->padSmpN, p->padSmpN ); + + } + + rc_t _create_instr_pv_map( instr_t* instr ) + { + rc_t rc = kOkRC; + + // each row contains the velocities for a given pitch + instr->pvM = mem::allocZ(midi::kMidiNoteCnt * midi::kMidiVelCnt ); + + for(unsigned i=0; ipitchN; ++i) + { + unsigned vel0 = 0; + + for(unsigned j=0; jpitchA[i].velN; ++j) + { + unsigned pitch = instr->pitchA[i].midi_pitch; + unsigned vel = instr->pitchA[i].velA[j].vel; + + if( pitch >= midi::kMidiNoteCnt ) + rc = cwLogError(kInvalidArgRC,"An invalid pitch value (%i) was encounted.",pitch); + + if( vel >= midi::kMidiVelCnt ) + rc = cwLogError(kInvalidArgRC,"An invalid velocity value (%i) was encountered.",vel); + + multi_ch_wt_seq_t* mcs = &instr->pitchA[i].velA[j].mc_seq; + + // if there is a gap between vel0 and vel + if( vel0 > 0 ) + { + // find the center of the gap + unsigned vel_c = vel0 + (vel-vel0)/2; + + // vel0:vel_c = mcs0 + for(unsigned v=vel0+1; v<=vel_c; ++v) + { + instr->pvM[(v*midi::kMidiNoteCnt) + pitch ] = instr->pvM[(vel0*midi::kMidiNoteCnt) + pitch ]; + } + + // vel_c+1:vel-1 = mcs + for(unsigned v=vel_c+1; vpvM[(v*midi::kMidiNoteCnt) + pitch ] = mcs; + } + } + + instr->pvM[(vel*midi::kMidiNoteCnt) + pitch ] = mcs; + vel0 = vel; + } + } + + if( rc != kOkRC ) + rc = cwLogError(rc,"Pitch-velocy map creation failed on instrument:'%s'.",cwStringNullGuard(instr->label)); + + return rc; + } + } +} + + +cw::rc_t cw::wt_bank::create( handle_t& hRef, unsigned padSmpN, const char* instr_json_fname ) +{ + rc_t rc = kOkRC; + + if(destroy(hRef) != kOkRC ) + return rc; + + wt_bank_t* p = mem::allocZ(); + p->padSmpN = padSmpN; + + hRef.set(p); + + if( instr_json_fname != nullptr ) + if((rc = load(hRef,instr_json_fname)) != kOkRC ) + { + hRef.clear(); + } + + + if(rc != kOkRC ) + { + rc = cwLogError(rc,"WT Bank create failed."); + _destroy(p); + } + + return rc; +} + +cw::rc_t cw::wt_bank::destroy( handle_t& hRef ) +{ + rc_t rc = kOkRC; + wt_bank_t* p =nullptr; + if( !hRef.isValid() ) + return rc; + + p = _handleToPtr(hRef); + + if((rc = _destroy(p)) != kOkRC ) + return rc; + + hRef.clear(); + + return rc; +} + +cw::rc_t cw::wt_bank::load( handle_t h, const char* instr_json_fname ) +{ + rc_t rc = kOkRC; + wt_bank_t* p = _handleToPtr(h); + object_t* f = nullptr; + const object_t* pitchL = nullptr; + const char* instr_label = nullptr; + instr_t* instr = nullptr; + audio_buf_t abuf{}; + + if((rc = objectFromFile(instr_json_fname,f)) != kOkRC ) + goto errLabel; + + if( f == nullptr || !f->is_dict() ) + { + rc = cwLogError(kSyntaxErrorRC,"The instrument file header is not valid."); + goto errLabel; + } + + if((rc = f->getv("instr",instr_label, + "pitchL",pitchL)) != kOkRC ) + { + rc = cwLogError(rc,"Instrument file syntax error while reading file header."); + goto errLabel; + } + + instr = mem::allocZ(); + instr->label = mem::duplStr(instr_label); + instr->pitchN = pitchL->child_count(); + instr->pitchA = mem::allocZ(instr->pitchN); + + for(unsigned i=0; ipitchN; ++i) + { + double hz = 0; + srate_t srate = 0; + const char* audio_fname = nullptr; + const object_t* velL = nullptr; + const object_t* pitchR = pitchL->child_ele(i); + pitch_t* pitch = instr->pitchA + i; + + if(pitchR == nullptr || !pitchR->is_dict() ) + { + rc = cwLogError(kSyntaxErrorRC,"The pitch record at index %i is not valid.",i); + goto errLabel; + } + + if((rc = pitchR->getv("midi_pitch",pitch->midi_pitch, + "srate",srate, + "est_hz_mean",hz, + "audio_fname",audio_fname, + "velL",velL)) != kOkRC ) + { + rc = cwLogError(rc,"Instrument file syntax error while reading pitch record at index %i.",i); + goto errLabel; + } + + cwLogInfo("pitch:%i %i %s",pitch->midi_pitch,p->allocAudioBytesN/(1024*1024),audio_fname); + + // read the audio file into abuf + if((rc = _audio_buf_alloc(abuf, audio_fname )) != kOkRC ) + goto errLabel; + + pitch->velN = velL->child_count(); + pitch->velA = mem::allocZ( pitch->velN ); + + for(unsigned j=0; jpitchA[i].velN; ++j) + { + const object_t* chL = nullptr; + const object_t* velR = velL->child_ele(j); + vel_t* vel = pitch->velA + j; + + if( velR==nullptr || !velR->is_dict() ) + { + rc = cwLogError(rc,"The velocity record at index %i on MIDI pitch %i is invalid.",j,pitch->midi_pitch); + goto errLabel; + } + + if((rc = velR->getv("vel",vel->vel, + "bsi",vel->bsi, + "chL",chL)) != kOkRC ) + { + rc = cwLogError(rc,"Instrument file syntax error while reading the velocity record at index %i from the pitch record for MIDI pitch:%i.",j,pitch->midi_pitch); + goto errLabel; + } + + + vel->mc_seq.chN = chL->child_count(); + vel->mc_seq.chA = mem::allocZ( vel->mc_seq.chN ); + + for(unsigned ch_idx=0; ch_idxpitchA[i].velA[j].mc_seq.chN; ++ch_idx) + { + + const object_t* wtL = chL->child_ele(ch_idx); + wt_seq_t* wts = vel->mc_seq.chA + ch_idx; + + wts->wtN = wtL->child_count() + 1; + wts->wtA = mem::allocZ( wts->wtN ); + + for(unsigned wti=0; wtiwtN-1; ++wti) + { + const object_t* wtR = wtL->child_ele(wti); + wt_t* wt = wts->wtA + wti + 1; + unsigned wtbi = kInvalidIdx; + unsigned wtei = kInvalidIdx; + double rms = 0; + + if((rc = wtR->getv("wtbi",wtbi, + "wtei",wtei, + "rms",rms, + "est_hz",wt->hz)) != kOkRC ) + { + rc = cwLogError(rc,"Instrument file syntax error in wavetable record at index %i, channel index:%i, velocity index:%i midi pitch:%i.",wti,ch_idx,j,pitch->midi_pitch); + goto errLabel; + } + + // if this is the first looping wave table then insert the attack wave table before it + if( wti==0 ) + { + _alloc_wt(p,wts->wtA,dsp::wt_osc::kOneShotWtTId,srate,abuf.ch_buf[ch_idx], vel->bsi, wtbi-vel->bsi,hz,0); + } + + _alloc_wt(p,wt,dsp::wt_osc::kLoopWtTId,srate,abuf.ch_buf[ch_idx],wtbi,wtei-wtbi,hz,rms); + } + } + } + } + + if((rc = _create_instr_pv_map( instr )) != kOkRC ) + goto errLabel; + + p->instrA = mem::resizeZ( p->instrA, p->instrN+1 ); + p->instrA[ p->instrN++ ] = instr; + +errLabel: + if(rc != kOkRC ) + { + if( instr != nullptr ) + _destroy_instr(instr); + rc = cwLogError(rc,"Wave table bank load failed on '%s'.",cwStringNullGuard(instr_json_fname)); + } + + _audio_buf_free(abuf); + + if( f != nullptr ) + f->free(); + + return rc; +} + +void cw::wt_bank::report( handle_t h ) +{ + wt_bank_t* p = _handleToPtr(h); + + for(unsigned instr_idx=0; instr_idxinstrN; ++instr_idx) + { + instr_t* instr = p->instrA[instr_idx]; + cwLogPrint("%s \n",instr->label); + for(unsigned i=0; ipitchN; ++i) + { + const pitch_t* pitch = instr->pitchA + i; + cwLogPrint(" pitch:%i\n",pitch->midi_pitch); + + for(unsigned j=0; jvelN; ++j) + { + vel_t* vel = pitch->velA + j; + bool fl = true; + + cwLogPrint(" vel:%i\n",vel->vel); + + for(unsigned wti=0; fl; ++wti) + { + + fl = false; + + const char* indent = " "; + for(unsigned ch_idx=0; ch_idxmc_seq.chN; ++ch_idx) + if( wti < vel->mc_seq.chA[ch_idx].wtN ) + { + wt_t* wt=vel->mc_seq.chA[ch_idx].wtA + wti; + + cwLogPrint("%s(%i %f %f) ",indent,wt->aN,wt->rms,wt->hz); + indent=""; + fl = true; + } + + if( fl ) + { + cwLogPrint("\n"); + } + } + + } + } + } + + +} + +unsigned cw::wt_bank::instr_count( handle_t h ) +{ + wt_bank_t* p = _handleToPtr(h); + return p->instrN; +} + +unsigned cw::wt_bank::instr_index( handle_t h, const char* instr_label ) +{ + wt_bank_t* p = _handleToPtr(h); + + for(unsigned i=0; iinstrN; ++i) + if( textIsEqual(p->instrA[i]->label,instr_label) ) + return i; + + return kInvalidIdx; +} + +cw::rc_t cw::wt_bank::instr_pitch_velocities( handle_t h, unsigned instr_idx, unsigned pitch, unsigned* velA, unsigned velCnt, unsigned& velCnt_Ref ) +{ + rc_t rc = kOkRC; + wt_bank_t* p = _handleToPtr(h); + + velCnt_Ref = 0; + + if( instr_idx > p->instrN || pitch >= midi::kMidiNoteCnt ) + { + rc = cwLogError(kInvalidArgRC,"Invalid wave table request : instr:%i (instrN:%i) pitch:%i.",instr_idx,p->instrN,pitch); + goto errLabel; + } + + for(unsigned i=0; iinstrA[instr_idx]->pitchN; ++i) + if( p->instrA[instr_idx]->pitchA[i].midi_pitch == pitch ) + { + if( p->instrA[instr_idx]->pitchA[i].velN > velCnt ) + { + rc = cwLogError(kBufTooSmallRC,"The velocity buffer is too small (%i>%i) for instr:%i pitch:%i.",p->instrA[instr_idx]->pitchA[i].velN,velCnt,instr_idx,pitch); + goto errLabel; + } + + for(unsigned j=0; jinstrA[instr_idx]->pitchA[i].velN; ++j) + velA[j] = p->instrA[instr_idx]->pitchA[i].velA[j].vel; + + velCnt_Ref = p->instrA[instr_idx]->pitchA[i].velN; + break; + } + + if( velCnt_Ref==0) + { + rc = cwLogError(kInvalidArgRC,"No sampled wave tables exist for instr:%i pitch:%i.",instr_idx,pitch); + goto errLabel; + } + +errLabel: + return rc; +} + + + +cw::rc_t cw::wt_bank::get_wave_table( handle_t h, unsigned instr_idx, unsigned pitch, unsigned vel, cw::wt_bank::multi_ch_wt_seq_t const * & mcs_Ref ) +{ + rc_t rc = kOkRC; + wt_bank_t* p = _handleToPtr(h); + + mcs_Ref = nullptr; + + if( instr_idx > p->instrN || pitch >= midi::kMidiNoteCnt || vel >= midi::kMidiVelCnt ) + { + rc = cwLogError(kInvalidArgRC,"Invalid wave table request : instr:%i (instrN:%i) pitch:%i vel:%i.",instr_idx,p->instrN,pitch,vel); + goto errLabel; + } + + + mcs_Ref = p->instrA[instr_idx]->pvM[(vel*midi::kMidiNoteCnt) + pitch ]; + + if( mcs_Ref == nullptr ) + { + rc = cwLogError(kInvalidStateRC,"The wave table request : instr:%i (instrN:%i) pitch:%i vel:%i is not populated.",instr_idx,p->instrN,pitch,vel); + goto errLabel; + } + +errLabel: + + return rc; +} + + +cw::rc_t cw::wt_bank::test( const test::test_args_t& args ) +{ + rc_t rc0 = kOkRC; + rc_t rc1 = kOkRC; + unsigned padN = 2; + const char* cfg_fname; + handle_t h; + + //unsigned instr_idx = 0; + //unsigned pitchA[] = { 21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21, 21, 21, 21, 21, 21, 21 }; + //unsigned pitchA[] = { 60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60, 60, 60, 60, 60, 60, 60 }; + //unsigned velA[] = { 1, 5,10,16,21,26,32,37,42,48,53,58,64,69,74,80,85,90,96,101,106,112,117,122,127 }; + + //unsigned velA[] = { 117, 122 }; + //double note_dur_sec = 2.5; + + + if((rc0 = args.test_args->getv("wtb_cfg_fname",cfg_fname)) != kOkRC ) + goto errLabel; + + if((rc0 = create(h,padN)) != kOkRC ) + goto errLabel; + + if((rc0 = load(h,cfg_fname)) != kOkRC ) + goto errLabel; + + //assert( sizeof(pitchA)/sizeof(pitchA[0]) == sizeof(velA)/sizeof(velA[0]) ); + + //gen_notes(h,instr_idx,pitchA,velA,sizeof(pitchA)/sizeof(pitchA[0]),note_dur_sec,"~/temp/temp.wav"); + + report(h); + +errLabel: + if((rc1 = destroy(h)) != kOkRC ) + { + rc1 = cwLogError(rc1,"Wave table bank destroy failed."); + } + + return rcSelect(rc0,rc1); +} diff --git a/cwWaveTableBank.h b/cwWaveTableBank.h new file mode 100644 index 0000000..e68106d --- /dev/null +++ b/cwWaveTableBank.h @@ -0,0 +1,41 @@ +#ifndef cwWaveTableBank_h +#define cwWaveTableBank_h + + +namespace cw +{ + namespace wt_bank + { + + typedef handle handle_t; + typedef dsp::sample_t sample_t; + typedef dsp::srate_t srate_t; + + typedef dsp::wt_osc::wt_tid_t wt_tid_t; + typedef struct dsp::wt_osc::wt_str wt_t; + typedef struct dsp::wt_seq_osc::wt_seq_str wt_seq_t; + typedef struct dsp::multi_ch_wt_seq_osc::multi_ch_wt_seq_str multi_ch_wt_seq_t; + + + rc_t create( handle_t& hRef, unsigned padSmpN, const char* instr_json_fname=nullptr ); + rc_t destroy( handle_t& hRef ); + + void report( handle_t h ); + + rc_t load( handle_t h, const char* instr_json_fname ); + + unsigned instr_count( handle_t h ); + + unsigned instr_index( handle_t h, const char* instr_label ); + + // Return the actual measured velocities, not the interpolated velocities, for a given instr/pitch. + rc_t instr_pitch_velocities( handle_t h, unsigned instr_idx, unsigned pitch, unsigned* velA, unsigned velCnt, unsigned& velCnt_Ref ); + + rc_t get_wave_table( handle_t h, unsigned instr_idx, unsigned pitch, unsigned vel, multi_ch_wt_seq_t const* & mcs_Ref ); + + rc_t test( const test::test_args_t& args ); + + } +} + +#endif diff --git a/cwWaveTableNotes.cpp b/cwWaveTableNotes.cpp new file mode 100644 index 0000000..7998eef --- /dev/null +++ b/cwWaveTableNotes.cpp @@ -0,0 +1,290 @@ +#include "cwCommon.h" +#include "cwLog.h" +#include "cwCommonImpl.h" +#include "cwTest.h" +#include "cwMem.h" +#include "cwFile.h" +#include "cwText.h" +#include "cwObject.h" +#include "cwFileSys.h" +#include "cwAudioFile.h" +#include "cwMath.h" +#include "cwVectOps.h" +#include "cwMidi.h" +#include "cwDspTypes.h" +#include "cwDsp.h" +#include "cwAudioTransforms.h" +#include "cwWaveTableBank.h" +#include "cwWaveTableNotes.h" + +cw::rc_t cw::wt_note::gen_note( wt_bank::handle_t wtbH, + unsigned instr_idx, + unsigned midi_pitch, + unsigned velocity, + srate_t srate, + sample_t** audioChA, + unsigned audioChN, + unsigned audioFrmN ) +{ + rc_t rc = kOkRC; + unsigned chN = audioChN; // output audio channels + const wt_bank::multi_ch_wt_seq_t* mcs = nullptr; // wave table ptr + sample_t* aM = nullptr; // temp. audio buffer + const unsigned kDspFrmN = 64; // default frame request count + unsigned reqFrmN = 0; // count of sample frames requestdd + unsigned retFrmN = 0; // countof sample frames returned + unsigned audioChFrmIdx = 0; // current audio frame index into audioChA[] + audiofile::handle_t afH; + + // multi-channel wave table oscillator + struct dsp::multi_ch_wt_seq_osc::obj_str osc; + + // get the requested wave table + if((rc = wt_bank::get_wave_table( wtbH, instr_idx, midi_pitch, velocity, mcs )) != kOkRC ) + { + goto errLabel; + } + + // if the wave table has fewer channels than the output audio buffer + if( mcs->chN < chN ) + chN = mcs->chN; + + // allocate,setup and validate the expected srate of the oscillator + if((rc = create(&osc,chN,mcs,srate)) != kOkRC ) + { + rc = cwLogError(rc,"multi-ch-wt-seq-osc create failed."); + goto errLabel; + } + + // allocate a tempory audio buffer + aM = mem::allocZ(chN*kDspFrmN); + + // for each sample in the output signal + while( retFrmN==reqFrmN && audioChFrmIdx < audioFrmN ) + { + // calc. the count of requested output audio frames on this iteration + reqFrmN = std::min(kDspFrmN,audioFrmN-audioChFrmIdx); + + // generate reqFrmN output samples with the oscillator + if((rc = process(&osc, aM, chN, reqFrmN, retFrmN)) != kOkRC ) + goto errLabel; + + // copy the generated signals into the output signal + for(unsigned i=0; idelta_sec; + + unsigned beg_frm_idx = (unsigned)floor(secs*srate); + unsigned dur_frm_cnt = (unsigned)floor(n->dur_sec*srate); + + if( beg_frm_idx > outFrmN ) + { + // TODO: + assert(0); + } + + if( beg_frm_idx + dur_frm_cnt > outFrmN ) + { + // TODO: + assert(0); + } + + for(unsigned j=0; jinstr_idx, n->pitch, n->velocity, srate, chA, outChN, dur_frm_cnt)) != kOkRC ) + { + rc = cwLogError(rc,"Generate note failed on 'instr:%i pitch:%i vel:%i.",n->instr_idx, n->pitch, n->velocity); + goto errLabel; + } + + } + +errLabel: + cwLogInfo("Generated %6.1f seconds of audio.",secs); + return rc; +} + +cw::rc_t cw::wt_note::gen_notes( wt_bank::handle_t wtbH, + const note_t* noteA, + unsigned noteN, + srate_t srate, + unsigned audioChN, + const char* out_audio_fname, + unsigned audio_bits) +{ + rc_t rc = kOkRC; + unsigned audioFrmN = 0; + sample_t* audioM = nullptr; + double max_sec = 0; + sample_t* chA[ audioChN ]; + + double secs = 0; + for(unsigned i=0; i max_sec ) + max_sec = secs+noteA[i].dur_sec; + } + + cwLogInfo("Allocated %i notes in %6.1f seconds of audio.",noteN,max_sec); + + audioFrmN = (unsigned)ceil( max_sec * srate ); + audioM = mem::allocZ( audioFrmN * audioChN ); + + for(unsigned i=0; i max_pitch ) + { + rc = cwLogError(kInvalidArgRC,"The min pitch:%i is greater than the max pitch:%i.",min_pitch,max_pitch); + goto errLabel; + } + + if((rc = wt_bank::create(wtbH,2,wtb_json_fname)) != kOkRC ) + { + goto errLabel; + } + + if(!filesys::isDir(out_dir)) + filesys::makeDir(out_dir); + + for(unsigned midi_pitch=min_pitch; midi_pitch<=max_pitch; ++midi_pitch) + { + unsigned velA[ midi::kMidiVelCnt ]; + unsigned velCnt = 0; + double secs = 0; + const unsigned fnameN = 32; + char fname[fnameN+1]; + + // get the sampled velocities for the instr_idx/midi_pitch + if((rc = wt_bank::instr_pitch_velocities( wtbH, instr_idx, midi_pitch, velA, midi::kMidiVelCnt, velCnt )) != kOkRC ) + { + goto errLabel; + } + + // allocate the note records array + noteA = mem::resizeZ(noteA,velCnt); + + // fill in the note record array with one note for each sampled velocity + 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; @@ -156,7 +162,7 @@ namespace cw break; case LWS_CALLBACK_ESTABLISHED: - cwLogInfo("Websocket session:%i opened: \n",ws->_nextSessionId); + cwLogInfo("Websocket session:%i opened",ws->_nextSessionId); sess->id = ws->_nextSessionId++; sess->protocolId = proto->id; @@ -171,7 +177,7 @@ namespace cw break; case LWS_CALLBACK_CLOSED: - cwLogInfo("Websocket connection closed.\n"); + cwLogInfo("Websocket connection closed."); ws->_connSessionN -= 1; @@ -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,10 @@ cw::rc_t cw::websock::create( const char* dfltHtmlPageFn, int port, const protocol_t* protocolArgA, - unsigned protocolN ) + unsigned protocolN, + unsigned queueBlkCnt, + unsigned queueBlkByteCnt, + bool extraLogsFl ) { rc_t rc; struct lws_context_creation_info info; @@ -540,8 +607,11 @@ cw::rc_t cw::websock::create( return rc; websock_t* p = mem::allocZ(); - - int logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE; + + int logs = LLL_USER | LLL_ERR | LLL_WARN; + if( extraLogsFl ) + logs |= LLL_NOTICE; + lws_set_log_level(logs, nullptr); p->_event_loop_ops_custom = {}; @@ -575,11 +645,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 +678,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 +727,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 +790,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 +865,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..f4e528e 100644 --- a/cwWebSock.h +++ b/cwWebSock.h @@ -45,7 +45,10 @@ namespace cw const char* dfltHtmlPageFn, int port, const protocol_t* protocolA, - unsigned protocolN ); + unsigned protocolN, + unsigned queueBlkCnt, + unsigned queueBlkByteCnt, + bool extraLogsFl); rc_t destroy( handle_t& h ); @@ -56,6 +59,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..e3d1528 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,10 @@ cw::rc_t cw::websockSrv::create( int port, const websock::protocol_t* protocolA, unsigned protocolN, - unsigned timeOutMs ) + unsigned timeOutMs, + unsigned queueBlkCnt, + unsigned queueBlkByteCnt, + bool extraLogsFl ) { rc_t rc; if((rc = destroy(h)) != kOkRC ) @@ -64,7 +68,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, extraLogsFl )) != kOkRC ) goto errLabel; @@ -176,6 +180,9 @@ cw::rc_t cw::websockSrvTest( const object_t* cfg ) int port = 5687; unsigned rcvBufByteN = 128; unsigned xmtBufByteN = 128; + unsigned queueBlkCnt = 3; + unsigned queueBlkByteCnt= 4096; + bool extraLogsFl = true; appCtx_t appCtx; enum @@ -198,7 +205,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, extraLogsFl )) != kOkRC ) return rc; appCtx.wsH = websockSrv::websockHandle(h); diff --git a/cwWebSockSvr.h b/cwWebSockSvr.h index fdab325..120a491 100644 --- a/cwWebSockSvr.h +++ b/cwWebSockSvr.h @@ -16,7 +16,10 @@ namespace cw { int port, const websock::protocol_t* protocolA, unsigned protocolN, - unsigned websockTimeOutMs ); + unsigned websockTimeOutMs, + unsigned queueBlkCnt, + unsigned queueBlkByteCnt, + bool extraLogsFl ); rc_t destroy( handle_t& h ); diff --git a/flow/flow_doc.md b/flow/flow_doc.md new file mode 100644 index 0000000..1aea91a --- /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. + | Each variable is given a unique suffix id. Variables that do not have + | this property may only be instantiated once per proc-instance - albeit with possibly mulitple channels. +`out` | This is a subnet output variable. [See Subnet Implementation](#subnet-implementation) +`no_ch` | This variable will have only a single value and 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: + +``` +