Merge branch 'poly'
This commit is contained in:
commit
eeeb2913db
2
.gitignore
vendored
2
.gitignore
vendored
@ -2,4 +2,4 @@ hold
|
||||
study/serial/serial
|
||||
cw_rt
|
||||
vg0.txt
|
||||
|
||||
__pycache__
|
||||
|
18
Makefile.am
18
Makefile.am
@ -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
806
README.md
@ -432,7 +432,7 @@ struct in_addr {
|
||||
|
||||
export LD_LIBRARY_PATH=~/sdk/libwebsockets/build/out/lib
|
||||
|
||||
*** Raspberry Pi Build Notes:
|
||||
### Raspberry Pi Build Notes:
|
||||
|
||||
cd sdk
|
||||
mkdir libwebsockets
|
||||
@ -444,10 +444,10 @@ struct in_addr {
|
||||
|
||||
|
||||
|
||||
*** Flow Notes:
|
||||
### Flow Notes:
|
||||
|
||||
|
||||
- When a variable has a variant with a numberic channel should the 'all' channel variant be removed?
|
||||
- When a variable has a variant with a numeric channel should the 'all' channel variant be removed?
|
||||
|
||||
- Check for duplicate 'vid'-'chIdx' pairs in var_regster().
|
||||
(The concatenation of 'vid' and 'chIdx' should be unique
|
||||
@ -469,16 +469,38 @@ specific types, to pass through. For example a 'selector' (n inputs, 1 output) o
|
||||
|
||||
DONE: Add a version of var_register() that both registers and returns the value of the variable.
|
||||
|
||||
*** Flow Instance Creation:
|
||||
### Flow Instance Creation:
|
||||
|
||||
1. Create all vars from the class description and initially set their
|
||||
value to the default value given in the class. chIdx=kAnyChIdx.
|
||||
0. Parse the 'in' list and create any 'mult' variables whose
|
||||
'in-var' contains an integer or underscore suffix. See
|
||||
"'in' List Syntax and Semantics" below.
|
||||
|
||||
Note that all vars must be included in the class description.
|
||||
1. Create all vars from the class description, that were not
|
||||
already instantiated during 'in' list processing, and set their
|
||||
initial value to the default value given in the class. chIdx=kAnyChIdx.
|
||||
|
||||
Notes:
|
||||
- All vars must be included in the class description.
|
||||
- All vars have a 'kAnyChIdx' instantiation. The kAnyChIdx variable serves two purposes:
|
||||
+ Setting the value of kAnyChIdx automatically broadcasts the value to all other channels.
|
||||
+ kAnyChIdx acts as a template when variables are created by 'channelization'.
|
||||
This allows the network designer to set the value of the kAnyIdx variable
|
||||
and have that become the default value for all subsequent variables
|
||||
which are created without an explicit value. (Note that his currently
|
||||
works for variables created from within `proc_create()` (i.e.
|
||||
where `sfx_id` == kBaseSfxId, but it doesn't work for mult
|
||||
variables that are automatically created via `var_register()`
|
||||
because `var_register()` does not have a value to assign to the
|
||||
kAnyChIdx instance. In this case the variable get assigned
|
||||
the class default value. The way around this is to explicitely
|
||||
set the mult variable value in the 'in' stmt or the 'args' stmt.)
|
||||
|
||||
|
||||
2. Apply the preset record from the class description according to the
|
||||
label given in the instance definition.
|
||||
|
||||
|
||||
|
||||
2. Apply the preset records from the class description according to the
|
||||
'presets' list given in the instance definition.
|
||||
|
||||
If the variable values are given as a scalar then the existing
|
||||
variable is simply given a new value.
|
||||
@ -489,17 +511,18 @@ index of the value in the list. This is referred
|
||||
to as 'channelizing' the variable because the variable will then
|
||||
be represented by multiple physical variable records - one for each channel.
|
||||
This means that all variables will have their initial record, with the chIdx set to 'any',
|
||||
and then they may also have further variable records will for each explicit
|
||||
and then they may also have further variable records for each explicit
|
||||
channel number. The complete list of channelized variable record
|
||||
is kept, in channel order, using the 'ch_link' links with the base of the list
|
||||
on the 'any' record.
|
||||
|
||||
3. Apply the variable values defined in the instance 'args' record.
|
||||
This application is treated similarly to the 'class'
|
||||
preset. If the variable value is presented in a list then
|
||||
the value is assigned to a specific channel if the channel
|
||||
already exists then the value is simply replaced, if the
|
||||
channel does not exist then the variable is 'channelized'.
|
||||
3. Apply the variable values defined in a instance 'args' record.
|
||||
|
||||
The application of the args record proceeds exactly the same as
|
||||
applying a 'class' preset. If the variable value is presented in a
|
||||
list then the value is assigned to a specific channel. If the channel
|
||||
already exists then the value is simply replaced. If the channel does
|
||||
not exist then the variable is 'channelized'.
|
||||
|
||||
4. The varaibles listed in the 'in' list of the instance cfg.
|
||||
are connected to their source variables.
|
||||
@ -519,6 +542,757 @@ before registering the variable.
|
||||
access to registered variables.
|
||||
|
||||
|
||||
# Notes on 'poly' and 'mult':
|
||||
|
||||
The 'in' statement is formed by a list of _Connect Expressions_ :
|
||||
|
||||
`<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.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
||||
//)
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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");
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -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 )
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
|
@ -13,16 +13,32 @@ namespace cw
|
||||
nanosleep(ts,NULL);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const char* cw::idToLabel( const idLabelPair_t* array, unsigned id, unsigned eolId )
|
||||
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 )
|
||||
return p->label;
|
||||
break;
|
||||
|
||||
return nullptr;
|
||||
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 = _idToSlot(array,id,eolId);
|
||||
|
||||
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;
|
||||
|
@ -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.
|
||||
|
16
cwCsv.cpp
16
cwCsv.cpp
@ -1,12 +1,14 @@
|
||||
#include "cwCommon.h"
|
||||
#include "cwLog.h"
|
||||
#include "cwCommonImpl.h"
|
||||
#include "cwTest.h"
|
||||
#include "cwMem.h"
|
||||
#include "cwText.h"
|
||||
#include "cwFile.h"
|
||||
#include "cwObject.h"
|
||||
#include "cwCsv.h"
|
||||
#include "cwNumericConvert.h"
|
||||
#include <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;
|
||||
|
@ -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"
|
||||
|
@ -1,6 +1,7 @@
|
||||
#include "cwCommon.h"
|
||||
#include "cwLog.h"
|
||||
#include "cwCommonImpl.h"
|
||||
#include "cwTest.h"
|
||||
#include "cwMem.h"
|
||||
#include "cwTime.h"
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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 );
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
#include "cwLog.h"
|
||||
#include "cwCommonImpl.h"
|
||||
#include "cwTest.h"
|
||||
|
||||
#include "cwMem.h"
|
||||
#include "cwTime.h"
|
||||
|
@ -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"
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 );
|
||||
}
|
||||
|
||||
}
|
||||
|
2544
cwFlow.cpp
2544
cwFlow.cpp
File diff suppressed because it is too large
Load Diff
100
cwFlow.h
100
cwFlow.h
@ -1,5 +1,5 @@
|
||||
#ifndef cwFlowSys_h
|
||||
#define cwFlowSys_h
|
||||
#ifndef cwFlow_h
|
||||
#define cwFlow_h
|
||||
|
||||
namespace cw
|
||||
{
|
||||
@ -8,68 +8,52 @@ namespace cw
|
||||
|
||||
typedef handle<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,
|
||||
const object_t& networkCfg,
|
||||
const object_t* classCfg, // processor class dictionary
|
||||
const object_t* pgmCfg, // top level program cfg
|
||||
const object_t* udpCfg = nullptr,
|
||||
const char* projDir = nullptr,
|
||||
ui_callback_t ui_callback = nullptr,
|
||||
void* ui_callback_arg = nullptr);
|
||||
|
||||
// Network cfg. information which is available following create().
|
||||
bool is_non_real_time( handle_t h );
|
||||
double sample_rate( handle_t h );
|
||||
unsigned frames_per_cycle( handle_t h );
|
||||
unsigned preset_cfg_flags( handle_t h );
|
||||
|
||||
// Get the count and labels of the top level presets
|
||||
unsigned preset_count( handle_t h );
|
||||
const char* preset_label( handle_t h, unsigned preset_idx );
|
||||
|
||||
// Instantiate the network and prepare for runtime.
|
||||
// The UI is not available until after initialization.
|
||||
rc_t initialize( handle_t handle,
|
||||
external_device_t* deviceA = nullptr,
|
||||
unsigned deviceN = 0);
|
||||
unsigned 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 );
|
||||
|
||||
|
||||
|
||||
|
@ -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)
|
||||
|
137
cwFlowDecl.h
137
cwFlowDecl.h
@ -5,6 +5,67 @@ namespace cw
|
||||
{
|
||||
namespace flow
|
||||
{
|
||||
|
||||
enum
|
||||
{
|
||||
kAudioDevTypeId,
|
||||
kMidiDevTypeId,
|
||||
kSerialDevTypeId,
|
||||
kSocketDevTypeId
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
kInFl = 0x01,
|
||||
kOutFl = 0x02
|
||||
};
|
||||
|
||||
|
||||
struct abuf_str;
|
||||
|
||||
typedef struct audio_dev_cfg_str
|
||||
{
|
||||
struct abuf_str* abuf; // Buffer to receive incoming or send outgoing audio for this device
|
||||
// The audio_in/audio_out proc's locate and use these buffers.
|
||||
} audio_dev_cfg_t;
|
||||
|
||||
struct external_device_str;
|
||||
|
||||
typedef rc_t (*send_midi_triple_func_t)( struct external_device_str* dev, uint8_t ch, uint8_t status, uint8_t d0, uint8_t d1 );
|
||||
|
||||
typedef struct midi_dev_cfg_str
|
||||
{
|
||||
// msgArray[] contains the current msgs for all devices NOT just the device that this record is embedded in.
|
||||
// We do this so that the order of messages as they arrived is maintained. Otherwise, to achieve this ordering,
|
||||
// the messages for all devices would need to be collected and sorted by time.
|
||||
const midi::ch_msg_t* msgArray;
|
||||
unsigned msgCnt;
|
||||
|
||||
unsigned maxMsgCnt; // max possible value of msgCnt
|
||||
send_midi_triple_func_t sendTripleFunc;
|
||||
} midi_dev_cfg_t;
|
||||
|
||||
// Generate external device record
|
||||
typedef struct external_device_str
|
||||
{
|
||||
void* reserved;
|
||||
const char* devLabel; // IO framework device label
|
||||
const char* portLabel; // IO framework MIDI port label (only used by MIDI devices)
|
||||
unsigned typeId; // see ???DevTypeId above
|
||||
unsigned flags; // see ???Fl above
|
||||
|
||||
unsigned ioDevIdx; // IO framework device index
|
||||
unsigned ioPortIdx; // IO framework MIDI port index (only used by MIDI devices)
|
||||
|
||||
union
|
||||
{
|
||||
audio_dev_cfg_t a; // audio devices use this record
|
||||
midi_dev_cfg_t m; // MIDI " " " "
|
||||
} u;
|
||||
|
||||
} external_device_t;
|
||||
|
||||
|
||||
enum {
|
||||
kPriPresetProbFl = 0x01,
|
||||
kSecPresetProbFl = 0x02,
|
||||
@ -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
3516
cwFlowNet.cpp
Normal file
File diff suppressed because it is too large
Load Diff
125
cwFlowNet.h
Normal file
125
cwFlowNet.h
Normal 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
|
6126
cwFlowProc.cpp
6126
cwFlowProc.cpp
File diff suppressed because it is too large
Load Diff
29
cwFlowProc.h
29
cwFlowProc.h
@ -2,15 +2,21 @@ namespace cw
|
||||
{
|
||||
namespace flow
|
||||
{
|
||||
namespace user_def_proc { extern class_members_t members; }
|
||||
namespace poly { extern class_members_t members; }
|
||||
namespace midi_in { extern class_members_t members; }
|
||||
namespace midi_out { extern class_members_t members; }
|
||||
namespace audio_in { extern class_members_t members; }
|
||||
namespace audio_out { extern class_members_t members; }
|
||||
namespace audioFileIn { extern class_members_t members; }
|
||||
namespace audioFileOut { extern class_members_t members; }
|
||||
namespace audio_file_in { extern class_members_t members; }
|
||||
namespace audio_file_out { extern class_members_t members; }
|
||||
namespace audio_gain { extern class_members_t members; }
|
||||
namespace audio_split { extern class_members_t members; }
|
||||
namespace audio_merge { extern class_members_t members; }
|
||||
namespace audio_duplicate { extern class_members_t members; }
|
||||
namespace audio_mix { extern class_members_t members; }
|
||||
namespace audio_marker { extern class_members_t members; }
|
||||
namespace audio_silence { extern class_members_t members; }
|
||||
namespace sine_tone { extern class_members_t members; }
|
||||
namespace pv_analysis { extern class_members_t members; }
|
||||
namespace pv_synthesis { extern class_members_t members; }
|
||||
@ -22,5 +28,24 @@ namespace cw
|
||||
namespace balance { extern class_members_t members; }
|
||||
namespace audio_meter { extern class_members_t members; }
|
||||
namespace audio_marker { extern class_members_t members; }
|
||||
namespace xfade_ctl { extern class_members_t members; }
|
||||
namespace midi_voice { extern class_members_t members; }
|
||||
namespace piano_voice { extern class_members_t members; }
|
||||
namespace poly_voice_ctl { extern class_members_t members; }
|
||||
namespace sample_hold { extern class_members_t members; }
|
||||
namespace number { extern class_members_t members; }
|
||||
namespace reg { extern class_members_t members; }
|
||||
namespace timer { extern class_members_t members; }
|
||||
namespace counter { extern class_members_t members; }
|
||||
namespace list { extern class_members_t members; }
|
||||
namespace add { extern class_members_t members; }
|
||||
namespace preset { extern class_members_t members; }
|
||||
namespace print { extern class_members_t members; }
|
||||
namespace halt { extern class_members_t members; }
|
||||
namespace midi_msg { extern class_members_t members; }
|
||||
namespace midi_split { extern class_members_t members; }
|
||||
namespace midi_file { extern class_members_t members; }
|
||||
namespace midi_merge { extern class_members_t members; }
|
||||
|
||||
}
|
||||
}
|
||||
|
88
cwFlowTest.cpp
Normal file
88
cwFlowTest.cpp
Normal 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
9
cwFlowTest.h
Normal file
@ -0,0 +1,9 @@
|
||||
|
||||
namespace cw
|
||||
{
|
||||
namespace flow
|
||||
{
|
||||
|
||||
rc_t test( const test::test_args_t& args );
|
||||
}
|
||||
}
|
2240
cwFlowTypes.cpp
2240
cwFlowTypes.cpp
File diff suppressed because it is too large
Load Diff
558
cwFlowTypes.h
558
cwFlowTypes.h
@ -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 );
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -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
301
cwIo.cpp
@ -1,6 +1,7 @@
|
||||
#include "cwCommon.h"
|
||||
#include "cwLog.h"
|
||||
#include "cwCommonImpl.h"
|
||||
#include "cwTest.h"
|
||||
#include "cwMem.h"
|
||||
#include "cwObject.h"
|
||||
#include "cwText.h"
|
||||
@ -95,6 +96,8 @@ namespace cw
|
||||
audioGroup_t* oGroup; //
|
||||
audio_group_dev_t* iagd; // Audio group device record assoc'd with this device
|
||||
audio_group_dev_t* oagd; //
|
||||
unsigned cycleCnt;
|
||||
unsigned framesPerCycle;
|
||||
|
||||
struct audioDev_str* clockInList; // List of devices sync'd to this devices input clock
|
||||
struct audioDev_str* clockOutList; // List of devices sync'd to this devices output clock
|
||||
@ -611,7 +614,7 @@ namespace cw
|
||||
//
|
||||
// MIDI
|
||||
//
|
||||
void _midiCallback( const midi::packet_t* pktArray, unsigned pktCnt )
|
||||
void _midiCallback( void* cbArg, const midi::packet_t* pktArray, unsigned pktCnt )
|
||||
{
|
||||
unsigned i;
|
||||
for(i=0; i<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
28
cwIo.h
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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:
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
74
cwIoFlow.cpp
74
cwIoFlow.cpp
@ -1,6 +1,7 @@
|
||||
#include "cwCommon.h"
|
||||
#include "cwLog.h"
|
||||
#include "cwCommonImpl.h"
|
||||
#include "cwTest.h"
|
||||
#include "cwMem.h"
|
||||
#include "cwObject.h"
|
||||
#include "cwFileSys.h"
|
||||
@ -8,6 +9,8 @@
|
||||
#include "cwTime.h"
|
||||
#include "cwVectOps.h"
|
||||
#include "cwMtx.h"
|
||||
#include "cwTime.h"
|
||||
#include "cwMidiDecls.h"
|
||||
|
||||
#include "cwDspTypes.h"
|
||||
#include "cwFlowDecl.h"
|
||||
@ -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
929
cwIoFlowCtl.cpp
Normal 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
87
cwIoFlowCtl.h
Normal 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
|
@ -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"
|
||||
|
@ -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;
|
||||
|
@ -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, 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,6 +3106,10 @@ 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 );
|
||||
break;
|
||||
@ -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
|
||||
|
@ -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"
|
||||
|
13
cwIoTest.cpp
13
cwIoTest.cpp
@ -1,6 +1,7 @@
|
||||
#include "cwCommon.h"
|
||||
#include "cwLog.h"
|
||||
#include "cwCommonImpl.h"
|
||||
#include "cwTest.h"
|
||||
#include "cwMem.h"
|
||||
#include "cwObject.h"
|
||||
#include "cwTime.h"
|
||||
@ -308,8 +309,16 @@ cw::rc_t cw::io::test( const object_t* cfg )
|
||||
// execuite the io framework
|
||||
while( !isShuttingDown(app.ioH))
|
||||
{
|
||||
exec(app.ioH);
|
||||
sleepMs(50);
|
||||
const unsigned wsTimeOutMs = 50;
|
||||
time::spec_t t0 = time::current_time();
|
||||
|
||||
exec(app.ioH,wsTimeOutMs);
|
||||
|
||||
time::spec_t t1 = time::current_time();
|
||||
unsigned dMs = time::elapsedMs(t0,t1);
|
||||
|
||||
if( dMs < wsTimeOutMs )
|
||||
sleepMs(wsTimeOutMs-dMs);
|
||||
}
|
||||
|
||||
errLabel:
|
||||
|
13
cwLex.cpp
13
cwLex.cpp
@ -3,7 +3,8 @@
|
||||
#include "cwCommonImpl.h"
|
||||
#include "cwMem.h"
|
||||
#include "cwFile.h"
|
||||
|
||||
#include "cwTest.h"
|
||||
#include "cwObject.h"
|
||||
#include "cwLex.h"
|
||||
|
||||
|
||||
@ -853,7 +854,7 @@ namespace cw
|
||||
//)
|
||||
|
||||
//(
|
||||
rc_t test()
|
||||
rc_t test( const test::test_args_t& args )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
unsigned tid = kInvalidId;
|
||||
@ -867,6 +868,8 @@ namespace cw
|
||||
"comment */"
|
||||
"\"quoted string\""
|
||||
"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 )
|
||||
{
|
||||
|
2
cwLex.h
2
cwLex.h
@ -129,7 +129,7 @@ namespace cw
|
||||
const char* idToLabel( handle_t h, unsigned typeId );
|
||||
|
||||
// Lexer testing stub.
|
||||
rc_t test( );
|
||||
rc_t test( const test::test_args_t& args );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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"
|
||||
|
36
cwLog.cpp
36
cwLog.cpp
@ -2,6 +2,7 @@
|
||||
#include "cwLog.h"
|
||||
|
||||
#include "cwCommonImpl.h"
|
||||
#include "cwTest.h"
|
||||
#include "cwMem.h"
|
||||
#include "cwTime.h"
|
||||
|
||||
@ -112,7 +113,7 @@ cw::rc_t cw::log::msg( handle_t h, unsigned level, const char* function, const c
|
||||
cw::rc_t cw::log::msg( handle_t h, unsigned level, const char* function, const char* filename, unsigned line, int systemErrorCode, rc_t returnCode, const char* fmt, ... )
|
||||
{
|
||||
rc_t rc = returnCode;
|
||||
if( level >= _handleToPtr(h)->level )
|
||||
if( level >= _handleToPtr(h)->level || level == kPrint_LogLevel )
|
||||
{
|
||||
va_list vl;
|
||||
va_start(vl,fmt);
|
||||
@ -146,6 +147,29 @@ unsigned cw::log::flags( handle_t h )
|
||||
return p->flags;
|
||||
}
|
||||
|
||||
void* cw::log::outputCbArg( handle_t h )
|
||||
{
|
||||
log_t* p = _handleToPtr(h);
|
||||
return p->outCbArg;
|
||||
}
|
||||
|
||||
cw::log::logOutputCbFunc_t cw::log::outputCb( handle_t h )
|
||||
{
|
||||
log_t* p = _handleToPtr(h);
|
||||
return p->outCbFunc;
|
||||
}
|
||||
|
||||
void* cw::log::formatCbArg( handle_t h )
|
||||
{
|
||||
log_t* p = _handleToPtr(h);
|
||||
return p->fmtCbArg;
|
||||
}
|
||||
|
||||
cw::log::logFormatCbFunc_t cw::log::formatCb( handle_t h )
|
||||
{
|
||||
log_t* p = _handleToPtr(h);
|
||||
return p->fmtCbFunc;
|
||||
}
|
||||
|
||||
void cw::log::setOutputCb( handle_t h, logOutputCbFunc_t outFunc, void* outCbArg )
|
||||
{
|
||||
@ -164,13 +188,7 @@ void cw::log::setFormatCb( handle_t h, logFormatCbFunc_t fmtFunc, void* fmtCbArg
|
||||
|
||||
|
||||
const char* cw::log::levelToLabel( unsigned level )
|
||||
{
|
||||
const char* label;
|
||||
if((label = idToLabel(logLevelLabelArray,level,kInvalid_LogLevel)) == nullptr)
|
||||
label = "<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 = "";
|
||||
|
6
cwLog.h
6
cwLog.h
@ -39,6 +39,12 @@ namespace cw
|
||||
void setLevel( handle_t h, unsigned level );
|
||||
unsigned level( handle_t h );
|
||||
|
||||
void* outputCbArg( handle_t h );
|
||||
logOutputCbFunc_t outputCb( handle_t h );
|
||||
|
||||
void* formatCbArg( handle_t h );
|
||||
logFormatCbFunc_t formatCb( handle_t h );
|
||||
|
||||
void setOutputCb( handle_t h, logOutputCbFunc_t outFunc, void* outCbArg );
|
||||
void setFormatCb( handle_t h, logFormatCbFunc_t fmtFunc, void* fmtCbArg );
|
||||
|
||||
|
@ -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 )
|
||||
|
@ -1,6 +1,7 @@
|
||||
#include "cwCommon.h"
|
||||
#include "cwLog.h"
|
||||
#include "cwCommonImpl.h"
|
||||
#include "cwTest.h"
|
||||
#include "cwMem.h"
|
||||
|
||||
#include "cwThread.h"
|
||||
|
@ -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"
|
||||
|
4
cwMidi.h
4
cwMidi.h
@ -161,6 +161,10 @@ namespace cw
|
||||
// of this range will be returned as kInvalidMidiPitch.
|
||||
uint8_t sciPitchToMidi( const char* sciPitchStr );
|
||||
|
||||
|
||||
#define midi_to_hz( midi_pitch ) ((13.75 * std::pow(2,(-9.0/12.0))) * std::pow(2.0,(midi_pitch / 12.0)))
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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 );
|
||||
|
||||
|
@ -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
|
||||
|
139
cwMidiDevice.cpp
139
cwMidiDevice.cpp
@ -1,6 +1,7 @@
|
||||
#include "cwCommon.h"
|
||||
#include "cwLog.h"
|
||||
#include "cwCommonImpl.h"
|
||||
#include "cwTest.h"
|
||||
#include "cwMem.h"
|
||||
#include "cwTime.h"
|
||||
#include "cwObject.h"
|
||||
@ -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;
|
||||
|
@ -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 );
|
||||
|
@ -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 )
|
||||
|
168
cwMidiFile.cpp
168
cwMidiFile.cpp
@ -1,6 +1,7 @@
|
||||
#include "cwCommon.h"
|
||||
#include "cwLog.h"
|
||||
#include "cwCommonImpl.h"
|
||||
#include "cwTest.h"
|
||||
#include "cwMem.h"
|
||||
#include "cwFile.h"
|
||||
#include "cwObject.h"
|
||||
@ -8,6 +9,7 @@
|
||||
#include "cwMidi.h"
|
||||
#include "cwMidiFile.h"
|
||||
#include "cwText.h"
|
||||
#include "cwCsv.h"
|
||||
|
||||
#ifdef cwBIG_ENDIAN
|
||||
#define mfSwap16(v) (v)
|
||||
@ -500,9 +502,8 @@ namespace cw
|
||||
trackMsg_t* nextTrkMsg[ p->trkN ]; // next msg in each track
|
||||
unsigned long long atick = 0;
|
||||
unsigned i;
|
||||
bool fl = true;
|
||||
|
||||
// iniitalize nextTrkTick[] and nextTrkMsg[] to the first msg in each track
|
||||
// iniitalize nextTrkMsg[] to the first msg in each track
|
||||
for(i=0; 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());
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 );
|
||||
|
||||
|
@ -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 );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 )
|
||||
|
@ -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"
|
||||
|
@ -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;
|
||||
|
@ -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
18
cwMtx.h
@ -732,7 +732,7 @@ namespace cw
|
||||
double v = ele( m, idxV );
|
||||
|
||||
// print the value
|
||||
printf("%*.*f ",colWidth,decPl,v);
|
||||
cwLogPrint("%*.*f ",colWidth,decPl,v);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -742,11 +742,11 @@ namespace cw
|
||||
{
|
||||
// print the dimension index for matrices with 3+ dim's
|
||||
if( i > 0 && j == 0 )
|
||||
printf("%i\n",idxV[i-1]);
|
||||
cwLogPrint("%i\n",idxV[i-1]);
|
||||
|
||||
// print the row index for matrices with 2+ dim's
|
||||
if( m.dimN>1 )
|
||||
printf("%i | ",j);
|
||||
cwLogPrint("%i | ",j);
|
||||
}
|
||||
|
||||
idxV[i] = j;
|
||||
@ -755,7 +755,7 @@ namespace cw
|
||||
|
||||
// prevent multiple newlines on last printed line
|
||||
if( m.dimN==1 || (m.dimN>=2 && i > m.dimN-2) )
|
||||
printf("\n");
|
||||
cwLogPrint("\n");
|
||||
}
|
||||
}
|
||||
|
||||
@ -774,10 +774,10 @@ namespace cw
|
||||
template< typename T >
|
||||
void report( const struct mtx_str<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 );
|
||||
|
||||
|
||||
}
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
|
@ -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 )
|
||||
|
@ -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 );
|
||||
|
||||
|
171
cwObject.cpp
171
cwObject.cpp
@ -5,6 +5,7 @@
|
||||
#include "cwCommonImpl.h"
|
||||
#include "cwMem.h"
|
||||
#include "cwFile.h"
|
||||
#include "cwTest.h"
|
||||
#include "cwLex.h"
|
||||
#include "cwText.h"
|
||||
#include "cwNumericConvert.h"
|
||||
@ -152,37 +153,37 @@ namespace cw
|
||||
void _objTypePrintIndent( const char* text, unsigned indent, const char* indentStr=" " )
|
||||
{
|
||||
for(unsigned i=0; 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;
|
||||
}
|
||||
|
||||
|
125
cwObject.h
125
cwObject.h
@ -31,7 +31,10 @@ namespace cw
|
||||
kRootTId = 0x00100000,
|
||||
|
||||
kHexFl = 0x10000000,
|
||||
kIdentFl = 0x20000000
|
||||
kIdentFl = 0x20000000,
|
||||
|
||||
kOptFl = 0x40000000,
|
||||
kReqFl = 0x00000000,
|
||||
|
||||
};
|
||||
|
||||
@ -135,12 +138,19 @@ namespace cw
|
||||
// Value containers are parents of leaf nodes. (A dictionary is not a value container because it's children are pairs with are not leaf nodes.)
|
||||
inline bool is_value_container() const { return type != nullptr && cwIsFlag(type->flags,kValueContainerFl); }
|
||||
|
||||
inline unsigned type_id() const { return type==nullptr ? (unsigned)kInvalidTId : type->id; }
|
||||
|
||||
// Containers have children and use the object.u.children pointer.
|
||||
inline bool is_container() const { return type != nullptr && cwIsFlag(type->flags,kContainerFl); }
|
||||
inline bool is_pair() const { return type != nullptr && type->id == kPairTId; }
|
||||
inline bool is_dict() const { return type != nullptr && type->id == kDictTId; }
|
||||
inline bool is_list() const { return type != nullptr && type->id == kListTId; }
|
||||
inline bool is_string() const { return type != nullptr && (type->id == kStringTId || type->id == kCStringTId); }
|
||||
inline bool is_unsigned_integer() const { return type->id==kCharTId || type->id==kUInt8TId || type->id==kUInt16TId || type->id==kUInt32TId || type->id==kUInt64TId; }
|
||||
inline bool is_signed_integer() const { return type->id==kInt8TId || type->id==kInt16TId || type->id==kInt32TId || type->id==kInt64TId; }
|
||||
inline bool is_floating_point() const { return type->id==kFloatTId || type->id==kDoubleTId; }
|
||||
inline bool is_integer() const { return is_unsigned_integer() || is_signed_integer(); }
|
||||
inline bool is_numeric() const { return is_integer() || is_floating_point(); }
|
||||
inline bool is_type( unsigned tid ) const { return type != nullptr && type->id == tid; }
|
||||
|
||||
rc_t value( void* dst, unsigned dstTypeId );
|
||||
@ -199,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 );
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
@ -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 )
|
||||
{
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -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 )
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -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;
|
||||
|
@ -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"
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user