Merge branch 'poly' of gitea.larke.org:kevin/libcw into poly

# Conflicts:
#	cwFlow.cpp
#	cwIoPresetSelApp.cpp
#	cwLex.cpp
#	cwLex.h
#	cwPresetSel.cpp
#	html/preset_sel/ui.cfg
This commit is contained in:
kevin 2024-05-29 14:06:54 -04:00
commit 8dde88f6fd
108 changed files with 12440 additions and 3418 deletions

View File

@ -5,7 +5,7 @@ libcwHDR += src/libcw/cwCommon.h src/libcw/cwCommonImpl.h src/libcw/cwMem.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
libcwSRC += src/libcw/cwString.cpp src/libcw/cwMath.cpp src/libcw/cwVectOps.cpp src/libcw/cwMtx.cpp src/libcw/cwVariant.cpp
libcwHDR += src/libcw/cwB23Tree.h
libcwSRC += src/libcw/cwB23Tree.cpp
@ -34,8 +34,8 @@ libcwSRC += src/libcw/cwAudioFile.cpp src/libcw/cwMidiFile.cpp
libcwHDR += src/libcw/cwAudioFileOps.h src/libcw/cwAudioTransforms.h src/libcw/cwDspTransforms.h src/libcw/cwAudioFileProc.h src/libcw/cwPvAudioFileProc.h
libcwSRC += src/libcw/cwAudioFileOps.cpp src/libcw/cwAudioTransforms.cpp src/libcw/cwDspTransforms.cpp src/libcw/cwAudioFileProc.cpp src/libcw/cwPvAudioFileProc.cpp
libcwHDR += src/libcw/cwFlow.h src/libcw/cwFlowDecl.h src/libcw/cwFlowTypes.h src/libcw/cwFlowProc.h src/libcw/cwFlowCross.h
libcwSRC += src/libcw/cwFlow.cpp src/libcw/cwFlowTypes.cpp src/libcw/cwFlowProc.cpp src/libcw/cwFlowCross.cpp
libcwHDR += src/libcw/cwFlow.h src/libcw/cwFlowDecl.h src/libcw/cwFlowTypes.h src/libcw/cwFlowNet.h src/libcw/cwFlowProc.h src/libcw/cwFlowCross.h src/libcw/cwFlowTest.h
libcwSRC += src/libcw/cwFlow.cpp src/libcw/cwFlowTypes.cpp src/libcw/cwFlowNet.cpp src/libcw/cwFlowProc.cpp src/libcw/cwFlowCross.cpp src/libcw/cwFlowTest.cpp
if cwWEBSOCK
libcwHDR += src/libcw/cwWebSock.h src/libcw/cwWebSockSvr.h
@ -46,8 +46,8 @@ libcwSRC += src/libcw/cwUi.cpp src/libcw/cwUiTest.cpp
endif
libcwHDR += src/libcw/cwKeyboard.h
libcwSRC += src/libcw/cwKeyboard.cpp
libcwHDR += src/libcw/cwKeyboard.h src/libcw/cwTest.h
libcwSRC += src/libcw/cwKeyboard.cpp src/libcw/cwTest.cpp
libcwHDR += src/libcw/cwSerialPortDecls.h src/libcw/cwSerialPort.h src/libcw/cwSerialPortSrv.h
libcwSRC += src/libcw/cwSerialPort.cpp src/libcw/cwSerialPortSrv.cpp

680
README.md
View File

