Merge branch 'poly'

This commit is contained in:
kevin 2024-11-29 11:13:59 -05:00
commit eeeb2913db
150 changed files with 24226 additions and 3908 deletions

2
.gitignore vendored
View File

@ -2,4 +2,4 @@ hold
study/serial/serial
cw_rt
vg0.txt
__pycache__

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
@ -28,14 +28,14 @@ libcwSRC += src/libcw/cwSpScBuf.cpp src/libcw/cwSpScQu
libcwHDR += src/libcw/cwNbMpScQueue.h
libcwSRC += src/libcw/cwNbMpScQueue.cpp
libcwHDR += src/libcw/cwAudioFile.h src/libcw/cwMidiFile.h
libcwSRC += src/libcw/cwAudioFile.cpp src/libcw/cwMidiFile.cpp
libcwHDR += src/libcw/cwAudioFile.h src/libcw/cwMidiFile.h src/libcw/cwWaveTableBank.h src/libcw/cwWaveTableNotes.h
libcwSRC += src/libcw/cwAudioFile.cpp src/libcw/cwMidiFile.cpp src/libcw/cwWaveTableBank.cpp src/libcw/cwWaveTableNotes.cpp
libcwHDR += src/libcw/cwAudioFileOps.h src/libcw/cwAudioTransforms.h src/libcw/cwDspTransforms.h src/libcw/cwAudioFileProc.h src/libcw/cwPvAudioFileProc.h
libcwSRC += src/libcw/cwAudioFileOps.cpp src/libcw/cwAudioTransforms.cpp src/libcw/cwDspTransforms.cpp src/libcw/cwAudioFileProc.cpp src/libcw/cwPvAudioFileProc.cpp
libcwHDR += src/libcw/cwFlow.h src/libcw/cwFlowDecl.h src/libcw/cwFlowTypes.h src/libcw/cwFlowProc.h src/libcw/cwFlowCross.h
libcwSRC += src/libcw/cwFlow.cpp src/libcw/cwFlowTypes.cpp src/libcw/cwFlowProc.cpp src/libcw/cwFlowCross.cpp
libcwHDR += src/libcw/cwFlow.h src/libcw/cwFlowDecl.h src/libcw/cwFlowTypes.h src/libcw/cwFlowNet.h src/libcw/cwFlowProc.h src/libcw/cwFlowCross.h src/libcw/cwFlowTest.h
libcwSRC += src/libcw/cwFlow.cpp src/libcw/cwFlowTypes.cpp src/libcw/cwFlowNet.cpp src/libcw/cwFlowProc.cpp src/libcw/cwFlowCross.cpp src/libcw/cwFlowTest.cpp
if cwWEBSOCK
libcwHDR += src/libcw/cwWebSock.h src/libcw/cwWebSockSvr.h
@ -46,8 +46,8 @@ libcwSRC += src/libcw/cwUi.cpp src/libcw/cwUiTest.cpp
endif
libcwHDR += src/libcw/cwKeyboard.h
libcwSRC += src/libcw/cwKeyboard.cpp
libcwHDR += src/libcw/cwKeyboard.h src/libcw/cwTest.h
libcwSRC += src/libcw/cwKeyboard.cpp src/libcw/cwTest.cpp
libcwHDR += src/libcw/cwSerialPortDecls.h src/libcw/cwSerialPort.h src/libcw/cwSerialPortSrv.h
libcwSRC += src/libcw/cwSerialPort.cpp src/libcw/cwSerialPortSrv.cpp
@ -71,8 +71,8 @@ if cwWEBSOCK
libcwHDR += src/libcw/cwIo.h src/libcw/cwIoTest.h src/libcw/cwIoMinTest.h src/libcw/cwIoSocketChat.h src/libcw/cwIoAudioPanel.h src/libcw/cwIoAudioMidi.h
libcwSRC += src/libcw/cwIo.cpp src/libcw/cwIoTest.cpp src/libcw/cwIoMinTest.cpp src/libcw/cwIoSocketChat.cpp src/libcw/cwIoAudioPanel.cpp src/libcw/cwIoAudioMidi.cpp
libcwHDR += src/libcw/cwIoMidiRecordPlay.h src/libcw/cwIoAudioRecordPlay.h src/libcw/cwIoAudioMidiApp.h src/libcw/cwIoFlow.h
libcwSRC += src/libcw/cwIoMidiRecordPlay.cpp src/libcw/cwIoAudioRecordPlay.cpp src/libcw/cwIoAudioMidiApp.cpp src/libcw/cwIoFlow.cpp
libcwHDR += src/libcw/cwIoMidiRecordPlay.h src/libcw/cwIoAudioRecordPlay.h src/libcw/cwIoAudioMidiApp.h src/libcw/cwIoFlow.h src/libcw/cwIoFlowCtl.h
libcwSRC += src/libcw/cwIoMidiRecordPlay.cpp src/libcw/cwIoAudioRecordPlay.cpp src/libcw/cwIoAudioMidiApp.cpp src/libcw/cwIoFlow.cpp src/libcw/cwIoFlowCtl.cpp
libcwHDR += src/libcw/cwIoPresetSelApp.h src/libcw/cwPianoScore.h src/libcw/cwPresetSel.h src/libcw/cwVelTableTuner.h src/libcw/cwGutimReg.h
libcwSRC += src/libcw/cwIoPresetSelApp.cpp src/libcw/cwPianoScore.cpp src/libcw/cwPresetSel.cpp src/libcw/cwVelTableTuner.cpp src/libcw/cwGutimReg.cpp

806
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,38 @@ specific types, to pass through. For example a 'selector' (n inputs, 1 output) o
DONE: Add a version of var_register() that both registers and returns the value of the variable.
*** Flow Instance Creation:
### Flow Instance Creation:
1. Create all vars from the class description and initially set their
value to the default value given in the class. chIdx=kAnyChIdx.
0. Parse the 'in' list and create any 'mult' variables whose
'in-var' contains an integer or underscore suffix. See
"'in' List Syntax and Semantics" below.
Note that all vars must be included in the class description.
1. Create all vars from the class description, that were not
already instantiated during 'in' list processing, and set their
initial value to the default value given in the class. chIdx=kAnyChIdx.
Notes:
- All vars must be included in the class description.
- All vars have a 'kAnyChIdx' instantiation. The kAnyChIdx variable serves two purposes:
+ Setting the value of kAnyChIdx automatically broadcasts the value to all other channels.
+ kAnyChIdx acts as a template when variables are created by 'channelization'.
This allows the network designer to set the value of the kAnyIdx variable
and have that become the default value for all subsequent variables
which are created without an explicit value. (Note that his currently
works for variables created from within `proc_create()` (i.e.
where `sfx_id` == kBaseSfxId, but it doesn't work for mult
variables that are automatically created via `var_register()`
because `var_register()` does not have a value to assign to the
kAnyChIdx instance. In this case the variable get assigned
the class default value. The way around this is to explicitely
set the mult variable value in the 'in' stmt or the 'args' stmt.)
2. Apply the preset record from the class description according to the
label given in the instance definition.
2. Apply the preset records from the class description according to the
'presets' list given in the instance definition.
If the variable values are given as a scalar then the existing
variable is simply given a new value.
@ -489,17 +511,18 @@ index of the value in the list. This is referred
to as 'channelizing' the variable because the variable will then
be represented by multiple physical variable records - one for each channel.
This means that all variables will have their initial record, with the chIdx set to 'any',
and then they may also have further variable records will for each explicit
and then they may also have further variable records for each explicit
channel number. The complete list of channelized variable record
is kept, in channel order, using the 'ch_link' links with the base of the list
on the 'any' record.
3. Apply the variable values defined in the instance 'args' record.
This application is treated similarly to the 'class'
preset. If the variable value is presented in a list then
the value is assigned to a specific channel if the channel
already exists then the value is simply replaced, if the
channel does not exist then the variable is 'channelized'.
3. Apply the variable values defined in a instance 'args' record.
The application of the args record proceeds exactly the same as
applying a 'class' preset. If the variable value is presented in a
list then the value is assigned to a specific channel. If the channel
already exists then the value is simply replaced. If the channel does
not exist then the variable is 'channelized'.
4. The varaibles listed in the 'in' list of the instance cfg.
are connected to their source variables.
@ -519,6 +542,757 @@ before registering the variable.
access to registered variables.
# Notes on 'poly' and 'mult':
The 'in' statement is formed by a list of _Connect Expressions_ :
`<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:
- Eliminate the value() custom proc_t function and replace it by setting a 'delta flag' on the
variables that change. Optionally a linked list of changed variables could be implemented to
avoid having to search for changed variable values - although this list might have to be implemented as a thread safe linked list.
- Allow proc's to send messages to the UI. Implementation: During exec() the proc builds a global list of variables whose values
should be passed to the UI. Adding to the list must be done atomically, but removing can be non-atomic because it will happen
at the end of the network 'exec' cycle when no proc's are being executed. See cwMpScNbQueue push() for an example of how to do this.
- Allow min/max limits on numeric variables.
- Add a 'doc' string-list to the class desc.
- Try using adding 'time' type and 'cfg' types to a 'log:{...}' stmt.
- print_network_fl and print_proc_dict_fl should be given from the command line.
(but it's ok to leave them as cfg flags also)
- Add 'doc' strings to all proc classes.
- Add 'doc' strings to user-defined proc data structure.
- It is an error to specify a suffix_id on a poly network proc because the suffix_id's are generated automatically.
This error should be caught by the compiler.
- How do user defined procedures handle suffix id's?
- Add a 'preset' arg to 'poly' so that a preset can be selected via the owning network.
Currently it is not possible to select a preset for a poly.
- Automatic assignment of sfx_id's should only occur when the network is a 'poly'.
This should be easy to detect.
- When a var value is given to var_create() it does not appear to channelize the
var if value is a list. Is a value ever given directly to `var_create()`?
Look at all the places `var_create()` is called can the value arg. be removed?
- var_channelize() should never be called at runtime.
- Re-write the currawong circuit with caw.
- Finish audio feedback example - this will probably involve writing an `audio_silence` class.
- Issue a warning if memory is allocated during runtime.
- String assignment is allocating memory:
See: `rc_t _val_set( value_t* val, const char* v ) cwFlowTypes.cpp line:464.`
- cwMpScNbQueue is allocating memory. This makes it blocking.
- Check for illegal variable names in class descriptions. (no periods, trailing digits, or trailing underscores)
- Check for unknown fields where the syntax clearly specifies only certain options via the 'readv()' method.
- Verify that all variables have been registered (have valid 'vid's) during post instantiation validation.
(this is apparently not currently happening)
- How is the number of channels for a given variable determined? Is it the widest (max channel) preset
that is encountered during preset compilation? What if a variable has a wide preset but it is not
initially applied - does that mean an uninitialized channel is just sitting there? (... no i think
the previous channel is duplicated in var_channelize())
- UI Issues:
+ When UI appIdMap[] labels do not match ui.cfg labels no error is generated. All appIdMap[] labels should be
validated to avoid this problem.
+ The reliance on using UUId to build UI's should be eliminated. It is very clunky.
+ UI elements should form proper tree's where elements know their children. As it is the links only go up the tree
from child to parent - searching down the tree is not possible.
+ Disabled "disp_str" should turn grey.
- Class presets cannot address 'mult' variables. Maybe this is ok since 'mult' variables are generally connected to a source?
... although 'gain' mult variables are not necessarily connected to a source see: `audio_split` or `audio_mix`.
Has this problem been addressed by allowing mult variables to be instantiated in the 'args' statement?
- Documentation w/ examples.
+ Write the rules for each implementing member functions.
- value() should return a special return-code value to indicate that the
value should not be updated and distinguish it from an error code - which should stop the system.
- flow classes and variable should have a consistent naming style: camelCase or snake_case.
- Variable attributes should be meaningful. e.g. src,src_opt,mult,init, ....
Should we check for 'src' or 'mult' attribute on var's?
(In other words: Enforce var attributes.)
- How much of the proc initialization implementation can use the preset compile/apply code?
- Reduce runtime overhead for var get/set operations.
- Implement matrix types.
- Should the `object_t` be used in place of `value_t`?
- log:
+ should print the values for all channels - right now it is only
printing the values for kAnyChIdx
+ log should print values for abuf (mean,max), fbuf (mean,max) mag, mbuf
- Audio inputs should be able to be initialized with a channel count and srate without actually connecting an input.
This will allow feedback connections to be attached to them at a later stage of the network
instantiation.
- Implement user-defined-proc preset application.
- Implement the var attributes and attribute checking.
- Port 'cm' and 'hum' processors.
- Implement Linux audio plugins loading
- Implement dynamic loading of procs.
- Implement a debug mode to aid in building networks and user-defined-procs (or is logging good enough)
- Implement multi-field messages.
- Implement user defined data types.
- Look more closely at the way of identify an in-stmt src-net or a out-stmt in-net.
It's not clear there is a difference between specifying `_` and the default behaviour.
Is there a way to tell it to search the entire network from the root? Isn't that
what '_' is supposed to do?
Host Environments:
------------------
- CLI, no GUI, no I/O, non-real-time only.
- CLI, no GUI, w/ I/O and real-time
- GUI, with configurable control panels
Done
----
- DONE: Remove `preset_label` and `type_src_label` from `_var_channelize()` and report error
locations from the point of call.
- DONE: Move proc_dict.cfg to libcw directory.
- DONE: The proc inst 'args' should be able to create mult variables. The only way to instantiate
new mult variables now is via the 'in' stmt.
- DONE: The `audio_merge` implementaiton is wrong. It should mimic `audio_mix` where all igain
coeff's are instantiated even if they are not referenced.
- DONE: Add the `caw` examples to the test suite.
- DONE: Remove the multiple 'args' thing and and 'argsLabel'. 'args' should be a simple set of arg's.
- DONE: Compile presets: at load time the presets should be resolved
to the proc and vars to which they will be assigned.
- DONE: (We are not removing the kAnyChIdx)
Should the var's with multiple channels remove the 'kAnyChIdx'?
This may be a good idea because 'kAnyChIdx' will in general not be used
if a var has been channelized - and yet it is possible for another
var to connect to it as a source ... which doesn't provoke an error
but would almost certainly not do what the user expects.
Note that the kAnyChIdx provides an easy way to set all of the channels
of a variable to the same value.
- DONE: verifiy that all proc variables values have a valid type - (i.e. (type & typeMask) != 0)
when the proc instance create is complete. This checks that both the type is assigned and
a valid value has been assigned - since the type is assigned the first time a value is set.
- DONE: 'poly' should be implemented as a proc-inst with an internal network - but the
elements of the network should be visible outside of it.
- DONE: 'sub' should be implemented as proc-inst with an internal network, but the
elements of the network should not be visible outside of it. Instead it should
include the idea of input and output ports which act as proxies to the physical
ports of the internal elements.
- DONE: 'poly' and 'sub' should be arbitrarily nestable.
- DONE: Allow multiple types on an input.
For example 'adder' should have a single input
which can by any numeric type.
- DONE: Make a standard way to turn on output printing from any port on any instance
This might be a better approach to logging than having a 'printer' object.
Add proc instance field: `log:{ var_label_0:0, var_label_1:0 } `
- Complete user-def-procs:
+ User-Def-Procs should have presets written in terms of the user-def-proc vars rather than the network vars
or the value application needs to follow the internal variable src_var back to the proxy var.
+ DONE: write a paragraph in the flow_doc.md about overall approach taken to user-def-proc implementation.
+ DONE: user-def-proc var desc's should be the same as non+user-def-proc vars but also include the 'proxy' field.
In particular they should get default values.
If a var desc is part of a user-def-proc then it must have a proxy.
The output variables of var desc's must have the 'out' attribute
+ DONE: improve the user-def-proc creating code by using consistent naming + use proxy or wrap but not both
+ DONE: improve code comments on user-def-proc creation
- DONE: Implement feedback
- DONE: Implement the ability to set backward connections - from late to early proc's.
This can be done by implementing the same process as 'in_stmt' but in a separate
'out_stmt'. The difficulty is that it prevents doing some checks until the network
is completely specified. For example if audio inputs can accept connections from
later proc's then they will not have all of their inputs when they are instantiated.
One way around this is to instantiate them with an initial set of inputs but then
allow those inputs to be replaced by a later connection.
BUGS:
- DONE: The counter modulo mode is not working as expected.
- DONE: Implement 'preset' proc. This will involve implementing the 'cfg' datatype.
- DONE: Finish the 'poly' frawework. We are making 'mult' var's, but do any of the procs explicitly deal with them?
- DONE: Turn on variable 'broadcast'. Why was it turned off? ... maybe multiple updates?
- DONE: There is no way for a proc in a poly context to use it's poly channel number to
select a mult variable. For example in an osc in a poly has no way to select
the frequency of the osc by conneting to a non-poly proc - like a list.
Consider:
1. Use a difference 'in' statememt (e.g. 'poly-in' but the
same syntax used for connecting 'mult' variables.)
2. Include the proc name in the 'in' var to indicate a poly index is being iterated
e.g. `lfo: { class:sine_tone, in:{ osc_.dc:list.value_ } }`
- DONE: Fix up the coding language - change the use of `instance_t` to `proc_t` and `inst` to `proc`, change use of `ctx` in cwFlowProc
DONE: After the network is fully instantiated the network and class presets
are compiled. At this point all preset values must be resolvable to
an actual proc variable. A warning is issued for presets with values
that cannot be resolved and they are disabled. The primary reason
that a preset might not be resolvable is by targetting a variable
channel that does not exist.
- DONE: All cfg to value conversion should go through `cfg_to_value()`.
Names
------
ixon -
hoot
caw, screech, warble, coo, peep, hoot, gobble, quack, honk, whistle, tweet, cheep, chirrup, trill, squawk, seet,
cluck,cackle,clack
cock-a-dooodle-doo
song,tune,aria
Proc instantiation
------------------
Prior to executing the custom constructor the values are assigned to the
variables as follows:
1. Default value as defined by the class are applied when the variable is created.
2. The proc instance 'preset' class preset is applied.
3. The proc instance 'args' values are applied.
During this stage of processing preset values may be given for
variables that do not yet exist. This will commonly occur when a
variable has multiple channels that will not be created until the
custom constructor is run. For these cases the variable will be
pre-emptively created and the preset value will be applied.
This approach has the advantage of communicating network information
to the proc constructor from the network configuration - thereby
allowing the network programmer to influence the configuration of the
proc. instance.
caw by example:
---------------
0. DONE: Add log object.
DONE: Add initial network preset selection system parameter.
1. sine->af
in-stmt
2. sine->af with network preset
topics:preset, class info
3. number,timer,counter,list,log
topics: log, data types, system parameters
4. sine->delay->mixer->af
-------->
topics: mult vars, system parameters
5. topic: modulate sine with sine
6. topic: modulate sine with sine and timed preset change.
7. topic: iterating input stmt 0 - connect multiple inputs to a single source
8. topic: iterating input stmt 1 - multiple inputs to multiple sources
9. topic: iterating input stmt 2 - two ranges
12. topic: poly
13. topic: poly w/iterating input stmt
14. topic: poly w/ xfade ctl and presets
15. topic: msg feedback
16. topic: audio feedback
17. topic: subnets
18. topic: subnet with presets
19. topic: presets w/ sfx id's
caw w/ UI
---------
1. If no program is given, but a cfg file is given, then load a menu with the pgm's in the file.
Selecting a prg name then loads the pgm
Otherwise, if a pgm is given then the pgm is automatically loaded.
If the pgm does not set 'use_ui_fl' then it is automatically initialized and executed.
Otherwise goto step 2
2. Once a pgm is loaded then it can be queried for UI information.
- get basic information from proc dict
- get override information from the network
3.

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"
@ -991,7 +992,7 @@ void cw::audio::buf::report(handle_t h)
/// [cwAudioBufExample]
cw::rc_t cw::audio::buf::test()
cw::rc_t cw::audio::buf::test(const test::test_args_t& args)
{
rc_t rc = kOkRC;
unsigned devIdx = 0;
@ -1086,8 +1087,8 @@ cw::rc_t cw::audio::buf::test()
}
for(i=0; i<sigN; ++i)
cwLogInfo("%f ",oSig[i]);
cwLogInfo("\n");
cwLogPrint("%f ",oSig[i]);
cwLogPrint("\n");
destroy(h);

View File

@ -236,7 +236,7 @@ namespace cw
void report( handle_t h );
// Run a buffer usage simulation to test the class. cmAudioPortTest.c calls this function.
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 "cwTime.h"
#include "cwAudioDevice.h"

View File

@ -1,8 +1,10 @@
#include "cwCommon.h"
#include "cwLog.h"
#include "cwCommonImpl.h"
#include "cwTest.h"
#include "cwMem.h"
#include "cwTime.h"
#include "cwText.h"
#include "cwTextBuf.h"
#include "cwThread.h"
#include "cwAudioDevice.h"
@ -1302,6 +1304,9 @@ cw::rc_t cw::audio::device::alsa::create( handle_t& hRef, struct driver_str*& dr
dr.nameStr = mem::printf(dr.nameStr,"hw:%i,%i,%i",cardNum,devNum,i);
dr.descStr = mem::printf(dr.descStr,"%s %s",cardNamePtr,snd_pcm_info_get_name(info));
// it's possible that trailing whitespace is left in the desc
removeTrailingWhitespace( dr.descStr );
// attempt to open the sub-device
if((err = _devOpen(&pcmH,dr.nameStr,inputFl)) < 0 )
_alsaSetupError(err,inputFl,&dr,"Unable to open the PCM handle");

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"
@ -1281,7 +1282,7 @@ namespace cw
{
rc_t rc = kOkRC;
// Determine the size of the temporary buffer used for deinterleaving, type conversion and file writing
// Determine the size of the temporary buffer used for interleaving, type conversion and file writing
unsigned bufFrmCnt = std::min(srcBufFrmCnt,4096u);
unsigned bufSmpCnt = bufFrmCnt*chCnt;
@ -1295,10 +1296,10 @@ namespace cw
// copy as many samples as are available into the temp. buffer
unsigned copyFrmN = std::min( bufFrmCnt, srcBufFrmCnt - sfi );
// deinterleave the source channel signal arrays into the temp buffer
// interleave the source channel signal arrays into the temp buffer
for(unsigned fi=0,si=0; fi<copyFrmN; ++fi)
for(unsigned sci=0; sci<chCnt; ++sci,++si)
sbuf[si] = srcPtrPtr[sci][sfi+fi];
for(unsigned sci=0; sci<chCnt; ++sci)
sbuf[si++] = srcPtrPtr[sci][sfi+fi];
// convert the sample data types and write the result to the output file
if((rc = _write_samples(p,copyFrmN*chCnt,sbuf,dbuf)) != kOkRC )

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"
@ -58,7 +59,7 @@ namespace cw
return rc;
}
rc_t test( const cw::object_t* args )
rc_t test()
{
double hann_15[] = { 0.0, 0.04951557, 0.1882551 , 0.38873953, 0.61126047, 0.8117449, 0.95048443, 1.0, 0.95048443, 0.8117449, 0.61126047, 0.38873953, 0.1882551, 0.04951557, 0.0 };
double hann_16[] = { 0.0, 0.04322727, 0.1654347 , 0.3454915 , 0.55226423, 0.75, 0.9045085 , 0.9890738, 0.9890738, 0.9045085, 0.75, 0.55226423, 0.3454915, 0.1654347 , 0.04322727, 0.0 };
@ -86,7 +87,7 @@ namespace cw
namespace ola
{
rc_t test( const cw::object_t* args )
rc_t test()
{
typedef float sample_t;
@ -149,7 +150,7 @@ namespace cw
namespace shift_buf
{
rc_t test( const object_t* args )
rc_t test()
{
rc_t rc = kOkRC;
typedef float sample_t;
@ -222,7 +223,7 @@ namespace cw
namespace pv_anl
{
rc_t test( const object_t* args )
rc_t test()
{
rc_t rc = kOkRC;
pv_anl::fobj_t* pva = nullptr;
@ -252,12 +253,256 @@ namespace cw
}
rc_t test( const cw::object_t* args )
namespace wt_osc
{
wnd_func::test(args);
ola::test(args);
shift_buf::test(args);
return kOkRC;
typedef float sample_t;
typedef float srate_t;
rc_t test()
{
rc_t rc = kOkRC;
srate_t srate = 8;
sample_t aV[] = { 7,0,1,2,3,4,5,6,7,0,1,2,3,4,5,6,7,0 };
struct wt_str<sample_t,srate_t> wt{
.tid = kLoopWtTId,
.cyc_per_loop = 0,
.aV = aV,
.aN = 16,
.rms = 0,
.hz = 1,
.srate = srate,
.pad_smpN = 1,
.posn_smp_idx = 0
};
struct obj_str<sample_t,srate_t> obj;
init(&obj,&wt);
unsigned yN = (int)(srate*2);
sample_t yV[yN];
unsigned actual = 0;
process(&obj, yV, yN,actual);
vop::print( yV, yN, "%f");
return rc;
}
}
namespace wt_seq_osc
{
typedef float sample_t;
typedef float srate_t;
rc_t test()
{
rc_t rc = kOkRC;
srate_t srate = 8;
sample_t aV[] = { 7,0,1,2,3,4,5,6,7,0,1,2,3,4,5,6,7,0 };
struct wt_osc::wt_str<sample_t,srate_t> wt{
.tid = wt_osc::kOneShotWtTId,
.cyc_per_loop = 0,
.aV = aV,
.aN = 16,
.rms = 0,
.hz = 1,
.srate = srate,
.pad_smpN = 1,
.posn_smp_idx = 0
};
struct wt_osc::wt_str<sample_t,srate_t> wt0 = wt;
struct wt_osc::wt_str<sample_t,srate_t> wt1 = wt;
struct wt_osc::wt_str<sample_t,srate_t> wt2 = wt;
wt1.tid = wt_osc::kLoopWtTId;
wt1.posn_smp_idx = 16;
wt2.tid = wt_osc::kLoopWtTId;
wt2.posn_smp_idx = 32;
struct wt_osc::wt_str<sample_t,srate_t> wtA[] = {
wt0,
wt1,
wt2
};
struct wt_seq_osc::wt_seq_str<sample_t,srate_t> wt_seq{
.wtA = wtA,
.wtN = 3
};
struct wt_seq_osc::obj_str<sample_t,srate_t> obj;
init(&obj,&wt_seq);
unsigned yN = (int)(srate*10);
sample_t yV[yN];
unsigned actual = 0;
unsigned yi = 0;
unsigned yDspSmpCnt = 8;
while( yi < yN && is_init(&obj) )
{
unsigned frmSmpN = std::min(yN-yi,yDspSmpCnt);
process(&obj, yV+yi, frmSmpN, actual);
assert( actual == frmSmpN );
yi += actual;
}
vop::print( yV, yi, "%5.3f",nullptr,8);
return rc;
}
}
namespace multi_ch_wt_seq_osc
{
typedef float sample_t;
typedef float srate_t;
rc_t test()
{
rc_t rc = kOkRC;
unsigned chN = 2;
srate_t srate = 8;
sample_t a0V[] = { 7,0,1,2,3,4,5,6,7,0,1,2,3,4,5,6,7,0 };
sample_t a1V[] = { 17,10,11,12,13,14,15,16,17,10,11,12,13,14,15,16,17,10 };
sample_t a2V[] = { 27,20,21,22,23,24,25,26,27,20,21,22,23,24,25,26,27,20 };
struct wt_osc::wt_str<sample_t,srate_t> wt{
.tid = wt_osc::kOneShotWtTId,
.cyc_per_loop = 0,
.aV = a0V,
.aN = 16,
.rms = 0,
.hz = 1,
.srate = srate,
.pad_smpN = 1,
.posn_smp_idx = 0
};
struct wt_osc::wt_str<sample_t,srate_t> wt0 = wt;
struct wt_osc::wt_str<sample_t,srate_t> wt1 = wt;
struct wt_osc::wt_str<sample_t,srate_t> wt2 = wt;
wt1.tid = wt_osc::kLoopWtTId;
wt1.posn_smp_idx = 16;
wt1.aV = a1V;
wt2.tid = wt_osc::kLoopWtTId;
wt2.posn_smp_idx = 32;
wt2.aV = a2V;
struct wt_osc::wt_str<sample_t,srate_t> wtA[] = {
wt0,
wt1,
wt2
};
struct wt_seq_osc::wt_seq_str<sample_t,srate_t> wt_seq{
.wtA = wtA,
.wtN = 3
};
struct wt_seq_osc::wt_seq_str<sample_t,srate_t> chA[] = {
wt_seq,
wt_seq
};
struct multi_ch_wt_seq_str<sample_t,srate_t> mcs = {
.chA = chA,
.chN = chN
};
struct obj_str<sample_t,srate_t> obj;
if((rc = create(&obj,chN)) != kOkRC )
goto errLabel;
if((rc = setup(&obj,&mcs)) != kOkRC )
goto errLabel;
else
{
unsigned yN = (int)(srate*10);
unsigned actual = 0;
unsigned yi = 0;
unsigned yDspSmpCnt = 8;
while( yi < yN && !is_done(&obj) )
{
unsigned frmSmpN = std::min(yN-yi,yDspSmpCnt);
sample_t yV[frmSmpN*chN];
if((rc = process(&obj, yV, chN, frmSmpN, actual)) != kOkRC )
goto errLabel;
assert( actual == frmSmpN );
vop::print( yV, frmSmpN*chN, "%5.3f",nullptr,8);
yi += actual;
}
}
errLabel:
destroy(&obj);
return rc;
}
}
rc_t test( const test::test_args_t& args )
{
rc_t rc = kOkRC;
if( textIsEqual(args.test_label,"wnd_func") )
{
rc = wnd_func::test();
goto errLabel;
}
if( textIsEqual(args.test_label,"ola") )
{
rc = ola::test();
goto errLabel;
}
if( textIsEqual(args.test_label,"shift_buf") )
{
rc = shift_buf::test();
goto errLabel;
}
if( textIsEqual(args.test_label,"wt_osc") )
{
rc = wt_osc::test();
goto errLabel;
}
if( textIsEqual(args.test_label,"wt_seq_osc") )
{
rc = wt_seq_osc::test();
goto errLabel;
}
if( textIsEqual(args.test_label,"multi_ch_wt_seq_osc") )
{
rc = multi_ch_wt_seq_osc::test();
goto errLabel;
}
rc = cwLogError(kInvalidArgRC,"Unknown test case module:%s test:%s.",args.module_label,args.test_label);
errLabel:
return rc;
}
}

View File

@ -1234,7 +1234,481 @@ namespace cw
}
rc_t test( const cw::object_t* args );
//---------------------------------------------------------------------------------------------------------------------------------
// wt_osc
//
namespace wt_osc
{
typedef enum {
kInvalidWtTId,
kOneShotWtTId,
kLoopWtTId
} wt_tid_t;
template< typename sample_t, typename srate_t >
struct wt_str
{
wt_tid_t tid;
unsigned cyc_per_loop; // count of cycles in the loop
sample_t* aV; // aV[ padN + aN + padN ]
unsigned aN; // Count of unique samples
double rms;
double hz;
srate_t srate;
unsigned pad_smpN;
unsigned posn_smp_idx; // The location of this sample in the original audio file.
};
template< typename sample_t >
sample_t table_read_2( const sample_t* tab, double frac )
{
unsigned i0 = floor(frac);
unsigned i1 = i0 + 1;
double f = frac - int(frac);
sample_t r = (sample_t)(tab[i0] + (tab[i1] - tab[i0]) * f);
//intf("r:%f frac:%f i0:%i f:%f\n",r,frac,i0,f);
return r;
}
template< typename sample_t >
sample_t hann_read( double x, double N )
{
while( x > N)
x -= N;
x = x - (N/2) ;
return (sample_t)(0.5 + 0.5 * cos(2*M_PI * x / N));
}
template< typename sample_t, typename srate_t >
struct obj_str
{
const wt_str<sample_t,srate_t>* wt;
double phs; // current fractional phase into wt->aV[]
double fsmp_per_wt; //
};
template< typename sample_t, typename srate_t >
bool validate_srate(const struct obj_str<sample_t,srate_t>* p, srate_t expected_srate)
{ return p->wt != nullptr && p->wt->srate == expected_srate; }
template< typename sample_t, typename srate_t >
bool is_init(const struct obj_str<sample_t,srate_t>* p)
{ return p->wt != nullptr; }
template< typename sample_t, typename srate_t >
void init(struct obj_str<sample_t,srate_t>* p, struct wt_str<sample_t,srate_t>* wt)
{
if( wt == nullptr )
p->wt = nullptr;
else
{
double fsmp_per_cyc = wt->srate/wt->hz;
p->fsmp_per_wt = fsmp_per_cyc * 2; // each wavetable contains 2
p->wt = wt;
p->phs = 0;
}
}
template< typename sample_t, typename srate_t >
void _process_loop(struct obj_str<sample_t,srate_t>* p, sample_t* aV, unsigned aN, unsigned& actual_Ref)
{
double phs0 = p->phs;
double phs1 = phs0 + p->fsmp_per_wt/2;
unsigned smp_per_wt = (int)floor(p->fsmp_per_wt); //
while(phs1 >= smp_per_wt)
phs1 -= smp_per_wt;
for(unsigned i=0; i<aN; ++i)
{
sample_t s0 = table_read_2( p->wt->aV+p->wt->pad_smpN, phs0 );
sample_t s1 = table_read_2( p->wt->aV+p->wt->pad_smpN, phs1 );
sample_t e0 = hann_read<sample_t>(phs0,p->fsmp_per_wt);
sample_t e1 = hann_read<sample_t>(phs1,p->fsmp_per_wt);
aV[ i ] = e0*s0 + e1*s1;
// advance the phases of the oscillators
phs0 += 1;
while(phs0 >= smp_per_wt)
phs0 -= smp_per_wt;
phs1 += 1;
while(phs1 >= smp_per_wt)
phs1 -= smp_per_wt;
}
p->phs = phs0;
actual_Ref = aN;
}
template< typename sample_t, typename srate_t >
void _process_one_shot(struct obj_str<sample_t,srate_t>* p, sample_t* aV, unsigned aN, unsigned& actual_Ref)
{
unsigned phs = (unsigned)p->phs;
unsigned i;
for(i=0; i<aN && phs<p->wt->aN; ++i,++phs)
aV[i] = p->wt->aV[ p->wt->pad_smpN + phs ];
p->phs = phs;
actual_Ref = i;
}
template< typename sample_t, typename srate_t >
void process(struct obj_str<sample_t,srate_t>* p, sample_t* aV, unsigned aN, unsigned& actual_Ref)
{
actual_Ref = 0;
switch( p->wt->tid )
{
case wt_osc::kLoopWtTId:
_process_loop(p,aV,aN,actual_Ref);
break;
case wt_osc::kOneShotWtTId:
_process_one_shot(p,aV,aN,actual_Ref);
break;
default:
assert(0);
}
}
rc_t test();
} // wt_osc
namespace wt_seq_osc
{
template< typename sample_t, typename srate_t >
struct wt_seq_str
{
struct wt_osc::wt_str<sample_t,srate_t>* wtA;
unsigned wtN;
};
template< typename sample_t, typename srate_t >
struct obj_str
{
struct wt_seq_osc::wt_seq_str<sample_t,srate_t>* wt_seq;
struct wt_osc::obj_str<sample_t,srate_t> osc0;
struct wt_osc::obj_str<sample_t,srate_t> osc1;
unsigned wt_idx; // index of wt0 in wt_seq->wtA[]
unsigned mix_interval_smp; // osc0/osc1 crossfade interval in samples
unsigned mix_phs; // current crossfade phase (0 <= mix_phs <= mix_interval_smp)
};
template< typename sample_t, typename srate_t >
rc_t _update_wt( struct obj_str<sample_t,srate_t>* p, unsigned wt_idx )
{
rc_t rc = kOkRC;
struct wt_osc::wt_str<sample_t,srate_t>* wt0 = nullptr;
struct wt_osc::wt_str<sample_t,srate_t>* wt1 = nullptr;
p->mix_interval_smp = 0;
if( wt_idx < p->wt_seq->wtN )
wt0 = p->wt_seq->wtA + wt_idx;
if( (wt_idx+1) < p->wt_seq->wtN )
{
wt1 = p->wt_seq->wtA + (wt_idx+1);
unsigned posn0_smp_idx = wt0->posn_smp_idx;
unsigned posn1_smp_idx = wt1->posn_smp_idx;
if( posn1_smp_idx < posn0_smp_idx )
{
rc = cwLogError(kInvalidStateRC,"The position of the wavetable at wt. seq index:%i must be greater than the position of the previous wt.",wt_idx+1);
goto errLabel;
}
p->mix_interval_smp = posn1_smp_idx - posn0_smp_idx;
}
wt_osc::init(&p->osc0,wt0);
wt_osc::init(&p->osc1,wt1);
p->wt_idx = wt_idx;
p->mix_phs = 0;
errLabel:
return rc;
}
template< typename sample_t, typename srate_t >
bool validate_srate(const struct obj_str<sample_t,srate_t>* p, srate_t expected_srate)
{
if( p->wt_seq == nullptr )
return false;
for(unsigned i=0; i<p->wt_seq->wtN; ++i)
if( p->wt_seq->wtA[i].srate != expected_srate )
return false;
return true;
}
template< typename sample_t, typename srate_t >
bool is_init( const struct obj_str<sample_t,srate_t>* p )
{
return is_init(&p->osc0);
}
template< typename sample_t, typename srate_t >
rc_t init(struct obj_str<sample_t,srate_t>* p, struct wt_seq_osc::wt_seq_str<sample_t,srate_t>* wt_seq)
{
rc_t rc = kOkRC;
p->wt_seq = wt_seq;
p->wt_idx = 0;
if((rc = _update_wt(p,0)) != kOkRC )
goto errLabel;
errLabel:
return rc;
}
template< typename sample_t, typename srate_t >
rc_t process(struct obj_str<sample_t,srate_t>* p, sample_t* aV, unsigned aN, unsigned& actual_Ref)
{
actual_Ref = 0;
rc_t rc = kOkRC;
unsigned actual;
bool atk_fl = p->wt_idx==0 && p->osc0.wt->tid == wt_osc::kOneShotWtTId;
// if the osc is in the attack phase
if( atk_fl )
{
// update aV[aN] from osc0
wt_osc::process(&p->osc0,aV,aN,actual);
actual_Ref = actual;
// if all requested samples were generated we are done ...
if( actual >= aN )
return rc;
// otherwise all requested samples were not generated
// fill the rest of aV[] from the next one or two wave tables.
aN -= actual;
aV += actual;
// initialize osc0 and osc1
if((rc = _update_wt(p, 1)) != kOkRC )
goto errLabel;
}
wt_osc::process(&p->osc0,aV,aN,actual);
// if the second oscillator is initialized
if( wt_osc::is_init(&p->osc1) )
{
unsigned actual1 = 0;
sample_t tV[ aN ];
// generate aN samples into tV[aN]
wt_osc::process(&p->osc1,tV,aN,actual1);
assert( actual1 == actual );
sample_t g = (sample_t)std::min(1.0,(double)p->mix_phs / p->mix_interval_smp);
// mix the output of the second oscillator into the output signal
vop::scale_add(aV,aV,(1.0f-g),tV,g,actual1);
p->mix_phs += actual;
// if the osc0/osc1 xfade is complete ...
if( p->mix_phs >= p->mix_interval_smp )
{
// ... then advance to the next set of wavetables
if((rc = _update_wt(p, p->wt_idx+1)) != kOkRC )
goto errLabel;
}
}
actual_Ref += actual;
errLabel:
return rc;
}
rc_t test();
} // wt_seq_osc
namespace multi_ch_wt_seq_osc
{
template< typename sample_t, typename srate_t >
struct multi_ch_wt_seq_str
{
struct wt_seq_osc::wt_seq_str<sample_t,srate_t>* chA;
unsigned chN;
};
template< typename sample_t, typename srate_t >
struct obj_str
{
const struct multi_ch_wt_seq_str<sample_t,srate_t>* mcs = nullptr;
struct wt_seq_osc::obj_str<sample_t,srate_t>* chA = nullptr;
unsigned chAllocN = 0;
unsigned chN = 0;
bool done_fl = true;
};
// if mcs != nullptr and expected_srate is non-zero then the expected_srate will be validated
template< typename sample_t, typename srate_t >
rc_t create(struct obj_str<sample_t,srate_t>* p, unsigned maxChN, const struct multi_ch_wt_seq_str<sample_t,srate_t>* mcs=nullptr, srate_t expected_srate=0 )
{
rc_t rc = kOkRC;
destroy(p);
p->chA = mem::allocZ< struct wt_seq_osc::obj_str<sample_t,srate_t> >(maxChN);
p->chAllocN = maxChN;
p->chN = 0;
p->done_fl = true;
if( mcs != nullptr )
setup(p,mcs);
return rc;
}
template< typename sample_t, typename srate_t >
rc_t destroy(struct obj_str<sample_t,srate_t>* p )
{
rc_t rc = kOkRC;
mem::release(p->chA);
p->chAllocN = 0;
p->chN = 0;
p->done_fl = true;
return rc;
}
// if mcs != nullptr and expected_srate is non-zero then the expected_srate will be validated
template< typename sample_t, typename srate_t >
rc_t setup( struct obj_str<sample_t,srate_t>* p, const struct multi_ch_wt_seq_str<sample_t,srate_t>* mcs, srate_t expected_srate=0 )
{
rc_t rc = kOkRC;
if( mcs->chN > p->chAllocN )
{
rc = cwLogError(kInvalidArgRC,"Invalid multi-ch-wt-osc channel count. (%i > %i)",mcs->chN,p->chAllocN);
goto errLabel;
}
p->mcs = mcs;
p->done_fl = false;
p->chN = mcs->chN;
for(unsigned i=0; i<mcs->chN; ++i)
if((rc = wt_seq_osc::init(p->chA+i,mcs->chA + i)) != kOkRC )
goto errLabel;
if( mcs != nullptr && expected_srate != 0 )
if( !validate_srate(p,expected_srate) )
{
rc = cwLogError(kInvalidArgRC,"The srate is not valid. All wave tables do not share the same sample rate.");
goto errLabel;
}
errLabel:
if( rc != kOkRC )
rc = cwLogError(rc,"multi-ch-wt-osc setup failed.");
return rc;
}
template< typename sample_t, typename srate_t >
bool validate_srate(const struct obj_str<sample_t,srate_t>* p, srate_t expected_srate)
{
if( p->chA == nullptr )
return false;
for(unsigned i=0; i<p->chN; ++i)
if( !validate_srate(p->chA+i,expected_srate) )
return false;
return true;
}
template< typename sample_t, typename srate_t >
rc_t is_done( struct obj_str<sample_t,srate_t>* p )
{ return p->done_fl; }
template< typename sample_t, typename srate_t >
rc_t process( struct obj_str<sample_t,srate_t>* p, sample_t* aM, unsigned chN, unsigned frmN, unsigned& actual_Ref )
{
rc_t rc = kOkRC;
unsigned actual = 0;
unsigned doneN = 0;
for(unsigned i=0; i<p->chN; ++i)
{
unsigned actual0 = 0;
sample_t* aV = aM + (i*frmN);
if( !wt_seq_osc::is_init(p->chA + i) )
{
vop::zero(aV,frmN);
actual0 = frmN;
doneN += 1;
}
else
{
if((rc = wt_seq_osc::process(p->chA + i, aV, frmN, actual0 )) != kOkRC )
goto errLabel;
}
if( i!=0 && actual0 != actual )
{
rc = cwLogError(kInvalidStateRC,"An inconsistent sample count was generated across channels (%i != !i).",actual0,actual);
goto errLabel;
}
actual = actual0;
}
actual_Ref = actual;
p->done_fl = doneN == p->chN;
errLabel:
if( rc != kOkRC )
rc = cwLogError(rc,"multi-ch-wt-osc process failed.");
return rc;
}
rc_t test();
} //multi_ch_wt_seq_osc
rc_t test( const test::test_args_t& args );
} // dsp
} // cw

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

View File

@ -1,12 +1,14 @@
#include "cwCommon.h"
#include "cwLog.h"
#include "cwCommonImpl.h"
#include "cwTest.h"
#include "cwMem.h"
#include "cwText.h"
#include "cwFile.h"
#include "cwObject.h"
#include "cwCsv.h"
#include "cwNumericConvert.h"
#include <type_traits>
namespace cw
{
@ -262,6 +264,8 @@ namespace cw
goto errLabel;
}
fieldStr_Ref = p->lineBuf + p->colA[colIdx].char_idx;
errLabel:
@ -278,11 +282,23 @@ namespace cw
if((rc = _get_field_str(p,colIdx,fieldStr)) != kOkRC )
goto errLabel;
if( fieldStr != nullptr )
{
// advance past white space
while( *fieldStr && isspace(*fieldStr) )
++fieldStr;
// the first char must be a number or decimal point
if( isdigit(*fieldStr) || (*fieldStr=='.' && std::is_floating_point<T>()) )
{
if((rc = string_to_number(fieldStr,valueRef)) != kOkRC )
{
rc = cwLogError(rc,"Numeric parse failed on column '%s' on line index:%i",cwStringNullGuard(p->colA[colIdx].title),p->curLineIdx);
goto errLabel;
}
}
}
errLabel:
return rc;

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

2526
cwFlow.cpp

File diff suppressed because it is too large Load Diff

100
cwFlow.h
View File

@ -1,5 +1,5 @@
#ifndef cwFlowSys_h
#define cwFlowSys_h
#ifndef cwFlow_h
#define cwFlow_h
namespace cw
{
@ -8,68 +8,52 @@ namespace cw
typedef handle<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 );
void print_external_device( const external_device_t* dev );
// Parse the cfg's but don't yet instantiate the network.
// Upon completion of this function the caller can
// query the network for configuration information which can
// be used to setup the extern_device_t array.
rc_t create(handle_t& hRef,
const object_t* classCfg, // processor class dictionary
const object_t* pgmCfg, // top level program cfg
const object_t* udpCfg = nullptr,
const char* projDir = nullptr,
ui_callback_t ui_callback = nullptr,
void* ui_callback_arg = nullptr);
rc_t create( handle_t& hRef,
const object_t& classCfg,
const object_t& networkCfg,
// Network cfg. information which is available following create().
bool is_non_real_time( handle_t h );
double sample_rate( handle_t h );
unsigned frames_per_cycle( handle_t h );
unsigned preset_cfg_flags( handle_t h );
// Get the count and labels of the top level presets
unsigned preset_count( handle_t h );
const char* preset_label( handle_t h, unsigned preset_idx );
// Instantiate the network and prepare for runtime.
// The UI is not available until after initialization.
rc_t initialize( handle_t handle,
external_device_t* deviceA = nullptr,
unsigned deviceN = 0);
unsigned deviceN = 0,
unsigned preset_idx = kInvalidIdx);
rc_t destroy( handle_t& hRef );
unsigned preset_cfg_flags( handle_t h );
// The ui_net() is not available until the network has been initialized.
const ui_net_t* ui_net( handle_t h );
// Run one cycle of the network.
rc_t exec_cycle( handle_t h );
// Run the network to completion.
// Run a non-real-time program to completion.
rc_t exec( handle_t h );
rc_t apply_preset( handle_t h, const char* presetLabel );
rc_t apply_preset( handle_t h, const multi_preset_selector_t& multi_preset_sel );
rc_t apply_dual_preset( handle_t h, const char* presetLabel_0, const char* presetLabel_1, double coeff );
rc_t apply_preset( handle_t h, const multi_preset_selector_t& multi_preset_sel );
rc_t set_variable_value( handle_t h, const char* inst_label, const char* var_label, unsigned chIdx, bool value );
@ -84,10 +68,28 @@ namespace cw
rc_t get_variable_value( handle_t h, const char* inst_label, const char* var_label, unsigned chIdx, float& valueRef );
rc_t get_variable_value( handle_t h, const char* inst_label, const char* var_label, unsigned chIdx, double& valueRef );
// The 'user_id' shows up as the 'user_id' in the ui_var field.
rc_t set_variable_user_id( handle_t h, const ui_var_t* ui_var, unsigned user_id );
rc_t set_variable_value( handle_t h, const ui_var_t* ui_var, bool value );
rc_t set_variable_value( handle_t h, const ui_var_t* ui_var, int value );
rc_t set_variable_value( handle_t h, const ui_var_t* ui_var, unsigned value );
rc_t set_variable_value( handle_t h, const ui_var_t* ui_var, float value );
rc_t set_variable_value( handle_t h, const ui_var_t* ui_var, double value );
rc_t set_variable_value( handle_t h, const ui_var_t* ui_var, const char* value );
rc_t get_variable_value( handle_t h, const ui_var_t* ui_var, bool& value_ref );
rc_t get_variable_value( handle_t h, const ui_var_t* ui_var, int& value_ref );
rc_t get_variable_value( handle_t h, const ui_var_t* ui_var, unsigned& value_ref );
rc_t get_variable_value( handle_t h, const ui_var_t* ui_var, float& value_ref );
rc_t get_variable_value( handle_t h, const ui_var_t* ui_var, double& value_ref );
rc_t get_variable_value( handle_t h, const ui_var_t* ui_var, const char*& value_ref );
void print_class_list( handle_t h );
void print_network( handle_t h );
rc_t test( const object_t* cfg );

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,11 +147,16 @@ 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 )) == kOkRC )
{
rc = cwLogError(rc,"Flow cross index %i network create failed.",net_idx);
}
if((rc = flow::initialize( net->flowH, net->deviceA, deviceN )) == kOkRC )
net->deviceN = deviceN;
else
{
cwLogError(rc,"Flow cross index %i network created failed.",net_idx);
cwLogError(rc,"Flow cross index %i network initialize failed.",net_idx);
goto errLabel;
}
@ -203,9 +225,20 @@ namespace cw
if( net->stateId == kFadeOutStateId && ef == 0.0 )
net->stateId = kInactiveStateId;
}
// Copy audio from the actual external audio device to a cloned audio device
void _update_midi_input( flow_cross_t* p, flow_network_t* net, unsigned devIdx )
{
flow::midi_dev_cfg_t& src = p->deviceA[devIdx].u.m; // src MIDI device
flow::midi_dev_cfg_t& dst = net->deviceA[devIdx].u.m; // dst MIDI device clone
// redirect the MIDI msg list array to the clones
dst.msgArray = src.msgArray;
dst.msgCnt = src.msgCnt;
}
// Copy audio from the actual external audio device to a cloned audio device
void _update_audio_input( flow_cross_t* p, flow_network_t* net, unsigned devIdx )
{
flow::abuf_t* src = p->deviceA[devIdx].u.a.abuf;
@ -216,6 +249,7 @@ namespace cw
//_fade_audio( src, dst, net );
}
void _zero_audio_output( flow_cross_t* p, flow_network_t* net, unsigned devIdx )
{
flow::abuf_t* dst = net->deviceA[devIdx].u.a.abuf;
@ -378,12 +412,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,
@ -32,6 +93,82 @@ namespace cw
unsigned presetN;
} multi_preset_selector_t;
typedef struct ui_preset_str
{
const char* label;
unsigned preset_idx;
} ui_preset_t;
typedef struct ui_proc_desc_str
{
const char* label; // class label
ui_preset_t* presetA; // presetA[ presetN ]
unsigned presetN;
} ui_proc_desc_t;
struct ui_proc_str;
typedef struct ui_var_str
{
struct ui_proc_str* ui_proc; // owning proc
const char* label; // flow::variable_t::label
unsigned label_sfx_id; // flow::variable_t::label_sfx_id
const object_t* desc_cfg; // var desc cfg from flow::var_desc_t
unsigned desc_flags; // flow::var_desc_t::flags
bool has_source_fl; // true if this var is connected to a source var
unsigned value_tid; // flow::variable_t::type
unsigned vid; // flow::variable_t::vid
unsigned ch_idx; // flow::variable_t::chIdx
unsigned ch_cnt; // 0=kAnyChIdx only, kInvalidCnt=no channels, 1=mono, 2=stereo, ...
unsigned user_id; // uuId of the UI element that represents this var
} ui_var_t;
struct proc_str;
typedef struct ui_proc_str
{
const struct ui_net_str* ui_net;
struct proc_str* proc;
const ui_proc_desc_t* desc;
const object_t* cfg; // complete proc inst. cfg
const char* label; // flow::proc_t::label
unsigned label_sfx_id; //
ui_var_t* varA; // varA[varN]
unsigned varN; //
struct ui_net_str* internal_net;
} ui_proc_t;
struct network_str;
typedef struct ui_net_str
{
struct network_str* net;
ui_proc_t* procA; // procA[procN]
unsigned procN;
ui_preset_t* presetA; // presetA[presetN] network presets
unsigned presetN;
struct ui_net_str* poly_link;
unsigned poly_idx;
} ui_net_t;
typedef rc_t (*ui_callback_t)( void* arg, const ui_var_t* ui_var );
}
}

3516
cwFlowNet.cpp Normal file

File diff suppressed because it is too large Load Diff

125
cwFlowNet.h Normal file
View File

@ -0,0 +1,125 @@
#ifndef cwFlowNet_h
#define cwFlowNet_h
namespace cw
{
namespace flow
{
// Instantiate a network.
// The root network always is instantiated with a single cfg. record - because it is never a poly network.
// The only time netCfgN will be greater than 1 is when a heterogenous poly network is being
// instantiated.
rc_t network_create( flow_t* p,
const object_t* const * netCfgA, // netCfgA[netCfgN]
unsigned netCfgN, // count of cfg. records in netCfgN
variable_t* proxyVarL, //
unsigned polyCnt, // Count of poly subnets to create or 1 if the network is not poly
const char* preset_label, // Optional top-level preset label
network_t*& net_ref // Returned network handle.
);
rc_t network_destroy( network_t*& net );
const object_t* find_network_preset( const network_t& net, const char* presetLabel );
// Instantiates net_t.ui_net.
rc_t create_net_ui_desc( flow_t* p );
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 );
rc_t set_variable_user_id( network_t&net, const ui_var_t* ui_var, unsigned user_id );
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;
}
template< typename T >
rc_t set_variable_value( network_t& net, const ui_var_t* ui_var, T value )
{
rc_t rc = kOkRC;
// set the variable value
if((rc = var_set( ui_var->ui_proc->proc, ui_var->vid, ui_var->ch_idx, value )) != kOkRC )
{
rc = cwLogError(kOpFailRC,"The variable set failed on instance:'%s:%i' variable:'%s:%i'.",cwStringNullGuard(ui_var->ui_proc->proc->label),ui_var->ui_proc->proc->label_sfx_id,cwStringNullGuard(ui_var->label),ui_var->label_sfx_id);
goto errLabel;
}
errLabel:
return rc;
}
template< typename T >
rc_t get_variable_value( network_t& net, const ui_var_t* ui_var, T& valueRef )
{
rc_t rc = kOkRC;
// get the variable value
if((rc = var_get( ui_var->ui_proc->proc, ui_var->vid, ui_var->ch_idx, valueRef )) != kOkRC )
{
rc = cwLogError(kOpFailRC,"The variable get failed on instance:'%s:%i' variable:'%s:%i'.",cwStringNullGuard(ui_var->ui_proc->proc->label),ui_var->ui_proc->proc->label_sfx_id,cwStringNullGuard(ui_var->label),ui_var->label_sfx_id);
goto errLabel;
}
errLabel:
return rc;
}
// 'proc_label_sfx_id' is the proc label_sfx_id to be used to identify all proc's which will
// be updated by the preset application. This is used to identify the set of procs to be updated
// for 'poly' networks.
// If 'proc_label_sfx_id' is set to 'kInvalidId' then the preset will be applied to all proc's.
rc_t network_apply_preset( network_t& net, const char* presetLabel, unsigned proc_label_sfx_id=kInvalidId );
rc_t network_apply_dual_preset( network_t& net, const char* presetLabel_0, const char* presetLabel_1, double coeff, unsigned proc_label_sfx_id=kInvalidId );
rc_t network_apply_preset( network_t& net, const multi_preset_selector_t& mps, unsigned proc_label_sfx_id=kInvalidId );
}
}
#endif

File diff suppressed because it is too large Load Diff

View File

@ -2,15 +2,21 @@ namespace cw
{
namespace flow
{
namespace user_def_proc { extern class_members_t members; }
namespace poly { extern class_members_t members; }
namespace midi_in { extern class_members_t members; }
namespace midi_out { extern class_members_t members; }
namespace audio_in { extern class_members_t members; }
namespace audio_out { extern class_members_t members; }
namespace audioFileIn { extern class_members_t members; }
namespace audioFileOut { extern class_members_t members; }
namespace audio_file_in { extern class_members_t members; }
namespace audio_file_out { extern class_members_t members; }
namespace audio_gain { extern class_members_t members; }
namespace audio_split { extern class_members_t members; }
namespace audio_merge { extern class_members_t members; }
namespace audio_duplicate { extern class_members_t members; }
namespace audio_mix { extern class_members_t members; }
namespace audio_marker { extern class_members_t members; }
namespace audio_silence { extern class_members_t members; }
namespace sine_tone { extern class_members_t members; }
namespace pv_analysis { extern class_members_t members; }
namespace pv_synthesis { extern class_members_t members; }
@ -22,5 +28,24 @@ namespace cw
namespace balance { extern class_members_t members; }
namespace audio_meter { extern class_members_t members; }
namespace audio_marker { extern class_members_t members; }
namespace xfade_ctl { extern class_members_t members; }
namespace midi_voice { extern class_members_t members; }
namespace piano_voice { extern class_members_t members; }
namespace poly_voice_ctl { extern class_members_t members; }
namespace sample_hold { extern class_members_t members; }
namespace number { extern class_members_t members; }
namespace reg { extern class_members_t members; }
namespace timer { extern class_members_t members; }
namespace counter { extern class_members_t members; }
namespace list { extern class_members_t members; }
namespace add { extern class_members_t members; }
namespace preset { extern class_members_t members; }
namespace print { extern class_members_t members; }
namespace halt { extern class_members_t members; }
namespace midi_msg { extern class_members_t members; }
namespace midi_split { extern class_members_t members; }
namespace midi_file { extern class_members_t members; }
namespace midi_merge { extern class_members_t members; }
}
}

88
cwFlowTest.cpp Normal file
View File

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

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,49 +3,59 @@ 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
srate_t srate; // Signal sample rate
unsigned chN; // Count of channels
unsigned frameN; // Count of sample frames per channel
unsigned bufAllocSmpN; // Size of allocated buf[] in samples.
sample_t* buf; // buf[ chN ][ frameN ]
} abuf_t;
enum {
kFbufVectN = 3, // count of signal vectors in fbuf (mag,phs,hz)
kAnyChIdx = kInvalidIdx,
kLocalValueN = 2
};
typedef struct fbuf_str
{
struct value_str* base;
unsigned memByteN; // Count of bytes in mem[].
void* mem; // mem[ memByteN ] All dynamically allocated memory used by this fbuf.
srate_t srate; // signal sample rate
unsigned flags; // See kXXXFbufFl
unsigned chN; // count of channels
unsigned* maxBinN_V; // max value that binN_V[i] is allowed to take
unsigned* maxBinN_V; // maxBinN_V[chN] max value that binN_V[i] is allowed to take
unsigned* binN_V; // binN_V[ chN ] count of sample frames per channel
unsigned* hopSmpN_V; // hopSmpN_V[ chN ] hop sample count
fd_real_t** magV; // magV[ chN ][ binN ]
fd_real_t** phsV; // phsV[ chN ][ binN ]
fd_real_t** hzV; // hzV[ chN ][ binN ]
fd_sample_t** magV; // magV[ chN ][ binN ]
fd_sample_t** phsV; // phsV[ chN ][ binN ]
fd_sample_t** hzV; // hzV[ chN ][ binN ]
bool* readyFlV; // readyFlV[chN] true if this channel is ready to be processed (used to sync. fbuf rate to abuf rate)
fd_real_t* buf; // memory used by this buffer (or NULL if magV,phsV,hzV point are proxied to another buffer)
} fbuf_t;
typedef struct mbuf_str
{
const midi::ch_msg_t* msgA;
unsigned msgN;
} mbuf_t;
enum
{
kInvalidTFl = 0x00000000,
@ -58,17 +68,23 @@ namespace cw
kBoolMtxTFl = 0x00000020,
kUIntMtxTFl = 0x00000040,
kIntMtxTFl = 0x00000080,
kRealMtxTFl = 0x00000100,
kFloatMtxTFl = 0x00000200,
kDoubleMtxTFl= 0x00000400,
kFloatMtxTFl = 0x00000100,
kDoubleMtxTFl= 0x00000200,
kABufTFl = 0x00000800,
kFBufTFl = 0x00001000,
kABufTFl = 0x00000400,
kFBufTFl = 0x00000800,
kMBufTFl = 0x00001000,
kStringTFl = 0x00002000,
kTimeTFl = 0x00004000,
kCfgTFl = 0x00008000,
kTypeMask = 0x00007fff,
kTypeMask = 0x0000ffff,
kRuntimeTFl = 0x80000000,
kNumericTFl = kBoolTFl | kUIntTFl | kIntTFl | kFloatTFl | kDoubleTFl,
kMtxTFl = kBoolMtxTFl | kUIntMtxTFl | kIntMtxTFl | kFloatMtxTFl | kDoubleMtxTFl,
kAllTFl = kTypeMask
};
typedef struct mtx_str
@ -76,7 +92,6 @@ namespace cw
union {
struct mtx::mtx_str< unsigned >* u;
struct mtx::mtx_str< int >* i;
struct mtx::mtx_str< real_t >* r;
struct mtx::mtx_str< float >* f;
struct mtx::mtx_str< double >* d;
} u;
@ -84,7 +99,8 @@ namespace cw
typedef struct value_str
{
unsigned flags;
unsigned tflag;
union {
bool b;
uint_t u;
@ -93,12 +109,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 +125,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,
kUdpOutVarDescFl = 0x20
};
typedef struct class_members_str
@ -139,56 +160,86 @@ 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; // preset linked list
class_members_t* members; // member functions for this class
unsigned polyLimitN; // max. poly copies of this class per network_t or 0 if no limit
ui_proc_desc_t* ui;
} class_desc_t;
enum {
kInvalidVarFl = 0x00,
kLogVarFl = 0x01,
kProxiedVarFl = 0x02,
kProxiedOutVarFl = 0x04
};
// Note: The concatenation of 'vid' and 'chIdx' should form a unique identifier among all variables
// on a given 'instance'.
typedef struct variable_str
{
struct instance_str* inst; // pointer to this variables instance
struct proc_str* proc; // pointer to this variables instance
char* label; // this variables label
unsigned vid; // this variables numeric id ( cat(vid,chIdx) forms a unique variable identifier on this 'inst'
var_desc_t* varDesc; // the variable description for this variable
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; // See kLogVarFl, kProxiedVarFl, etc
unsigned type; // This is the value type as established when the var is initialized - it never changes for the life of the var.
var_desc_t* classVarDesc; // pointer to this variables class var desc
var_desc_t* localVarDesc; // pointer to this variables local var desc - if it doesn't match classVarDesc.
var_desc_t* varDesc; // the effective variable description for this variable (set to classVarDesc or localVarDesc)
value_t local_value[ kLocalValueN ]; // the local value instance (actual value if this is not a 'src' variable)
unsigned local_value_idx; // local_value[] is double buffered to allow the cur value of the buf[] to be held while the next value is validated (see _var_set_template())
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.
ui_var_t* ui_var; // this variables UI description
std::atomic<struct variable_str*> ui_var_link; // UI update var link based on flow_t ui_var_head;
} 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 char* arg_label; // optional args label
const object_t* arg_cfg; // optional args configuration
const object_t* proc_cfg; // instance configuration
void* userPtr; // instance state
@ -199,32 +250,144 @@ 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 net_global_var_str
{
const char* class_label;
char* var_label;
void* blob;
unsigned blobByteN;
struct net_global_var_str* link;
} net_global_var_t;
typedef struct network_str
{
const object_t* procsCfg; // network proc list
const object_t* presetsCfg; // presets designed for this network
struct proc_str** procA;
unsigned procN;
network_preset_t* presetA;
unsigned presetN;
// Preset pair table used by network_apply_dual_preset()
network_preset_pair_t* preset_pairA;
unsigned preset_pairN;
net_global_var_t* globalVarL;
struct network_str* poly_link;
unsigned poly_idx;
ui_net_t* ui_net;
} network_t;
typedef struct flow_str
{
const object_t* networkCfg; // complete cfg used to create this network
const object_t* presetCfg; // presets designed for this network
const object_t* pgmCfg; // complete program cfg
const object_t* networkCfg; // 'network' cfg from pgmCfg
bool printNetworkFl;
bool non_real_time_fl; // set if this is a non-real-time program
unsigned framesPerCycle; // sample frames per cycle (64)
srate_t sample_rate; // default sample rate (48000.0)
unsigned maxCycleCount; // count of cycles to run on flow::exec() or 0 if there is no limit.
const char* init_net_preset_label;// network initialization preset label or nullptr if there is no net. init. preset
bool isInRuntimeFl; // Set when compile-time is complete
unsigned cycleIndex; // Incremented with each processing cycle
bool printLogHdrFl;
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* udpDescA; //
unsigned udpDescN; //
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
// Top-level preset list.
network_preset_t* presetA; // presetA[presetN] partial (label and tid only) parsing of the network presets
unsigned presetN; //
network_t* net; // The root of the network instance
ui_callback_t ui_callback;
void* ui_callback_arg;
std::atomic<variable_t*> ui_var_head; // Linked lists of var's to send to the UI
variable_t ui_var_stub;
variable_t* ui_var_tail;
} flow_t;
//------------------------------------------------------------------------------------------------------------------------
@ -232,32 +395,66 @@ 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 );
// If 'dst' is null then a new abuf is allocated, filled with the contents of 'src'.
// If 'dst' is non-null and there is enough space for the contents of 'src' then only a copy is executed.
// If there is not enough space then dst is reallocated.
abuf_t* abuf_duplicate( abuf_t* dst, const abuf_t* src );
rc_t abuf_set_channel( abuf_t* buf, unsigned chIdx, const sample_t* v, unsigned vN );
const sample_t* abuf_get_channel( abuf_t* buf, unsigned chIdx );
fbuf_t* fbuf_create( srate_t srate, unsigned chN, const unsigned* maxBinN_V, const unsigned* binN_V, const unsigned* hopSmpN_V, const fd_real_t** magV=nullptr, const fd_real_t** phsV=nullptr, const fd_real_t** hzV=nullptr );
fbuf_t* fbuf_create( srate_t srate, unsigned chN, unsigned maxBinN, unsigned binN, unsigned hopSmpN, const fd_real_t** magV=nullptr, const fd_real_t** phsV=nullptr, const fd_real_t** hzV=nullptr );
fbuf_t* fbuf_create( srate_t srate, unsigned chN, const unsigned* maxBinN_V, const unsigned* binN_V, const unsigned* hopSmpN_V, const fd_sample_t** magV=nullptr, const fd_sample_t** phsV=nullptr, const fd_sample_t** hzV=nullptr );
fbuf_t* fbuf_create( srate_t srate, unsigned chN, unsigned maxBinN, unsigned binN, unsigned hopSmpN, const fd_sample_t** magV=nullptr, const fd_sample_t** phsV=nullptr, const fd_sample_t** hzV=nullptr );
void fbuf_destroy( fbuf_t*& buf );
fbuf_t* fbuf_duplicate( const fbuf_t* src );
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; }
// Memory allocation will only occur if dst is null, or the size of dst's internal buffer are too small.
fbuf_t* fbuf_duplicate( fbuf_t* dst, const fbuf_t* src );
mbuf_t* mbuf_create( const midi::ch_msg_t* msgA=nullptr, unsigned msgN=0 );
void mbuf_destroy( mbuf_t*& buf );
mbuf_t* mbuf_duplicate( const mbuf_t* src );
inline bool value_is_abuf( const value_t* v ) { return v->tflag & kABufTFl; }
inline bool value_is_fbuf( const value_t* v ) { return v->tflag & kFBufTFl; }
unsigned value_type_label_to_flag( const char* type_desc );
const char* value_type_flag_to_label( unsigned flag );
void value_duplicate( value_t& dst, const value_t& src );
void value_print( const value_t* value, bool info_fl=false);
//------------------------------------------------------------------------------------------------------------------------
//
// Class and Variable Description
//
var_desc_t* var_desc_find( class_desc_t* cd, const char* var_label );
rc_t var_desc_find( class_desc_t* cd, const char* label, var_desc_t*& vdRef );
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 +462,47 @@ namespace cw
//
// Network
//
void network_print( flow_t* p );
// Access a blob stored via network_global_var()
void* network_global_var( proc_t* proc, const char* var_label );
// Copy a named blob into the network global variable space.
rc_t network_global_var_alloc( proc_t* proc, const char* var_label, const void* blob, unsigned blobByteN );
void network_print(const network_t& net );
const network_preset_t* network_preset_from_label( const network_t& net, const char* preset_label );
unsigned proc_mult_count( const network_t& net, const char* proc_label );
rc_t proc_mult_sfx_id_array( const network_t& net, const char* proc_label, unsigned* idA, unsigned idAllocN, unsigned& idN_ref );
unsigned network_poly_count( const network_t& net );
//------------------------------------------------------------------------------------------------------------------------
//
// Instance
// Proc
//
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 +511,78 @@ namespace cw
//
// Create a variable but do not assign it a value. Return a pointer to the new variable.
// Note: `value_cfg` is optional. Set it to NULL to ignore
rc_t var_create( instance_t* inst, const char* var_label, unsigned vid, unsigned chIdx, const object_t* value_cfg, variable_t*& varRef );
// Notes:
// 1) `value_cfg` is optional. Set it to NULL to ignore
// 2) If `altTypeFl` is not set to kInvalidTFl then the var is assigned this type.
rc_t var_create( proc_t* proc, const char* var_label, unsigned sfx_id, unsigned vid, unsigned chIdx, const object_t* value_cfg, unsigned altTypeFlag, variable_t*& varRef );
void var_destroy( variable_t* var );
// Channelizing creates a new var record with an explicit channel index to replace the
// automatically generated variable whose channel index is set to 'all'.
rc_t var_channelize( instance_t* inst, const char* var_label, unsigned chIdx, const object_t* value_cfg, unsigned vid, variable_t*& varRef );
// automatically generated variable whose channel index is set to 'kAnyChIdx'.
rc_t var_channelize( proc_t* proc, const char* var_label, unsigned sfx_id, unsigned chIdx, const object_t* value_cfg, unsigned vid, variable_t*& varRef );
// Get the count of channels attached to var_label:sfx_id:kAnyChIdx.
// Returns 0 if only kAnyChIdx exists,
// Returns kInvalidCnt if var_label:sfx_id does not exist.
// Otherwise returns count of channels no including kAnyChIdx. (e.g. mono=1, stereo=2, quad=4 ...)
unsigned var_channel_count( proc_t* proc, const char* var_label, unsigned sfx_id );
// Wrapper around call to var->proc->members->value()
rc_t var_call_custom_value_func( variable_t* var );
// Sets and get the var->flags field
unsigned var_flags( proc_t* proc, unsigned chIdx, const char* var_label, unsigned sfx_id, unsigned& flags_ref );
rc_t var_set_flags( proc_t* proc, unsigned chIdx, const char* var_label, unsigned sfx_id, unsigned flags );
rc_t var_clr_flags( proc_t* proc, unsigned chIdx, const char* var_label, unsigned sfx_id, unsigned flags );
// `value_cfg` is optional. Set it to NULL to ignore
rc_t var_register( instance_t* inst, const char* var_label, unsigned vid, unsigned chIdx, const object_t* value_cfg, variable_t*& varRef );
rc_t var_register( proc_t* proc, const char* var_label, unsigned sfx_id, unsigned vid, unsigned chIdx, const object_t* value_cfg, variable_t*& varRef );
// Returns true if this var is connected to an external proc variable
bool is_connected_to_external_proc( const variable_t* var );
// Returns true if this var is connected to a source proc variable
bool is_connected_to_source( const variable_t* var );
// Return true if this var is acting as a source for another var.
bool is_a_source_var( const variable_t* var );
// Connect in_var to src_var.
void var_connect( variable_t* src_var, variable_t* in_var );
// Disconnect an in_var from it's source
void var_disconnect( variable_t* in_var );
// Get the count of 'mult' vars associated with this var label.
unsigned var_mult_count( proc_t* proc, const char* var_label );
// Get all the label-sfx-id's associated with a give var label
rc_t var_mult_sfx_id_array( proc_t* proc, const char* var_label, unsigned* idA, unsigned idAllocN, unsigned& idN_ref );
// Send a variable value to the UI
rc_t var_send_to_ui( variable_t* var );
rc_t var_send_to_ui( proc_t* proc, unsigned vid, unsigned chIdx );
//-----------------
//
// var_register
//
inline rc_t _var_reg(cw::flow::instance_t*, unsigned int ) { return kOkRC; }
inline rc_t _var_reg(cw::flow::proc_t*, unsigned int ) { return kOkRC; }
template< typename T0, typename T1, typename... ARGS >
rc_t _var_reg( instance_t* inst, unsigned chIdx, T0 vid, T1 var_label, ARGS&&... args )
rc_t _var_reg( proc_t* proc, unsigned chIdx, T0 vid, T1 var_label, unsigned sfx_id, ARGS&&... args )
{
rc_t rc;
variable_t* dummy = nullptr;
if((rc = var_register( inst, var_label, vid, chIdx, nullptr, dummy )) == kOkRC )
if((rc = _var_reg( inst, chIdx, std::forward<ARGS>(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 +591,28 @@ namespace cw
// var_register_and_get
//
inline rc_t _var_register_and_get(cw::flow::instance_t*, unsigned int ) { return kOkRC; }
inline rc_t _var_register_and_get(cw::flow::proc_t*, unsigned int ) { return kOkRC; }
template< typename T>
rc_t var_register_and_get( instance_t* inst, const char* var_label, unsigned vid, unsigned chIdx, T& valRef )
rc_t var_register_and_get( proc_t* proc, const char* var_label, unsigned sfx_id, unsigned vid, unsigned chIdx, T& valRef )
{
rc_t rc;
variable_t* var;
if((rc = var_register(inst,var_label,vid,chIdx,nullptr,var)) == kOkRC )
if((rc = var_register(proc,var_label,sfx_id,vid,chIdx,nullptr,var)) == kOkRC )
rc = var_get(var,valRef);
return rc;
}
inline rc_t _var_reg_and_get(cw::flow::instance_t*, unsigned int ) { return kOkRC; }
inline rc_t _var_reg_and_get(cw::flow::proc_t*, unsigned int ) { return kOkRC; }
template< typename T0, typename T1, typename T2, typename... ARGS >
rc_t _var_reg_and_get( instance_t* inst, unsigned chIdx, T0 vid, T1 var_label, T2& valRef, ARGS&&... args )
rc_t _var_reg_and_get( proc_t* proc, unsigned chIdx, T0 vid, T1 var_label, unsigned sfx_id, T2& valRef, ARGS&&... args )
{
rc_t rc;
if((rc = var_register_and_get( inst, var_label, vid, chIdx, valRef )) == kOkRC )
if((rc = _var_reg_and_get( inst, chIdx, std::forward<ARGS>(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 +620,8 @@ namespace cw
// Call var_register_and_get() on a list of variables.
template< typename... ARGS >
rc_t var_register_and_get( instance_t* inst, unsigned chIdx, unsigned vid, const char* var_label, ARGS&&... args )
{ return _var_reg_and_get( inst, chIdx, vid, var_label, std::forward<ARGS>(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 +633,27 @@ namespace cw
// var_register_and_set(). If the variable has not yet been created then it is created and assigned a value.
// If the variable has already been created then 'vid' and the value are updated.
// (Note that abuf and fbuf values are not changed by this function only the 'vid' is updated.)
rc_t var_register_and_set( instance_t* inst, const char* label, unsigned vid, unsigned chIdx, variable_t*& varRef );
rc_t var_register_and_set( proc_t* proc, const char* label, unsigned sfx_id, unsigned vid, unsigned chIdx, variable_t*& varRef );
rc_t var_register_and_set( instance_t* inst, const char* var_label, unsigned vid, unsigned chIdx, srate_t srate, unsigned chN, unsigned frameN );
rc_t var_register_and_set( instance_t* inst, const char* var_label, unsigned vid, unsigned chIdx, srate_t srate, unsigned chN, const unsigned* maxBinN_V, const unsigned* binN_V, const unsigned* hopSmpN_V, const fd_real_t** magV=nullptr, const fd_real_t** phsV=nullptr, const fd_real_t** hzV=nullptr );
rc_t var_register_and_set( instance_t* inst, const char* var_label, unsigned vid, unsigned chIdx, srate_t srate, unsigned chN, unsigned maxBinN, unsigned binN, unsigned hopSmpN, const fd_real_t** magV=nullptr, const fd_real_t** phsV=nullptr, const fd_real_t** hzV=nullptr );
rc_t var_register_and_set( proc_t* proc, const char* var_label, unsigned sfx_id, unsigned vid, unsigned chIdx, srate_t srate, unsigned chN, unsigned frameN );
rc_t var_register_and_set( proc_t* proc, const char* var_label, unsigned sfx_id, unsigned vid, unsigned chIdx, midi::ch_msg_t* midiA, unsigned midiN );
rc_t var_register_and_set( proc_t* proc, const char* var_label, unsigned sfx_id, unsigned vid, unsigned chIdx, srate_t srate, unsigned chN, const unsigned* maxBinN_V, const unsigned* binN_V, const unsigned* hopSmpN_V, const fd_sample_t** magV=nullptr, const fd_sample_t** phsV=nullptr, const fd_sample_t** hzV=nullptr );
rc_t var_register_and_set( proc_t* proc, const char* var_label, unsigned sfx_id, unsigned vid, unsigned chIdx, srate_t srate, unsigned chN, unsigned maxBinN, unsigned binN, unsigned hopSmpN, const fd_sample_t** magV=nullptr, const fd_sample_t** phsV=nullptr, const fd_sample_t** hzV=nullptr );
inline rc_t _var_register_and_set(cw::flow::instance_t*, unsigned int ) { return kOkRC; }
inline rc_t _var_register_and_set(cw::flow::proc_t*, unsigned int ) { return kOkRC; }
template< typename T0, typename T1, typename T2, typename... ARGS >
rc_t _var_register_and_set( instance_t* inst, unsigned chIdx, T0 vid, T1 var_label, T2 val, ARGS&&... args )
rc_t _var_register_and_set( proc_t* proc, unsigned chIdx, T0 vid, T1 var_label, unsigned sfx_id, T2 val, ARGS&&... args )
{
rc_t rc;
variable_t* var = nullptr;
if((rc = var_register_and_set( inst, var_label, vid, chIdx, var)) == kOkRC )
if((rc = var_register_and_set( proc, var_label, sfx_id, vid, chIdx, var)) == kOkRC )
{
var_set( inst, vid, chIdx, val );
if((rc = var_set( proc, vid, chIdx, val )) != kOkRC )
return rc;
if((rc = _var_register_and_set( inst, chIdx, std::forward<ARGS>(args)...)) != kOkRC )
if((rc = _var_register_and_set( proc, chIdx, std::forward<ARGS>(args)...)) != kOkRC )
return rc;
}
@ -398,24 +662,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 +700,60 @@ 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, mbuf_t* val );
rc_t var_set( proc_t* proc, unsigned vid, unsigned chIdx, const object_t* val );
const preset_t* class_preset_find( class_desc_t* cd, const char* preset_label );
}
}

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"

301
cwIo.cpp
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"
@ -95,6 +96,8 @@ namespace cw
audioGroup_t* oGroup; //
audio_group_dev_t* iagd; // Audio group device record assoc'd with this device
audio_group_dev_t* oagd; //
unsigned cycleCnt;
unsigned framesPerCycle;
struct audioDev_str* clockInList; // List of devices sync'd to this devices input clock
struct audioDev_str* clockOutList; // List of devices sync'd to this devices output clock
@ -611,7 +614,7 @@ namespace cw
//
// MIDI
//
void _midiCallback( const midi::packet_t* pktArray, unsigned pktCnt )
void _midiCallback( void* cbArg, const midi::packet_t* pktArray, unsigned pktCnt )
{
unsigned i;
for(i=0; i<pktCnt; ++i)
@ -619,7 +622,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;
@ -975,7 +978,6 @@ namespace cw
goto errLabel;
}
if((rc = audio::device::alsa::destroy(p->alsaH)) != kOkRC )
{
rc = cwLogError(rc,"ALSA sub-system shutdown failed.");
@ -988,7 +990,6 @@ namespace cw
goto errLabel;
}
if((rc = audio::device::destroy(p->audioH)) != kOkRC )
{
rc = cwLogError(rc,"Audio device sub-system shutdown failed.");
@ -1638,6 +1639,85 @@ namespace cw
return rc;
}
rc_t _audioDeviceConfigure( io_t* p, audioDev_t* ad, audioGroup_t* iag, audioGroup_t* oag, unsigned cycleCnt, unsigned framesPerCycle )
{
rc_t rc = kOkRC;
double israte = 0;
double osrate = 0;
double srate = 0;
unsigned iDspFrameCnt = 0;
unsigned oDspFrameCnt = 0;
unsigned dspFrameCnt = 0;
unsigned iChCnt = 0;
unsigned oChCnt = 0;
const char* inGroupLabel = iag==nullptr || iag->msg.label==nullptr ? "<no in-group>" : iag->msg.label;
const char* outGroupLabel = oag==nullptr || oag->msg.label==nullptr ? "<no out-group>" : oag->msg.label;
// get the ingroup
if( iag != nullptr )
{
israte = iag->msg.srate;
iDspFrameCnt = iag->msg.dspFrameCnt;
}
// get the outgroup
if( oag != nullptr )
{
osrate = oag->msg.srate;
oDspFrameCnt = oag->msg.dspFrameCnt;
}
// in-srate and out-srate must be equal or one must be 0
if( osrate==0 || israte==0 || osrate==israte )
{
// the true sample rate is the non-zero sample rate
srate = std::max(israte,osrate);
}
else
{
rc = cwLogError(kInvalidArgRC,"The device '%s' belongs to two groups (%s and %s) at different sample rates (%f != %f).", cwStringNullGuard(ad->devName), cwStringNullGuard(inGroupLabel), cwStringNullGuard(outGroupLabel), israte, osrate );
goto errLabel;
}
// in-dspFrameCnt an out-dspFrameCnt must be equal or one must be 0
if( oDspFrameCnt==0 || iDspFrameCnt==0 || oDspFrameCnt==iDspFrameCnt)
{
// the true sample rate is the non-zero sample rate
dspFrameCnt = std::max(iDspFrameCnt,oDspFrameCnt);
}
else
{
rc = cwLogError(kInvalidArgRC,"The device '%s' belongs to two groups (%s and %s) width different dspFrameCnt values (%i != %i).", cwStringNullGuard(ad->devName), cwStringNullGuard(inGroupLabel), cwStringNullGuard(outGroupLabel), iDspFrameCnt, oDspFrameCnt );
goto errLabel;
}
// setup the device based on the configuration
if((rc = audio::device::setup(p->audioH, ad->devIdx, srate, framesPerCycle, _audioDeviceCallback, p)) != kOkRC )
{
rc = cwLogError(rc,"Unable to setup the audio hardware device:'%s'.", ad->devName);
goto errLabel;
}
// get the device channel counts
iChCnt = audio::device::channelCount(p->audioH,ad->devIdx,true);
oChCnt = audio::device::channelCount(p->audioH,ad->devIdx,false);
// initialize the audio bufer for this device
if((rc = audio::buf::setup( p->audioBufH, ad->devIdx, srate, dspFrameCnt, cycleCnt, iChCnt, framesPerCycle, oChCnt, framesPerCycle )) != kOkRC )
{
rc = cwLogError(rc,"Audio device buffer channel setup failed.");
goto errLabel;
}
errLabel:
return rc;
}
// Create the audio device records by parsing the cfg audio.deviceL[] list.
rc_t _audioDeviceParseAudioDeviceList( io_t* p, const object_t* cfg )
{
@ -1666,7 +1746,7 @@ namespace cw
// fill in the audio device cfg list
for(unsigned i=0; i<deviceL_Node->child_count(); ++i)
{
audioDev_t* ad = nullptr; //p->audioDevA + i;
audioDev_t* ad = nullptr;
bool activeFl = false;
bool meterFl = false;
char* userLabel = nullptr;
@ -1677,7 +1757,7 @@ namespace cw
audioGroup_t* iag = nullptr;
audioGroup_t* oag = nullptr;
/*
double israte = 0;
double osrate = 0;
double srate = 0;
@ -1685,7 +1765,7 @@ namespace cw
unsigned iDspFrameCnt = 0;
unsigned oDspFrameCnt = 0;
unsigned dspFrameCnt = 0;
*/
char* inGroupLabel = nullptr;
char* outGroupLabel = nullptr;
@ -1739,11 +1819,17 @@ namespace cw
goto errLabel;
}
// get the device channel counts
unsigned iChCnt = 0; //audio::device::channelCount(p->audioH,ad->devIdx,true);
unsigned oChCnt = 0; //audio::device::channelCount(p->audioH,ad->devIdx,false);
if( inGroupLabel != nullptr )
iag = _audioGroupFromLabel(p, inGroupLabel );
// get the outgroup
if( outGroupLabel != nullptr )
oag = _audioGroupFromLabel(p, outGroupLabel);
if((rc = _audioDeviceConfigure(p, ad, iag, oag, cycleCnt, framesPerCycle )) != kOkRC )
goto errLabel;
/*
// get the ingroup
if( inGroupLabel != nullptr )
if((iag = _audioGroupFromLabel(p, inGroupLabel )) != nullptr )
@ -1760,7 +1846,7 @@ namespace cw
oDspFrameCnt = oag->msg.dspFrameCnt;
}
// in-srate an out-srate must be equal or one must be 0
// in-srate and out-srate must be equal or one must be 0
if( osrate==0 || israte==0 || osrate==israte )
{
// the true sample rate is the non-zero sample rate
@ -1792,8 +1878,8 @@ namespace cw
}
// get the device channel counts
iChCnt = audio::device::channelCount(p->audioH,ad->devIdx,true);
oChCnt = audio::device::channelCount(p->audioH,ad->devIdx,false);
unsigned iChCnt = audio::device::channelCount(p->audioH,ad->devIdx,true);
unsigned oChCnt = audio::device::channelCount(p->audioH,ad->devIdx,false);
// initialize the audio bufer for this device
@ -1802,6 +1888,11 @@ namespace cw
rc = cwLogError(rc,"Audio device buffer channel setup failed.");
goto errLabel;
}
*/
unsigned iChCnt = audio::device::channelCount(p->audioH,ad->devIdx,true);
unsigned oChCnt = audio::device::channelCount(p->audioH,ad->devIdx,false);
// if an input group was assigned to this device then create a assoc'd audio_group_dev_t
if( iag != nullptr )
@ -1832,6 +1923,8 @@ namespace cw
ad->userId = userId;
ad->iGroup = iag;
ad->oGroup = oag;
ad->cycleCnt = cycleCnt;
ad->framesPerCycle = framesPerCycle;
}
}
@ -2115,9 +2208,11 @@ namespace cw
goto errLabel;
}
errLabel:
if( rc != kOkRC && p->audioH.isValid() )
audio::device::report( p->audioH );
errLabel:
return rc;
}
@ -2439,7 +2534,8 @@ cw::rc_t cw::io::stop( handle_t h )
return rc;
}
cw::rc_t cw::io::exec( handle_t h, void* execCbArg )
cw::rc_t cw::io::exec( handle_t h, unsigned timeOutMs, void* execCbArg )
{
rc_t rc = kOkRC;
io_t* p = _handleToPtr(h);
@ -2447,8 +2543,9 @@ cw::rc_t cw::io::exec( handle_t h, void* execCbArg )
if( p->wsUiH.isValid() )
{
ui::flushCache( ui::ws::uiHandle( p->wsUiH ));
// Note this call blocks on the websocket handle: See cwUi.h:ws:exec()
rc = ui::ws::exec( p->wsUiH );
rc = ui::ws::exec( p->wsUiH, timeOutMs );
}
time::get(p->t0);
@ -2490,14 +2587,23 @@ void cw::io::report( handle_t h )
}
for(unsigned i=0; 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);
}
@ -2655,6 +2761,11 @@ cw::rc_t cw::io::timerStop( handle_t h, unsigned timerIdx )
//
// Serial
//
bool cw::io::serialIsEnabled( handle_t h )
{
io_t* p = _handleToPtr(h);
return p->serialN != 0;
}
unsigned cw::io::serialDeviceCount( handle_t h )
{
@ -2719,9 +2830,18 @@ errLabel:
// MIDI
//
bool cw::io::midiIsEnabled( handle_t h )
{
io_t* p = _handleToPtr(h);
return p->midiH.isValid();
}
unsigned cw::io::midiDeviceCount( handle_t h )
{
io_t* p = _handleToPtr(h);
if( !p->midiH.isValid() )
return 0;
return midi::device::count(p->midiH);
}
@ -2761,6 +2881,24 @@ cw::rc_t cw::io::midiDeviceSend( handle_t h, unsigned devIdx, unsigned portIdx,
return midi::device::send( p->midiH, devIdx, portIdx, status, d0, d1 );
}
unsigned cw::io::midiDeviceMaxBufferMsgCount( handle_t h )
{
io_t* p = _handleToPtr(h);
return midi::device::maxBufferMsgCount(p->midiH );
}
const cw::midi::ch_msg_t* cw::io::midiDeviceBuffer( handle_t h, unsigned& msgCntRef )
{
io_t* p = _handleToPtr(h);
return midi::device::getBuffer(p->midiH, msgCntRef );
}
cw::rc_t cw::io::midiDeviceClearBuffer( handle_t h, unsigned msgCnt )
{
io_t* p = _handleToPtr(h);
return midi::device::clearBuffer(p->midiH, msgCnt );
}
cw::rc_t cw::io::midiOpenMidiFile( handle_t h, unsigned devIdx, unsigned portIdx, const char* fname )
{
return midi::device::openMidiFile( _handleToPtr(h)->midiH, devIdx, portIdx, fname );
@ -3158,6 +3296,64 @@ unsigned cw::io::audioGroupDspFrameCount( handle_t h, unsigned groupIdx )
return 0;
}
cw::rc_t cw::io::audioGroupReconfigure( handle_t h, unsigned groupIdx, double srate, unsigned dspFrameN )
{
rc_t rc = kOkRC;
audioGroup_t* ag = nullptr;
io_t* p = _handleToPtr(h);
// locate the group record
if((ag = _audioGroupFromIndex( p, groupIdx )) == nullptr )
goto errLabel;
// if the parameters are not changing then there is nothing to do
if( ag->msg.dspFrameCnt == dspFrameN && ag->msg.srate == srate )
goto errLabel;
// change the parameters in the group record
ag->msg.dspFrameCnt = dspFrameN;
ag->msg.srate = srate;
// stop the audio sub-system
if((rc = _audioDeviceStartStop(p,false)) != kOkRC )
goto errLabel;
// TODO: be sure the audio subsystem is really stopped
// for each audio device
for(unsigned i=0; i<p->audioDevN; ++i)
{
audioDev_t* ad = p->audioDevA + i;
// if this devices in-group/out-group was reconfigured
bool iGroupFl = ad->iGroup != nullptr && ad->iGroup->msg.groupIndex == groupIdx;
bool oGroupFl = ad->oGroup != nullptr && ad->oGroup->msg.groupIndex == groupIdx;
if( iGroupFl || oGroupFl )
{
// reconfigure the device with the updated srate and framesPerCycle
if((rc = _audioDeviceConfigure(p, ad, ad->oGroup, ad->oGroup, ad->cycleCnt, ad->framesPerCycle )) != kOkRC )
goto errLabel;
cwLogInfo("The audio device: '%s' was reconfigured srate=%f dspFrameCnt:%i.",cwStringNullGuard(ad->label), srate,dspFrameN);
}
}
// restart the audio sub-system
if((rc = _audioDeviceStartStop(p,true)) != kOkRC )
goto errLabel;
errLabel:
if( rc != kOkRC )
rc = cwLogError(rc,"Audio group reconfiguration failed.");
return rc;
}
unsigned cw::io::audioGroupDeviceCount( handle_t h, unsigned groupIdx, unsigned inOrOutFl )
{
audioGroup_t* ag;
@ -3204,6 +3400,11 @@ unsigned cw::io::audioGroupDeviceIndex( handle_t h, unsigned groupIdx, unsigned
// Socket
//
bool cw::io::socketIsEnabled( handle_t h )
{
io_t* p = _handleToPtr(h);
return p->sockN != 0;
}
unsigned cw::io::socketCount( handle_t h )
{
@ -3351,6 +3552,13 @@ cw::rc_t cw::io::socketSend( handle_t h, unsigned sockIdx, const void* data,
//
// UI
//
bool cw::io::uiIsEnabled( handle_t h )
{
io_t* p = _handleToPtr(h);
return p->wsUiH.isValid();
}
unsigned cw::io::parentAndNameToAppId( handle_t h, unsigned parentAppId, const char* eleName )
{
rc_t rc;
@ -3623,6 +3831,24 @@ cw::rc_t cw::io::uiCreateLog( handle_t h, unsigned& uuIdRef, unsigned pare
return rc;
}
cw::rc_t cw::io::uiCreateVList( handle_t h, unsigned& uuIdRef, unsigned parentUuId, const char* eleName, unsigned appId, unsigned chanId, const char* clas, const char* title )
{
rc_t rc;
ui::handle_t uiH;
if((rc = _handleToUiHandle(h,uiH)) == kOkRC )
rc = ui::createVList(uiH,uuIdRef,parentUuId,eleName,appId,chanId,clas,title);
return rc;
}
cw::rc_t cw::io::uiCreateHList( handle_t h, unsigned& uuIdRef, unsigned parentUuId, const char* eleName, unsigned appId, unsigned chanId, const char* clas, const char* title )
{
rc_t rc;
ui::handle_t uiH;
if((rc = _handleToUiHandle(h,uiH)) == kOkRC )
rc = ui::createHList(uiH,uuIdRef,parentUuId,eleName,appId,chanId,clas,title);
return rc;
}
cw::rc_t cw::io::uiSetNumbRange( handle_t h, unsigned uuId, double minValue, double maxValue, double stepValue, unsigned decPl, double value )
{
rc_t rc;
@ -3650,6 +3876,16 @@ cw::rc_t cw::io::uiSetLogLine( handle_t h, unsigned uuId, const char* text )
return rc;
}
cw::rc_t cw::io::uiEmptyParent( handle_t h, unsigned uuId)
{
rc_t rc;
ui::handle_t uiH;
if((rc = _handleToUiHandle(h,uiH)) == kOkRC )
rc = ui::emptyParent(uiH,uuId);
return rc;
}
cw::rc_t cw::io::uiSetClickable( handle_t h, unsigned uuId, bool clickableFl )
{
rc_t rc;
@ -3788,6 +4024,14 @@ cw::rc_t cw::io::uiSetScrollTop( handle_t h, unsigned uuId )
return rc;
}
cw::rc_t cw::io::uiSetTitle( handle_t h, unsigned uuId, const char* title )
{
rc_t rc;
ui::handle_t uiH;
if((rc = _handleToUiHandle(h,uiH)) == kOkRC )
rc = ui::setTitle(uiH,uuId,title);
return rc;
}
cw::rc_t cw::io::uiSetBlob( handle_t h, unsigned uuId, const void* blob, unsigned blobByteN )
{
@ -3808,6 +4052,21 @@ const void* cw::io::uiGetBlob( handle_t h, unsigned uuId, unsigned& blobByteN_
return nullptr;
}
cw::rc_t cw::io::uiGetBlob( handle_t h, unsigned uuId, void* buf, unsigned& bufByteN_Ref )
{
unsigned bN = 0;
const void* b = uiGetBlob(h,uuId,bN);
if( bN > bufByteN_Ref )
{
bufByteN_Ref = 0;
return cwLogError(kBufTooSmallRC,"UI blob buffer is too small.");
}
memcpy(buf,b,bN);
bufByteN_Ref = bN;
return kOkRC;
}
cw::rc_t cw::io::uiClearBlob( handle_t h, unsigned uuId )
{
rc_t rc;
@ -3981,8 +4240,8 @@ void cw::io::uiReport( handle_t h )
void cw::io::uiRealTimeReport( handle_t h )
{
ui::handle_t uiH;
if(_handleToUiHandle(h,uiH) == kOkRC )
ui::realTimeReport(uiH);
ui::ws::handle_t uiH;
if(_handleToWsUiHandle(h,uiH) == kOkRC )
ui::ws::realTimeReport(uiH);
}

28
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
@ -208,6 +211,7 @@ namespace cw
// Serial
//
bool serialIsEnabled( handle_t h );
unsigned serialDeviceCount( handle_t h );
unsigned serialDeviceIndex( handle_t h, const char* label );
const char* serialDeviceLabel( handle_t h, unsigned devIdx );
@ -221,6 +225,7 @@ namespace cw
// MIDI
//
bool midiIsEnabled( handle_t h );
unsigned midiDeviceCount( handle_t h );
const char* midiDeviceName( handle_t h, unsigned devIdx );
unsigned midiDeviceIndex( handle_t h, const char* devName );
@ -229,6 +234,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 );
@ -275,6 +285,7 @@ namespace cw
rc_t audioGroupSetUserId( handle_t h, unsigned groupIdx, unsigned userId );
double audioGroupSampleRate( handle_t h, unsigned groupIdx );
unsigned audioGroupDspFrameCount( handle_t h, unsigned groupIdx );
rc_t audioGroupReconfigure( handle_t h, unsigned groupIdx, double srate, unsigned dspFrameN );
// Get the count of in or out devices assigned to this group.
unsigned audioGroupDeviceCount( handle_t h, unsigned groupIdx, unsigned inOrOutFl );
@ -287,6 +298,7 @@ namespace cw
// Socket
//
bool socketIsEnabled( handle_t h );
unsigned socketCount( handle_t h );
unsigned socketLabelToIndex( handle_t h, const char* label );
unsigned socketUserId( handle_t h, unsigned sockIdx );
@ -320,6 +332,7 @@ namespace cw
// UI
//
bool uiIsEnabled( handle_t h );
// Find id's associated with elements.
unsigned parentAndNameToAppId( handle_t h, unsigned parentAppId, const char* eleName );
@ -370,10 +383,16 @@ namespace cw
rc_t uiCreateLog( handle_t h, unsigned& uuIdRef, unsigned parentUuId, const char* eleName, unsigned appId, unsigned chanId, const char* clas, const char* title );
rc_t uiCreateVList( handle_t h, unsigned& uuIdRef, unsigned parentUuId, const char* eleName, unsigned appId, unsigned chanId, const char* clas, const char* title );
rc_t uiCreateHList( handle_t h, unsigned& uuIdRef, unsigned parentUuId, const char* eleName, unsigned appId, unsigned chanId, const char* clas, const char* title );
rc_t uiSetTitle( handle_t h, unsigned uuId, const char* title );
rc_t uiSetNumbRange( handle_t h, unsigned uuId, double minValue, double maxValue, double stepValue, unsigned decPl, double value );
rc_t uiSetProgRange( handle_t h, unsigned uuId, double minValue, double maxValue, double value );
rc_t uiSetLogLine( handle_t h, unsigned uuId, const char* text );
rc_t uiEmptyParent( handle_t h, unsigned uuId); // empty a list or selection menu of all children
rc_t uiSetClickable( handle_t h, unsigned uuId, bool clickableFl=true );
rc_t uiClearClickable( handle_t h, unsigned uuId );
@ -396,8 +415,13 @@ namespace cw
rc_t uiSetScrollTop( handle_t h, unsigned uuId );
// uiSetBlob() allocates internal memory and copies the contents of blob[blobByeN]
rc_t uiSetBlob( handle_t h, unsigned uuId, const void* blob, unsigned blobByteN );
const void* uiGetBlob( handle_t h, unsigned uuId, unsigned& blobByteN_Ref );
// On call bufByteN_Ref holds the size of buf in bytes, on return it is set to the count of bytes in buf[].
// If buf[] is not large enough to hold all bytes kBufTooSmallRC is returned.
rc_t uiGetBlob( handle_t h, unsigned uuId, void* buf, unsigned& bufByteN_Ref );
rc_t uiClearBlob( handle_t h, unsigned uuId );
// Register parent/child/name app id's

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

929
cwIoFlowCtl.cpp Normal file
View File

@ -0,0 +1,929 @@
#include "cwCommon.h"
#include "cwLog.h"
#include "cwCommonImpl.h"
#include "cwTest.h"
#include "cwMem.h"
#include "cwText.h"
#include "cwObject.h"
#include "cwFileSys.h"
#include "cwFile.h"
#include "cwTime.h"
#include "cwVectOps.h"
#include "cwMtx.h"
#include "cwTime.h"
#include "cwMidiDecls.h"
#include "cwTime.h"
#include "cwMidiDecls.h"
#include "cwDspTypes.h"
#include "cwFlowDecl.h"
#include "cwFlow.h"
#include "cwFlowTypes.h"
#include "cwIo.h"
#include "cwIoFlowCtl.h"
namespace cw
{
namespace io_flow_ctl
{
// An audio_dev_t record exists for each possible input or output device.
typedef struct audio_dev_str
{
unsigned ioDevIdx; // device index in the io:: API
unsigned ioDevId; // device id in the io:: API
flow::abuf_t abuf; // src/dst buffer for incoming/outgoing (record/play) samples used by flow proc 'audio_in' and 'audio_out'.
} audio_dev_t;
typedef struct audio_group_str
{
double srate;
unsigned dspFrameCnt;
unsigned ioGroupIdx;
audio_dev_t* iDeviceA;
unsigned iDeviceN;
audio_dev_t* oDeviceA;
unsigned oDeviceN;
} audio_group_t;
typedef struct pgm_str
{
const char* label;
const object_t* cfg;
} pgm_t;
typedef struct io_flow_ctl_str
{
const char* base_dir;
object_t* proc_class_dict_cfg;
object_t* udp_dict_cfg;
io::handle_t ioH;
flow::external_device_t* deviceA; // Array of generic device descriptions used by the ioFlow controller
unsigned deviceN; // (This array must exist for the life of ioFlow controller)
audio_group_t* audioGroupA; // Array of real time audio device control records.
unsigned audioGroupN; //
pgm_t* pgmA; // pgmA[ pgmN ]
unsigned pgmN;
unsigned pgm_idx; // current program index
flow::handle_t flowH; //
char* proj_dir; // current project directory
bool init_fl;
bool done_fl;
} io_flow_ctl_t;
io_flow_ctl_t* _handleToPtr( handle_t h )
{ return handleToPtr<handle_t,io_flow_ctl_t>(h); }
void _destroy_device_setup( io_flow_ctl_t* p )
{
mem::release(p->deviceA);
p->deviceN = 0;
for(unsigned gi=0; gi<p->audioGroupN; ++gi)
{
audio_group_t* ag = p->audioGroupA + gi;
for(unsigned di=0; di<ag->iDeviceN; ++di)
mem::release( ag->iDeviceA[di].abuf.buf );
for(unsigned di=0; di<ag->oDeviceN; ++di)
mem::release( ag->oDeviceA[di].abuf.buf );
mem::release( ag->iDeviceA);
mem::release( ag->oDeviceA);
}
mem::release(p->audioGroupA);
p->audioGroupN = 0;
}
rc_t _program_unload( io_flow_ctl_t* p )
{
rc_t rc;
if((rc = destroy(p->flowH)) != kOkRC )
{
rc = cwLogError(rc,"Program unload failed.");
goto errLabel;
}
_destroy_device_setup(p);
mem::release(p->proj_dir);
p->pgm_idx = kInvalidIdx;
p->done_fl = true;
p->init_fl = false;
errLabel:
return rc;
}
rc_t _destroy( io_flow_ctl_t* p )
{
rc_t rc = kOkRC;
destroy( p->flowH );
if( p->proc_class_dict_cfg != nullptr )
p->proc_class_dict_cfg->free();
if( p->udp_dict_cfg != nullptr )
p->udp_dict_cfg->free();
_program_unload(p);
p->pgmN = 0;
mem::release(p->pgmA);
mem::release(p);
return rc;
}
rc_t _validate_pgm_idx( io_flow_ctl_t* p, unsigned pgm_idx )
{
rc_t rc = kOkRC;
if( pgm_idx == kInvalidIdx || pgm_idx >= p->pgmN )
{
rc = cwLogError(kInvalidArgRC,"The program index '%i' is invalid. Program count=%i.",pgm_idx,p->pgmN);
goto errLabel;
}
errLabel:
return rc;
}
rc_t _parse_cfg( io_flow_ctl_t* p, const object_t* cfg )
{
rc_t rc = kOkRC;
const char* proc_cfg_fname = nullptr;
const char* udp_cfg_fname = nullptr;
const char* io_cfg_fname = nullptr;
const object_t* pgmL = nullptr;
// parse the cfg parameters
if((rc = cfg->readv("base_dir", kReqFl, p->base_dir,
"proc_dict", kReqFl, proc_cfg_fname,
"udp_dict", kReqFl, udp_cfg_fname,
"io_dict", kOptFl, io_cfg_fname,
"programs", kDictTId, pgmL)) != kOkRC )
{
rc = cwLogError(rc,"'caw' system parameter processing failed.");
goto errLabel;
}
// parse the proc dict. file
if((rc = objectFromFile(proc_cfg_fname,p->proc_class_dict_cfg)) != kOkRC )
{
rc = cwLogError(rc,"The flow proc dictionary could not be read from '%s'.",cwStringNullGuard(proc_cfg_fname));
goto errLabel;
}
// parse the udp dict file
if((rc = objectFromFile(udp_cfg_fname,p->udp_dict_cfg)) != kOkRC )
{
rc = cwLogError(rc,"The flow user-defined-proc dictionary could not be read from '%s'.",cwStringNullGuard(udp_cfg_fname));
goto errLabel;
}
p->pgmN = pgmL->child_count();
p->pgmA = mem::allocZ<pgm_t>(p->pgmN);
// find the parameters for the requested program
for(unsigned i=0; i<p->pgmN; i++)
{
const object_t* pgm = pgmL->child_ele(i);
if( pgm->pair_label()==nullptr || pgm->pair_value()==nullptr || !pgm->pair_value()->is_dict() )
{
rc = cwLogError(kSyntaxErrorRC,"The program at index %i has a syntax error.",i);
goto errLabel;
}
p->pgmA[i].label = pgm->pair_label();
p->pgmA[i].cfg = pgm->pair_value();
}
errLabel:
return rc;
}
unsigned _calc_device_count(io_flow_ctl_t* p)
{
unsigned devN = 0;
devN += socketCount(p->ioH);
devN += serialDeviceCount(p->ioH);
unsigned midiDevN = midiDeviceCount(p->ioH);
for(unsigned i=0; 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;
return devN;
}
void _setup_audio_device( io_flow_ctl_t* p,audio_dev_t* dev, unsigned inOrOutFl, unsigned ioDevIdx, unsigned dspFrameCnt )
{
dev->ioDevIdx = ioDevIdx;
dev->ioDevId = audioDeviceUserId( p->ioH, ioDevIdx );
dev->abuf.srate = audioDeviceSampleRate( p->ioH, ioDevIdx );
dev->abuf.chN = audioDeviceChannelCount( p->ioH, ioDevIdx, inOrOutFl );
dev->abuf.frameN = dspFrameCnt;
dev->abuf.buf = mem::allocZ< flow::sample_t >( dev->abuf.chN * dev->abuf.frameN );
//printf("%i %s\n", dev->abuf.chN, audioDeviceLabel( p->ioH, ioDevIdx ) );
}
rc_t _setup_audio_groups( io_flow_ctl_t* p, double srate, unsigned dspFrameN )
{
rc_t rc = kOkRC;
p->audioGroupN = audioGroupCount( p->ioH );
p->audioGroupA = mem::allocZ<audio_group_t>( p->audioGroupN );
for(unsigned gi=0; gi<audioGroupCount(p->ioH); ++gi)
{
audio_group_t* ag = p->audioGroupA + gi;
if((rc = audioGroupReconfigure(p->ioH, gi, srate, dspFrameN )) != kOkRC )
{
rc = cwLogError(rc,"Audio group reconfiguration to srate=%f dspFrameN:%i failed.",srate,dspFrameN);
goto errLabel;
}
ag->srate = audioGroupSampleRate( p->ioH, gi );
ag->dspFrameCnt = audioGroupDspFrameCount( p->ioH, gi );
ag->ioGroupIdx = gi;
ag->iDeviceN = audioGroupDeviceCount( p->ioH, gi, io::kInFl );
ag->iDeviceA = mem::allocZ< audio_dev_t >( ag->iDeviceN );
for(unsigned gdi=0; gdi<ag->iDeviceN; ++gdi)
_setup_audio_device( p, ag->iDeviceA + gdi, io::kInFl, audioGroupDeviceIndex( p->ioH, gi, io::kInFl, gdi), ag->dspFrameCnt );
ag->oDeviceN = audioGroupDeviceCount( p->ioH, gi, io::kOutFl );
ag->oDeviceA = mem::allocZ< audio_dev_t >( ag->oDeviceN );
for(unsigned gdi=0; gdi<ag->oDeviceN; ++gdi)
_setup_audio_device( p, ag->oDeviceA + gdi, io::kOutFl, audioGroupDeviceIndex( p->ioH, gi, io::kOutFl, gdi), ag->dspFrameCnt );
}
errLabel:
return rc;
}
rc_t _send_midi_triple( flow::external_device_t* dev, uint8_t ch, uint8_t status, uint8_t d0, uint8_t d1 )
{
return midiDeviceSend(((io_flow_ctl_t*)dev->reserved)->ioH, dev->ioDevIdx, dev->ioPortIdx, status |= ch, d0, d1);
}
void _setup_device_cfg( io_flow_ctl_t* p, flow::external_device_t* d, const char* devLabel, unsigned ioDevIdx, unsigned typeId, unsigned flags, const char* midiPortLabel=nullptr, unsigned midiPortIdx=kInvalidIdx )
{
d->reserved = p;
d->devLabel = devLabel;
d->portLabel = midiPortLabel;
d->typeId = typeId;
d->flags = flags;
d->ioDevIdx = ioDevIdx;
d->ioPortIdx = midiPortIdx;
}
void _setup_midi_device_cfg( io_flow_ctl_t* p, flow::external_device_t* d, const char* devLabel, unsigned ioDevIdx, unsigned flags, unsigned ioMidiPortIdx )
{
const char* midiPortLabel = io::midiDevicePortName(p->ioH,ioDevIdx, flags & flow::kInFl ? true : false,ioMidiPortIdx);
_setup_device_cfg( p, d, devLabel, ioDevIdx, flow::kMidiDevTypeId, flags, midiPortLabel, ioMidiPortIdx );
d->u.m.maxMsgCnt = io::midiDeviceMaxBufferMsgCount(p->ioH);
d->u.m.sendTripleFunc = _send_midi_triple;
}
void _setup_audio_device_cfg( io_flow_ctl_t* p, flow::external_device_t* d, audio_group_t* ag, audio_dev_t* ad, unsigned flags )
{
_setup_device_cfg( p, d, io::audioDeviceLabel(p->ioH,ad->ioDevIdx), ad->ioDevIdx, flow::kAudioDevTypeId, flags );
// Each audio device is given a flow::abuf to hold incoming or outgoing audio.
// This buffer also allows the 'audio_in' and 'audio_out' flow procs to configure themselves.
d->u.a.abuf = &ad->abuf;
}
void _setup_generic_device_array( io_flow_ctl_t* p )
{
unsigned i = 0;
// allocate the generic device control records
p->deviceN = _calc_device_count(p);
p->deviceA = mem::allocZ<flow::external_device_t>( p->deviceN );
// get serial devices
for(unsigned di=0; i<p->deviceN && di<serialDeviceCount(p->ioH); ++di,++i)
_setup_device_cfg( p, p->deviceA + i, io::serialDeviceLabel(p->ioH,di), di, flow::kSerialDevTypeId, flow::kInFl | flow::kOutFl );
// get sockets
for(unsigned di=0; i<p->deviceN && di<socketCount(p->ioH); ++di,++i)
_setup_device_cfg( p, p->deviceA + i, io::socketLabel(p->ioH,di), di, flow::kSocketDevTypeId, flow::kInFl | flow::kOutFl );
// get midi devices
for(unsigned di=0; 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
for(unsigned gi=0; gi<p->audioGroupN; ++gi)
{
audio_group_t* ag = p->audioGroupA + gi;
for(unsigned di=0; i<p->deviceN && di<ag->iDeviceN; ++di,++i)
_setup_audio_device_cfg( p, p->deviceA + i, ag, ag->iDeviceA + di, flow::kInFl );
for(unsigned di=0; i<p->deviceN && di<ag->oDeviceN; ++di,++i)
_setup_audio_device_cfg( p, p->deviceA + i, ag, ag->oDeviceA + di, flow::kOutFl );
}
assert( i == p->deviceN );
}
rc_t _device_index_to_abuf( io_flow_ctl_t* p, unsigned ioGroupIdx, unsigned ioDevIdx, unsigned inOrOutFl, flow::abuf_t*& abuf_ref )
{
rc_t rc = kOkRC;
for(unsigned gi=0; gi<p->audioGroupN; ++gi)
if( p->audioGroupA[gi].ioGroupIdx == ioGroupIdx )
{
audio_dev_t* adA = inOrOutFl == flow::kInFl ? p->audioGroupA[gi].iDeviceA : p->audioGroupA[gi].oDeviceA;
unsigned adN = inOrOutFl == flow::kInFl ? p->audioGroupA[gi].iDeviceN : p->audioGroupA[gi].oDeviceN;
for(unsigned di=0; di<adN; ++di)
if( adA[di].ioDevIdx == ioDevIdx )
{
abuf_ref = &adA[di].abuf;
return rc;
}
}
const char* dir = inOrOutFl==flow::kInFl ? "in" : "out";
return cwLogError(kOpFailRC,"The '%s' audio group index:%i ,device index '%i' was not found.", dir, ioGroupIdx, ioDevIdx);
}
void _fill_input_buffer( flow::sample_t** bufChArray, unsigned bufChArrayN, flow::abuf_t* dst_abuf )
{
for(unsigned i=0; i<bufChArrayN; ++i)
{
const flow::sample_t* src = bufChArray[i];
flow::sample_t* dst = dst_abuf->buf + (i*dst_abuf->frameN);
memcpy(dst,src,dst_abuf->frameN*sizeof(flow::sample_t));
}
}
void _zero_output_buffer( flow::abuf_t* dst_abuf )
{
memset(dst_abuf->buf,0, dst_abuf->chN*dst_abuf->frameN*sizeof(flow::sample_t));
}
void _fill_output_buffer( const flow::abuf_t* src_abuf, flow::sample_t** bufChArray, unsigned bufChArrayN )
{
for(unsigned i=0; i<src_abuf->chN; ++i)
{
const flow::sample_t* src = src_abuf->buf + (i*src_abuf->frameN);
flow::sample_t* dst = bufChArray[i];
memcpy(dst,src,src_abuf->frameN*sizeof(flow::sample_t));
}
}
rc_t _audio_callback( io_flow_ctl_t* p, io::audio_msg_t& m )
{
rc_t rc = kOkRC;
flow::abuf_t* abuf = nullptr;
// Get an array of incoming MIDI events which have occurred since the last call to 'io::midiDeviceBuffer()'
unsigned midiBufMsgCnt = 0;
const midi::ch_msg_t* midiBuf = midiDeviceBuffer(p->ioH,midiBufMsgCnt);
if( p->done_fl )
{
rc = cwLogError(kInvalidStateRC,"Cannot execute an already completed program.");
goto errLabel;
}
// Give each MIDI input device a pointer to the incoming MIDI msgs
for(unsigned i=0; 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 )
{
unsigned chIdx = 0;
// for each input device in this group
for(io::audio_group_dev_t* agd = m.iDevL; agd!=nullptr; agd=agd->link)
{
// get the abuf associated with each device in this group
if((rc = _device_index_to_abuf( p, m.groupIndex, agd->devIdx, flow::kInFl, abuf )) != kOkRC )
goto errLabel;
// fill the input audio buf from the the external audio device
_fill_input_buffer( m.iBufArray + chIdx, agd->chCnt, abuf );
chIdx += agd->chCnt;
}
}
// if there are empty output (playback) buffers
if( m.oBufChCnt > 0 )
{
// for each output device in this group
for(io::audio_group_dev_t* agd=m.oDevL; agd!=nullptr; agd=agd->link)
{
// get the output audio buf associated with this external audio device
if((rc = _device_index_to_abuf( p, m.groupIndex, agd->devIdx, flow::kOutFl, abuf )) != kOkRC )
goto errLabel;
// zerot the output buffer
_zero_output_buffer( abuf );
}
}
// update the flow network - this will generate audio into the output audio buffers
if((rc = flow::exec_cycle(p->flowH)) != kOkRC )
{
if( rc == kEofRC )
{
p->done_fl = true;
p->init_fl = false;
rc = kOkRC;
}
}
// if there are empty output (playback) buffers
if( m.oBufChCnt > 0 )
{
unsigned chIdx = 0;
// for each output device in this group
for(io::audio_group_dev_t* agd=m.oDevL; agd!=nullptr; agd=agd->link)
{
// get the output audio buf associated with this external audio device
if((rc = _device_index_to_abuf( p, m.groupIndex, agd->devIdx, flow::kOutFl, abuf )) != kOkRC )
goto errLabel;
// copy the samples from the flow 'audio_out' buffers to the outgoing buffer passed from the device driver
_fill_output_buffer( abuf, m.oBufArray + chIdx, agd->chCnt );
chIdx += agd->chCnt;
}
}
errLabel:
// Drop the MIDI messages that were processed on this call.
midiDeviceClearBuffer(p->ioH,midiBufMsgCnt);
return rc;
}
template< typename T >
rc_t _ui_callback_tpl( io_flow_ctl_t* p, const flow::ui_var_t* ui_var )
{
rc_t rc;
T value;
if((rc = get_variable_value(p->flowH,ui_var,value)) != kOkRC )
{
rc = cwLogError(rc,"The variable value could not be read.");
goto errLabel;
}
if((rc = uiSendValue(p->ioH,ui_var->user_id,value)) != kOkRC )
{
rc = cwLogError(rc,"UI element data transmission failed.");
goto errLabel;
}
errLabel:
return rc;
}
// This function is called with messages for the UI from the flow proc instances
rc_t _ui_callback( void* arg, const flow::ui_var_t* ui_var )
{
rc_t rc = kOkRC;
io_flow_ctl_t* p = (io_flow_ctl_t*)arg;
if( ui_var->user_id == kInvalidId )
{
rc = cwLogError(kInvalidArgRC,"The user_id (uuid) of the variable was not set.");
goto errLabel;
}
switch( ui_var->value_tid & flow::kTypeMask )
{
case flow::kBoolTFl:
rc = _ui_callback_tpl<bool>(p,ui_var);
break;
case flow::kIntTFl:
rc = _ui_callback_tpl<int>(p,ui_var);
break;
case flow::kUIntTFl:
rc = _ui_callback_tpl<unsigned>(p,ui_var);
break;
case flow::kFloatTFl:
rc = _ui_callback_tpl<float>(p,ui_var);
break;
case flow::kDoubleTFl:
rc = _ui_callback_tpl<double>(p,ui_var);
break;
case flow::kStringTFl:
rc = _ui_callback_tpl<const char*>(p,ui_var);
break;
}
errLabel:
if( rc != kOkRC )
rc = cwLogError(rc,"Update of UI element of '%s:%i-%s:%i' failed.",cwStringNullGuard(ui_var->ui_proc->label),ui_var->ui_proc->label_sfx_id,cwStringNullGuard(ui_var->label),ui_var->label_sfx_id);
return rc;
}
}
}
cw::rc_t cw::io_flow_ctl::create( handle_t& hRef, io::handle_t ioH, const object_t* flow_cfg )
{
rc_t rc = kOkRC;
if((rc = destroy(hRef)) != kOkRC )
return rc;
io_flow_ctl_t* p = mem::allocZ<io_flow_ctl_t>();
p->pgm_idx = kInvalidIdx;
p->ioH = ioH;
if((rc = _parse_cfg(p,flow_cfg)) != kOkRC )
goto errLabel;
hRef.set(p);
errLabel:
if( rc != kOkRC )
{
rc = cwLogError(rc,"io_flow create failed.");
_destroy(p);
}
return rc;
}
cw::rc_t cw::io_flow_ctl::destroy( handle_t& hRef )
{
rc_t rc = kOkRC;
io_flow_ctl_t* p = nullptr;
if(!hRef.isValid())
return rc;
p = _handleToPtr(hRef);
if((rc = _destroy(p)) != kOkRC )
goto errLabel;
hRef.clear();
errLabel:
return rc;
}
unsigned cw::io_flow_ctl::program_count(handle_t h)
{
io_flow_ctl_t* p = _handleToPtr(h);
return p->pgmN;
}
const char* cw::io_flow_ctl::program_title( handle_t h, unsigned pgm_idx )
{
io_flow_ctl_t* p = _handleToPtr(h);
const char* pgm_title = nullptr;
if(_validate_pgm_idx(p,pgm_idx) != kOkRC )
goto errLabel;
pgm_title = p->pgmA[pgm_idx].label;
errLabel:
return pgm_title;
}
unsigned cw::io_flow_ctl::program_index( handle_t h, const char* pgm_title )
{
io_flow_ctl_t* p = _handleToPtr(h);
for(unsigned i=0; i<p->pgmN; ++i)
if( textIsEqual(pgm_title,p->pgmA[i].label) )
return i;
return kInvalidIdx;
}
cw::rc_t cw::io_flow_ctl::program_load( handle_t h, unsigned pgm_idx )
{
rc_t rc = kOkRC;
io_flow_ctl_t* p = _handleToPtr(h);
if((rc = _validate_pgm_idx(p,pgm_idx)) != kOkRC )
goto errLabel;
if((rc = _program_unload(p)) != kOkRC )
goto errLabel;
// form the program project directory
if((p->proj_dir = filesys::makeFn(p->base_dir,nullptr,nullptr,p->pgmA[pgm_idx].label,nullptr)) == nullptr )
{
rc = cwLogError(kOpFailRC,"The project directory formation failed.");
goto errLabel;
}
// create the project directory if it doesn't already exist
if( !filesys::isDir(p->proj_dir) )
if((rc = filesys::makeDir(p->proj_dir)) != kOkRC )
goto errLabel;
// configure the flow network
if((rc = create( p->flowH,
p->proc_class_dict_cfg,
p->pgmA[ pgm_idx ].cfg,
p->udp_dict_cfg,
p->proj_dir,
_ui_callback,
p)) != kOkRC )
{
rc = cwLogError(rc,"Network configuration failed.");
goto errLabel;
}
// allocate p->audioGroupA[] and create the audio input/output buffers associated with each audio device
_setup_audio_groups(p, sample_rate(p->flowH), frames_per_cycle(p->flowH) );
// setup the control record for each external device known to the IO interface
_setup_generic_device_array(p);
p->pgm_idx = pgm_idx;
p->done_fl = false;
p->init_fl = false;
errLabel:
return rc;
}
unsigned cw::io_flow_ctl::program_current_index( handle_t h )
{
io_flow_ctl_t* p = _handleToPtr(h);
return p->pgm_idx;
}
bool cw::io_flow_ctl::is_program_nrt( handle_t h )
{
io_flow_ctl_t* p = _handleToPtr(h);
bool nrt_fl = false;
if( !p->flowH.isValid() )
{
cwLogWarning("No program is loaded.");
goto errLabel;
}
if(_validate_pgm_idx(p,p->pgm_idx) != kOkRC )
goto errLabel;
nrt_fl = is_non_real_time(p->flowH);
errLabel:
return nrt_fl;
}
unsigned cw::io_flow_ctl::program_preset_count( handle_t h )
{
io_flow_ctl_t* p = _handleToPtr(h);
if(!p->flowH.isValid() || _validate_pgm_idx(p,p->pgm_idx) != kOkRC )
return 0;
return preset_count(p->flowH);
}
const char* cw::io_flow_ctl::program_preset_title( handle_t h, unsigned preset_idx )
{
io_flow_ctl_t* p = _handleToPtr(h);
if(!p->flowH.isValid() || _validate_pgm_idx(p,p->pgm_idx) != kOkRC )
return nullptr;
return preset_label(p->flowH,preset_idx);
}
cw::rc_t cw::io_flow_ctl::program_initialize( handle_t h, unsigned preset_idx )
{
rc_t rc = kOkRC;
io_flow_ctl_t* p = _handleToPtr(h);
if( p->pgm_idx == kInvalidIdx || p->init_fl || p->done_fl || !p->flowH.isValid() )
{
cwLogError(kInvalidStateRC,"A valid pre-initialized program is not loaded.");
goto errLabel;
}
// create the flow network
if((rc = initialize( p->flowH,
p->deviceA,
p->deviceN,
preset_idx )) != kOkRC )
{
rc = cwLogError(rc,"Network create failed.");
goto errLabel;
}
p->init_fl = true;
errLabel:
return rc;
}
bool cw::io_flow_ctl::program_is_initialized( handle_t h )
{
io_flow_ctl_t* p = _handleToPtr(h);
return p->init_fl;
}
const cw::flow::ui_net_t* cw::io_flow_ctl::program_ui_net( handle_t h )
{
io_flow_ctl_t* p = _handleToPtr(h);
return ui_net(p->flowH);
}
cw::rc_t cw::io_flow_ctl::exec_nrt( handle_t h )
{
rc_t rc = kOkRC;
io_flow_ctl_t* p = _handleToPtr(h);
if( p->pgm_idx == kInvalidIdx )
{
rc = cwLogWarning("No program is loaded.");
goto errLabel;
}
if( p->done_fl )
{
rc = cwLogError(kInvalidStateRC,"Cannot execute an already completed program.");
goto errLabel;
}
if((rc = exec( p->flowH )) != kOkRC )
{
if(rc == kEofRC )
{
p->done_fl = true;
rc = kOkRC;
}
else
{
rc = cwLogError(rc,"%s execution failed.", cwStringNullGuard(p->pgmA[p->pgm_idx].label));
goto errLabel;
}
}
errLabel:
return rc;
}
cw::rc_t cw::io_flow_ctl::exec( handle_t h, const io::msg_t& msg )
{
rc_t rc = kOkRC;
io_flow_ctl_t* p = _handleToPtr(h);
switch( msg.tid )
{
case io::kAudioTId:
if( msg.u.audio != nullptr )
rc = _audio_callback(p,*msg.u.audio);
break;
default:
rc = kOkRC;
}
return rc;
}
bool cw::io_flow_ctl::is_executable( handle_t h )
{
io_flow_ctl_t* p = _handleToPtr(h);
// A program must be loaded, initialized and execution cannot be complete
return p->pgm_idx != kInvalidIdx && p->init_fl && p->done_fl==false;
}
bool cw::io_flow_ctl::is_exec_complete( handle_t h )
{
io_flow_ctl_t* p = _handleToPtr(h);
return p->done_fl;
}
cw::rc_t cw::io_flow_ctl::get_variable_value( handle_t h, const flow::ui_var_t* ui_var, bool& value_ref )
{ return get_variable_value( _handleToPtr(h)->flowH, ui_var, value_ref ); }
cw::rc_t cw::io_flow_ctl::get_variable_value( handle_t h, const flow::ui_var_t* ui_var, int& value_ref )
{ return get_variable_value( _handleToPtr(h)->flowH, ui_var, value_ref ); }
cw::rc_t cw::io_flow_ctl::get_variable_value( handle_t h, const flow::ui_var_t* ui_var, unsigned& value_ref )
{ return get_variable_value( _handleToPtr(h)->flowH, ui_var, value_ref ); }
cw::rc_t cw::io_flow_ctl::get_variable_value( handle_t h, const flow::ui_var_t* ui_var, float& value_ref )
{ return get_variable_value( _handleToPtr(h)->flowH, ui_var, value_ref ); }
cw::rc_t cw::io_flow_ctl::get_variable_value( handle_t h, const flow::ui_var_t* ui_var, double& value_ref )
{ return get_variable_value( _handleToPtr(h)->flowH, ui_var, value_ref ); }
cw::rc_t cw::io_flow_ctl::get_variable_value( handle_t h, const flow::ui_var_t* ui_var, const char*& value_ref )
{ return get_variable_value( _handleToPtr(h)->flowH, ui_var, value_ref ); }
cw::rc_t cw::io_flow_ctl::set_variable_user_id( handle_t h, const flow::ui_var_t* ui_var, unsigned user_id )
{ return set_variable_user_id( _handleToPtr(h)->flowH, ui_var, user_id ); }
cw::rc_t cw::io_flow_ctl::set_variable_value( handle_t h, const flow::ui_var_t* ui_var, bool value )
{ return set_variable_value( _handleToPtr(h)->flowH, ui_var, value ); }
cw::rc_t cw::io_flow_ctl::set_variable_value( handle_t h, const flow::ui_var_t* ui_var, int value )
{ return set_variable_value( _handleToPtr(h)->flowH, ui_var, value ); }
cw::rc_t cw::io_flow_ctl::set_variable_value( handle_t h, const flow::ui_var_t* ui_var, unsigned value )
{ return set_variable_value( _handleToPtr(h)->flowH, ui_var, value ); }
cw::rc_t cw::io_flow_ctl::set_variable_value( handle_t h, const flow::ui_var_t* ui_var, float value )
{ return set_variable_value( _handleToPtr(h)->flowH, ui_var, value ); }
cw::rc_t cw::io_flow_ctl::set_variable_value( handle_t h, const flow::ui_var_t* ui_var, double value )
{ return set_variable_value( _handleToPtr(h)->flowH, ui_var, value ); }
cw::rc_t cw::io_flow_ctl::set_variable_value( handle_t h, const flow::ui_var_t* ui_var, const char* value )
{ return set_variable_value( _handleToPtr(h)->flowH, ui_var, value ); }
void cw::io_flow_ctl::report( handle_t h )
{
io_flow_ctl_t* p = _handleToPtr(h);
if( p->flowH.isValid() )
print_network(p->flowH);
}
void cw::io_flow_ctl::print_network( handle_t h )
{
io_flow_ctl_t* p = _handleToPtr(h);
if( p->flowH.isValid() )
print_network(p->flowH);
}

87
cwIoFlowCtl.h Normal file
View File

@ -0,0 +1,87 @@
#ifndef cwIoFlowCtl_H
#define cwIoFlowCtl_H
namespace cw
{
namespace io_flow_ctl
{
typedef handle< struct io_flow_ctl_str > handle_t;
rc_t create( handle_t& hRef, io::handle_t ioH, const char* flow_cfg_fn );
rc_t create( handle_t& hRef, io::handle_t ioH, const object_t* flow_cfg );
rc_t destroy( handle_t& hRef );
// Query the available programs from the 'flow_cfg' file.
unsigned program_count( handle_t h);
const char* program_title( handle_t h, unsigned pgm_idx );
unsigned program_index( handle_t h, const char* pgm_title);
// Create the parse the program but do not instantiate the network.
rc_t program_load( handle_t h, unsigned pgm_idx );
// Return the index of the currently loaded program or kInvalidIdx if no program is loaded.
unsigned program_current_index( handle_t h );
// Is the currently loaded program in non-real-time mode
bool is_program_nrt( handle_t h );
// Return the count of network presets and the associated labels for the currently loaded program.
unsigned program_preset_count( handle_t h );
const char* program_preset_title( handle_t h, unsigned preset_idx );
// Create the network and prepare to enter runtime.
rc_t program_initialize( handle_t h, unsigned preset_idx=kInvalidIdx );
bool program_is_initialized( handle_t h );
// Get the UI description data structures for the current program.
const flow::ui_net_t* program_ui_net( handle_t h );
// Execute the currently loaded non-real-time program to completion.
rc_t exec_nrt( handle_t h );
// Handle an incoming IO msg. This is the main point of entry for executing
// real-time programs.
rc_t exec( handle_t h, const io::msg_t& msg );
// Is the current program loaded, initialized and not yet complete.
bool is_executable( handle_t h );
// The current program has completed.
bool is_exec_complete( handle_t h );
rc_t get_variable_value( handle_t h, const flow::ui_var_t* ui_var, bool& value_ref );
rc_t get_variable_value( handle_t h, const flow::ui_var_t* ui_var, int& value_ref );
rc_t get_variable_value( handle_t h, const flow::ui_var_t* ui_var, unsigned& value_ref );
rc_t get_variable_value( handle_t h, const flow::ui_var_t* ui_var, float& value_ref );
rc_t get_variable_value( handle_t h, const flow::ui_var_t* ui_var, double& value_ref );
rc_t get_variable_value( handle_t h, const flow::ui_var_t* ui_var, const char*& value_ref );
template< typename T >
rc_t get_variable( handle_t h, const flow::ui_var_t* ui_var, T& value_ref )
{ return get_variable_value(h,ui_var,value_ref); }
rc_t set_variable_user_id( handle_t h, const flow::ui_var_t* ui_var, unsigned user_id );
rc_t set_variable_value( handle_t h, const flow::ui_var_t* ui_var, bool value );
rc_t set_variable_value( handle_t h, const flow::ui_var_t* ui_var, int value );
rc_t set_variable_value( handle_t h, const flow::ui_var_t* ui_var, unsigned value );
rc_t set_variable_value( handle_t h, const flow::ui_var_t* ui_var, float value );
rc_t set_variable_value( handle_t h, const flow::ui_var_t* ui_var, double value );
rc_t set_variable_value( handle_t h, const flow::ui_var_t* ui_var, const char* value );
template< typename T >
rc_t set_variable( handle_t h, const flow::ui_var_t* ui_var, T value )
{ return set_variable_value(h,ui_var,value); }
void report( handle_t h );
void print_network( handle_t h );
}
}
#endif

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

@ -161,6 +161,10 @@ namespace cw
// of this range will be returned as kInvalidMidiPitch.
uint8_t sciPitchToMidi( const char* sciPitchStr );
#define midi_to_hz( midi_pitch ) ((13.75 * std::pow(2,(-9.0/12.0))) * std::pow(2.0,(midi_pitch / 12.0)))
}
}

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"
@ -54,6 +55,7 @@ namespace cw
unsigned devCnt; // MIDI devices attached to this computer
dev_t* devArray;
cbFunc_t cbFunc; // MIDI input application callback
bool filterRtSenseFl;
void* cbDataPtr;
snd_seq_t* h; // ALSA system sequencer handle
snd_seq_addr_t alsa_addr; // ALSA client/port address representing the application
@ -72,6 +74,7 @@ namespace cw
bool latency_meas_enable_out_fl;
latency_meas_result_t latency_meas_result;
} alsa_device_t;
#define _cmMpErrMsg( rc, alsaRc, str ) cwLogError(kOpFailRC,"%s : ALSA Error:%i %s",(str),(alsaRc),snd_strerror(alsaRc))
@ -253,7 +256,7 @@ namespace cw
case SND_SEQ_EVENT_START: status = kSysRtStartMdId; break;
case SND_SEQ_EVENT_CONTINUE: status = kSysRtContMdId; break;
case SND_SEQ_EVENT_STOP: status = kSysRtStopMdId; break;
case SND_SEQ_EVENT_SENSING: status = kSysRtSenseMdId; break;
case SND_SEQ_EVENT_SENSING: status = p->filterRtSenseFl ? 0 : kSysRtSenseMdId; break;
case SND_SEQ_EVENT_RESET: status = kSysRtResetMdId; break;
case SND_SEQ_EVENT_SYSEX:
@ -283,6 +286,7 @@ namespace cw
time::spec_t ts;
ts.tv_sec = ev->time.time.tv_sec;
ts.tv_nsec = ev->time.time.tv_nsec;
parser::midiTriple(p->prvRcvPort->parserH, &ts, status | ch, d0, d1 );
p->prvTimeMicroSecs = microSecs1;
@ -439,7 +443,7 @@ namespace cw
{
assert(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 +465,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 +479,11 @@ namespace cw
++k;
}
}
// The capabilities of some ports may not have been as expected
// decrease the in/out port count to account for these ports
p->devArray[i].iPortCnt = j;
p->devArray[i].oPortCnt = k;
}
errLabel:
@ -592,7 +601,12 @@ namespace cw
} // cw
cw::rc_t cw::midi::device::alsa::create( handle_t& h, cbFunc_t cbFunc, void* cbArg, unsigned parserBufByteCnt, const char* appNameStr )
cw::rc_t cw::midi::device::alsa::create( handle_t& h,
cbFunc_t cbFunc,
void* cbArg,
unsigned parserBufByteCnt,
const char* appNameStr,
bool filterRtSenseFl )
{
rc_t rc = kOkRC;
int arc = 0;
@ -626,6 +640,7 @@ cw::rc_t cw::midi::device::alsa::create( handle_t& h, cbFunc_t cbFunc, void* cb
p->cbFunc = cbFunc;
p->cbDataPtr = cbArg;
p->filterRtSenseFl = filterRtSenseFl;
// start the sequencer queue
if((arc = snd_seq_start_queue(p->h, p->alsa_queue, NULL)) < 0 )
@ -736,7 +751,7 @@ const char* cw::midi::device::alsa::portName( handle_t h, unsigned devIdx,
if( cwIsFlag(flags,kInMpFl) )
{
if( portIdx >= p->devArray[devIdx].iPortCnt )
return 0;
return nullptr;
return p->devArray[devIdx].iPortArray[portIdx].nameStr;
}
@ -796,8 +811,11 @@ cw::rc_t cw::midi::device::alsa::send( handle_t h, unsigned devIdx, unsigned po
snd_seq_ev_set_direct(&ev);
snd_seq_ev_set_fixed(&ev);
uint8_t status_wo_ch = status;
if( midi::isChStatus(status) )
status_wo_ch = status & 0xf0;
switch( status & 0xf0 )
switch( status_wo_ch )
{
case kNoteOffMdId:
ev.type = SND_SEQ_EVENT_NOTEOFF;
@ -854,8 +872,32 @@ cw::rc_t cw::midi::device::alsa::send( handle_t h, unsigned devIdx, unsigned po
}
break;
case kSysRtClockMdId:
ev.type = SND_SEQ_EVENT_CLOCK;
break;
case kSysRtStartMdId:
ev.type =SND_SEQ_EVENT_START;
break;
case kSysRtContMdId:
ev.type = SND_SEQ_EVENT_CONTINUE;
break;
case kSysRtStopMdId:
ev.type = SND_SEQ_EVENT_STOP;
break;
case kSysRtSenseMdId:
ev.type = SND_SEQ_EVENT_SENSING;
break;
case kSysRtResetMdId:
ev.type = SND_SEQ_EVENT_RESET;
break;
default:
rc = _cmMpErrMsg1(kInvalidArgRC,0,"Cannot send an invalid MIDI status byte:0x%x.",status & 0xf0);
rc = _cmMpErrMsg1(kInvalidArgRC,0,"Cannot send an invalid MIDI status byte:0x%x.",status);
goto errLabel;
}

View File

@ -9,7 +9,13 @@ namespace cw
typedef handle< struct alsa_device_str> handle_t;
rc_t create( handle_t& h, cbFunc_t cbFunc, void* cbDataPtr, unsigned parserBufByteCnt, const char* appNameStr );
rc_t create( handle_t& h,
cbFunc_t cbFunc,
void* cbDataPtr,
unsigned parserBufByteCnt,
const char* appNameStr,
bool filterRtSenseFl );
rc_t destroy( handle_t& h);
bool isInitialized( handle_t h );

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,13 @@ 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;
bool filterRtSenseFl;
} device_t;
device_t* _handleToPtr( handle_t h )
@ -102,8 +110,11 @@ namespace cw
goto errLabel;
}
destroy(p->alsaDevH);
destroy(p->fileDevH);
mem::release(p->buf);
mem::release(p);
errLabel:
@ -173,6 +184,45 @@ namespace cw
return true;
}
void _callback( void* cbArg, const packet_t* pktArray, unsigned pktCnt )
{
device_t* p = (device_t*)cbArg;
for(unsigned i=0; 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 +238,10 @@ cw::rc_t cw::midi::device::create( handle_t& hRef,
const char* appNameStr,
const char* fileDevName,
unsigned fileDevReadAheadMicros,
unsigned parserBufByteCnt )
unsigned parserBufByteCnt,
bool enableBufFl,
unsigned bufferMsgCnt,
bool filterRtSenseFl )
{
rc_t rc = kOkRC;
rc_t rc1 = kOkRC;
@ -198,7 +251,12 @@ cw::rc_t cw::midi::device::create( handle_t& hRef,
device_t* p = mem::allocZ<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,
filterRtSenseFl)) != kOkRC )
{
rc = cwLogError(rc,"ALSA MIDI device create failed.");
goto errLabel;
@ -206,16 +264,29 @@ cw::rc_t cw::midi::device::create( handle_t& hRef,
p->alsa_dev_cnt = count(p->alsaDevH);
if((rc = create( p->fileDevH, cbFunc, cbArg, p->alsa_dev_cnt, filePortLabelA, max_file_cnt, fileDevName, fileDevReadAheadMicros )) != kOkRC )
if((rc = create( p->fileDevH,
enableBufFl ? _callback : cbFunc,
enableBufFl ? p : cbArg,
p->alsa_dev_cnt,
filePortLabelA,
max_file_cnt,
fileDevName,
fileDevReadAheadMicros )) != kOkRC )
{
rc = cwLogError(rc,"MIDI file device create failed.");
goto errLabel;
}
p->cbFunc = cbFunc;
p->cbArg = cbArg;
p->file_dev_cnt = count(p->fileDevH);
p->total_dev_cnt = p->alsa_dev_cnt + p->file_dev_cnt;
p->alsaPollfdA = pollFdArray(p->alsaDevH,p->alsaPollfdN);
p->fileDevStateId = kStoppedStateId;
p->buf = mem::allocZ<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,14 +328,20 @@ cw::rc_t cw::midi::device::create( handle_t& h,
const char* fileDevName = "file_dev";
unsigned fileDevReadAheadMicros = 3000;
unsigned parseBufByteCnt = 1024;
bool enableBufFl = false;
unsigned bufMsgCnt = 0;
const object_t* file_ports = nullptr;
const object_t* port = nullptr;
bool filterRtSenseFl = true;;
if((rc = args->getv("appNameStr",appNameStr,
"fileDevName",fileDevName,
"fileDevReadAheadMicros",fileDevReadAheadMicros,
"parseBufByteCnt",parseBufByteCnt,
"file_ports",file_ports)) != kOkRC )
"enableBufFl",enableBufFl,
"bufferMsgCnt",bufMsgCnt,
"file_ports",file_ports,
"filterRtSenseFl",filterRtSenseFl)) != kOkRC )
{
rc = cwLogError(rc,"MIDI port parse args. failed.");
}
@ -290,7 +367,18 @@ cw::rc_t cw::midi::device::create( handle_t& h,
}
}
rc = create(h,cbFunc,cbArg,labelArray,fpi,appNameStr,fileDevName,fileDevReadAheadMicros,parseBufByteCnt);
rc = create(h,
cbFunc,
cbArg,
labelArray,
fpi,
appNameStr,
fileDevName,
fileDevReadAheadMicros,
parseBufByteCnt,
enableBufFl,
bufMsgCnt,
filterRtSenseFl);
}
@ -445,7 +533,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 +679,43 @@ errLabel:
}
unsigned cw::midi::device::maxBufferMsgCount( handle_t h )
{
device_t* p = _handleToPtr(h);
return p->bufN;
}
const cw::midi::ch_msg_t* cw::midi::device::getBuffer( handle_t h, unsigned& msgCntRef )
{
device_t* p = _handleToPtr(h);
unsigned ii = p->buf_ii.load();
unsigned oi = p->buf_oi.load();
ch_msg_t* m = nullptr;
msgCntRef = ii >= oi ? ii-oi : p->bufN - oi;
if( msgCntRef > 0 )
m = p->buf + oi;
return m;
}
cw::rc_t cw::midi::device::clearBuffer( handle_t h, unsigned msgCnt )
{
if( msgCnt > 0 )
{
device_t* p = _handleToPtr(h);
unsigned oi = p->buf_oi.load();
oi = (oi + msgCnt) % p->bufN;
p->buf_oi.store(oi);
}
return kOkRC;
}
cw::rc_t cw::midi::device::start( handle_t h )
{
rc_t rc = kOkRC;
@ -669,6 +794,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,10 @@ namespace cw
const char* appNameStr,
const char* fileDevName = "file_dev",
unsigned fileDevReadAheadMicros = 3000,
unsigned parserBufByteCnt = 1024 );
unsigned parserBufByteCnt = 1024,
bool enableBufFl = false, // Enable buffer to hold all incoming msg's until RT thread can pick them up.
unsigned bufferMsgCnt = 4096, // Count of messages in input buffer.
bool filterRtSenseFl = true);
rc_t create( handle_t& h,
cbFunc_t cbFunc,
@ -57,6 +60,10 @@ namespace cw
rc_t seekToMsg( handle_t h, unsigned devIdx, unsigned portIdx, unsigned msgIdx );
rc_t setEndMsg( handle_t h, unsigned devIdx, unsigned portidx, unsigned msgIdx );
unsigned maxBufferMsgCount( handle_t h ); // max number of msg's which will ever be returned in a buffer
const ch_msg_t* getBuffer( handle_t h, unsigned& msgCntRef );
rc_t clearBuffer( handle_t h, unsigned msgCnt );
rc_t start( handle_t h );
rc_t stop( handle_t h );
rc_t pause( handle_t h, bool pause_fl );

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"
@ -8,6 +9,7 @@
#include "cwMidi.h"
#include "cwMidiFile.h"
#include "cwText.h"
#include "cwCsv.h"
#ifdef cwBIG_ENDIAN
#define mfSwap16(v) (v)
@ -500,9 +502,8 @@ namespace cw
trackMsg_t* nextTrkMsg[ p->trkN ]; // next msg in each track
unsigned long long atick = 0;
unsigned i;
bool fl = true;
// iniitalize nextTrkTick[] and nextTrkMsg[] to the first msg in each track
// iniitalize nextTrkMsg[] to the first msg in each track
for(i=0; i<p->trkN; ++i)
if((nextTrkMsg[i] = p->trkV[i].base) != NULL )
nextTrkMsg[i]->atick = nextTrkMsg[i]->dtick;
@ -511,7 +512,7 @@ namespace cw
{
unsigned k = kInvalidIdx;
// find the trk which has the next msg (min atick time)
// find the index of the track in nextTrkMsg[] which has the min atick
for(i=0; i<p->trkN; ++i)
if( nextTrkMsg[i]!=NULL && (k==kInvalidIdx || nextTrkMsg[i]->atick < nextTrkMsg[k]->atick) )
k = i;
@ -520,13 +521,6 @@ namespace cw
if( k == kInvalidIdx )
break;
if( fl && nextTrkMsg[k]->dtick > 0 )
{
fl = false;
nextTrkMsg[k]->dtick = 1;
nextTrkMsg[k]->atick = 1;
}
// store the current atick
atick = nextTrkMsg[k]->atick;
@ -542,7 +536,7 @@ namespace cw
void _setAbsoluteTime( file_t* mfp )
{
const trackMsg_t** msgV = _msgArray(mfp);
double microsPerQN = 60000000/120; // default tempo;
double microsPerQN = 60000000.0/120.0; // default tempo;
double microsPerTick = microsPerQN / mfp->ticksPerQN;
unsigned long long amicro = 0;
unsigned i;
@ -555,7 +549,7 @@ namespace cw
if( i > 0 )
{
// atick must have already been set and sorted
// atick must have already been set and sortedh
assert( mp->atick >= msgV[i-1]->atick );
dtick = mp->atick - msgV[i-1]->atick;
}
@ -566,7 +560,7 @@ namespace cw
// track tempo changes
if( mp->status == kMetaStId && mp->metaId == kTempoMdId )
microsPerTick = mp->u.iVal / mfp->ticksPerQN;
microsPerTick = (double)mp->u.iVal / mfp->ticksPerQN;
}
}
@ -683,6 +677,14 @@ namespace cw
}
void _init( file_t* p, unsigned trkN, unsigned ticksPerQN )
{
p->ticksPerQN = ticksPerQN;
p->fmtId = 1;
p->trkN = trkN;
p->trkV = mem::allocZ<track_t>(p->trkN);
}
rc_t _write8( file_t* mfp, unsigned char v )
{
rc_t rc = kOkRC;
@ -1146,7 +1148,7 @@ namespace cw
return rc;
}
rc_t _testCsv( const object_t* cfg )
rc_t _testGenCsv( const object_t* cfg )
{
rc_t rc = kOkRC;
const char* midiFn = nullptr;
@ -1164,6 +1166,30 @@ namespace cw
return rc;
}
rc_t _testOpenCsv( const object_t* cfg )
{
rc_t rc = kOkRC;
rc_t rc1 = kOkRC;
midi::file::handle_t mfH;
const char* csvFn = nullptr;
if((rc = cfg->getv("csvFn",csvFn)) != kOkRC )
return cwLogError(kSyntaxErrorRC,"Invalid parameter to MIDI to CSV file conversion.");
if(( rc = midi::file::open_csv(mfH,csvFn)) != kOkRC )
goto errLabel;
midi::file::printMsgs(mfH,log::globalHandle());
errLabel:
if((rc1 = close(mfH)) != kOkRC )
rc1 = cwLogError(rc1,"MIDI file close failed on '%s'.",cwStringNullGuard(csvFn));
return rcSelect(rc,rc1);
}
rc_t _testBatchConvert( const object_t* cfg )
{
@ -1308,6 +1334,107 @@ cw::rc_t cw::midi::file::open( handle_t& hRef, const char* fn ){
return rc;
}
cw::rc_t cw::midi::file::open_csv( handle_t& hRef, const char* csv_fname )
{
rc_t rc = kOkRC;
csv::handle_t csvH;
const char* titleA[] = { "uid","tpQN","bpm","dticks","ch","status","d0","d1" };
unsigned titleN = sizeof(titleA)/sizeof(titleA[0]);
unsigned TpQN = 1260;
unsigned BpM = 60;
unsigned lineN = 0;
unsigned line_idx = 0;
//double asecs = 0;
unsigned uid = kInvalidId;
unsigned dtick = 0;
unsigned ch = 0;
unsigned status = 0;
unsigned d0 = 0;
unsigned d1 = 0;
file_t* p = nullptr;
unsigned aticks = 0;
if((rc = _create(hRef)) != kOkRC )
goto errLabel;
if((p = _handleToPtr(hRef)) == nullptr )
goto errLabel;
if((rc = csv::create(csvH,csv_fname,titleA,titleN)) != kOkRC )
{
rc = cwLogError(rc,"MIDI CSV file open failed.");
goto errLabel;
}
if((rc = line_count(csvH,lineN)) != kOkRC )
{
rc = cwLogError(rc,"MIDI CSV line count access failed.");
goto errLabel;
}
for(; (rc = next_line(csvH)) == kOkRC; ++line_idx )
{
if((rc = getv(csvH,"uid",uid,"tpQN",TpQN,"bpm",BpM,"dticks",dtick,"ch",ch,"status",status,"d0",d0,"d1",d1)) != kOkRC )
{
cwLogError(rc,"Error reading CSV line %i.",line_idx+1);
goto errLabel;
}
//printf("%i %i tpqn:%i bpm:%i dtick:%i ch:%i st:%i d0:%i d1:%i\n",line_idx,uid,TpQN,BpM,dtick,ch,status,d0,d1);
//double ticks_per_sec = TpQN * BpM / 60;
//double dsecs = dtick * ticks_per_sec;
//asecs += dsecs;
if( line_idx == 0 )
_init(p,1,TpQN);
aticks += dtick;
if( BpM != 0 )
{
if((rc = insertTrackTempoMsg(hRef, 0, aticks, BpM )) != kOkRC )
{
rc = cwLogError(rc,"BPM insert failed.");
goto errLabel;
}
}
if( status != 0 )
{
if((rc = insertTrackChMsg(hRef, 0, aticks, ch+status, d0, d1 )) != kOkRC )
{
rc = cwLogError(rc,"Channel msg insert failed.");
goto errLabel;
}
}
TpQN = 0;
BpM = 0;
status = 0;
dtick = 0;
}
if( rc == kEofRC )
rc = kOkRC;
errLabel:
if( rc != kOkRC )
close(hRef);
destroy(csvH);
return rc;
}
cw::rc_t cw::midi::file::create( handle_t& hRef, unsigned trkN, unsigned ticksPerQN )
{
rc_t rc = kOkRC;
@ -1317,10 +1444,7 @@ cw::rc_t cw::midi::file::create( handle_t& hRef, unsigned trkN, unsigned ticksPe
file_t* p = _handleToPtr(hRef);
p->ticksPerQN = ticksPerQN;
p->fmtId = 1;
p->trkN = trkN;
p->trkV = mem::allocZ<track_t>(p->trkN);
_init(p,trkN,ticksPerQN);
return rc;
}
@ -2539,14 +2663,18 @@ cw::rc_t cw::midi::file::test( const object_t* cfg )
if( textIsEqual(o->pair_label(),"rpt") )
rc = _testReport(o->pair_value());
if( textIsEqual(o->pair_label(),"csv") )
rc = _testCsv(o->pair_value());
if( textIsEqual(o->pair_label(),"gen_csv") )
rc = _testGenCsv(o->pair_value());
if( textIsEqual(o->pair_label(),"open_csv") )
rc = _testOpenCsv(o->pair_value());
if( textIsEqual(o->pair_label(),"batch_convert") )
rc = _testBatchConvert(o->pair_value());
if( textIsEqual(o->pair_label(),"rpt_beg_end") )
rc = _testRptBeginEnd(o->pair_value());
}
}

View File

@ -114,6 +114,12 @@ namespace cw
// Read a MIDI file.
rc_t open( handle_t& hRef, const char* fn );
// Read from a CSV.
// Columns: "uid","tpQN","bpm","dticks","ch","status","d0","d1"
// tpQN = ticks per quarter note should be given on the first line. (Defaults to 1260).
// bpm = beats per minute should be given on the first line. (Defaults to 60).
rc_t open_csv( handle_t& hRef, const char* csv_fname );
// Create an empty MIDI file object.
rc_t create( handle_t& hRef, unsigned trkN, unsigned ticksPerQN );

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

@ -32,6 +32,7 @@ namespace cw
void push( T* payload )
{
// BUG: malloc() isn't non-blocking
node_t* new_node = mem::allocZ<node_t>(1);
new_node->payload = payload;
@ -48,6 +49,10 @@ namespace cw
// 2. Set the old-head next pointer to the new node (thereby adding the new node to the list)
prev->next.store(new_node,std::memory_order_release); // RELEASE 'next' to consumer
// After the first insertion:
// tail -> stub
// stub.next -> new_node
// head -> new_node
}
@ -60,7 +65,7 @@ namespace cw
{
_tail = next;
payload = next->payload;
mem::free(t);
mem::free(t); // BUG: free() isn't non-blocking
}
return payload;

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"
@ -88,9 +89,10 @@ namespace cw
cw::rc_t cw::mtx::test( const object_t* cfg )
cw::rc_t cw::mtx::test( const test::test_args_t& args )
{
rc_t rc = kOkRC;
const object_t* cfg = args.test_args;
d_t* mtx0 = nullptr;
d_t* mtx1 = nullptr;
@ -134,7 +136,7 @@ cw::rc_t cw::mtx::test( const object_t* cfg )
mtx_y1 = allocCfg<double>(y1);
unsigned n = offset(*mtx1,1,1);
printf("offset: %i\n",n);
cwLogPrint("offset: %i\n",n);
report(*mtx0,"m0");

18
cwMtx.h
View File

@ -732,7 +732,7 @@ namespace cw
double v = ele( m, idxV );
// print the value
printf("%*.*f ",colWidth,decPl,v);
cwLogPrint("%*.*f ",colWidth,decPl,v);
}
else
{
@ -742,11 +742,11 @@ namespace cw
{
// print the dimension index for matrices with 3+ dim's
if( i > 0 && j == 0 )
printf("%i\n",idxV[i-1]);
cwLogPrint("%i\n",idxV[i-1]);
// print the row index for matrices with 2+ dim's
if( m.dimN>1 )
printf("%i | ",j);
cwLogPrint("%i | ",j);
}
idxV[i] = j;
@ -755,7 +755,7 @@ namespace cw
// prevent multiple newlines on last printed line
if( m.dimN==1 || (m.dimN>=2 && i > m.dimN-2) )
printf("\n");
cwLogPrint("\n");
}
}
@ -774,10 +774,10 @@ namespace cw
template< typename T >
void report( const struct mtx_str<T>& m, const char* label, unsigned decPl=3, unsigned colWidth=10 )
{
printf("%s :",label);
cwLogPrint("%s :",label);
for(unsigned i=0; i<m.dimN; ++i)
printf("%i ", m.dimV[i] );
printf("\n");
cwLogPrint("%i ", m.dimV[i] );
cwLogPrint("\n");
print(m,decPl,colWidth);
}
@ -896,7 +896,7 @@ namespace cw
if( mcn != xrn )
return cwLogError(kInvalidArgRC, "Mtx mult. failed. Size mismatch: m[%i,%i] x[%i,%i].",mrn,mcn,xrn,xcn);
//printf("%i %i : %i %i\n",mrn,mcn,xrn,xcn);
//cwLogPrint("%i %i : %i %i\n",mrn,mcn,xrn,xcn);
resize(&y,yDimV, 2 );
@ -936,7 +936,7 @@ namespace cw
typedef struct mtx_str<float> f_t;
typedef struct mtx_str<double> d_t;
rc_t test( const struct object_str* cfg );
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 "cwMutex.h"
#include "cwTime.h"

View File

@ -15,6 +15,8 @@ namespace cw
rc_t lock( handle_t h );
rc_t unlock( handle_t h );
// Set timeOutMs to 0 to wait forever.
//
// Set 'lockThenWaitFl' if the function should lock the mutex prior to waiting.
// If 'lockThenWaitFl' is false then the function assumes the mutex is already locked
// and directly waits. If 'lockThenWaitFl' is set and the mutex is not already locked

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 );
}
@ -930,8 +937,12 @@ cw::rc_t cw::objectFromString( const char* s, object_t*& objRef )
// then make the parent 'object' the current node
if( cnp->is_pair() && cnp->child_count()==2 )
cnp = cnp->parent;
}
if( lexId == lex::kErrorLexTId )
{
rc = cwLogError(kSyntaxErrorRC,"A lexical element was not recognized.");
goto errLabel;
}
// if the root has only one child then make the child the root
@ -1007,4 +1018,118 @@ cw::rc_t cw::objectToFile( const char* fn, const object_t* obj )
return rc;
}
namespace cw
{
rc_t _object_test_basic( const test::test_args_t& args )
{
rc_t rc = kOkRC;
cw::object_t* o = nullptr;
const char s [] = "{ a:1, b:2, c:[ 1.23, 4.56 ], d:true, e:false, f:true }";
int a = 0;
int b = 0;
const cw::object_t* c = nullptr;
bool d,e,f;
const unsigned bufN = 128;
char buf[bufN];
unsigned i = 0;
cw::object_t* oo = nullptr;
if((rc = cw::objectFromString(s,o)) != kOkRC )
goto errLabel;
int v;
if((rc = o->get("b",v)) != kOkRC )
goto errLabel;
cwLogPrint("value:%i\n",v);
o->print();
if((rc = o->getv("a",a,"b",b)) != kOkRC )
goto errLabel;
cwLogPrint("G: %i %i\n",a,b);
if((rc = o->readv("a",0,a,
"b",0,b,
"c",cw::kOptFl | cw::kListTId,c,
"d",0,d,
"e",0,e,
"f",0,f)) != kOkRC )
{
goto errLabel;
}
cwLogPrint("R: %i %i : %i %i %i\n",a,b,d,e,f);
i = o->to_string(buf,bufN);
cwLogPrint("%i : %s\n",i, buf);
oo = o->duplicate();
oo->print();
oo->free();
o->free();
errLabel:
return rc;
}
rc_t _object_test_to_json( const test::test_args_t& args )
{
double v0[] = {1.23,2.34,3.45};
unsigned v0N = sizeof(v0)/sizeof(v0[0]);
int v1[] = {-1,0,1,2,3,4};
unsigned v1N = sizeof(v1)/sizeof(v1[0]);
cw::object_t* d = cw::newDictObject();
d->putv("A","Abc","B",1.234);
d->put_numeric_list("v0",v0,v0N);
d->put_numeric_list("v1",v1,v1N);
char* s = d->to_string();
cwLogPrint("%s\n",s);
cw::mem::release(s);
d->free();
return kOkRC;
}
}
cw::rc_t cw::object_test( const test::test_args_t& args )
{
rc_t rc = kOkRC;
if( textIsEqual(args.test_label,"basic") )
{
rc = _object_test_basic(args);
goto errLabel;
}
if( textIsEqual(args.test_label,"to_json") )
{
rc = _object_test_to_json(args);
goto errLabel;
}
rc = cwLogError(kInvalidArgRC,"Unknown test case module:%s test:%s.",args.module_label,args.test_label);
errLabel:
return rc;
}

View File

@ -31,7 +31,10 @@ namespace cw
kRootTId = 0x00100000,
kHexFl = 0x10000000,
kIdentFl = 0x20000000
kIdentFl = 0x20000000,
kOptFl = 0x40000000,
kReqFl = 0x00000000,
};
@ -135,12 +138,19 @@ namespace cw
// Value containers are parents of leaf nodes. (A dictionary is not a value container because it's children are pairs with are not leaf nodes.)
inline bool is_value_container() const { return type != nullptr && cwIsFlag(type->flags,kValueContainerFl); }
inline unsigned type_id() const { return type==nullptr ? (unsigned)kInvalidTId : type->id; }
// Containers have children and use the object.u.children pointer.
inline bool is_container() const { return type != nullptr && cwIsFlag(type->flags,kContainerFl); }
inline bool is_pair() const { return type != nullptr && type->id == kPairTId; }
inline bool is_dict() const { return type != nullptr && type->id == kDictTId; }
inline bool is_list() const { return type != nullptr && type->id == kListTId; }
inline bool is_string() const { return type != nullptr && (type->id == kStringTId || type->id == kCStringTId); }
inline bool is_unsigned_integer() const { return type->id==kCharTId || type->id==kUInt8TId || type->id==kUInt16TId || type->id==kUInt32TId || type->id==kUInt64TId; }
inline bool is_signed_integer() const { return type->id==kInt8TId || type->id==kInt16TId || type->id==kInt32TId || type->id==kInt64TId; }
inline bool is_floating_point() const { return type->id==kFloatTId || type->id==kDoubleTId; }
inline bool is_integer() const { return is_unsigned_integer() || is_signed_integer(); }
inline bool is_numeric() const { return is_integer() || is_floating_point(); }
inline bool is_type( unsigned tid ) const { return type != nullptr && type->id == tid; }
rc_t value( void* dst, unsigned dstTypeId );
@ -199,6 +209,117 @@ 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 kReqFl/kOptFl for required/optional fields.
// Use kListTId and kDictTId to validate the type of container fields.
// In general it should not be necessary to validate numeric and string types because
// they are validated by virtue of being converted to the returned value.
// Note that readv() assumes that the list of possible fields given as input is complete
// and any fields that it finds which are not in the list are not valid. This
// validity check is the main difference between readv() and getv()/getv_opt().
template< typename T0, typename T1, typename... ARGS >
rc_t readv( T0 label, unsigned flags, T1& valRef, ARGS&&... args ) const
{ return _readv(nullptr, label,flags,valRef,args...); }
// Set flag 'kRecurseFl' to recurse into the object in search of the value.
// Set flag 'kOptionalFl' if the label is optional and may not exist.
@ -344,6 +465,8 @@ namespace cw
rc_t objectToFile( const char* fn, const object_t* obj );
rc_t object_test( const test::test_args_t& args );
}

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 "cwSpScBuf.h"
#include "cwThread.h"

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