@ -432,7 +432,7 @@ struct in_addr {
export LD_LIBRARY_PATH=~/sdk/libwebsockets/build/out/lib
*** Raspberry Pi Build Notes:
### Raspberry Pi Build Notes:
cd sdk
mkdir libwebsockets
@ -444,10 +444,10 @@ struct in_addr {
*** Flow Notes:
### Flow Notes:
- When a variable has a variant with a numberic channel should the 'all' channel variant be removed?
- When a variable has a variant with a numeric channel should the 'all' channel variant be removed?
- Check for duplicate 'vid'-'chIdx' pairs in var_regster().
(The concatenation of 'vid' and 'chIdx' should be unique
@ -469,16 +469,21 @@ specific types, to pass through. For example a 'selector' (n inputs, 1 output) o
DONE: Add a version of var_register() that both registers and returns the value of the variable.
*** Flow Instance Creation:
### Flow Instance Creation:
1. Create all vars from the class description and initially set their
value to the default value given in the class. chIdx=kAnyChIdx.
0. Parse the 'in' list and create any 'mult' variables whose
'in-var' contains an integer or underscore suffix. See
"'in' List Syntax and Semantics" below.
1. Create all vars from the class description, that were not
already instantiated during 'in' list processing, and set their
initial value to the default value given in the class. chIdx=kAnyChIdx.
Note that all vars must be included in the class description.
2. Apply the preset record from the class description according to the
label given in the instance definition.
2. Apply the preset records from the class description according to the
'presets' list given in the instance definition.
If the variable values are given as a scalar then the existing
variable is simply given a new value.
@ -489,17 +494,23 @@ index of the value in the list. This is referred
to as 'channelizing' the variable because the variable will then
be represented by multiple physical variable records - one for each channel.
This means that all variables will have their initial record, with the chIdx set to 'any',
and then they may also have further variable records will for each explicit
and then they may also have further variable records for each explicit
channel number. The complete list of channelized variable record
is kept, in channel order, using the 'ch_link' links with the base of the list
on the 'any' record.
3. Apply the variable values defined in the instance 'args' record.
This application is treated similarly to the 'class'
preset. If the variable value is presented in a list then
the value is assigned to a specific channel if the channel
already exists then the value is simply replaced, if the
channel does not exist then the variable is 'channelized'.
3. Apply the variable values defined in a instance 'args' record.
The 'args' record may have multiple sets of args.
If the preset instance includes an 'argsLabel' value then this record
is selected to be applied. If No 'argsLabel' is given then
the record named 'default' is selected. If there is no record
named 'default' then no record is applied.
The application of the args record proceeds exactly the same as
applying a 'class' preset. If the variable value is presented in a
list then the value is assigned to a specific channel. If the channel
already exists then the value is simply replaced. If the channel does
not exist then the variable is 'channelized'.
4. The varaibles listed in the 'in' list of the instance cfg.
are connected to their source variables.
@ -518,6 +529,645 @@ before registering the variable.
6. The internal variable id map is created to implement fast
access to registered variables.
7. The
# Notes on 'poly' and 'mult':
The 'in' statement is formed by a list of _Connect Expressions_ :
`<input_var>:<source_inst.source_var>`
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:
`(<proc_label><proc_label_sfx_id>)*,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 `<proc_label_sfx_id>` without
asking for it at runtime. For example for the cross-fader control the
application must ask for the `<proc_label_sfx_id>` current or next
poly channel depending on which one it is targetting.
It is notable that any proc with an internal network has
to solve this problem. The problem is especially acute
for proc's which change the 'current' poly channel at runtime.
The alternative is to include the concept of special values
in the address (e.g. kInvalidIdx means the application isn't
sure and the network should decide how to resolve the address)
The problem with this is that the information
to make that decision may require more information than
just a simple 'special value' can encode. It also means
complicating the var set/get pipeline with 'escape' routines.
There are at least two known use cases which need to address
this issue:
1. The cross-fader: The application may wish to address
updates to the current or next poly channel but this
channel can't be determined until runtime.
- The application asks for the current or next `proc_label_sfx_id`
at runtime depending on what its interested in doing,
and sets the update address accordingly.
- Two interface objects are setup as sources for the `xfade_ctl`
object. The address of each of these objects can be
determined prior to runtime. The application then simply addresses
the object corresponding to method (direct vs deferred) it requires.
This solution is particularly appealing because it means that
presets may be completely resolved to their potential
target procs (there would be up to 'poly-count' potential targets)
prior to runtime.
As it stands now the problem with this approach is that it
does not allow for the message to be resolved to its final
destination. If the message is addressed to a proxy proc
then that proxy must mimic all the vars on the object which
it is providing an interface for. (This is actually possible
and may be a viable solution???)
One solution to this is to create a data type which is an
address/value packet. The packet would then be directed
to a router which would in turn use the value to forward
the packet to the next destination. Each router that the
packet passed through would strip off a value and
pass along the message. This is sensible since the 'value'
associated with a router is in fact another address.
2. The polyphonic sampler:
- How can voices be addressed once they are started?
+ A given note is started - how do we later address that note to turn it off?
Answer: MIDI pitch and channel - only one note may be sounding on a given MIDI pitch and channel at any one time.
- Extending ths idea to the xfader: There are two channels: current and deferred,
but which are redirected to point to 2 of the 3 physical channels .... this would
require the idea of 'redirected' networks, i.e. networks whose proc lists were
really pointers to the physical procs.
- sd_poly maintains the physical networks as it is currently implemnted.
- xfade_ctl maintains the redirected networks - requests for proc/var addresses
on the redirected networks will naturally resolve to physical networks.
- Required modifications:
+ variable getters and setters must use a variable args scheme specify the var address:
`(proc-name,proc-sfx-id)*, var-name,var-sfx-id`
Example: `xfad_ctl,0,pva,1,wnd_len,0,0`
- The first 0 is known because there is only one `xfad_ctl`.
- The 1 indicates the 'deferred' channel.
- The second 0 is known because there is only one `wnd_len` per `pva`.
- The third 0 indicates the channel index of the var.
+ the address resolver must then recognize how to follow internal networks
+ Networks must be maintained as lists of pointers to procs
rather than a linked list of physical pointers.
+ `xfade_ctl` must be instantiated after `sd_poly` and be able
to access the internal network built by `sd_poly`.
Generalizing the Addressing
---------------------------
Change the set/get interface to include a list of (proc-label,proc-sfx-id)
to determine the address of the var.
Note that this still requires knowing the final address in advance.
In general a router will not know how to resolve a msg to the
next destination without having a final address.
In otherwords setting 'proc-sfx-id' to kInvalidId is not
resolvable without more information.
### TODO:
- Check for illegal variable names in class descriptions. (no periods, trailing digits, or trailing underscores)
- Check for unknown fields where the syntax clearly specifies only certain options.
- Class presets cannot address 'mult' variables (maybe this is ok since 'mult' variables are generally connected to a source.).
- Documentation w/ examples.
+ Write the rules for each implementing member function.
- value() should return a special return-code value to indicate that the
value should not be updated and distinguish it from an error code - which should stop the system.
- flow classes and variable should have a consistent naming style: camelCase or snake_case.
- String assignment is allocating memory:
See: `rc_t _val_set( value_t* val, const char* v ) cwFlowTypes.cpp line:464.`
- Variable attributes should be meaningful. e.g. src,src_opt,mult,init, ....
Should we check for 'src' or 'mult' attribute on var's?
(In other words: Enforce var attributes.)
- Reduce runtime overhead for var get/set operations.
- Implement matrix types.
- Should the `object_t` be used in place of `value_t`?
- Allow min/max limits on numeric variables.
- log:
+ should print the values for all channels - right now it is only
printing the values for kAnyChIdx
+ log should print values for abuf (mean,max), fbuf (mean,max) mag, mbuf
- DONE: Compile presets: at load time the presets should be resolved
to the proc and vars to which they will be assigned.
- DONE: (We are not removing the kAnyChIdx)
Should the var's with multiple channels remove the 'kAnyChIdx'?
This may be a good idea because 'kAnyChIdx' will in general not be used
if a var has been channelized - and yet it is possible for another
var to connect to it as a source ... which doesn't provoke an error
but would almost certainly not do what the user expects.
Note that the kAnyChIdx provides an easy way to set all of the channels
of a variable to the same value.
- DONE: verifiy that all proc variables values have a valid type - (i.e. (type & typeMask) != 0)
when the proc instance create is complete. This checks that both the type is assigned and
a valid value has been assigned - since the type is assigned the first time a value is set.
- DONE: 'poly' should be implemented as a proc-inst with an internal network - but the
elements of the network should be visible outside of it.
- DONE: 'sub' should be implemented as proc-inst with an internal network, but the
elements of the network should not be visible outside of it. Instead it should
include the idea of input and output ports which act as proxies to the physical
ports of the internal elements.
- DONE: 'poly' and 'sub' should be arbitrarily nestable.
- DONE: Allow multiple types on an input.
For example 'adder' should have a single input
which can by any numeric type.
- DONE: Make a standard way to turn on output printing from any port on any instance
This might be a better approach to logging than having a 'printer' object.
Add proc instance field: `log:{ var_label_0:0, var_label_1:0 } `
Next:
- Complete subnets:
+ Subnets should have presets written in terms of the subnet vars rather than the network vars
or the value application needs to follow the internal variable src_var back to the proxy var.
+ DONE: write a paragraph in the flow_doc.md about overall approach taken to subnet implementation.
+ DONE: subnet var desc's should be the same as non+subnet vars but also include the 'proxy' field.
In particular they should get default values.
If a var desc is part of a subnet then it must have a proxy.
The output variables of var desc's must have the 'out' attribute
+ DONE: improve the subnet creating code by using consistent naming + use proxy or wrap but not both
+ DONE: improve code comments on subnet creation
- Audio inputs should be able to be initialized with a channel count and srate without actually connecting an input.
This will allow feedback connections to be attached to them at a later stage of the network
instantiation.
- Remove the multiple 'args' thing and and 'argsLabel'. 'args' should be a simple set of arg's.
- Implement subnet preset application.
- Implement the var attributes and attribute checking.
- Implement dynamic loading of procs.
- Implement a debug mode to aid in building networks and subnets (or is logging good enough)
- Implement multi-field messages.
- Look more closely at the way of identify an in-stmt src-net or a out-stmt in-net.
It's not clear there is a difference between specifying `_` and the default behaviour.
Is there a way to tell it to search the entire network from the root? Isn't that
what '_' is supposed to do.
- DONE: Implement feedback
- DONE: Implement the ability to set backward connections - from late to early proc's.
This can be done by implementing the same process as 'in_stmt' but in a separate
'out_stmt'. The difficulty is that it prevents doing some checks until the network
is completely specified. For example if audio inputs can accept connections from
later proc's then they will not have all of their inputs when they are instantiated.
One way around this is to instantiate them with an initial set of inputs but then
allow those inputs to be replaced by a later connection.
BUGS:
- The counter modulo mode is not working as expected.
Host Environments:
------------------
- CLI, no GUI, no I/O, non-real-time only.
- CLI, no GUI, w/ I/O and real-time
- GUI, with configurable control panels
- DONE: Implement 'preset' proc. This will involve implementing the 'cfg' datatype.
- DONE: Finish the 'poly' frawework. We are making 'mult' var's, but do any of the procs explicitly deal with them?
- DONE: Turn on variable 'broadcast'. Why was it turned off? ... maybe multiple updates?
- DONE: There is no way for a proc in a poly context to use it's poly channel number to
select a mult variable. For example in an osc in a poly has no way to select
the frequency of the osc by conneting to a non-poly proc - like a list.
Consider:
1. Use a difference 'in' statememt (e.g. 'poly-in' but the
same syntax used for connecting 'mult' variables.)
2. Include the proc name in the 'in' var to indicate a poly index is being iterated
e.g. `lfo: { class:sine_tone, in:{ osc_.dc:list.value_ } }`
- DONE: Fix up the coding language - change the use of `instance_t` to `proc_t` and `inst` to `proc`, change use of `ctx` in cwFlowProc
Prior to executing the custom constructor the values are assigned to the
variables as follows:
1. Default value as defined by the class are applied when the variable is created.
2. The proc instance 'preset' class preset is applied.
3. The proc instance 'args' values are applied.
During this stage of processing preset values may be given for
variables that do not yet exist. This will commonly occur when a
variable has multiple channels that will not be created until the
custom constructor is run. For these cases the variable will be
pre-emptively created and the preset value will be applied.
This approach has the advantage of communicating network information
to the proc constructor from the network configuration - thereby
allowing the network programmer to influence the configuration of the
proc. instance.
DONE: After the network is fully instantiated the network and class presets
are compiled. At this point all preset values must be resolvable to
an actual proc variable. A warning is issued for presets with values
that cannot be resolved and they are disabled. The primary reason
that a preset might not be resolvable is by targetting a variable
channel that does not exist.
- How much of the proc initialization implementation can use the preset compile/apply code?
- DONE: All cfg to value conversion should go through `cfg_to_value()`.
Names:
ixon -
hoot
caw, screech, warble, coo, peep, hoot, gobble, quack, honk, whistle, tweet, cheep, chirrup, trill, squawk, seet,
cluck,cackle,clack
cock-a-dooodle-doo
song,tune,aria
caw by example:
0. DONE: Add log object.
DONE: Add initial network preset selection system parameter.
1. sine->af
in-stmt
2. sine->af with network preset
topics:preset, class info
3. number,timer,counter,list,log
topics: log, data types, system parameters
4. sine->delay->mixer->af
-------->
topics: mult vars, system parameters
5. topic: modulate sine with sine
6. topic: modulate sine with sine and timed preset change.
7. topic: iterating input stmt 0 - connect multiple inputs to a single source
8. topic: iterating input stmt 1 - multiple inputs to multiple sources
9. topic: iterating input stmt 2 - two ranges
12. topic: poly
13. topic: poly w/iterating input stmt
14. topic: poly w/ xfade ctl and presets
15. topic: msg feedback
16. topic: audio feedback
17. topic: subnets
18. topic: subnet with presets
19. topic: presets w/ sfx id's

View File

@ -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"

View File

@ -1,6 +1,7 @@
#include "cwCommon.h"
#include "cwLog.h"
#include "cwCommonImpl.h"
#include "cwTest.h"
#include "cwMem.h"
#include "cwTime.h"
#include "cwAudioDevice.h"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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;

View File

@ -138,8 +138,8 @@ namespace cw
{
#define cwAssert(C) while(1){ if(!(C)){ cwLogFatal(kAssertFailRC,"Assert failed on condition:%s",#C ); } break; }
#define cwAssert(C) while(1){ if(!(C)) { cwLogFatal(kAssertFailRC,"Assert failed on condition:%s",#C ); } break; }
#define cwRuntimeCheck(C) while(1){ if(!(C)) { rc=cwLogError(kAssertFailRC,"Runtime error check failed on condition:%s",#C); goto errLabel; } break; }
@ -160,6 +160,9 @@ namespace cw
} idLabelPair_t;
// Return nullptr if id is not found.
const char* idToLabelNull( const idLabelPair_t* array, unsigned id, unsigned eolId );
// Returns label in 'eolId' slot if id is not found.
const char* idToLabel( const idLabelPair_t* array, unsigned id, unsigned eolId );
// Returns eolId if the id is not found.

View File

@ -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"

View File

@ -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"

View File

@ -1,6 +1,7 @@
#include "cwCommon.h"
#include "cwLog.h"
#include "cwCommonImpl.h"
#include "cwTest.h"
#include "cwMem.h"
#include "cwTime.h"

View File

@ -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"

View File

@ -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<obj_t>();
@ -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<obj_t>();
@ -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<n; ++i)
{
@ -227,7 +228,7 @@ cw::rc_t cw::dsp::limiter::exec( obj_t* p, const sample_t* x, sample_t* y, unsig
// dc-filter
//
cw::rc_t cw::dsp::dc_filter::create( obj_t*& p, real_t srate, unsigned procSmpCnt, real_t gain, bool bypassFl )
cw::rc_t cw::dsp::dc_filter::create( obj_t*& p, srate_t srate, unsigned procSmpCnt, coeff_t gain, bool bypassFl )
{
p = mem::allocZ<obj_t>();
@ -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<sample_t,real_t>(y,n,x,n,p->b0, p->b, p->a, p->d, 1 );
vop::filter<sample_t,coeff_t>(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<obj_t>();
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);

View File

@ -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 );
}
}

View File

@ -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

View File

@ -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"

View File

@ -2,6 +2,7 @@
#include "cwLog.h"
#include "cwCommonImpl.h"
#include "cwTest.h"
#include "cwMem.h"
#include "cwTime.h"

View File

@ -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"

View File

@ -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;
}

View File

@ -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 );
}
}

2141
cwFlow.cpp

File diff suppressed because it is too large Load Diff

View File

@ -8,43 +8,6 @@ namespace cw
typedef handle<struct flow_str> 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 );
@ -52,8 +15,10 @@ namespace cw
rc_t create( handle_t& hRef,
const object_t& classCfg,
const object_t& networkCfg,
const object_t* classCfg,
const object_t* networkCfg,
const object_t* subnetCfg = nullptr,
const char* projDir = nullptr,
external_device_t* deviceA = nullptr,
unsigned deviceN = 0);
@ -68,8 +33,8 @@ namespace cw
rc_t exec( handle_t h );
rc_t apply_preset( handle_t h, const char* presetLabel );
rc_t apply_preset( handle_t h, const multi_preset_selector_t& multi_preset_sel );
rc_t apply_dual_preset( handle_t h, const char* presetLabel_0, const char* presetLabel_1, double coeff );
rc_t apply_preset( handle_t h, const multi_preset_selector_t& multi_preset_sel );
rc_t set_variable_value( handle_t h, const char* inst_label, const char* var_label, unsigned chIdx, bool value );
@ -87,7 +52,6 @@ namespace cw
void print_class_list( handle_t h );
void print_network( handle_t h );
rc_t test( const object_t* cfg );

View File

@ -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,11 +31,12 @@ 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;
@ -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; i<devN; ++i)
if( devA[i].typeId == flow::kAudioDevTypeId )
{
switch( devA[i].typeId )
{
case flow::kAudioDevTypeId:
devA[i].u.a.abuf = _clone_abuf( srcDevA[i].u.a.abuf );
break;
case flow::kMidiDevTypeId:
devA[i].u.m = srcDevA[i].u.m;
break;
default:
break;
}
}
return devA;
}
@ -130,7 +147,7 @@ namespace cw
net->stateId = net_idx == 0 ? kActiveStateId : kInactiveStateId;
net->net_idx = net_idx;
if((rc = flow::create( net->flowH, classCfg, networkCfg, net->deviceA, deviceN )) == kOkRC )
if((rc = flow::create( net->flowH, &classCfg, &networkCfg, nullptr, nullptr, net->deviceA, deviceN )) == kOkRC )
net->deviceN = deviceN;
else
{
@ -203,9 +220,20 @@ namespace cw
if( net->stateId == kFadeOutStateId && ef == 0.0 )
net->stateId = kInactiveStateId;
}
// Copy audio from the actual external audio device to a cloned audio device
void _update_midi_input( flow_cross_t* p, flow_network_t* net, unsigned devIdx )
{
flow::midi_dev_cfg_t& src = p->deviceA[devIdx].u.m; // src MIDI device
flow::midi_dev_cfg_t& dst = net->deviceA[devIdx].u.m; // dst MIDI device clone
// redirect the MIDI msg list array to the clones
dst.msgArray = src.msgArray;
dst.msgCnt = src.msgCnt;
}
// Copy audio from the actual external audio device to a cloned audio device
void _update_audio_input( flow_cross_t* p, flow_network_t* net, unsigned devIdx )
{
flow::abuf_t* src = p->deviceA[devIdx].u.a.abuf;
@ -216,6 +244,7 @@ namespace cw
//_fade_audio( src, dst, net );
}
void _zero_audio_output( flow_cross_t* p, flow_network_t* net, unsigned devIdx )
{
flow::abuf_t* dst = net->deviceA[devIdx].u.a.abuf;
@ -378,12 +407,26 @@ cw::rc_t cw::flow_cross::exec_cycle( handle_t h )
{
flow_network_t* net = p->netA + i;
for(unsigned j=0; j<p->deviceN; ++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
for(unsigned j=0; j<p->deviceN; ++j)
if( p->deviceA[j].typeId == flow::kAudioDevTypeId && cwIsFlag(p->deviceA[j].flags, flow::kInFl ) )
// 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; j<p->deviceN; ++j)

View File

@ -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,

3591
cwFlowNet.cpp Normal file

File diff suppressed because it is too large Load Diff

87
cwFlowNet.h Normal file
View File

@ -0,0 +1,87 @@
#ifndef cwFlowNet_h
#define cwFlowNet_h
namespace cw
{
namespace flow
{
typedef enum {
kNetFirstPolyOrderId,
kProcFirstPolyOrderId
} network_order_id_t;
rc_t network_create( flow_t* p,
const object_t* networkCfg,
network_t& net, // Network object to be filled with new proc instances
variable_t* proxyVarL, //
unsigned polyCnt = 1, // Count of networks to create
network_order_id_t orderId = kNetFirstPolyOrderId // Set the network exec order.
);
rc_t network_destroy( network_t& net );
const object_t* find_network_preset( const network_t& net, const char* presetLabel );
rc_t exec_cycle( network_t& net );
rc_t get_variable( network_t& net, const char* inst_label, const char* var_label, unsigned chIdx, proc_t*& instPtrRef, variable_t*& varPtrRef );
template< typename T >
rc_t set_variable_value( network_t& net, const char* inst_label, const char* var_label, unsigned chIdx, T value )
{
rc_t rc = kOkRC;
proc_t* inst = nullptr;
variable_t* var = nullptr;
// get the variable
if((rc = get_variable(net,inst_label,var_label,chIdx,inst,var)) != kOkRC )
goto errLabel;
// set the variable value
if((rc = var_set( inst, var->vid, chIdx, value )) != kOkRC )
{
rc = cwLogError(kOpFailRC,"The variable set failed on instance:'%s' variable:'%s'.",cwStringNullGuard(inst_label),cwStringNullGuard(var_label));
goto errLabel;
}
errLabel:
return rc;
}
template< typename T >
rc_t get_variable_value( network_t& net, const char* inst_label, const char* var_label, unsigned chIdx, T& valueRef )
{
rc_t rc = kOkRC;
proc_t* inst = nullptr;
variable_t* var = nullptr;
// get the variable
if((rc = get_variable(net,inst_label,var_label,chIdx,inst,var)) != kOkRC )
goto errLabel;
// get the variable value
if((rc = var_get( inst, var->vid, chIdx, valueRef )) != kOkRC )
{
rc = cwLogError(kOpFailRC,"The variable get failed on instance:'%s' variable:'%s'.",cwStringNullGuard(inst_label),cwStringNullGuard(var_label));
goto errLabel;
}
errLabel:
return rc;
}
// 'proc_label_sfx_id' is the proc label_sfx_id to be used to identify all proc's which will
// be updated by the preset application. This is used to identify the set of procs to be updated
// for 'poly' networks.
// If 'proc_label_sfx_id' is set to 'kInvalidId' then the preset will be applied to all proc's.
rc_t network_apply_preset( network_t& net, const char* presetLabel, unsigned proc_label_sfx_id=kInvalidId );
rc_t network_apply_dual_preset( network_t& net, const char* presetLabel_0, const char* presetLabel_1, double coeff, unsigned proc_label_sfx_id=kInvalidId );
rc_t network_apply_preset( network_t& net, const multi_preset_selector_t& mps, unsigned proc_label_sfx_id=kInvalidId );
}
}
#endif

File diff suppressed because it is too large Load Diff

View File

@ -2,10 +2,14 @@ namespace cw
{
namespace flow
{
namespace subnet { extern class_members_t members; }
namespace poly { extern class_members_t members; }
namespace midi_in { extern class_members_t members; }
namespace midi_out { extern class_members_t members; }
namespace audio_in { extern class_members_t members; }
namespace audio_out { extern class_members_t members; }
namespace audioFileIn { extern class_members_t members; }
namespace audioFileOut { extern class_members_t members; }
namespace audio_file_in { extern class_members_t members; }
namespace audio_file_out { extern class_members_t members; }
namespace audio_gain { extern class_members_t members; }
namespace audio_split { extern class_members_t members; }
namespace audio_merge { extern class_members_t members; }
@ -22,5 +26,16 @@ namespace cw
namespace balance { extern class_members_t members; }
namespace audio_meter { extern class_members_t members; }
namespace audio_marker { extern class_members_t members; }
namespace xfade_ctl { extern class_members_t members; }
namespace poly_merge { extern class_members_t members; }
namespace sample_hold { extern class_members_t members; }
namespace number { extern class_members_t members; }
namespace timer { extern class_members_t members; }
namespace counter { extern class_members_t members; }
namespace list { extern class_members_t members; }
namespace add { extern class_members_t members; }
namespace preset { extern class_members_t members; }
namespace print { extern class_members_t members; }
}
}

81
cwFlowTest.cpp Normal file
View File

@ -0,0 +1,81 @@
#include "cwCommon.h"
#include "cwLog.h"
#include "cwCommonImpl.h"
#include "cwTest.h"
#include "cwMem.h"
#include "cwText.h"
#include "cwNumericConvert.h"
#include "cwObject.h"
#include "cwTime.h"
#include "cwMidiDecls.h"
#include "cwMidi.h"
#include "cwFlowDecl.h"
#include "cwFlow.h"
#include "cwFlowTest.h"
cw::rc_t cw::flow::test( const test::test_args_t& args )
{
rc_t rc = kOkRC;
const char* proc_cfg_fname = nullptr;
const char* subnet_cfg_fname = nullptr;
object_t* class_cfg = nullptr;
object_t* subnet_cfg = nullptr;
handle_t flowH;
if( args.module_args == nullptr )
{
rc = cwLogError(kInvalidArgRC,"The flow test cases require module args.");
goto errLabel;
}
if((rc = args.module_args->readv("proc_cfg_fname",0,proc_cfg_fname,
"subnet_cfg_fname",0,subnet_cfg_fname)) != kOkRC )
{
rc = cwLogError(rc,"Flow module arg's parse failed.");
goto errLabel;
}
// parse the proc dict. file
if((rc = objectFromFile(proc_cfg_fname,class_cfg)) != kOkRC )
{
rc = cwLogError(rc,"The flow proc dictionary could not be read from '%s'.",cwStringNullGuard(proc_cfg_fname));
goto errLabel;
}
// parse the subnet dict file
if((rc = objectFromFile(subnet_cfg_fname,subnet_cfg)) != kOkRC )
{
rc = cwLogError(rc,"The flow subnet dictionary could not be read from '%s'.",cwStringNullGuard(subnet_cfg_fname));
goto errLabel;
}
// create the flow object
if((rc = create( flowH, class_cfg, args.test_args, subnet_cfg, args.out_dir)) != kOkRC )
{
rc = cwLogError(rc,"Flow object create failed.");
goto errLabel;
}
// run the network
if((rc = exec( flowH )) != kOkRC )
rc = cwLogError(rc,"Execution failed.");
errLabel:
// destroy the flow object
if((rc = destroy(flowH)) != kOkRC )
{
rc = cwLogError(rc,"Close the flow object.");
goto errLabel;
}
if( class_cfg != nullptr )
class_cfg->free();
if( subnet_cfg != nullptr )
subnet_cfg->free();
return rc;
}

9
cwFlowTest.h Normal file
View File

@ -0,0 +1,9 @@
namespace cw
{
namespace flow
{
rc_t test( const test::test_args_t& args );
}
}

File diff suppressed because it is too large Load Diff

View File

@ -3,20 +3,28 @@ namespace cw
namespace flow
{
#define kRealTFl kFloatTFl
typedef dsp::real_t real_t;
typedef dsp::coeff_t coeff_t;
typedef dsp::sample_t sample_t;
typedef dsp::fd_real_t fd_real_t;
typedef dsp::fd_sample_t fd_sample_t;
typedef dsp::srate_t srate_t;
typedef dsp::ftime_t ftime_t;
typedef unsigned uint_t;
typedef int int_t;
typedef unsigned vid_t;
enum {
kBaseSfxId = 0,
kFbufVectN = 3, // count of signal vectors in fbuf (mag,phs,hz)
kAnyChIdx = kInvalidIdx,
kLocalValueN = 2,
kDefaultFramesPerCycle=64,
kDefaultSampleRate=48000
};
typedef struct abuf_str
{
struct value_str* base;
srate_t srate; // signal sample rate
unsigned chN; // count of channels
unsigned frameN; // count of sample frames per channel
@ -24,28 +32,27 @@ namespace cw
} abuf_t;
enum {
kFbufVectN = 3, // count of signal vectors in fbuf (mag,phs,hz)
kAnyChIdx = kInvalidIdx,
kLocalValueN = 2
};
typedef struct fbuf_str
{
struct value_str* base;
srate_t srate; // signal sample rate
unsigned flags; // See kXXXFbufFl
unsigned chN; // count of channels
unsigned* maxBinN_V; // max value that binN_V[i] is allowed to take
unsigned* binN_V; // binN_V[ chN ] count of sample frames per channel
unsigned* hopSmpN_V; // hopSmpN_V[ chN ] hop sample count
fd_real_t** magV; // magV[ chN ][ binN ]
fd_real_t** phsV; // phsV[ chN ][ binN ]
fd_real_t** hzV; // hzV[ chN ][ binN ]
fd_sample_t** magV; // magV[ chN ][ binN ]
fd_sample_t** phsV; // phsV[ chN ][ binN ]
fd_sample_t** hzV; // hzV[ chN ][ binN ]
bool* readyFlV; // readyFlV[chN] true if this channel is ready to be processed (used to sync. fbuf rate to abuf rate)
fd_real_t* buf; // memory used by this buffer (or NULL if magV,phsV,hzV point are proxied to another buffer)
fd_sample_t* buf; // memory used by this buffer (or NULL if magV,phsV,hzV point are proxied to another buffer)
} fbuf_t;
typedef struct mbuf_str
{
const midi::ch_msg_t* msgA;
unsigned msgN;
} mbuf_t;
enum
{
kInvalidTFl = 0x00000000,
@ -58,17 +65,23 @@ namespace cw
kBoolMtxTFl = 0x00000020,
kUIntMtxTFl = 0x00000040,
kIntMtxTFl = 0x00000080,
kRealMtxTFl = 0x00000100,
kFloatMtxTFl = 0x00000200,
kDoubleMtxTFl= 0x00000400,
kFloatMtxTFl = 0x00000100,
kDoubleMtxTFl= 0x00000200,
kABufTFl = 0x00000800,
kFBufTFl = 0x00001000,
kABufTFl = 0x00000400,
kFBufTFl = 0x00000800,
kMBufTFl = 0x00001000,
kStringTFl = 0x00002000,
kTimeTFl = 0x00004000,
kCfgTFl = 0x00008000,
kTypeMask = 0x00007fff,
kTypeMask = 0x0000ffff,
kRuntimeTFl = 0x80000000,
kNumericTFl = kBoolTFl | kUIntTFl | kIntTFl | kFloatTFl | kDoubleTFl,
kMtxTFl = kBoolMtxTFl | kUIntMtxTFl | kIntMtxTFl | kFloatMtxTFl | kDoubleMtxTFl,
kAllTFl = kTypeMask
};
typedef struct mtx_str
@ -76,7 +89,6 @@ namespace cw
union {
struct mtx::mtx_str< unsigned >* u;
struct mtx::mtx_str< int >* i;
struct mtx::mtx_str< real_t >* r;
struct mtx::mtx_str< float >* f;
struct mtx::mtx_str< double >* d;
} u;
@ -84,7 +96,8 @@ namespace cw
typedef struct value_str
{
unsigned flags;
unsigned tflag;
union {
bool b;
uint_t u;
@ -93,12 +106,14 @@ namespace cw
double d;
mtx_t* mtx;
abuf_t* abuf;
fbuf_t* fbuf;
mbuf_t* mbuf;
char* s;
char* fname;
const object_t* cfg;
void* p;
} u;
@ -107,19 +122,22 @@ namespace cw
} value_t;
inline bool is_numeric( const value_t* v ) { return cwIsFlag(v->flags,kBoolTFl|kUIntTFl|kIntTFl|kFloatTFl|kDoubleTFl); }
inline bool is_matrix( const value_t* v ) { return cwIsFlag(v->flags,kBoolMtxTFl|kUIntMtxTFl|kIntMtxTFl|kFloatMtxTFl|kDoubleMtxTFl); }
struct instance_str;
struct proc_str;
struct variable_str;
typedef rc_t (*member_func_t)( struct instance_str* ctx );
typedef rc_t (*member_value_func_t)( struct instance_str* ctx, struct variable_str* var );
typedef rc_t (*member_func_t)( struct proc_str* ctx );
typedef rc_t (*member_value_func_t)( struct proc_str* ctx, struct variable_str* var );
// var_desc_t attribute flags
enum
{
kSrcVarFl = 0x01,
kSrcOptVarFl = 0x02
kInvalidVarDescFl = 0x00,
kSrcVarDescFl = 0x01,
kSrcOptVarDescFl = 0x02,
kNoSrcVarDescFl = 0x04,
kInitVarDescFl = 0x08,
kMultVarDescFl = 0x10,
kSubnetOutVarDescFl = 0x20
};
typedef struct class_members_str
@ -139,53 +157,83 @@ 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;
struct class_preset_str* link;
} class_preset_t;
typedef struct class_desc_str
{
const object_t* cfg; //
const object_t* cfg; // class cfg
const char* label; // class label;
var_desc_t* varDescL; // varDescA[varDescN] value description list
preset_t* presetL; // presetA[ presetN ]
var_desc_t* varDescL; // varDescL variable description linked on var_desc_t.link
class_preset_t* presetL; // presetA[ presetN ]
class_members_t* members; // member functions for this class
unsigned polyLimitN; // max. poly copies of this class per network_t or 0 if no limit
} class_desc_t;
enum {
kInvalidVarFl = 0x00,
kLogVarFl = 0x01,
kProxiedVarFl = 0x02,
kProxiedOutVarFl = 0x04
};
// Note: The concatenation of 'vid' and 'chIdx' should form a unique identifier among all variables
// on a given 'instance'.
typedef struct variable_str
{
struct instance_str* inst; // pointer to this variables instance
struct proc_str* proc; // pointer to this variables instance
char* label; // this variables label
unsigned vid; // this variables numeric id ( cat(vid,chIdx) forms a unique variable identifier on this 'inst'
var_desc_t* varDesc; // the variable description for this variable
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
unsigned flags; // kLogVarFl
unsigned type; // This is the value type as established when the var is initialized - it never changes for the life of the var.
var_desc_t* classVarDesc; // pointer to this variables class var desc
var_desc_t* localVarDesc; // pointer to this variables local var desc - if it doesn't match classVarDesc.
var_desc_t* varDesc; // the effective variable description for this variable (set to classVarDesc or localVarDesc)
value_t local_value[ kLocalValueN ]; // the local value instance (actual value if this is not a 'src' variable)
unsigned local_value_idx; // local_value[] is double buffered to allow the cur value of the buf[] to be held while the next value is validated (see _var_set_template())
value_t* value; // pointer to the value associated with this variable
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
value_t* value; // pointer to the value associated with this variable
struct variable_str* var_link; // instance.varL list link
struct variable_str* ch_link; // list of channels that share this variable (rooted on 'any' channel - in order by channel number)
struct variable_str* dst_head; // Pointer to list of out-going connections (null on var's that do not have out-going connections)
struct variable_str* dst_tail; //
struct variable_str* dst_link; // Link used by dst_head list.
} variable_t;
typedef struct instance_str
struct network_str;
typedef struct proc_str
{
struct flow_str* ctx; // global system context
struct network_str* net; // network which owns this proc
class_desc_t* class_desc; //
const char* label; // instance label
const object_t* inst_cfg; // instance configuration
char* label; // instance label
unsigned label_sfx_id; // label suffix id (set to kBaseSfxId (0) unless poly is non-null)
const object_t* proc_cfg; // instance configuration
const char* arg_label; // optional args label
const object_t* arg_cfg; // optional args configuration
@ -199,32 +247,115 @@ namespace cw
unsigned varMapN; // varMapN = varMapIdN * varMapChN
variable_t** varMapA; // varMapA[ varMapN ] = allows fast lookup from ('vid','chIdx) to variable
struct instance_str* link;
} instance_t;
struct network_str* internal_net;
} proc_t;
// preset_value_t holds a preset value and the proc/var to which it will be applied.
typedef struct preset_value_str
{
proc_t* proc; // proc target for this preset value
variable_t* var; // var target for this preset value
value_t value; // Preset value.
unsigned pairTblIdx; // Index into the preset pair table for this preset value
struct preset_value_str* link;
} preset_value_t;
typedef struct preset_value_list_str
{
preset_value_t* value_head; // List of preset_value_t for this preset.
preset_value_t* value_tail; // Last preset value in the list.
} preset_value_list_t;
struct network_preset_str;
typedef struct dual_preset_str
{
const struct network_preset_str* pri;
const struct network_preset_str* sec;
double coeff;
} dual_preset_t;
typedef enum {
kPresetVListTId,
kPresetDualTId
} preset_type_id_t;
typedef struct network_preset_str
{
const char* label; // Preset label
preset_type_id_t tid;
union {
preset_value_list_t vlist;
dual_preset_t dual;
} u;
} network_preset_t;
// Preset-pair record used to apply dual presets.
typedef struct network_preset_pair_str
{
const proc_t* proc; //
const variable_t* var; //
unsigned chIdx; //
unsigned chN; //
const value_t* value; //
} network_preset_pair_t;
typedef struct network_str
{
const object_t* procsCfg; // network proc list
const object_t* presetsCfg; // presets designed for this network
unsigned poly_cnt; // count of duplicated networks in the list
struct proc_str** proc_array;
unsigned proc_arrayAllocN;
unsigned proc_arrayN;
network_preset_t* presetA;
unsigned presetN;
// Preset pair table used by network_apply_dual_preset()
network_preset_pair_t* preset_pairA;
unsigned preset_pairN;
} network_t;
typedef struct flow_str
{
const object_t* networkCfg; // complete cfg used to create this network
const object_t* presetCfg; // presets designed for this network
const object_t* flowCfg; // complete cfg used to create this flow
unsigned framesPerCycle; // sample frames per cycle (64)
srate_t sample_rate; // default sample rate (48000.0)
unsigned maxCycleCount; // count of cycles to run on flow::exec() or 0 if there is no limit.
const char* init_net_preset_label;// network initialization preset label or nullptr if there is no net. init. preset
bool isInRuntimeFl; // Set when compile-time is complete
unsigned cycleIndex; // Incremented with each processing cycle
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.
class_desc_t* classDescA; //
unsigned classDescN; //
class_desc_t* subnetDescA; //
unsigned subnetDescN; //
external_device_t* deviceA; // deviceA[ deviceN ] external device description array
unsigned deviceN; //
struct instance_str* network_head; // first instance
struct instance_str* network_tail; // last insance
const char* proj_dir; // default input/output directory
network_t net;
} flow_t;
//------------------------------------------------------------------------------------------------------------------------
@ -232,32 +363,58 @@ namespace cw
// 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 );
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 );
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; }
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->tflag & kABufTFl; }
inline bool value_is_fbuf( const value_t* v ) { return v->tflag & kFBufTFl; }
unsigned value_type_label_to_flag( const char* type_desc );
const char* value_type_flag_to_label( unsigned flag );
void value_print( const value_t* value, bool info_fl=false);
//------------------------------------------------------------------------------------------------------------------------
//
// Class and Variable Description
//
var_desc_t* var_desc_find( class_desc_t* cd, const char* var_label );
rc_t var_desc_find( class_desc_t* cd, const char* label, var_desc_t*& vdRef );
var_desc_t* var_desc_create( const char* label, const object_t* value_cfg );
void var_desc_destroy( var_desc_t* var_desc );
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 );
@ -265,19 +422,37 @@ namespace cw
//
// Network
//
void network_print( flow_t* p );
void network_print(const network_t& net );
const network_preset_t* network_preset_from_label( const network_t& net, const char* preset_label );
unsigned proc_mult_count( const network_t& net, const char* proc_label );
rc_t proc_mult_sfx_id_array( const network_t& net, const char* proc_label, unsigned* idA, unsigned idAllocN, unsigned& idN_ref );
//------------------------------------------------------------------------------------------------------------------------
//
// Instance
// Proc
//
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 );
void proc_destroy( proc_t* proc );
rc_t proc_validate( proc_t* proc );
void instance_print( instance_t* inst );
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 );
external_device_t* external_device_find( flow_t* p, const char* device_label, unsigned typeId, unsigned inOrOutFl, const char* midiPortLabel=nullptr );
void proc_print( proc_t* proc );
// Count of all var instances on this proc. This is a count of the length of proc->varL.
unsigned proc_var_count( proc_t* proc );
// If fname has a '$' prefix then the system project directory is prepended to it.
// If fname has a '~' then the users home directory is prepended to it.
// The returned string must be release with a call to mem::free().
char* proc_expand_filename( const proc_t* proc, const char* fname );
//------------------------------------------------------------------------------------------------------------------------
@ -286,41 +461,68 @@ namespace cw
//
// Create a variable but do not assign it a value. Return a pointer to the new variable.
// Note: `value_cfg` is optional. Set it to NULL to ignore
rc_t var_create( instance_t* inst, const char* var_label, unsigned vid, unsigned chIdx, const object_t* value_cfg, variable_t*& varRef );
// Notes:
// 1) `value_cfg` is optional. Set it to NULL to ignore
// 2) If `altTypeFl` is not set to kInvalidTFl then the var is assigned this type.
rc_t var_create( proc_t* proc, const char* var_label, unsigned sfx_id, unsigned vid, unsigned chIdx, const object_t* value_cfg, unsigned altTypeFlag, variable_t*& varRef );
void var_destroy( variable_t* var );
// Channelizing creates a new var record with an explicit channel index to replace the
// automatically generated variable whose channel index is set to 'all'.
rc_t var_channelize( instance_t* inst, const char* var_label, unsigned chIdx, const object_t* value_cfg, unsigned vid, variable_t*& varRef );
rc_t var_channelize( proc_t* proc, const char* var_label, unsigned sfx_id, unsigned chIdx, const object_t* value_cfg, unsigned vid, variable_t*& varRef );
// Wrapper around call to var->proc->members->value()
rc_t var_call_custom_value_func( variable_t* var );
// Sets and get the var->flags field
unsigned var_flags( proc_t* proc, unsigned chIdx, const char* var_label, unsigned sfx_id, unsigned& flags_ref );
rc_t var_set_flags( proc_t* proc, unsigned chIdx, const char* var_label, unsigned sfx_id, unsigned flags );
rc_t var_clr_flags( proc_t* proc, unsigned chIdx, const char* var_label, unsigned sfx_id, unsigned flags );
// `value_cfg` is optional. Set it to NULL to ignore
rc_t var_register( instance_t* inst, const char* var_label, unsigned vid, unsigned chIdx, const object_t* value_cfg, variable_t*& varRef );
rc_t var_register( proc_t* proc, const char* var_label, unsigned sfx_id, unsigned vid, unsigned chIdx, const object_t* value_cfg, variable_t*& varRef );
// Returns true if this var is connected to an external proc variable
bool is_connected_to_external_proc( const variable_t* var );
// Returns true if this var is connected to a source proc variable
bool is_connected_to_source( const variable_t* var );
// Return true if this var is acting as a source for another var.
bool is_a_source_var( const variable_t* var );
// Connect in_var to src_var.
void var_connect( variable_t* src_var, variable_t* in_var );
// Disconnect an in_var from it's source
void var_disconnect( variable_t* in_var );
// Get the count of 'mult' vars associated with this var label.
unsigned var_mult_count( proc_t* proc, const char* var_label );
// Get all the label-sfx-id's associated with a give var label
rc_t var_mult_sfx_id_array( proc_t* proc, const char* var_label, unsigned* idA, unsigned idAllocN, unsigned& idN_ref );
//-----------------
//
// var_register
//
inline rc_t _var_reg(cw::flow::instance_t*, unsigned int ) { return kOkRC; }
inline rc_t _var_reg(cw::flow::proc_t*, unsigned int ) { return kOkRC; }
template< typename T0, typename T1, typename... ARGS >
rc_t _var_reg( instance_t* inst, unsigned chIdx, T0 vid, T1 var_label, ARGS&&... args )
rc_t _var_reg( proc_t* proc, unsigned chIdx, T0 vid, T1 var_label, unsigned sfx_id, ARGS&&... args )
{
rc_t rc;
variable_t* dummy = nullptr;
if((rc = var_register( inst, var_label, vid, chIdx, nullptr, dummy )) == kOkRC )
if((rc = _var_reg( inst, chIdx, std::forward<ARGS>(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>(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>(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>(args)...); }
@ -329,28 +531,28 @@ namespace cw
// var_register_and_get
//
inline rc_t _var_register_and_get(cw::flow::instance_t*, unsigned int ) { return kOkRC; }
inline rc_t _var_register_and_get(cw::flow::proc_t*, unsigned int ) { return kOkRC; }
template< typename T>
rc_t var_register_and_get( instance_t* inst, const char* var_label, unsigned vid, unsigned chIdx, T& valRef )
rc_t var_register_and_get( proc_t* proc, const char* var_label, unsigned sfx_id, unsigned vid, unsigned chIdx, T& valRef )
{
rc_t rc;
variable_t* var;
if((rc = var_register(inst,var_label,vid,chIdx,nullptr,var)) == kOkRC )
if((rc = var_register(proc,var_label,sfx_id,vid,chIdx,nullptr,var)) == kOkRC )
rc = var_get(var,valRef);
return rc;
}
inline rc_t _var_reg_and_get(cw::flow::instance_t*, unsigned int ) { return kOkRC; }
inline rc_t _var_reg_and_get(cw::flow::proc_t*, unsigned int ) { return kOkRC; }
template< typename T0, typename T1, typename T2, typename... ARGS >
rc_t _var_reg_and_get( instance_t* inst, unsigned chIdx, T0 vid, T1 var_label, T2& valRef, ARGS&&... args )
rc_t _var_reg_and_get( proc_t* proc, unsigned chIdx, T0 vid, T1 var_label, unsigned sfx_id, T2& valRef, ARGS&&... args )
{
rc_t rc;
if((rc = var_register_and_get( inst, var_label, vid, chIdx, valRef )) == kOkRC )
if((rc = _var_reg_and_get( inst, chIdx, std::forward<ARGS>(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>(args)...)) != kOkRC )
return rc;
return rc;
@ -358,8 +560,8 @@ namespace cw
// Call var_register_and_get() on a list of variables.
template< typename... ARGS >
rc_t var_register_and_get( instance_t* inst, unsigned chIdx, unsigned vid, const char* var_label, ARGS&&... args )
{ return _var_reg_and_get( inst, chIdx, vid, var_label, std::forward<ARGS>(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>(args)...); }
@ -371,25 +573,27 @@ namespace cw
// var_register_and_set(). If the variable has not yet been created then it is created and assigned a value.
// If the variable has already been created then 'vid' and the value are updated.
// (Note that abuf and fbuf values are not changed by this function only the 'vid' is updated.)
rc_t var_register_and_set( instance_t* inst, const char* label, unsigned vid, unsigned chIdx, variable_t*& varRef );
rc_t var_register_and_set( proc_t* proc, const char* label, unsigned sfx_id, unsigned vid, unsigned chIdx, variable_t*& varRef );
rc_t var_register_and_set( instance_t* inst, const char* var_label, unsigned vid, unsigned chIdx, srate_t srate, unsigned chN, unsigned frameN );
rc_t var_register_and_set( instance_t* inst, const char* var_label, unsigned vid, unsigned chIdx, srate_t srate, unsigned chN, const unsigned* maxBinN_V, const unsigned* binN_V, const unsigned* hopSmpN_V, const fd_real_t** magV=nullptr, const fd_real_t** phsV=nullptr, const fd_real_t** hzV=nullptr );
rc_t var_register_and_set( instance_t* inst, const char* var_label, unsigned vid, unsigned chIdx, srate_t srate, unsigned chN, unsigned maxBinN, unsigned binN, unsigned hopSmpN, const fd_real_t** magV=nullptr, const fd_real_t** phsV=nullptr, const fd_real_t** hzV=nullptr );
rc_t var_register_and_set( proc_t* proc, const char* var_label, unsigned sfx_id, unsigned vid, unsigned chIdx, srate_t srate, unsigned chN, unsigned frameN );
rc_t var_register_and_set( proc_t* proc, const char* var_label, unsigned sfx_id, unsigned vid, unsigned chIdx, midi::ch_msg_t* midiA, unsigned midiN );
rc_t var_register_and_set( proc_t* proc, const char* var_label, unsigned sfx_id, unsigned vid, unsigned chIdx, srate_t srate, unsigned chN, const unsigned* maxBinN_V, const unsigned* binN_V, const unsigned* hopSmpN_V, const fd_sample_t** magV=nullptr, const fd_sample_t** phsV=nullptr, const fd_sample_t** hzV=nullptr );
rc_t var_register_and_set( proc_t* proc, const char* var_label, unsigned sfx_id, unsigned vid, unsigned chIdx, srate_t srate, unsigned chN, unsigned maxBinN, unsigned binN, unsigned hopSmpN, const fd_sample_t** magV=nullptr, const fd_sample_t** phsV=nullptr, const fd_sample_t** hzV=nullptr );
inline rc_t _var_register_and_set(cw::flow::instance_t*, unsigned int ) { return kOkRC; }
inline rc_t _var_register_and_set(cw::flow::proc_t*, unsigned int ) { return kOkRC; }
template< typename T0, typename T1, typename T2, typename... ARGS >
rc_t _var_register_and_set( instance_t* inst, unsigned chIdx, T0 vid, T1 var_label, T2 val, ARGS&&... args )
rc_t _var_register_and_set( proc_t* proc, unsigned chIdx, T0 vid, T1 var_label, unsigned sfx_id, T2 val, ARGS&&... args )
{
rc_t rc;
variable_t* var = nullptr;
if((rc = var_register_and_set( inst, var_label, vid, chIdx, var)) == kOkRC )
if((rc = var_register_and_set( proc, var_label, sfx_id, vid, chIdx, var)) == kOkRC )
{
var_set( inst, vid, chIdx, val );
if((rc = var_set( proc, vid, chIdx, val )) != kOkRC )
return rc;
if((rc = _var_register_and_set( inst, chIdx, std::forward<ARGS>(args)...)) != kOkRC )
if((rc = _var_register_and_set( proc, chIdx, std::forward<ARGS>(args)...)) != kOkRC )
return rc;
}
@ -398,24 +602,33 @@ 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>(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>(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( 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 );
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 );
// 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 );
@ -427,37 +640,59 @@ namespace cw
rc_t var_get( variable_t* var, abuf_t*& valRef );
rc_t var_get( const variable_t* var, const fbuf_t*& valRef );
rc_t var_get( variable_t* var, fbuf_t*& valRef );
rc_t var_get( const variable_t* var, const mbuf_t*& valRef );
rc_t var_get( variable_t* var, mbuf_t*& valRef );
rc_t var_get( const variable_t* var, const object_t*& valRef );
template< typename T>
rc_t var_get( instance_t* inst, unsigned vid, unsigned chIdx, T& valRef)
rc_t var_get( proc_t* proc, unsigned vid, unsigned chIdx, T& valRef)
{
rc_t rc = kOkRC;
variable_t* var = nullptr;
if((rc = var_find(inst, vid, chIdx, var )) == kOkRC )
if((rc = var_find(proc, vid, chIdx, var )) == kOkRC )
rc = var_get(var,valRef);
return rc;
}
template< typename T >
T val_get( instance_t* inst, unsigned vid, unsigned chIdx )
T val_get( proc_t* proc, unsigned vid, unsigned chIdx )
{
T value;
var_get(inst,vid,chIdx,value);
var_get(proc,vid,chIdx,value);
return value;
}
rc_t var_set( instance_t* inst, unsigned vid, unsigned chIdx, bool val );
rc_t var_set( instance_t* inst, unsigned vid, unsigned chIdx, uint_t val );
rc_t var_set( instance_t* inst, unsigned vid, unsigned chIdx, int_t val );
rc_t var_set( instance_t* inst, unsigned vid, unsigned chIdx, float val );
rc_t var_set( instance_t* inst, unsigned vid, unsigned chIdx, double val );
rc_t var_set( instance_t* inst, unsigned vid, unsigned chIdx, const char* val );
rc_t var_set( instance_t* inst, unsigned vid, unsigned chIdx, abuf_t* val );
rc_t var_set( instance_t* inst, unsigned vid, unsigned chIdx, fbuf_t* val );
//
// var_set() coerces the incoming value to the type of the variable (var->type)
//
rc_t var_set_from_cfg( variable_t* var, const object_t* cfg_value );
rc_t var_set( variable_t* var, const value_t* val );
rc_t var_set( variable_t* var, bool val );
rc_t var_set( variable_t* var, uint_t val );
rc_t var_set( variable_t* var, int_t val );
rc_t var_set( variable_t* var, float val );
rc_t var_set( variable_t* var, double val );
rc_t var_set( variable_t* var, const char* val );
rc_t var_set( variable_t* var, abuf_t* val );
rc_t var_set( variable_t* var, fbuf_t* val );
rc_t var_set( variable_t* var, mbuf_t* val );
rc_t var_set( variable_t* var, const object_t* val );
rc_t var_set( proc_t* proc, unsigned vid, unsigned chIdx, const value_t* val );
rc_t var_set( proc_t* proc, unsigned vid, unsigned chIdx, bool val );
rc_t var_set( proc_t* proc, unsigned vid, unsigned chIdx, uint_t val );
rc_t var_set( proc_t* proc, unsigned vid, unsigned chIdx, int_t val );
rc_t var_set( proc_t* proc, unsigned vid, unsigned chIdx, float val );
rc_t var_set( proc_t* proc, unsigned vid, unsigned chIdx, double val );
rc_t var_set( proc_t* proc, unsigned vid, unsigned chIdx, const char* val );
rc_t var_set( proc_t* proc, unsigned vid, unsigned chIdx, abuf_t* val );
rc_t var_set( proc_t* proc, unsigned vid, unsigned chIdx, fbuf_t* val );
rc_t var_set( proc_t* proc, unsigned vid, unsigned chIdx, const object_t* val );
const preset_t* class_preset_find( class_desc_t* cd, const char* preset_label );
}
}

View File

@ -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"

View File

@ -1,6 +1,7 @@
#include "cwCommon.h"
#include "cwLog.h"
#include "cwCommonImpl.h"
#include "cwTest.h"
#include "cwMem.h"
#include "cwObject.h"
#include "cwText.h"
@ -611,7 +612,7 @@ namespace cw
//
// MIDI
//
void _midiCallback( const midi::packet_t* pktArray, unsigned pktCnt )
void _midiCallback( void* cbArg, const midi::packet_t* pktArray, unsigned pktCnt )
{
unsigned i;
for(i=0; i<pktCnt; ++i)
@ -619,7 +620,7 @@ namespace cw
msg_t m;
midi_msg_t mm;
const midi::packet_t* pkt = pktArray + i;
io_t* p = reinterpret_cast<io_t*>(pkt->cbArg);
io_t* p = reinterpret_cast<io_t*>(cbArg);
rc_t rc = kOkRC;
@ -2115,9 +2116,11 @@ namespace cw
goto errLabel;
}
errLabel:
if( rc != kOkRC && p->audioH.isValid() )
audio::device::report( p->audioH );
errLabel:
return rc;
}
@ -2439,7 +2442,8 @@ cw::rc_t cw::io::stop( handle_t h )
return rc;
}
cw::rc_t cw::io::exec( handle_t h, void* execCbArg )
cw::rc_t cw::io::exec( handle_t h, unsigned timeOutMs, void* execCbArg )
{
rc_t rc = kOkRC;
io_t* p = _handleToPtr(h);
@ -2447,8 +2451,9 @@ cw::rc_t cw::io::exec( handle_t h, void* execCbArg )
if( p->wsUiH.isValid() )
{
ui::flushCache( ui::ws::uiHandle( p->wsUiH ));
// Note this call blocks on the websocket handle: See cwUi.h:ws:exec()
rc = ui::ws::exec( p->wsUiH );
rc = ui::ws::exec( p->wsUiH, timeOutMs );
}
time::get(p->t0);
@ -2490,14 +2495,23 @@ void cw::io::report( handle_t h )
}
for(unsigned i=0; i<audioDeviceCount(h); ++i)
printf("audio: %s\n", audioDeviceName(h,i));
printf("audio: %s\n", cwStringNullGuard(audioDeviceName(h,i)));
}
void cw::io::hardwareReport( handle_t h )
{
io_t* p = _handleToPtr(h);
audio::device::report( p->audioH );
midi::device::report(p->midiH);
}
void cw::io::realTimeReport( handle_t h )
{
io_t* p = _handleToPtr(h);
audio::device::realTimeReport(p->audioH);
uiRealTimeReport(h);
}
@ -2747,6 +2761,24 @@ cw::rc_t cw::io::midiDeviceSend( handle_t h, unsigned devIdx, unsigned portIdx,
return midi::device::send( p->midiH, devIdx, portIdx, status, d0, d1 );
}
unsigned cw::io::midiDeviceMaxBufferMsgCount( handle_t h )
{
io_t* p = _handleToPtr(h);
return midi::device::maxBufferMsgCount(p->midiH );
}
const cw::midi::ch_msg_t* cw::io::midiDeviceBuffer( handle_t h, unsigned& msgCntRef )
{
io_t* p = _handleToPtr(h);
return midi::device::getBuffer(p->midiH, msgCntRef );
}
cw::rc_t cw::io::midiDeviceClearBuffer( handle_t h, unsigned msgCnt )
{
io_t* p = _handleToPtr(h);
return midi::device::clearBuffer(p->midiH, msgCnt );
}
cw::rc_t cw::io::midiOpenMidiFile( handle_t h, unsigned devIdx, unsigned portIdx, const char* fname )
{
return midi::device::openMidiFile( _handleToPtr(h)->midiH, devIdx, portIdx, fname );
@ -3967,8 +3999,8 @@ void cw::io::uiReport( handle_t h )
void cw::io::uiRealTimeReport( handle_t h )
{
ui::handle_t uiH;
if(_handleToUiHandle(h,uiH) == kOkRC )
ui::realTimeReport(uiH);
ui::ws::handle_t uiH;
if(_handleToWsUiHandle(h,uiH) == kOkRC )
ui::ws::realTimeReport(uiH);
}

12
cwIo.h
View File

@ -165,14 +165,17 @@ namespace cw
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 );
//----------------------------------------------------------------------------------------------------------
//
// Thread
@ -229,6 +232,11 @@ namespace cw
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
unsigned midiMsgCount( handle_t h, unsigned devIdx, unsigned portIdx );

View File

@ -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:

View File

@ -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:

View File

@ -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"

View File

@ -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"

View File

@ -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"
@ -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; i<midiDevN; ++i)
devN += midiDevicePortCount(p->ioH,i,true) + midiDevicePortCount(p->ioH,i,false);
for(unsigned i=0; i<p->audioGroupN; ++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,18 +151,33 @@ 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;
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.
@ -175,15 +195,25 @@ namespace cw
// get serial devices
for(unsigned di=0; i<p->deviceN && di<serialDeviceCount(p->ioH); ++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; i<p->deviceN && di<midiDeviceCount(p->ioH); ++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; i<p->deviceN && di<socketCount(p->ioH); ++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; i<p->deviceN && di<midiDeviceCount(p->ioH); ++di)
{
// input port setup
for(unsigned pi=0; pi<midiDevicePortCount(p->ioH,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; pi<midiDevicePortCount(p->ioH,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
@ -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; i<p->deviceN; ++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;
}

View File

@ -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"

View File

@ -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;

View File

@ -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);
//app_t* app = (app_t*)arg;
//unsigned logUuId = uiFindElementUuId( app->ioH, kLogId);
uiSetLogLine( app->ioH, logUuId, text );
//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;
}
@ -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,8 +3106,12 @@ rc_t _on_ui_play_loc(app_t* app, unsigned appId, unsigned loc);
io::report( app->ioH );
break;
case kIoHwReportBtnId:
io::hardwareReport( app->ioH );
break;
case kIoRtReportBtnId:
io::realTimeReport(app->ioH);
io::realTimeReport( app->ioH );
break;
case kNetPrintBtnId:
@ -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<mapN; ++i)
bigMap[i] = mapA[i];
@ -3822,18 +3836,27 @@ cw::rc_t cw::preset_sel_app::main( const object_t* cfg, int argc, const char* ar
// execute the io framework
while( !io::isShuttingDown(app.ioH))
{
//time::spec_t t0;
//time::get(t0);
const unsigned wsTimeOutMs = 50;
time::spec_t t0 = time::current_time();
unsigned timeOutMs = app.psNextFrag != nullptr ? 0 : wsTimeOutMs;
// This call may block on the websocket handle.
io::exec(app.ioH);
io::exec(app.ioH,timeOutMs);
//unsigned dMs = time::elapsedMs(t0);
//if( dMs < 50 && app.psNextFrag == nullptr )
//{
// sleepMs( 50-dMs );
//}
time::spec_t t1 = time::current_time();
unsigned dMs = time::elapsedMs(t0,t1);
if( dMs < wsTimeOutMs && app.psNextFrag == nullptr )
{
sleepMs( wsTimeOutMs-dMs );
}
if( app.run_dur_secs != 0 && time::elapsedMs( start_time )/1000 > 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

View File

@ -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"

View File

@ -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:

View File

@ -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 )
{

View File

@ -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 );
}
}

View File

@ -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"

View File

@ -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 = "<unknown>";
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 = "";

View File

@ -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 );

View File

@ -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 )

View File

@ -1,6 +1,7 @@
#include "cwCommon.h"
#include "cwLog.h"
#include "cwCommonImpl.h"
#include "cwTest.h"
#include "cwMem.h"
#include "cwThread.h"

View File

@ -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"

View File

@ -1,6 +1,7 @@
#include "cwCommon.h"
#include "cwLog.h"
#include "cwCommonImpl.h"
#include "cwTest.h"
#include "cwMem.h"
#include "cwTime.h"
#include "cwText.h"
@ -283,6 +284,7 @@ namespace cw
time::spec_t ts;
ts.tv_sec = ev->time.time.tv_sec;
ts.tv_nsec = ev->time.time.tv_nsec;
parser::midiTriple(p->prvRcvPort->parserH, &ts, status | ch, d0, d1 );
p->prvTimeMicroSecs = microSecs1;
@ -439,7 +441,7 @@ namespace cw
{
assert(j<p->devArray[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 ? "<None>" : port);
p->devArray[i].iPortArray[j].alsa_type = type;
p->devArray[i].iPortArray[j].alsa_cap = caps;
p->devArray[i].iPortArray[j].alsa_addr = addr;
@ -461,7 +463,7 @@ namespace cw
{
assert(k<p->devArray[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 ? "<None>" : port);
p->devArray[i].oPortArray[k].alsa_type = type;
p->devArray[i].oPortArray[k].alsa_cap = caps;
p->devArray[i].oPortArray[k].alsa_addr = addr;
@ -475,6 +477,11 @@ namespace cw
++k;
}
}
// The capabilities of some ports may not have been as expected
// decrease the in/out port count to account for these ports
p->devArray[i].iPortCnt = j;
p->devArray[i].oPortCnt = k;
}
errLabel:
@ -736,7 +743,7 @@ const char* cw::midi::device::alsa::portName( handle_t h, unsigned devIdx,
if( cwIsFlag(flags,kInMpFl) )
{
if( portIdx >= p->devArray[devIdx].iPortCnt )
return 0;
return nullptr;
return p->devArray[devIdx].iPortArray[portIdx].nameStr;
}
@ -796,8 +803,11 @@ cw::rc_t cw::midi::device::alsa::send( handle_t h, unsigned devIdx, unsigned po
snd_seq_ev_set_direct(&ev);
snd_seq_ev_set_fixed(&ev);
uint8_t status_wo_ch = status;
if( midi::isChStatus(status) )
status_wo_ch = status & 0xf0;
switch( status & 0xf0 )
switch( status_wo_ch )
{
case kNoteOffMdId:
ev.type = SND_SEQ_EVENT_NOTEOFF;
@ -854,8 +864,32 @@ cw::rc_t cw::midi::device::alsa::send( handle_t h, unsigned devIdx, unsigned po
}
break;
case kSysRtClockMdId:
ev.type = SND_SEQ_EVENT_CLOCK;
break;
case kSysRtStartMdId:
ev.type =SND_SEQ_EVENT_START;
break;
case kSysRtContMdId:
ev.type = SND_SEQ_EVENT_CONTINUE;
break;
case kSysRtStopMdId:
ev.type = SND_SEQ_EVENT_STOP;
break;
case kSysRtSenseMdId:
ev.type = SND_SEQ_EVENT_SENSING;
break;
case kSysRtResetMdId:
ev.type = SND_SEQ_EVENT_RESET;
break;
default:
rc = _cmMpErrMsg1(kInvalidArgRC,0,"Cannot send an invalid MIDI status byte:0x%x.",status & 0xf0);
rc = _cmMpErrMsg1(kInvalidArgRC,0,"Cannot send an invalid MIDI status byte:0x%x.",status);
goto errLabel;
}

View File

@ -17,7 +17,7 @@ namespace cw
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

View File

@ -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"
@ -55,6 +56,11 @@ namespace cw
unsigned long long last_posn_micros;
time::spec_t start_time;
ch_msg_t* buf;
unsigned bufN;
std::atomic<unsigned> buf_ii;
std::atomic<unsigned> buf_oi;
} device_t;
device_t* _handleToPtr( handle_t h )
@ -102,8 +108,11 @@ namespace cw
goto errLabel;
}
destroy(p->alsaDevH);
destroy(p->fileDevH);
mem::release(p->buf);
mem::release(p);
errLabel:
@ -173,6 +182,45 @@ namespace cw
return true;
}
void _callback( void* cbArg, const packet_t* pktArray, unsigned pktCnt )
{
device_t* p = (device_t*)cbArg;
for(unsigned i=0; i<pktCnt; ++i)
{
const packet_t* pkt = pktArray + i;
if( pkt->msgArray != nullptr )
{
unsigned ii = p->buf_ii.load();
unsigned oi = p->buf_oi.load();
for(unsigned j=0; j<pkt->msgCnt; ++j)
{
ch_msg_t* m = p->buf + ii;
m->devIdx = pkt->devIdx;
m->portIdx = pkt->portIdx;
m->timeStamp = pkt->msgArray[j].timeStamp;
m->uid = pkt->msgArray[j].uid;
m->ch = pkt->msgArray[j].ch;
m->status = pkt->msgArray[j].status;
m->d0 = pkt->msgArray[j].d0;
m->d1 = pkt->msgArray[j].d1;
ii = (ii+1 == p->bufN ? 0 : ii+1);
if( ii == oi )
{
cwLogError(kBufTooSmallRC,"The MIDI device buffer is full %i.",p->bufN);
}
}
p->buf_ii.store(ii);
}
}
if( p->cbFunc != nullptr )
p->cbFunc(p->cbArg,pktArray,pktCnt);
}
} // device
} // midi
@ -188,7 +236,9 @@ cw::rc_t cw::midi::device::create( handle_t& hRef,
const char* appNameStr,
const char* fileDevName,
unsigned fileDevReadAheadMicros,
unsigned parserBufByteCnt )
unsigned parserBufByteCnt,
bool enableBufFl,
unsigned bufferMsgCnt )
{
rc_t rc = kOkRC;
rc_t rc1 = kOkRC;
@ -198,7 +248,11 @@ cw::rc_t cw::midi::device::create( handle_t& hRef,
device_t* p = mem::allocZ<device_t>();
if((rc = create( p->alsaDevH, cbFunc, cbArg, parserBufByteCnt, appNameStr )) != kOkRC )
if((rc = create( p->alsaDevH,
enableBufFl ? _callback : cbFunc,
enableBufFl ? p : cbArg,
parserBufByteCnt,
appNameStr )) != kOkRC )
{
rc = cwLogError(rc,"ALSA MIDI device create failed.");
goto errLabel;
@ -206,16 +260,29 @@ cw::rc_t cw::midi::device::create( handle_t& hRef,
p->alsa_dev_cnt = count(p->alsaDevH);
if((rc = create( p->fileDevH, cbFunc, cbArg, p->alsa_dev_cnt, filePortLabelA, max_file_cnt, fileDevName, fileDevReadAheadMicros )) != kOkRC )
if((rc = create( p->fileDevH,
enableBufFl ? _callback : cbFunc,
enableBufFl ? p : cbArg,
p->alsa_dev_cnt,
filePortLabelA,
max_file_cnt,
fileDevName,
fileDevReadAheadMicros )) != kOkRC )
{
rc = cwLogError(rc,"MIDI file device create failed.");
goto errLabel;
}
p->cbFunc = cbFunc;
p->cbArg = cbArg;
p->file_dev_cnt = count(p->fileDevH);
p->total_dev_cnt = p->alsa_dev_cnt + p->file_dev_cnt;
p->alsaPollfdA = pollFdArray(p->alsaDevH,p->alsaPollfdN);
p->fileDevStateId = kStoppedStateId;
p->buf = mem::allocZ<ch_msg_t>( bufferMsgCnt );
p->bufN = bufferMsgCnt;
p->buf_ii.store(0);
p->buf_oi.store(0);
if((rc = thread::create(p->threadH,
_thread_func,
@ -257,6 +324,8 @@ cw::rc_t cw::midi::device::create( handle_t& h,
const char* fileDevName = "file_dev";
unsigned fileDevReadAheadMicros = 3000;
unsigned parseBufByteCnt = 1024;
bool enableBufFl = false;
unsigned bufMsgCnt = 0;
const object_t* file_ports = nullptr;
const object_t* port = nullptr;
@ -264,6 +333,8 @@ cw::rc_t cw::midi::device::create( handle_t& h,
"fileDevName",fileDevName,
"fileDevReadAheadMicros",fileDevReadAheadMicros,
"parseBufByteCnt",parseBufByteCnt,
"enableBufFl",enableBufFl,
"bufferMsgCnt",bufMsgCnt,
"file_ports",file_ports)) != kOkRC )
{
rc = cwLogError(rc,"MIDI port parse args. failed.");
@ -290,7 +361,7 @@ cw::rc_t cw::midi::device::create( handle_t& h,
}
}
rc = create(h,cbFunc,cbArg,labelArray,fpi,appNameStr,fileDevName,fileDevReadAheadMicros,parseBufByteCnt);
rc = create(h,cbFunc,cbArg,labelArray,fpi,appNameStr,fileDevName,fileDevReadAheadMicros,parseBufByteCnt,enableBufFl,bufMsgCnt);
}
@ -445,7 +516,7 @@ const char* cw::midi::device::portName( handle_t h, unsigned devIdx, unsigned
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;
}
@ -591,6 +662,43 @@ errLabel:
}
unsigned cw::midi::device::maxBufferMsgCount( handle_t h )
{
device_t* p = _handleToPtr(h);
return p->bufN;
}
const cw::midi::ch_msg_t* cw::midi::device::getBuffer( handle_t h, unsigned& msgCntRef )
{
device_t* p = _handleToPtr(h);
unsigned ii = p->buf_ii.load();
unsigned oi = p->buf_oi.load();
ch_msg_t* m = nullptr;
msgCntRef = ii >= oi ? ii-oi : p->bufN - oi;
if( msgCntRef > 0 )
m = p->buf + oi;
return m;
}
cw::rc_t cw::midi::device::clearBuffer( handle_t h, unsigned msgCnt )
{
if( msgCnt > 0 )
{
device_t* p = _handleToPtr(h);
unsigned oi = p->buf_oi.load();
oi = (oi + msgCnt) % p->bufN;
p->buf_oi.store(oi);
}
return kOkRC;
}
cw::rc_t cw::midi::device::start( handle_t h )
{
rc_t rc = kOkRC;
@ -669,6 +777,8 @@ cw::rc_t cw::midi::device::report( handle_t h )
report(h,tbH);
printf("%s\n",text(tbH));
errLabel:
destroy(tbH);
return rc;

View File

@ -30,7 +30,9 @@ namespace cw
const char* appNameStr,
const char* fileDevName = "file_dev",
unsigned fileDevReadAheadMicros = 3000,
unsigned parserBufByteCnt = 1024 );
unsigned parserBufByteCnt = 1024,
bool enableBufFl = false, // Enable buffer to hold all incoming msg's until RT thread can pick them up.
unsigned bufferMsgCnt = 4096); // Count of messages in input buffer.
rc_t create( handle_t& h,
cbFunc_t cbFunc,
@ -57,6 +59,10 @@ namespace cw
rc_t seekToMsg( handle_t h, unsigned devIdx, unsigned portIdx, unsigned msgIdx );
rc_t setEndMsg( handle_t h, unsigned devIdx, unsigned portidx, unsigned msgIdx );
unsigned maxBufferMsgCount( handle_t h ); // max number of msg's which will ever be returned in a buffer
const ch_msg_t* getBuffer( handle_t h, unsigned& msgCntRef );
rc_t clearBuffer( handle_t h, unsigned msgCnt );
rc_t start( handle_t h );
rc_t stop( handle_t h );
rc_t pause( handle_t h, bool pause_fl );

View File

@ -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; j<p->msgCnt; ++j)
if( p->msgArray != NULL )

View File

@ -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"

View File

@ -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 );
}
}

View File

@ -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);
// fill the buffer msg
msgPtr->timeStamp = *timeStamp;
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->uid = kInvalidId;
switch( p->dataCnt )

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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<struct node_str*> next;
block_t* block;
unsigned blobByteN;
std::atomic<struct node_str*> 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.
@ -52,6 +56,8 @@ namespace cw
node_t* tail; // first-out
node_t* peek;
} nbmpscq_t;
nbmpscq_t* _handleToPtr( handle_t h )
@ -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
{
@ -134,6 +166,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;
@ -152,17 +196,17 @@ cw::rc_t cw::nbmpscq::create( handle_t& hRef, unsigned initBlkN, unsigned blkByt
p->stub = mem::allocZ<node_t>();
p->head = p->stub; // last-in
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<uint8_t>(byteN);
for(unsigned i=0; i<byteN; i+=(sizeof(block_t) + blkByteN))
for(unsigned i=0; i<initBlkN; ++i)
{
block_t* b = (block_t*)(p->mem+i);
b->buf = (uint8_t*)(b + 1);
block_t* b = mem::allocZ<block_t>();
b->buf = mem::allocZ<uint8_t>(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);
@ -216,6 +261,17 @@ cw::rc_t cw::nbmpscq::push( handle_t h, const void* blob, unsigned blobByteN )
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)
{
if( b->full_flag.load(std::memory_order_acquire) == false )
@ -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;
@ -277,47 +339,99 @@ 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
// We always access the tail element through tail->next.
node_t* node = p->tail->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;
}
_init_blob( blob, node );
return blob;
}
cw::rc_t cw::nbmpscq::advance( handle_t h )
cw::nbmpscq::blob_t cw::nbmpscq::advance( handle_t h )
{
blob_t blob;
nbmpscq_t* p = _handleToPtr(h);
rc_t rc = kOkRC;
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 );
}
}
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 )

View File

@ -39,25 +39,53 @@ namespace cw
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 );

View File

@ -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; i<indent; ++i)
printf("%s",indentStr);
printf("%s",text);
cwLogPrint("%s",indentStr);
cwLogPrint("%s",text);
}
void _objTypePrintChild( const object_t* o, print_ctx_t& c, const char* eolStr=",\n", const char* indentStr=" " )
{
_objTypePrintIndent(" ",c.indent,indentStr);
o->type->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("<vect>"); }
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("<vect>"); }
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,10 +870,16 @@ 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:
if( tokenIsUnsigned(lexH) )
_objCreateValueNode( cnp, lex::tokenUInt(lexH), "uint" );
else
_objCreateValueNode( cnp, lex::tokenInt(lexH), "int" );
break;
@ -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<char*>( cnp, s, "string", identFl );
}
@ -1007,4 +1014,118 @@ cw::rc_t cw::objectToFile( const char* fn, const object_t* obj )
return rc;
}
namespace cw
{
rc_t _object_test_basic( const test::test_args_t& args )
{
rc_t rc = kOkRC;
cw::object_t* o = nullptr;
const char s [] = "{ a:1, b:2, c:[ 1.23, 4.56 ], d:true, e:false, f:true }";
int a = 0;
int b = 0;
const cw::object_t* c = nullptr;
bool d,e,f;
const unsigned bufN = 128;
char buf[bufN];
unsigned i = 0;
cw::object_t* oo = nullptr;
if((rc = cw::objectFromString(s,o)) != kOkRC )
goto errLabel;
int v;
if((rc = o->get("b",v)) != kOkRC )
goto errLabel;
cwLogPrint("value:%i\n",v);
o->print();
if((rc = o->getv("a",a,"b",b)) != kOkRC )
goto errLabel;
cwLogPrint("G: %i %i\n",a,b);
if((rc = o->readv("a",0,a,
"b",0,b,
"c",cw::kOptFl | cw::kListTId,c,
"d",0,d,
"e",0,e,
"f",0,f)) != kOkRC )
{
goto errLabel;
}
cwLogPrint("R: %i %i : %i %i %i\n",a,b,d,e,f);
i = o->to_string(buf,bufN);
cwLogPrint("%i : %s\n",i, buf);
oo = o->duplicate();
oo->print();
oo->free();
o->free();
errLabel:
return rc;
}
rc_t _object_test_to_json( const test::test_args_t& args )
{
double v0[] = {1.23,2.34,3.45};
unsigned v0N = sizeof(v0)/sizeof(v0[0]);
int v1[] = {-1,0,1,2,3,4};
unsigned v1N = sizeof(v1)/sizeof(v1[0]);
cw::object_t* d = cw::newDictObject();
d->putv("A","Abc","B",1.234);
d->put_numeric_list("v0",v0,v0N);
d->put_numeric_list("v1",v1,v1N);
char* s = d->to_string();
cwLogPrint("%s\n",s);
cw::mem::release(s);
d->free();
return kOkRC;
}
}
cw::rc_t cw::object_test( const test::test_args_t& args )
{
rc_t rc = kOkRC;
if( textIsEqual(args.test_label,"basic") )
{
rc = _object_test_basic(args);
goto errLabel;
}
if( textIsEqual(args.test_label,"to_json") )
{
rc = _object_test_to_json(args);
goto errLabel;
}
rc = cwLogError(kInvalidArgRC,"Unknown test case module:%s test:%s.",args.module_label,args.test_label);
errLabel:
return rc;
}

View File

@ -31,7 +31,8 @@ namespace cw
kRootTId = 0x00100000,
kHexFl = 0x10000000,
kIdentFl = 0x20000000
kIdentFl = 0x20000000,
kOptFl = 0x40000000
};
@ -135,12 +136,19 @@ namespace cw
// Value containers are parents of leaf nodes. (A dictionary is not a value container because it's children are pairs with are not leaf nodes.)
inline bool is_value_container() const { return type != nullptr && cwIsFlag(type->flags,kValueContainerFl); }
inline unsigned type_id() const { return type==nullptr ? (unsigned)kInvalidTId : type->id; }
// Containers have children and use the object.u.children pointer.
inline bool is_container() const { return type != nullptr && cwIsFlag(type->flags,kContainerFl); }
inline bool is_pair() const { return type != nullptr && type->id == kPairTId; }
inline bool is_dict() const { return type != nullptr && type->id == kDictTId; }
inline bool is_list() const { return type != nullptr && type->id == kListTId; }
inline bool is_string() const { return type != nullptr && (type->id == kStringTId || type->id == kCStringTId); }
inline bool is_unsigned_integer() const { return type->id==kCharTId || type->id==kUInt8TId || type->id==kUInt16TId || type->id==kUInt32TId || type->id==kUInt64TId; }
inline bool is_signed_integer() const { return type->id==kInt8TId || type->id==kInt16TId || type->id==kInt32TId || type->id==kInt64TId; }
inline bool is_floating_point() const { return type->id==kFloatTId || type->id==kDoubleTId; }
inline bool is_integer() const { return is_unsigned_integer() || is_signed_integer(); }
inline bool is_numeric() const { return is_integer() || is_floating_point(); }
inline bool is_type( unsigned tid ) const { return type != nullptr && type->id == tid; }
rc_t value( void* dst, unsigned dstTypeId );
@ -199,6 +207,114 @@ 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; i<childN; ++i)
{
const struct object_str* child = child_ele(i);
const char* label = nullptr;
const read_t* r = list;
if( child == nullptr )
{
rc = cwLogError(kAssertFailRC,"A null child was encountered.");
goto errLabel;
}
if( !child->is_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>(args)...); // ... recurse to find next label/value pair
else
rc = cwLogError(rc,"Object parse failed for the pair label:'%s'.",cwStringNullGuard(label));
return rc;
}
// readv("label0",flags0,v0,"label1",flags0,v1, ... )
// Use kOptFl for optional fields.
// Use kListTId and kDictTId to validate the type of container fields.
// In general it should not be necessary to validate numeric and string types because
// they are validated by virtue of being converted to the returned value.
template< typename T0, typename T1, typename... ARGS >
rc_t readv( T0 label, unsigned flags, T1& valRef, ARGS&&... args ) const
{ return _readv(nullptr, label,flags,valRef,args...); }
// Set flag 'kRecurseFl' to recurse into the object in search of the value.
// Set flag 'kOptionalFl' if the label is optional and may not exist.
@ -344,6 +460,8 @@ namespace cw
rc_t objectToFile( const char* fn, const object_t* obj );
rc_t object_test( const test::test_args_t& args );
}

View File

@ -74,6 +74,12 @@ namespace cw
return obj;
}
template<> object_t* _objSetLeafValue<float>( object_t* obj, float value )
{
obj->u.f = value;
obj->type = _objIdToType(kFloatTId);
return obj;
}
template<> object_t* _objSetLeafValue<double>( object_t* obj, double value )
{

View File

@ -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"

View File

@ -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"

View File

@ -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"
@ -107,33 +109,47 @@ namespace cw
rc_t _delete_fragment( preset_sel_t* p, unsigned fragId )
{
frag_t* f0 = nullptr;
frag_t* f1 = p->fragL;
rc_t rc = kOkRC;
frag_t* f = p->fragL;
for(; f1!=nullptr; f1=f1->link)
for(; f!=nullptr; f=f->link)
{
if( f1->fragId == fragId )
if( f->fragId == fragId )
{
if( f0 == nullptr )
p->fragL = f1->link;
// if this is the first frag in the list
if( f->prev == nullptr )
p->fragL = f->link;
else
f0->link = f1->link;
{
for(unsigned i=0; i<f1->presetN; ++i)
mem::release(f1->presetA[i].alt_str);
// 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; i<f->presetN; ++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;
}
rc = cwLogError(kEleNotFoundRC,"The fragment with id %i was not found.",fragId);
return kOkRC;
errLabel:
return rc;
}
void _destroy_all_frags( preset_sel_t* p )
@ -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
@ -261,6 +277,9 @@ namespace cw
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 )

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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;

View File

@ -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"

View File

@ -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"

View File

@ -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"

540
cwTest.cpp Normal file
View File

@ -0,0 +1,540 @@
#include "cwCommon.h"
#include "cwLog.h"
#include "cwCommonImpl.h"
#include "cwTest.h"
#include "cwMem.h"
#include "cwText.h"
#include "cwNumericConvert.h"
#include "cwObject.h"
#include "cwLex.h"
#include "cwTime.h"
#include "cwFile.h"
#include "cwFileSys.h"
#include "cwVectOps.h"
#include "cwFlowTest.h"
namespace cw
{
namespace test
{
typedef struct test_map_str
{
const char* module_name;
test_func_t test_func;
} test_map_t;
test_map_t _test_map[] = {
{ "/lex", lex::test },
{ "/filesys", filesys::test },
{ "/object", object_test },
{ "/vop", vop::test },
{ "/time", time::test },
{ "/flow", flow::test },
{ nullptr, nullptr },
};
typedef struct test_str
{
int argc; // extra cmd line arguments to be passes to the test cases
const char** argv;
const char* base_dir; // base test dictionary
const object_t* test_cfg; // top level test cfg.
const char* rsrc_folder; // name of the 'rsrc' folder in the base dir
const char* out_folder; // name of the output folder in the base dir
const char* ref_folder; // name of the test reference folder in the base dir
const char* sel_module_label; // selected module label
const char* sel_test_label; // selected test label
bool all_module_fl; // true if all modules should be run
bool all_test_fl; // true if all tests in the selected module should be run
bool compare_fl; // true if compare operation should be run
bool echo_fl; // echo test output to the console (false=write all output to log file only)
bool gen_report_fl; // print module/test names as the gen phase is executes
void* logCbArg; // original log callback args
log::logOutputCbFunc_t logCbFunc;
const char* cur_test_label; // current test label
file::handle_t cur_log_fileH; // current log file handle
unsigned gen_cnt; // count of tests which generated output
unsigned compare_ok_cnt; // count of tests which passed the compare pass
unsigned compare_fail_cnt; // count of tests which failed the compare test
} test_t;
rc_t _parse_args( test_t& test, const object_t* cfg, int argc, const char** argv )
{
rc_t rc = kOkRC;
unsigned argN = 1;
char* out_dir = nullptr;
int argi = 1;
test.all_module_fl = true;
test.all_test_fl = true;
if( argc > 1 )
{
test.sel_module_label = argv[1];
test.all_module_fl = textIsEqual(test.sel_module_label,"all");
argi += 1;
}
if( argc > 2 )
{
test.sel_test_label = argv[2];
test.all_test_fl = textIsEqual(test.sel_test_label,"all");
argi += 1;
}
for(int i=argi; i<argc; ++i)
{
if( textIsEqual(argv[i],"compare") )
test.compare_fl = true;
if( textIsEqual(argv[i],"echo") )
test.echo_fl = true;
if( textIsEqual(argv[i],"gen_report"))
test.gen_report_fl = true;
// test specific args follow the 'args' keyword
if( textIsEqual(argv[i],"args") )
{
test.argc = argc - (i+1);
test.argv = argv + (i+1);
}
}
if((rc = cfg->readv("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 && j<testRefDirN; ++j)
if( testRefDirA[j].name != nullptr )
{
char* testRefFn = filesys::makeFn( ref_dir, testRefDirA[j].name, nullptr, nullptr );
char* testCmpFn = filesys::makeFn( test_dir, testRefDirA[j].name, nullptr, nullptr );
bool isEqualFl = false;
// compare two files
if((rc = file::compare( testRefFn, testCmpFn, isEqualFl)) != kOkRC )
rc = cwLogError(rc,"The comparison failed on '%s' == '%s'.",cwStringNullGuard(testRefFn),cwStringNullGuard(testCmpFn));
else
{
// check compare status
if( !isEqualFl )
{
ok_fl = false;
cwLogInfo("Test failed on: '%s'",testRefFn);
}
}
mem::release(testRefFn);
mem::release(testCmpFn);
}
mem::release(testRefDirA);
errLabel:
if( ok_fl )
test.compare_ok_cnt += 1;
else
test.compare_fail_cnt += 1;
return rc;
}
rc_t _exec_one_test( test_t& test, const char* module_label, const object_t* module_args, const char* test_label, const object_t* test_args )
{
rc_t rc = kOkRC;
char* rsrc_dir = nullptr;
char* out_dir = nullptr;
char* ref_dir = nullptr;
char* log_fname = nullptr;
test_args_t args = {};
if((rsrc_dir = filesys::makeFn( test.base_dir, nullptr, nullptr, test.rsrc_folder, module_label, test_label, nullptr )) == nullptr )
{
rc = cwLogError(kOpFailRC,"Resource directory '%s' formation failed.",cwStringNullGuard(rsrc_dir));
goto errLabel;
}
if((out_dir = filesys::makeFn( test.base_dir, nullptr, nullptr, test.out_folder, module_label, test_label, nullptr )) == nullptr )
{
rc = cwLogError(kOpFailRC,"Output directory '%s' formation failed.",cwStringNullGuard(out_dir));
goto errLabel;
}
if((ref_dir = filesys::makeFn( test.base_dir, nullptr, nullptr, test.ref_folder, module_label, test_label, nullptr )) == nullptr )
{
rc = cwLogError(kOpFailRC,"Reference directory '%s' formation failed.",cwStringNullGuard(ref_dir));
goto errLabel;
}
if((log_fname = filesys::makeFn( out_dir, "log","txt",nullptr,nullptr)) == nullptr )
{
rc = cwLogError(kOpFailRC,"Log filename '%s' formation failed.",cwStringNullGuard(log_fname));
goto errLabel;
}
if( !filesys::isDir(out_dir))
{
if((rc = filesys::makeDir(out_dir)) != kOkRC )
{
rc = cwLogError(kOpFailRC,"Output directory '%s' create failed.",cwStringNullGuard(out_dir));
goto errLabel;
}
}
// open the log file
if((rc = file::open(test.cur_log_fileH, log_fname,file::kWriteFl)) != kOkRC )
{
rc = cwLogError(rc,"The log file '%s' could not be created.",cwStringNullGuard(log_fname));
goto errLabel;
}
if( test.gen_report_fl )
cwLogInfo("%s %s",module_label,test_label);
// save the log to a
test.logCbArg = log::outputCbArg( log::globalHandle() );
test.logCbFunc = log::outputCb( log::globalHandle() );
log::setOutputCb( log::globalHandle(), _exec_test_log_cb, &test );
args.module_label = module_label;
args.test_label = test_label;
args.module_args = module_args;
args.test_args = test_args;
args.rsrc_dir = rsrc_dir;
args.out_dir = out_dir;
args.argc = test.argc;
args.argv = test.argv;
if((rc = _exec_dispatch(test, args )) != kOkRC )
goto errLabel;
test.gen_cnt += 1;
errLabel:
file::close(test.cur_log_fileH);
log::setOutputCb( log::globalHandle(), test.logCbFunc, test.logCbArg );
// if compare is enabled
if( rc == kOkRC && test.compare_fl )
rc = _compare_one_test(test, ref_dir, out_dir );
if( rc != kOkRC )
rc = cwLogError(rc,"Test process failed on module:%s test:%s.",module_label,test_label);
mem::release(log_fname);
mem::release(rsrc_dir);
mem::release(out_dir);
mem::release(ref_dir);
return rc;
}
rc_t _exec_cases( test_t& test, const char* module_label, const object_t* module_args, const object_t* cases_cfg )
{
rc_t rc = kOkRC;
// get count of test cases
unsigned testN = cases_cfg->child_count();
// for each test case
for(unsigned i=0; i<testN; ++i)
{
const object_t* test_pair = cases_cfg->child_ele(i);
const char* test_label = test_pair->pair_label();
// apply filter/test label filter
if( (test.all_module_fl || textIsEqual(module_label,test.sel_module_label)) &&
( test.all_test_fl || textIsEqual(test_label,test.sel_test_label)))
{
if((rc = _exec_one_test(test, module_label, module_args, test_label, test_pair->pair_value() )) != kOkRC )
{
goto errLabel;
}
}
}
errLabel:
return rc;
}
rc_t _exec_module( test_t& test, const char* module_label, const object_t* module_args, const object_t* module_cfg )
{
rc_t rc = kOkRC;
const object_t* cases_cfg = nullptr;
const object_t* mod_args_cfg = nullptr;
if((rc = module_cfg->getv_opt("cases",cases_cfg,
"module_args",mod_args_cfg)) != kOkRC )
{
rc = cwLogError(rc,"Parse failed on module fields in '%s'.",cwStringNullGuard(module_label));
goto errLabel;
}
if( cases_cfg == nullptr )
cases_cfg = module_cfg;
if( mod_args_cfg == nullptr )
mod_args_cfg = module_args;
if((rc = _exec_cases( test,module_label,mod_args_cfg,cases_cfg)) != kOkRC )
goto errLabel;
errLabel:
return rc;
}
rc_t _proc_test_cfg( test_t& test, const char* module_label, const object_t* test_cfg );
rc_t _proc_test_from_file( test_t& test, const char* module_label, const char* fname )
{
rc_t rc = kOkRC;
char* cfg_fname;
object_t* test_cases_cfg = nullptr;
const char* orig_base_dir = test.base_dir;
char* new_base_dir = nullptr;
if((cfg_fname = filesys::makeFn(test.base_dir, fname, nullptr, nullptr )) == nullptr )
{
rc = cwLogError(kOpFailRC,"The test cases file name for the module '%s' in '%s' / '%s' could not be formed.",cwStringNullGuard(module_label),cwStringNullGuard(test.base_dir),cwStringNullGuard(fname));
goto errLabel;
}
if((rc = objectFromFile( cfg_fname, test_cases_cfg )) != kOkRC )
{
rc = cwLogError(kOpFailRC,"Parse failed on the test case module file '%s'.",cwStringNullGuard(cfg_fname));
goto errLabel;
}
if((new_base_dir = filesys::makeFn(test.base_dir, nullptr, nullptr, test.out_folder, module_label, nullptr )) == nullptr )
{
rc = cwLogError(kOpFailRC,"The base test directory name for the module '%s' could not be formed in '%s'.",cwStringNullGuard(test.base_dir),cwStringNullGuard(module_label));
goto errLabel;
}
if( !filesys::isDir(new_base_dir))
{
rc = cwLogError(kOpFailRC,"The base test directory '%s' for the module '%s' does not exist.",cwStringNullGuard(test.base_dir),cwStringNullGuard(module_label));
goto errLabel;
}
rc = _proc_test_cfg( test, module_label, test_cases_cfg );
errLabel:
mem::release(cfg_fname);
mem::release(new_base_dir);
if( test_cases_cfg != nullptr )
test_cases_cfg->free();
return rc;
}
rc_t _proc_module( test_t& test, const char* base_module_label, const char* module_label, const object_t* module_cfg )
{
rc_t rc = kOkRC;
char* new_module_label = filesys::makeFn(base_module_label,nullptr,nullptr,module_label,nullptr );
switch( module_cfg->type_id() )
{
case kStringTId:
{
const char* s = nullptr;
if((rc = module_cfg->value(s)) != kOkRC )
{
rc = cwLogError(rc,"Parse failed on module filename in '%s'.",cwStringNullGuard(module_label));
goto errLabel;
}
rc = _proc_test_from_file(test, new_module_label, s );
}
break;
case kDictTId:
rc = _proc_test_cfg(test, new_module_label, module_cfg );
break;
default:
break;
}
errLabel:
mem::release(new_module_label);
return rc;
}
rc_t _proc_test_cfg( test_t& test, const char* module_label, const object_t* test_cfg )
{
rc_t rc = kOkRC;
const object_t* module_args = nullptr;
const object_t* modules_cfg = nullptr;
const object_t* cases_cfg = nullptr;
if((rc = test_cfg->getv_opt("module_args",module_args,
"modules",modules_cfg,
"cases",cases_cfg )) != kOkRC )
{
rc = cwLogError(rc,"The 'module_args' parse failed on module '%s'.",cwStringNullGuard(module_label));
goto errLabel;
}
// if a list of modules was given
if( modules_cfg != nullptr )
{
unsigned modulesN = modules_cfg->child_count();
for(unsigned i=0; i<modulesN; ++i)
{
const object_t* mod_pair = modules_cfg->child_ele(i);
if((rc = _proc_module(test, module_label, mod_pair->pair_label(), mod_pair->pair_value() )) != kOkRC )
goto errLabel;
}
}
// if
if(module_args==nullptr && modules_cfg==nullptr && cases_cfg==nullptr )
{
cases_cfg = test_cfg;
}
// if a list of cases was given
if( cases_cfg != nullptr )
{
if((rc = _exec_cases(test,module_label,module_args,cases_cfg)) != kOkRC )
goto errLabel;
}
errLabel:
return rc;
}
}
}
cw::rc_t cw::test::test( const struct object_str* cfg, int argc, const char** argv )
{
test_t test = {};
rc_t rc = kOkRC;
if((rc = _parse_args(test, cfg, argc, argv )) != kOkRC )
{
rc = cwLogError(rc,"Test arguments parse failed.");
goto errLabel;
}
if((rc = _proc_test_cfg(test,"/",test.test_cfg)) != kOkRC )
{
goto errLabel;
}
errLabel:
cwLogInfo("Test Gen Count:%i.",test.gen_cnt);
if( test.compare_fl )
cwLogInfo("Test Compare - ok:%i fail:%i.",test.compare_ok_cnt,test.compare_fail_cnt);
if( rc != kOkRC )
rc = cwLogError(rc,"Testing process failed.");
return rc;
}

31
cwTest.h Normal file
View File

@ -0,0 +1,31 @@
#ifndef cwTest_h
#define cwTest_h
namespace cw
{
struct object_str;
namespace test
{
typedef struct test_args_str
{
const char* module_label; // test module this test belongs to
const char* test_label; // test label
const struct object_str* module_args; // arguments for all tests in this module
const struct object_str* test_args; // arguments specific to this test
const char* rsrc_dir; // input data dir. for this test
const char* out_dir; // output data dir. for this test
int argc; // cmd line arg count
const char** argv; // cmd line arg's
} test_args_t;
typedef rc_t (*test_func_t)(const test_args_t& args);
rc_t test( const struct object_str* cfg, int argc, const char** argv );
}
}
#endif

View File

@ -146,7 +146,7 @@ const char* cw::nextNonWhiteChar( const char* s )
const char* cw::nextNonWhiteCharEOS( const char* s )
{ return _nextNonWhiteChar(s,true); }
const char* cw::firstMatchChar( const char* s, char c )
char* cw::firstMatchChar( char* s, char c )
{
if( s == nullptr )
return nullptr;
@ -157,7 +157,12 @@ const char* cw::firstMatchChar( const char* s, char c )
return nullptr;
}
const char* cw::lastMatchChar( const char* s, char c )
const char* cw::firstMatchChar( const char* s, char c )
{
return firstMatchChar((char*)s,c);
}
char* cw::lastMatchChar( char* s, char c )
{
unsigned sn;
@ -168,13 +173,18 @@ const char* cw::lastMatchChar( const char* s, char c )
if( sn == 0 )
return nullptr;
for(const char* s1=s+(sn-1); s<=s1; --s1)
for(char* s1=s+(sn-1); s<=s1; --s1)
if( *s1 == c )
return s1;
return nullptr;
}
const char* cw::lastMatchChar( const char* s, char c )
{
return lastMatchChar((char*)s,c);
}
bool cw::isInteger( const char* s )
{
for(; *s; ++s)

View File

@ -8,7 +8,7 @@ namespace cw
unsigned textLength( const char* s );
// If dst is non-null then dst is always 0-terminated.
// If src will be truncated if srcN > dstN-1.
// src will be truncated if srcN > dstN-1.
// If dst is null then null is returned
// if src is null then dst[0] = 0.
// if srcN is 0 then textLength(src) is used for srcN
@ -65,8 +65,11 @@ namespace cw
// Return a pointer to the first occurrence of 'c' in s[] or nullptr
// if 'c' does not occur in s[]
char* firstMatchChar( char* s, char c );
const char* firstMatchChar( const char* s, char c );
// Find the last occurrent of 'c' in s[].
char* lastMatchChar( char* s, char c );
const char* lastMatchChar( const char* s, char c );
bool isInteger( const char* ); // text contains only [0-9]

View File

@ -1,6 +1,7 @@
#include "cwCommon.h"
#include "cwLog.h"
#include "cwCommonImpl.h"
#include "cwTest.h"
#include "cwTime.h"
#ifdef OS_OSX
@ -399,7 +400,7 @@ unsigned cw::time::formatDateTime( char* buffer, unsigned bufN, bool includeDate
return (unsigned)n;
}
cw::rc_t cw::time::test()
cw::rc_t cw::time::test(const test::test_args_t& test )
{
spec_t t0,t1;
@ -410,15 +411,15 @@ cw::rc_t cw::time::test()
unsigned dMs = elapsedMs(t0,t1);
printf("dMs:%i : GTE:%i LTE:%i\n",dMs, isGTE(t0,t1), isLTE(t0,t1) );
cwLogPrint("dMs:%i : GTE:%i LTE:%i\n",dMs, isGTE(t0,t1), isLTE(t0,t1) );
microsecondsToSpec( t0, 2500000 ); // 2.5 seconds
printf("%li %li\n",t0.tv_sec,t0.tv_nsec);
cwLogPrint("%li %li\n",t0.tv_sec,t0.tv_nsec);
subtractMicros( t0, 750000 ); // subtract .75 seconds
printf("%li %li\n",t0.tv_sec,t0.tv_nsec);
cwLogPrint("%li %li\n",t0.tv_sec,t0.tv_nsec);
subtractMicros( t0, 500000 ); // subtract .5 seconds
printf("%li %li\n",t0.tv_sec,t0.tv_nsec);
cwLogPrint("%li %li\n",t0.tv_sec,t0.tv_nsec);
time::get(t0);
@ -429,11 +430,11 @@ cw::rc_t cw::time::test()
int usec = time::elapsedMicros(t0,t1);
printf("usec:%i\n",usec);
cwLogPrint("usec:%i\n",usec);
t0 = current_time();
sleepMs(1000);
printf("sleep %i ms\n",elapsedMs(t0));
cwLogPrint("sleep %i ms\n",elapsedMs(t0));
return kOkRC;

View File

@ -85,7 +85,7 @@ namespace cw
// Return count of bytes in in buf[]
unsigned formatDateTime( char* buf, unsigned bufN, bool includeDateFl=false );
rc_t test();
rc_t test( const test::test_args_t& test );
//)

128
cwUi.cpp
View File

@ -1,6 +1,7 @@
#include "cwCommon.h"
#include "cwLog.h"
#include "cwCommonImpl.h"
#include "cwTest.h"
#include "cwMem.h"
#include "cwTime.h"
#include "cwFileSys.h"
@ -117,7 +118,7 @@ namespace cw
unsigned sentMsgN;
unsigned recvMsgN;
bucket_t hashA[ hashN ];
bucket_t hashA[ hashN+1 ];
} ui_t;
@ -129,7 +130,11 @@ namespace cw
assert( parentUuId != kInvalidId && appId != kInvalidId );
unsigned hc = parentUuId + cwSwap32(appId);
return (unsigned short)(((hc & 0xffff0000)>>16) + (hc & 0x0000ffff));
unsigned short hash_idx = (unsigned short)(((hc & 0xffff0000)>>16) + (hc & 0x0000ffff));
assert( hash_idx <= hashN );
return hash_idx;
}
@ -162,7 +167,7 @@ namespace cw
bucket_t* b = p->hashA + hash_idx;
for(; b!=nullptr; b=b->link)
if( b->ele->appId==appId && b->ele->logical_parent->uuId==parentUuId && (chanId==kInvalidId || b->ele->chanId==chanId) )
if( b->ele != nullptr && b->ele->logical_parent!=nullptr && b->ele->appId==appId && b->ele->logical_parent->uuId==parentUuId && (chanId==kInvalidId || b->ele->chanId==chanId) )
return b->ele->uuId;
}
@ -225,6 +230,18 @@ namespace cw
m = m0;
}
// Note: hashA[] has hashN+1 elements
for(unsigned i=0; i<=hashN; ++i)
{
bucket_t* b = p->hashA[i].link;
while( b!=nullptr )
{
bucket_t* b0 = b->link;
mem::release(b);
b = b0;
}
}
mem::release(p->sessA);
mem::release(p->eleA);
mem::release(p->buf);
@ -2219,6 +2236,7 @@ void cw::ui::realTimeReport( handle_t h )
{
ui_t* p = _handleToPtr(h);
printf("UI msg count: recv:%i send:%i\n",p->recvMsgN,p->sentMsgN);
}
@ -2235,7 +2253,6 @@ namespace cw
void* cbArg;
uiCallback_t uiCbFunc;
websock::cbFunc_t wsCbFunc;
unsigned wsTimeOutMs;
unsigned idleMsgPeriodMs;
time::spec_t lastRecvMsgTimeStamp;
} ui_ws_t;
@ -2317,13 +2334,18 @@ cw::rc_t cw::ui::ws::parseArgs( const object_t& o, args_t& args, const char* ob
"rcvBufByteN", args.rcvBufByteN,
"xmtBufByteN", args.xmtBufByteN,
"fmtBufByteN", args.fmtBufByteN,
"websockTimeOutMs", args.wsTimeOutMs,
"idleMsgPeriodMs", args.idleMsgPeriodMs,
"queueBlkCnt", args.queueBlkCnt,
"queueBlkByteCnt", args.queueBlkByteCnt,
"uiCfgFn", uiCfgFn )) != kOkRC )
{
rc = cwLogError(rc,"'ui' cfg. parse failed.");
}
if( op->find_child("websockTimeOutMs") != nullptr )
cwLogWarning("The UI parameter 'websockTimeOutMs' is obsolete and ignored.");
// expand the physical root directory
if((physRootDir = filesys::expandPath( args.physRootDir)) == nullptr )
{
@ -2374,11 +2396,12 @@ cw::rc_t cw::ui::ws::create( handle_t& h,
appIdMapN,
wsCbFunc,
args.dfltPageFn,
args.wsTimeOutMs,
args.idleMsgPeriodMs,
args.rcvBufByteN,
args.xmtBufByteN,
args.fmtBufByteN );
args.fmtBufByteN,
args.queueBlkCnt,
args.queueBlkByteCnt);
}
@ -2392,11 +2415,12 @@ cw::rc_t cw::ui::ws::create( handle_t& h,
unsigned appIdMapN,
websock::cbFunc_t wsCbFunc,
const char* dfltPageFn,
unsigned websockTimeOutMs,
unsigned idleMsgPeriodMs,
unsigned rcvBufByteN,
unsigned xmtBufByteN,
unsigned fmtBufByteN )
unsigned fmtBufByteN,
unsigned queueBlkCnt,
unsigned queueBlkByteCnt )
{
rc_t rc = kOkRC;
@ -2416,7 +2440,7 @@ cw::rc_t cw::ui::ws::create( handle_t& h,
void* wsCbA = wsCbFunc==nullptr ? p : cbArg;
// create the websocket
if((rc = websock::create(p->wsH, wsCbF, wsCbA, physRootDir, dfltPageFn, port, protocolA, protocolN )) != kOkRC )
if((rc = websock::create(p->wsH, wsCbF, wsCbA, physRootDir, dfltPageFn, port, protocolA, protocolN, queueBlkCnt, queueBlkByteCnt )) != kOkRC )
{
cwLogError(rc,"UI Websock create failed.");
goto errLabel;
@ -2432,7 +2456,6 @@ cw::rc_t cw::ui::ws::create( handle_t& h,
p->cbArg = cbArg;
p->uiCbFunc = uiCbFunc;
p->wsCbFunc = wsCbFunc;
p->wsTimeOutMs = websockTimeOutMs;
p->idleMsgPeriodMs = idleMsgPeriodMs;
// initialize the last received msg
time::get(p->lastRecvMsgTimeStamp);
@ -2465,13 +2488,13 @@ cw::rc_t cw::ui::ws::destroy( handle_t& h )
return rc;
}
cw::rc_t cw::ui::ws::exec( handle_t h )
cw::rc_t cw::ui::ws::exec( handle_t h, unsigned wsTimeOutMs )
{
rc_t rc = kOkRC;
ui_ws_t* p = _handleToPtr(h);
rc_t rc = kOkRC;
time::spec_t t;
if((rc = websock::exec( p->wsH, p->wsTimeOutMs )) != kOkRC)
if((rc = websock::exec( p->wsH, wsTimeOutMs )) != kOkRC)
cwLogError(rc,"The UI websock execution failed.");
// make the idle callback
@ -2486,6 +2509,7 @@ cw::rc_t cw::ui::ws::exec( handle_t h )
return rc;
}
cw::rc_t cw::ui::ws::onReceive( handle_t h, unsigned protocolId, unsigned sessionId, websock::msgTypeId_t msg_type, const void* msg, unsigned byteN )
{
ui_ws_t* p = _handleToPtr(h);
@ -2508,6 +2532,13 @@ cw::ui::handle_t cw::ui::ws::uiHandle( handle_t h )
}
void cw::ui::ws::realTimeReport( handle_t h )
{
ui_ws_t* p = _handleToPtr(h);
report(p->wsH);
realTimeReport(p->uiH);
}
namespace cw
{
@ -2519,6 +2550,7 @@ namespace cw
{
ws::handle_t wsUiH;
thread::handle_t thH;
unsigned wsTimeOutMs;
} ui_ws_srv_t;
ui_ws_srv_t* _handleToPtr(handle_t h )
@ -2543,7 +2575,7 @@ namespace cw
ui_ws_srv_t* p = static_cast<ui_ws_srv_t*>(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,6 +2586,34 @@ 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,
@ -2562,12 +2622,15 @@ cw::rc_t cw::ui::srv::create( handle_t& h,
const object_t* uiRsrc,
const appIdMap_t* appIdMapA,
unsigned appIdMapN,
unsigned wsTimeOutMs,
websock::cbFunc_t wsCbFunc,
const char* dfltPageFn,
unsigned websockTimeOutMs,
unsigned idleMsgPeriodMs,
unsigned rcvBufByteN,
unsigned xmtBufByteN,
unsigned fmtBufByteN )
unsigned fmtBufByteN,
unsigned queueBlkCnt,
unsigned queueBlkByteCnt )
{
rc_t rc = kOkRC;
if((rc = destroy(h)) != kOkRC )
@ -2575,7 +2638,7 @@ cw::rc_t cw::ui::srv::create( handle_t& h,
ui_ws_srv_t* p = mem::allocZ<ui_ws_srv_t>();
if((rc = ws::create(p->wsUiH, port, physRootDir, cbArg, uiCbFunc, uiRsrc,appIdMapA, appIdMapN, wsCbFunc, dfltPageFn, websockTimeOutMs, rcvBufByteN, xmtBufByteN, fmtBufByteN )) != kOkRC )
if((rc = ws::create(p->wsUiH, port, physRootDir, cbArg, uiCbFunc, uiRsrc,appIdMapA, appIdMapN, wsCbFunc, dfltPageFn, idleMsgPeriodMs, rcvBufByteN, xmtBufByteN, fmtBufByteN, queueBlkCnt, queueBlkByteCnt )) != kOkRC )
{
cwLogError(rc,"The websock UI creationg failed.");
goto errLabel;
@ -2587,6 +2650,8 @@ cw::rc_t cw::ui::srv::create( handle_t& h,
goto errLabel;
}
p->wsTimeOutMs = wsTimeOutMs;
h.set(p);
errLabel:
@ -2596,32 +2661,6 @@ cw::rc_t cw::ui::srv::create( handle_t& h,
return rc;
}
cw::rc_t cw::ui::srv::create( handle_t& h,
const ws::args_t& args,
void* cbArg,
uiCallback_t uiCbFunc,
const appIdMap_t* appIdMapA,
unsigned appIdMapN,
websock::cbFunc_t wsCbFunc )
{
return create(h,
args.port,
args.physRootDir,
cbArg,
uiCbFunc,
args.uiRsrc,
appIdMapA,
appIdMapN,
wsCbFunc,
args.dfltPageFn,
args.wsTimeOutMs,
args.rcvBufByteN,
args.xmtBufByteN,
args.fmtBufByteN );
}
cw::rc_t cw::ui::srv::destroy( handle_t& h )
{
rc_t rc = kOkRC;
@ -2677,3 +2716,4 @@ cw::ui::handle_t cw::ui::srv::uiHandle( handle_t h )
ui_ws_srv_t* p = _handleToPtr(h);
return ws::uiHandle(p->wsUiH);
}

23
cwUi.h
View File

@ -192,8 +192,9 @@ namespace cw
unsigned rcvBufByteN;
unsigned xmtBufByteN;
unsigned fmtBufByteN;
unsigned wsTimeOutMs;
unsigned idleMsgPeriodMs; // min period without messages before an idle message is generated
unsigned queueBlkCnt;
unsigned queueBlkByteCnt;
} args_t;
rc_t parseArgs( const object_t& o, args_t& args, const char* object_label="ui" );
@ -218,20 +219,20 @@ namespace cw
unsigned appIdMapN = 0,
websock::cbFunc_t wsCbFunc = nullptr,
const char* dfltPageFn = "index.html",
unsigned websockTimeOutMs = 50,
unsigned idleMsgPeriodMs = 50,
unsigned rcvBufByteN = 1024,
unsigned xmtBufByteN = 1024,
unsigned fmtBufByteN = 4096
);
unsigned fmtBufByteN = 4096,
unsigned queueBlkCnt = 4,
unsigned queueBlkByteCnt = 4096 );
rc_t destroy( handle_t& h );
// This function should be called periodically to send and receive
// queued messages to and from the websocket.
// Note that this call may block for up to 'wsTimeOutMs' milliseconds
// on the websocket handle.
rc_t exec( handle_t h );
// while waiting for incoming websocket messages.
rc_t exec( handle_t h, unsigned wsTimeOutMs );
// This function executes the internal default websock callback function.
// It is useful if the user provides a custom websock callback function
@ -240,6 +241,8 @@ namespace cw
websock::handle_t websockHandle( handle_t h );
ui::handle_t uiHandle( handle_t h );
void realTimeReport( handle_t h );
}
namespace srv
@ -253,6 +256,7 @@ namespace cw
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,
@ -263,12 +267,15 @@ namespace cw
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 websockTimeOutMs = 50,
unsigned idleMsgPeriodMs = 50,
unsigned rcvBufByteN = 1024,
unsigned xmtBufByteN = 1024,
unsigned fmtBufByteN = 4096 );
unsigned fmtBufByteN = 4096,
unsigned queueBlkCnt = 4,
unsigned queueBlkByteCnt = 4096 );
rc_t destroy( handle_t& h );

View File

@ -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"
@ -379,6 +380,7 @@ namespace cw
cw::rc_t cw::ui::test( const object_t* cfg )
{
rc_t rc = kOkRC;
unsigned wsTimeOutMs = 50;
ui::ws::args_t args = {};
// Application Id's for the resource based UI elements.
@ -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 )

15
cwVectOps.cpp Normal file
View File

@ -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;
}

Some files were not shown because too many files have changed in this diff Show More