diff --git a/Makefile.am b/Makefile.am index b234f34..85acce7 100644 --- a/Makefile.am +++ b/Makefile.am @@ -65,29 +65,29 @@ include src/libcw/Makefile.am #lib_LTLIBRARIES += libcw.la #include_HEADERS += $(libcwHDR) -src_proj_proj_SOURCES = $(libcwHDR) $(libcwSRC) src/proj/main.cpp +src_caw_caw_SOURCES = $(libcwHDR) $(libcwSRC) src/caw/main.cpp # 1) autoconfig manual recommends setting direct referenes to non-3rd party libraries rather than using -L and -l # 2) -ldl is required for dlopen(),dlclose() ... -# src_proj_proj_LDADD = libcw.la -lpthread -ldl +# src_caw_caw_LDADD = libcw.la -lpthread -ldl -src_proj_proj_LDADD = -lpthread -ldl +src_caw_caw_LDADD = -lpthread -ldl if cwFFTW - src_proj_proj_LDADD += -lfftw3 -lfftw3f + src_caw_caw_LDADD += -lfftw3 -lfftw3f endif if cwWEB - src_proj_proj_LDADD += -lfftw3 -lfftw3f + src_caw_caw_LDADD += -lfftw3 -lfftw3f endif if cwWEBSOCK - src_proj_proj_LDADD += -lwebsockets + src_caw_caw_LDADD += -lwebsockets endif -# src_proj_proj_CPPFLAGS = -I$(srcdir)/src/libcw $(AM_CPPFLAGS) -bin_PROGRAMS += src/proj/proj +# src_caw_caw_CPPFLAGS = -I$(srcdir)/src/libcw $(AM_CPPFLAGS) +bin_PROGRAMS += src/caw/caw # ${exec_prefix} is the install prefix given to 'configure' by the user. # ${srcdir} is the directory of this Makefile and is set by autoconf. diff --git a/configure.ac b/configure.ac index b91d8e4..9b09c8f 100644 --- a/configure.ac +++ b/configure.ac @@ -3,9 +3,9 @@ # this configure.ac or any of the Makefile.am files. # -AC_COPYRIGHT([Copyright (C) 2019-2022 Kevin Larke]) -AC_INIT([proj],[1.0],[proj@larke.org]) -AC_CONFIG_SRCDIR([src/proj/main.cpp]) +AC_COPYRIGHT([Copyright (C) 2019-2024 Kevin Larke]) +AC_INIT([caw],[1.0],[caw@larke.org]) +AC_CONFIG_SRCDIR([src/caw/main.cpp]) AC_CONFIG_AUX_DIR([build-aux]) # put aux files in build-aux AM_INIT_AUTOMAKE([1.9 -Wall foreign subdir-objects]) # subdir-objects needed for non-recursive make AC_CONFIG_HEADERS([config.h]) diff --git a/examples/proc_dict.cfg b/examples/proc_dict.cfg new file mode 100644 index 0000000..7f49fcc --- /dev/null +++ b/examples/proc_dict.cfg @@ -0,0 +1,644 @@ +{ + balance: { + vars: { + in: { type:coeff, value:0.5, doc:"Input vaue" }, + out: { type:coeff, doc:"Ouput value. Same as input value."}, + inv_out: { type:coeff, doc:"1.0 minus output value."} + } + } + + audio_in: { + vars: { + dev_label: { type:string, doc:"Audio device label." }, + out: { type:audio, doc:"Audio output" }, + } + } + + audio_out: { + vars: { + dev_label: { type:string, doc:"Audio device label." }, + in: { type:audio, flags:["src"], doc:"Audio input." } + } + } + + audio_file_in: { + vars: { + fname: { type:string, doc:"Audio file name." }, + out:{ type:audio, doc:"Audio file output" }, + on_off:{ type:bool, value:false, doc:"1=on 0=off" },, + seekSecs:{ type:ftime, value:0.0, doc:"Seek to the specified seconds offset." } + eofFl:{ type:bool, value: true, doc:"Set the system 'halt' flag when the audio is completely read."}, + } + } + + audio_file_out: { + vars: { + fname: { type:string, doc:"Audio file name." }, + bits: { type:uint, value:32u, doc:"Audio file word width. (8,16,24,32,0=float32)."}, + in: { type:audio, flags:["src"], doc:"Audio file input." } + } + } + + audio_gain: { + vars: { + in: { type:audio, flags:["src"], doc:"Audio input." }, + gain: { type:coeff, value:1.0, doc:"Gain coefficient." } + out: { type:audio, doc:"Audio output." }, + } + } + + audio_split: { + vars: { + in: { type:audio, flags:["src"], doc:"Audio input." }, + select: { type:int, doc:"Give a list of integers where each integer selects an output channel for the associated input channel." } + igain: { type:coeff, value:1.0, doc:"Audio gain for each input channel." } + ogain: { type:coeff, value:1.0, doc:"Audio gain for each output channel." } + out: { type:audio, doc:"Audio output." }, + } + + presets: + { + mute_off: { gain:1 }, + mute_on: { gain:0 }, + } + } + + audio_duplicate: { + vars: { + in: { type:audio, flags:["src"], doc:"Audio input."}, + duplicate: { type: uint, doc:"Count of times to repeat this channel." }, + gain: { type: coeff, value:1.0, doc:"Audio gain." }, + out: { type:audio, doc:"Audio output containing repeat * input channel count channels."} + } + } + + audio_merge: { + vars: { + in0: { type:audio, flags:["src"], doc:"First audio input." }, + in1: { type:audio, flags:["src"], doc:"Second audio input." }, + in2: { type:audio, flags:["src","src_opt"], doc:"Third audio input." }, + in3: { type:audio, flags:["src","src_opt"], doc:"Fourth audio input." }, + in4: { type:audio, flags:["src","src_opt"], doc:"Fifth audio input." }, + in5: { type:audio, flags:["src","src_opt"], doc:"Sixth audio input." }, + in6: { type:audio, flags:["src","src_opt"], doc:"Seventh audio input." }, + in7: { type:audio, flags:["src","src_opt"], doc:"Eigth audio input." }, + gain: { type:coeff, value:1.0, doc:"Audio gain for each selected (output) channel." } + out: { type:audio, doc:"Audio output. Channel count is the sum of the input channel count." }, + } + } + + audio_mix: { + vars: { + in0: { type:audio, flags:["src"], doc:"First audio input." }, + in1: { type:audio, flags:["src"], doc:"Second audio input." }, + gain0: { type:coeff, value:0.5, doc:"Audio gain for input 0." }, + gain1: { type:coeff, value:0.5, doc:"Audio gain for input 1." }, + out: { type:audio, doc:"Audio output. Channel count is max of the input signal channels." }, + } + } + + audio_delay: { + vars: { + in: { type:audio, flags:["src"], doc:"Audio input." }, + maxDelayMs: { type:ftime, value:1000.0 doc:"Maximum possible delay in milliseconds." }, + delayMs: { type:ftime, doc:"Delay in milliseconds." }, + out: { type:audio, doc:"Audio output." }, + } + } + + + sine_tone: { + vars: { + srate: { type:srate, value:0, doc:"Sine tone sample rate. 0=Use default system sample rate"} + chCnt: { type:uint, value:2, doc:"Output signal channel count."}, + hz: { type:coeff, value:440.0, doc:"Frequency in Hertz."}, + phase: { type:coeff, value:0.0, doc:"Offset phase in radians."}, + dc: { type:coeff, value:0.0, doc:"DC offset applied after gain."}, + gain: { type:coeff, value:0.8, doc:"Signal frequency."}, + out: { type:audio, doc:"Audio output" }, + } + + presets: { + a220 : { hz:220 }, + a440 : { hz:440 }, + a880 : { hz:880 }, + } + } + + pv_analysis: { + vars: { + in: { type:audio, flags:["src"], doc:"Audio input." }, + maxWndSmpN: { type:uint, value: 512, doc:"Maximum window sample count." }, + wndSmpN: { type:uint, value: 512, doc:"Window sample count." }, + hopSmpN: { type:uint, value: 128, doc:"Hop sample count." }, + hzFl: { type:bool, value: false, doc:"Calculate frequency via the method of phase changeof each bin." }, + out: { type:spectrum, doc:"Spectrum output." } + } + + presets: { + + dry: { + wndSmpN: 512, + hopSmpN: 128 + } + + kc: { + wndSmpN: 512, + hopSmpN: 128 + } + + a: { + wndSmpN: 512, + hopSmpN: 128 + } + + b: { + wndSmpN: 512, + hopSmpN: 128 + } + + c: { + wndSmpN: 512, + hopSmpN: 128 + } + + d: { + wndSmpN: 512, + hopSmpN: 128 + } + + f_1: { + wndSmpN: 512, + hopSmpN: 128 + } + + f_2: { + wndSmpN: 512, + hopSmpN: 128 + } + + f_3: { + wndSmpN: 512, + hopSmpN: 128 + } + + f_4: { + wndSmpN: 512, + hopSmpN: 128 + } + + g: { + wndSmpN: 512, + hopSmpN: 128 + } + + g_a: { + wndSmpN: 512, + hopSmpN: 128 + } + + g_1_a: { + wndSmpN: 512, + hopSmpN: 128 + } + + g_1_d: { + wndSmpN: 512, + hopSmpN: 128 + } + } + } + + pv_synthesis: { + vars: { + in: { type:spectrum, flags:["src"], doc:"Spectrum input." }, + out: { type:audio, doc:"Audio output." } + } + } + + spec_dist: { + vars: { + in: { type:spectrum, flags:["src"], doc:"Spectrum input." }, + + bypass: { type:bool, value: false, doc:"Copy input to output without transform."}, + ceiling: { type:coeff, value: 30.0, doc:"Ceiling parameter."}, + expo: { type:coeff, value: 2.0, doc:"Exponent parameter."}, + thresh: { type:coeff, value: 54.0, doc:"Threshold parameter."}, + upr: { type:coeff, value: -0.7, doc:"Upper slope parameter."}, + lwr: { type:coeff, value: 2.0, doc:"Lower slope parameter."}, + mix: { type:coeff, value: 0.0, doc:"Basic/Bump Mix parameter."}, + + out: { type:spectrum, doc:"Spectrum output." }, + + } + + presets: { + + dry: { + + } + + kc: { + ceiling: 20.0, + expo: 2.0, + thresh: 65.0, + upr: 0.0, + lwr: 2.0, + mix: 0.0 + } + + a: { + ceiling: 20.0 + expo: 2.0 + thresh: 60.0 + upr: [ -1.1, -0.99], + lwr: 2.0 + mix: 0.0 + } + + b: { + ceiling: 20.0 + expo: 2.0 + thresh: [ 77.0, 74.0 ], + upr: -0.5 + lwr: [ 3.0, 2.0 ], + mix: 0.0 + } + + c: { + ceiling: 20.0 + expo: 2.0 + thresh: 80.0 + upr: -0.5 + lwr: 5.0 + mix: 0.0 + } + + d: { + ceiling: 20.0 + expo: 2.0 + thresh: 70.0 + upr: [ -3.9, 04.5] + lwr: 4.0 + mix: 0.0 + } + + f_1: { + ceiling: 20.0 + expo: 2.0 + thresh: 50.0 + upr: -3.0 + lwr: 1.0 + mix: 0.0 + } + + f_2: { + ceiling: 20.0 + expo: 2.0 + thresh: 60.0 + upr: -3.0 + lwr: 1.0 + mix: 0.0 + } + + f_3: { + ceiling: 20.0 + expo: 2.0 + thresh: 55.0 + upr: -3.0 + lwr: 1.0 + mix: 0.0 + } + + f_4: { + ceiling: 20.0 + expo: 2.0 + thresh: 55.0 + upr: -5.0 + lwr: 1.0 + mix: 0.0 + } + + g: { + ceiling: 40.0 + expo: 8.0 + thresh: [60.0 64.0] + upr: -0.7 + lwr: 8.0 + mix: 1.0 + } + + g_a: { + ceiling: 40.0 + expo: 2.0 + thresh: [50.0 54.0] + upr: -0.7 + lwr: 2.0 + mix: 1.0 + } + + g_1_a: { + ceiling: 20.0 + expo: 2.0 + thresh: [50.0 54.0] + upr: -0.7 + lwr: 8.0 + mix: 1.0 + } + + g_1_d: { + ceiling: [60.0 64.0] + expo: [ 7.0 5.0] + thresh: [40.0 34.0] + upr: [-0.4 -0.3] + lwr: [ 7.0 5.0] + mix: 1.0 + } + } + } + + + compressor: { + vars: { + in: { type:audio, flags:["src"] true, doc:"Audio input." }, + bypass: { type:bool, value: false, doc:"Bypass the compressor."}, + igain: { type:coeff, value: 1.0, doc:"Input gain."}, + thresh: { type:coeff, value: 90.0, doc:"Attack threshold in dB."}, + ratio: { type:coeff, value: 2.0, doc:"Compression ratio."}, + atk_ms: { type:coeff, value: 20.0, doc:"Attack time in milliseconds."}, + rls_ms: { type:coeff, value: 20.0, doc:"Release time in milliseconds."}, + wnd_ms: { type:coeff, value: 200.0, doc:"RMS calc. window length in milliseconds."}, + maxWnd_ms: { type:coeff, value: 1000.0, doc:"Maximim (allocated) window length in milliseconds."}, + ogain: { type:coeff, value: 1.0, doc:"Output gain."}, + out: { type:audio, doc:"Audio output." }, + } + + presets: { + dflt: { + igain: 3.0 + thresh: 60.0 + ratio: 5.0 + atk_ms: 5.0 + rls_ms: 20.0 + wnd_ms:100.0 + ogain: 1.0 + } + + kc: { + bypass: false + igain: 3.0 + thresh: 80.0 + ratio: 2.0 + atk_ms: 20.0 + rls_ms: 1000.0 + wnd_ms: 200.0 + ogain: 1.0 + } + + input: { + bypass: false + igain: 2.0 + thresh: 30.0 + ratio: 12.0 + atk_ms: 5.0 + rls_ms: 20.0 + wnd_ms: 20.0 + ogain: 0.5 + } + + dry: { + + } + + a: { + igain: 6.0 + ogain: 1.0 + } + + b: { + igain: 10.0 + ogain: 1.0 + } + + c: { + igain: 11.0 + ogain: 1.0 + } + + d: { + igain: 9.0 + ogain: 1.0 + } + + f_1: { + igain: 6.0 + ogain: 1.0 + } + + f_2: { + igain: 6.0 + ogain: 1.0 + } + + f_3: { + igain: 6.0 + ogain: 1.0 + } + + f_4: { + igain: 6.0 + ogain: 1.0 + } + + g: { + igain: 10.0 + ogain: 0.75 + } + + g_a: { + igain: 10.0 + ogain: 0.75 + } + + g_1_a: { + igain: 10.0 + ogain: 0.75 + } + + g_1_d: { + igain: 10.0 + ogain: 0.75 + } + + + } + } + + limiter: { + vars: { + in: { type:audio, flags:["src"] true, doc:"Audio input." }, + bypass: { type:bool, value: false, doc:"Bypass the limiter."}, + igain: { type:coeff, value: 1.0, doc:"Input gain."}, + thresh: { type:coeff, value: 0.0, doc:"Linear (0.0-1.0) threshold."}, + ogain: { type:coeff, value: 1.0, doc:"Output gain."}, + out: { type:audio, doc:"Audio output." }, + } + + presets: { + dflt: { + bypass: false, + igain: 1.0 + thresh: 0.9, + ogain: 1.0 + } + + } + } + + dc_filter: { + vars: { + in: { type:audio, flags:["src"], doc:"Audio input." }, + bypass: { type:bool, value: false, doc:"Bypass the DC filter."}, + gain: { type:coeff, value: 1.0, doc:"Output gain."}, + out: { type:audio, doc:"Audio output." }, + } + + presets: { + dflt: { + bypass: false, + gain: 1.0 + } + + } + } + + audio_meter: { + vars: { + in: { type:audio, flags:["src"], doc:"Audio input." }, + dbFl: { type:bool, value: true, doc:"Output in Decibels." }, + wndMs: { type:ftime, value: 100.0, doc:"RMS window length." }, + peakDb: { type:coeff, value: -10.0, doc:"Peak threshold." }, + out: { type:coeff, value: 0.0, doc:"Meter output." }, + peakFl: { type:bool, value: false, doc:"Peak output." } + clipFl: { type:bool, value: false, doc:"Clip indicator output."} + } + } + + subnet: { + vars: { + } + } + + poly: { + vars: { + count: { type:uint, doc:"Count of network duplicates." }, + order: { type:string, value:"net", doc:"Execution order 'net'=net first 'proc'=proc first" } + } + } + + sample_hold: { + vars: { + in: { type:audio, flags:["src"], doc:"Audio input source." }, + period_ms: { type:ftime, value:50, doc:"Sample period in milliseconds." }, + out: { type:sample, value:0.0, doc:"First value in the sample period." }, + mean: { type:sample, value:0.0, doc:"Mean value of samples in period." }, + } + } + + + number: { + vars: { + value: { type:numeric, value:0.0, doc:"Input and output value."}, + store: { type:numeric, value:0.0, doc:"Store but don't emit until the next exec."} + } + } + + timer: { + vars: { + srate: { type:srate, value:0, flags["src"], doc:"Sample rate to use as the time base. 0=Use default system sample rate." }, + period_ms: { type:ftime, value:100, doc:"Timer period in milliseconds." }, + out: { type:bool, value:false, doc:"Output pulse." }, + } + } + + counter: { + vars: { + trigger: { type:bool, flags["src"], doc:"Counter increments with each toggle of trigger." }, + reset: { type:bool, value:false, doc:"Reset the counter to the initial value." }, + init: { type:numeric, value:0.0, doc:"Counter initial value." }, + min: { type:numeric, value:0.0, doc:"Minimum output value." }, + max: { type:numeric, value:10.0, doc:"Maximum output value." }, + inc: { type:numeric, value:1.0, doc:"Incrment value." }, + repeat_fl: { type:bool, value:true, doc:"Repeat on reaching the limits." }, + mode: { type:string, value:"modulo", doc:"limit mode: 'modulo'=wrap, 'reverse'=count in opposite direction, 'clip'=repeat limit value."}, + out_type: { type:string, value:double, flags["init"], doc:"The type of the output value." }, + out: { type:runtime, value:0.0, doc:"Counter output value."}, + } + } + + // All elements of the list must belong to the same of three possible types: + // string,cfg,numeric (uint,int,float,double) + list: { + vars: { + in: { type:uint, flags:["src"], doc:"List selection index." }, + list: { type:cfg, doc:"List as a 'cfg' object." }, + out: { type:runtime, doc:"List output value." }, + value:{ type:runtime, flags["mult"], doc:"List 'mult' output per list value." }, + } + } + + add: { + vars: { + in: { type:numeric, flags:["src","mult"], doc:"Operands" }, + otype: { type:string, value:double, flags:["init"], doc:"The type of the output value." }, + out: { type:runtime, flags:["no_src"], doc:"Result" }, + } + } + + preset: { + vars: { + in: { type:string, flags:["src"], doc:"Preset to select." }, + } + } + + + xfade_ctl: { + + poly_limit_cnt: 1, + + // Notes: + // 1. It would be better to setup the source net-proc as a 'in' variable with type 'net'. + // 2. The only purpose for the 'srateSrc' is to get the sample rate of the system. + + vars: { + net: { type:string, doc:"Proc name of the poly network."}, + netSfxId: { type:uint, value: 0, doc:"Label sfx id of the source poly instance."}, + srateSrc: { type:audio, flags:["src"], doc:"Audio source to derive the sample rate."}, + durMs: { type:uint, value:1000, doc:"Cross-fade duration in milliseconds" }, + trigger: { type:all, doc:"Start cross-fade." }, + preset: { type:string, doc:"Preset to apply to the poly network." }, + + gain: { type:coeff, flags:["mult"], value:0, doc:"Cross-fade gain output." } + } + }, + + poly_merge: { + + vars: { + in: { type:audio, flags:["src", "mult"], doc:"Audio input channel." }, + gain: { type:coeff, value: 0, flags:["src", "mult"], doc:"Input channel gain." }, + out_gain: { type:coeff, value: 1, doc:"Output gain" }, + out: { type:audio, doc:"Audio output." }, + } + }, + + print: { + vars: { + in: { type:all, flags:["mult"], doc: "Value to print." }, + eol_fl: { type:all, doc: "Trigger an end-of-line." }, + text: { type:cfg, doc: "List of labels." }, + } + } + + +} diff --git a/examples/subnet_dict.cfg b/examples/subnet_dict.cfg new file mode 100644 index 0000000..c0b236d --- /dev/null +++ b/examples/subnet_dict.cfg @@ -0,0 +1,34 @@ +{ + mod_osc: { + + vars: { + hz: { proxy:hz_lfo.dc, doc:"Audio frequency" }, + hz_mod_hz: { proxy:hz_lfo.hz, doc:"Frequency modulator hz" }, + hz_mod_depth: { proxy:hz_lfo.gain, doc:"Frequency modulator depth" }, + amp_mod_hz: { proxy:amp_lfo.hz, doc:"Amplitude modulator hz" }, + amp_mod_depth: { proxy:amp_lfo.gain, doc:"Amplutide modulator depth."}, + mo_out: { proxy:ogain.out flags:[out] doc:"Oscillator output."}, + }, + + network: { + procs: { + hz_lfo: { class: sine_tone, args: { chCnt:1 }} + hz_sh: { class: sample_hold, in:{ in:hz_lfo.out }} + + amp_lfo: { class: sine_tone, args: { chCnt:1 }} + amp_sh: { class: sample_hold, in:{ in:amp_lfo.out }} + + osc: { class: sine_tone, in:{ hz: hz_sh.out }} + ogain: { class: audio_gain, in:{ in:osc.out, gain:amp_sh.out}} + } + + presets: { + net_a: { hz_lfo: { dc:220, gain:55 }, amp_lfo: { gain:0.8 } }, + net_b: { hz_lfo: { dc:110, gain:25 }, amp_lfo: { gain:0.7 } }, + } + } + } + + + +} \ No newline at end of file diff --git a/src/proj/main.cfg b/src/caw/cfg/io.cfg similarity index 94% rename from src/proj/main.cfg rename to src/caw/cfg/io.cfg index 58b5014..4067f74 100644 --- a/src/proj/main.cfg +++ b/src/caw/cfg/io.cfg @@ -1,13 +1,12 @@ -{ - param: 5, - - libcw: { + { io: { callbackMutexTimeOutMs: 100, } ui: { + enableFl: true, + asyncFl: false, physRootDir: "~/src/cw_io_template/src/proj/html", dfltPageFn: "index.html", port: 5687, @@ -17,10 +16,10 @@ websockTimeOutMs: 50, // max time out while blocking for a websock event idleMsgPeriodMs: 50, // period without messages before an idle message is generated uiCfgFn: "ui.cfg", // default UI resource description - asyncFl: false }, serial: { + enableFl: false, pollPeriodMs: 50, recvBufByteN: 512, @@ -39,6 +38,7 @@ }, midi: { + enableFl: false, parseBufByteCnt: 1024, appNameStr: "cwtest", fileDevName: "file_dev", @@ -50,13 +50,15 @@ { "label":"file_0", //"file": "/home/kevin/src/cwtest/src/cwtest/cfg/gutim_full/data1/beck1/record_4/midi.mid", - "enable_fl": false }, + "enableFl": false }, ] asyncFl: true, }, audio: { + enableFl: false, + meterMs: 50, // audio meter filter length and meter callback period threadTimeOutMs: 50, // audio thread cond var time out @@ -92,6 +94,7 @@ }, socket: { + enableFl: false, asyncFl: false, maxSocketCnt: 10, recvBufByteCnt: 4096, @@ -100,5 +103,3 @@ } } - -} \ No newline at end of file diff --git a/src/proj/html/css/preset_sel.css b/src/caw/html/css/preset_sel.css similarity index 100% rename from src/proj/html/css/preset_sel.css rename to src/caw/html/css/preset_sel.css diff --git a/src/proj/html/css/ui.css b/src/caw/html/css/ui.css similarity index 100% rename from src/proj/html/css/ui.css rename to src/caw/html/css/ui.css diff --git a/src/proj/html/index.html b/src/caw/html/index.html similarity index 100% rename from src/proj/html/index.html rename to src/caw/html/index.html diff --git a/src/proj/html/js/ui.js b/src/caw/html/js/ui.js similarity index 100% rename from src/proj/html/js/ui.js rename to src/caw/html/js/ui.js diff --git a/src/proj/html/ui.cfg b/src/caw/html/ui.cfg similarity index 100% rename from src/proj/html/ui.cfg rename to src/caw/html/ui.cfg diff --git a/src/caw/main.cfg b/src/caw/main.cfg new file mode 100644 index 0000000..2003e1f --- /dev/null +++ b/src/caw/main.cfg @@ -0,0 +1,28 @@ +{ + param: 5, + base_dir: "~/src/caw/examples", + proc_dict: "~/src/caw/examples/proc_dict.cfg", + subnet_dict: "~/src/caw/examples/subnet_dict.cfg", + mode: non_real_time, + + + programs: { + + example_01: { + + durLimitSecs:5.0, + + network: { + + procs: { + osc: { class: sine_tone }, + af: { class: audio_file_out, in: { in:osc.out } args:{ fname:"$/out.wav"} } + } + } + } + + + + + } +} \ No newline at end of file diff --git a/src/caw/main.cpp b/src/caw/main.cpp new file mode 100644 index 0000000..e028d59 --- /dev/null +++ b/src/caw/main.cpp @@ -0,0 +1,432 @@ +#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 "cwTime.h" +#include "cwMidiDecls.h" +#include "cwFlowDecl.h" +#include "cwFlow.h" + +#include "cwIo.h" + + +using namespace cw; + +rc_t test( const object_t* cfg, int argc, char* argv[] ); + +typedef struct app_str +{ + object_t* cfg; // complete cfg. + + bool real_time_fl; // Execute in non-real-time mode + const char* pgm_label; // + const object_t* pgm_cfg; // + const char* base_dir; // + char* proj_dir; // Project directory / + object_t* io_cfg; // IO lib cfg. + object_t* proc_class_dict_cfg; // + object_t* subnet_dict_cfg; + + unsigned value; + io::handle_t ioH; + +} app_t; + +enum +{ + kPanelDivId, + kQuitBtnId, + kIoReportBtnId, + kNetPrintBtnId, + kReportBtnId, + kLatencyBtnId, + kValueNumbId +}; + +ui::appIdMap_t appIdMapA[] = { + + { ui::kRootAppId, kPanelDivId, "panelDivId" }, + { kPanelDivId, kQuitBtnId, "quitBtnId" }, + { kPanelDivId, kIoReportBtnId, "ioReportBtnId" }, + { kPanelDivId, kNetPrintBtnId, "netPrintBtnId" }, + { kPanelDivId, kReportBtnId, "reportBtnId" }, + { kPanelDivId, kLatencyBtnId, "latencyBtnId" }, + { kPanelDivId, kValueNumbId, "valueNumbId" } +}; + +const unsigned appIdMapN = sizeof(appIdMapA)/sizeof(appIdMapA[0]); + +void print( void* arg, const char* text ) +{ + printf("%s\n",text); +} + + +rc_t _ui_value_callback(app_t* app, const io::ui_msg_t& m ) +{ + switch( m.appId ) + { + case kQuitBtnId: + io::stop( app->ioH ); + break; + + case kIoReportBtnId: + io::report(app->ioH); + break; + + case kNetPrintBtnId: + break; + + case kReportBtnId: + break; + + case kLatencyBtnId: + latency_measure_report(app->ioH); + latency_measure_setup(app->ioH); + break; + + case kValueNumbId: + app->value = m.value->u.u; + cwLogInfo("Setting value:%i",app->value); + break; + + } + return kOkRC; +} + +rc_t _ui_echo_callback(app_t* app, const io::ui_msg_t& m ) +{ + switch( m.appId ) + { + case kValueNumbId: + { + uiSendValue( app->ioH, io::uiFindElementUuId( app->ioH, kValueNumbId ), app->value ); + } + break; + + } + return kOkRC; +} + +rc_t _ui_callback( app_t* app, const io::ui_msg_t& m ) +{ + rc_t rc = kOkRC; + + switch( m.opId ) + { + case ui::kConnectOpId: + cwLogInfo("UI Connected: wsSessId:%i.",m.wsSessId); + break; + + case ui::kDisconnectOpId: + cwLogInfo("UI Disconnected: wsSessId:%i.",m.wsSessId); + break; + + case ui::kInitOpId: + cwLogInfo("UI Init."); + break; + + case ui::kValueOpId: + _ui_value_callback( app, m ); + break; + + case ui::kCorruptOpId: + cwLogInfo("UI Corrupt."); + break; + + case ui::kClickOpId: + cwLogInfo("UI Click."); + break; + + case ui::kSelectOpId: + cwLogInfo("UI Select."); + break; + + case ui::kEchoOpId: + _ui_echo_callback( app, m ); + break; + + case ui::kIdleOpId: + break; + + case ui::kInvalidOpId: + // fall through + default: + assert(0); + break; + + } + + return rc; +} + + +rc_t _io_callback( void* arg, const io::msg_t* m ) +{ + app_t* app = (app_t*)arg; + + switch( m->tid ) + { + case io::kThreadTId: + break; + + case io::kTimerTId: + break; + + case io::kSerialTId: + break; + + case io::kMidiTId: + break; + + case io::kAudioTId: + break; + + case io::kAudioMeterTId: + break; + + case io::kSockTId: + break; + + case io::kWebSockTId: + break; + + case io::kUiTId: + _ui_callback(app,m->u.ui); + break; + + case io::kExecTId: + break; + + default: + assert(0); + } + + return kOkRC; +} + +rc_t _parse_cfg( app_t& app, int argc, char* argv[] ) +{ + rc_t rc = kOkRC; + const char* io_cfg_fn = nullptr; + const char* mode_label = nullptr; + const object_t* pgmL = nullptr; + const char* proc_cfg_fname = nullptr; + const char* subnet_cfg_fname = nullptr; + if( argc < 3 ) + { + cwLogPrint("Usage: caw "); + rc = kInvalidArgRC; + goto errLabel; + } + else + { + // parse the cfg. file + if((rc = objectFromFile(argv[1],app.cfg)) != kOkRC ) + { + rc = cwLogError(rc,"Parsing failed on the cfg. file '%s'.",argv[1]); + goto errLabel; + } + + // parse the cfg parameters + if((rc = app.cfg->readv("param", 0, app.value, + "io_cfg", kOptFl, io_cfg_fn, + "base_dir", 0, app.base_dir, + "proc_dict",0,proc_cfg_fname, + "subnet_dict",0,subnet_cfg_fname, + "mode", 0, mode_label, + "programs", kDictTId, pgmL)) != kOkRC ) + { + rc = cwLogError(rc,"'caw' system parameter processing failed."); + goto errLabel; + } + + // set the real-time mode flag + app.real_time_fl = !textIsEqual(mode_label,"non_real_time"); + + // if we are in real-time mode then the io_cfg file must be given + if( app.real_time_fl && io_cfg_fn == nullptr ) + { + rc = cwLogError(kSyntaxErrorRC,"To operation in real-time mode an 'io_cfg' file must be provided."); + goto errLabel; + } + + // parse the 'io_cfg' file + if( app.real_time_fl ) + if((rc = objectFromFile(io_cfg_fn,app.io_cfg)) != kOkRC ) + { + rc = cwLogError(rc,"'caw' IO cfg. file parsing failed on '%s'.",cwStringNullGuard(io_cfg_fn)); + goto errLabel; + } + + + // parse the proc dict. file + if((rc = objectFromFile(proc_cfg_fname,app.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 subnet dict file + if((rc = objectFromFile(subnet_cfg_fname,app.subnet_dict_cfg)) != kOkRC ) + { + rc = cwLogError(rc,"The flow subnet dictionary could not be read from '%s'.",cwStringNullGuard(subnet_cfg_fname)); + goto errLabel; + } + + // get the pgm label + if( textLength(argv[2]) == 0 ) + { + rc = cwLogError(kSyntaxErrorRC,"No 'caw' program label was given."); + goto errLabel; + } + + app.pgm_label = argv[2]; + + // find the parameters for the requested program + for(unsigned i=0; ichild_count(); i++) + { + const object_t* pgm = pgmL->child_ele(i); + if( textIsEqual( pgm->pair_label(), app.pgm_label ) ) + { + if( pgm->pair_value() == nullptr || !pgm->pair_value()->is_dict() ) + { + rc = cwLogError(kSyntaxErrorRC,"The parameters for the program '%s' is not a dictionary.",cwStringNullGuard(app.pgm_label)); + goto errLabel; + } + + app.pgm_cfg = pgm->pair_value(); + break; + } + } + + if( app.pgm_cfg == nullptr ) + rc =cwLogError(kEleNotFoundRC,"The program '%s' was not found in the cfg. program list.",cwStringNullGuard(app.pgm_label)); + else + { + if((app.proj_dir = filesys::makeFn(app.base_dir,nullptr,nullptr,app.pgm_label,nullptr)) == nullptr ) + { + rc = cwLogError(kOpFailRC,"An error occurred while forming the the project directory name for '%s' / '%s'.",cwStringNullGuard(app.base_dir),cwStringNullGuard(app.pgm_label)); + goto errLabel; + } + + if( !filesys::isDir(app.proj_dir)) + { + if((rc = filesys::makeDir(app.proj_dir)) != kOkRC ) + { + rc = cwLogError(kOpFailRC,"Project directory create failed."); + goto errLabel; + } + } + } + + + } +errLabel: + if( rc != kOkRC ) + rc = cwLogError(rc,"App. cfg. parse failed."); + return rc; +} + +rc_t _main_gui( app_t& app ) +{ + rc_t rc = kOkRC; + + if((rc = create( app.ioH, app.io_cfg, _io_callback, &app, appIdMapA, appIdMapN ) ) != kOkRC ) + { + rc = cwLogError(rc,"IO create failed."); + goto errLabel; + } + + + // start the IO framework instance + if((rc = io::start(app.ioH)) != kOkRC ) + { + rc = cwLogError(rc,"Preset-select app start failed."); + goto errLabel; + } + + + // execute the io framework + while( !io::isShuttingDown(app.ioH)) + { + // This call will block on the websocket handle + // for up to io_cfg->ui.websockTimeOutMs milliseconds + io::exec(app.ioH,50); + + + } + + // stop the io framework + if((rc = io::stop(app.ioH)) != kOkRC ) + { + rc = cwLogError(rc,"IO API stop failed."); + goto errLabel; + } + +errLabel: + return rc; +} + + + +int main( int argc, char* argv[] ) +{ + rc_t rc = kOkRC; + app_t app = {}; + flow::handle_t flowH; + + cw::log::createGlobal(); + + cwLogInfo("caw: args:%i", argc); + + if((rc = _parse_cfg(app,argc,argv)) != kOkRC ) + goto errLabel; + + // create the flow object + if((rc = create( flowH, + app.proc_class_dict_cfg, + app.pgm_cfg, + app.subnet_dict_cfg, + app.proj_dir)) != kOkRC ) + { + //rc = cwLogError(rc,"Flow object create failed."); + goto errLabel; + } + + // run the network + if((rc = exec( flowH )) != kOkRC ) + rc = cwLogError(rc,"Execution failed."); + +errLabel: + // destroy the flow object + if((rc = destroy(flowH)) != kOkRC ) + { + rc = cwLogError(rc,"Close the flow object."); + goto errLabel; + } + + destroy(app.ioH); + + mem::release(app.proj_dir); + + if( app.proc_class_dict_cfg != nullptr ) + app.proc_class_dict_cfg->free(); + + if( app.subnet_dict_cfg != nullptr ) + app.subnet_dict_cfg->free(); + + if( app.io_cfg != nullptr ) + app.io_cfg->free(); + + if( app.cfg != nullptr ) + app.cfg->free(); + + cw::log::destroyGlobal(); + + return 0; +} diff --git a/src/proj/main.cpp b/src/proj/main.cpp deleted file mode 100644 index 7ec9bad..0000000 --- a/src/proj/main.cpp +++ /dev/null @@ -1,276 +0,0 @@ -#include "cwCommon.h" -#include "cwLog.h" -#include "cwCommonImpl.h" -#include "cwText.h" -#include "cwObject.h" - -#include "cwIo.h" - -using namespace cw; - -typedef struct app_str -{ - object_t* cfg; - unsigned value; - const object_t* io_cfg; - io::handle_t ioH; -} app_t; - -enum -{ - kPanelDivId, - kQuitBtnId, - kIoReportBtnId, - kNetPrintBtnId, - kReportBtnId, - kLatencyBtnId, - kValueNumbId -}; - -ui::appIdMap_t appIdMapA[] = { - - { ui::kRootAppId, kPanelDivId, "panelDivId" }, - { kPanelDivId, kQuitBtnId, "quitBtnId" }, - { kPanelDivId, kIoReportBtnId, "ioReportBtnId" }, - { kPanelDivId, kNetPrintBtnId, "netPrintBtnId" }, - { kPanelDivId, kReportBtnId, "reportBtnId" }, - { kPanelDivId, kLatencyBtnId, "latencyBtnId" }, - { kPanelDivId, kValueNumbId, "valueNumbId" } -}; - -const unsigned appIdMapN = sizeof(appIdMapA)/sizeof(appIdMapA[0]); - -void print( void* arg, const char* text ) -{ - printf("%s\n",text); -} - - -rc_t _ui_value_callback(app_t* app, const io::ui_msg_t& m ) -{ - switch( m.appId ) - { - case kQuitBtnId: - io::stop( app->ioH ); - break; - - case kIoReportBtnId: - io::report(app->ioH); - break; - - case kNetPrintBtnId: - break; - - case kReportBtnId: - break; - - case kLatencyBtnId: - latency_measure_report(app->ioH); - latency_measure_setup(app->ioH); - break; - - case kValueNumbId: - app->value = m.value->u.u; - cwLogInfo("Setting value:%i",app->value); - break; - - } - return kOkRC; -} - -rc_t _ui_echo_callback(app_t* app, const io::ui_msg_t& m ) -{ - switch( m.appId ) - { - case kValueNumbId: - { - uiSendValue( app->ioH, io::uiFindElementUuId( app->ioH, kValueNumbId ), app->value ); - } - break; - - } - return kOkRC; -} - -rc_t _ui_callback( app_t* app, const io::ui_msg_t& m ) -{ - rc_t rc = kOkRC; - - switch( m.opId ) - { - case ui::kConnectOpId: - cwLogInfo("UI Connected: wsSessId:%i.",m.wsSessId); - break; - - case ui::kDisconnectOpId: - cwLogInfo("UI Disconnected: wsSessId:%i.",m.wsSessId); - break; - - case ui::kInitOpId: - cwLogInfo("UI Init."); - break; - - case ui::kValueOpId: - _ui_value_callback( app, m ); - break; - - case ui::kCorruptOpId: - cwLogInfo("UI Corrupt."); - break; - - case ui::kClickOpId: - cwLogInfo("UI Click."); - break; - - case ui::kSelectOpId: - cwLogInfo("UI Select."); - break; - - case ui::kEchoOpId: - _ui_echo_callback( app, m ); - break; - - case ui::kIdleOpId: - break; - - case ui::kInvalidOpId: - // fall through - default: - assert(0); - break; - - } - - return rc; -} - - -rc_t _io_callback( void* arg, const io::msg_t* m ) -{ - app_t* app = (app_t*)arg; - - switch( m->tid ) - { - case io::kThreadTId: - break; - - case io::kTimerTId: - break; - - case io::kSerialTId: - break; - - case io::kMidiTId: - break; - - case io::kAudioTId: - break; - - case io::kAudioMeterTId: - break; - - case io::kSockTId: - break; - - case io::kWebSockTId: - break; - - case io::kUiTId: - _ui_callback(app,m->u.ui); - break; - - case io::kExecTId: - break; - - default: - assert(0); - } - - return kOkRC; -} - -rc_t _parse_cfg( app_t& app, int argc, char* argv[] ) -{ - rc_t rc = kOkRC; - - if( argc < 2 || textLength(argv[1])==0 ) - { - rc = cwLogError(kInvalidArgRC,"No cfg. file was given."); - goto errLabel; - } - else - { - if((rc = objectFromFile(argv[1],app.cfg)) != kOkRC ) - { - rc = cwLogError(rc,"The file '%s'.",argv[1]); - goto errLabel; - } - - if((rc = app.cfg->getv("param", app.value, - "libcw", app.io_cfg)) != kOkRC ) - { - rc = cwLogError(kSyntaxErrorRC,"The 'param' cfg. field was not found."); - goto errLabel; - } - - } -errLabel: - if( rc != kOkRC ) - rc = cwLogError(rc,"App. cfg. parse failed."); - return rc; -} - -int main( int argc, char* argv[] ) -{ - rc_t rc = kOkRC; - app_t app = {}; - cw::log::createGlobal(); - - cwLogInfo("Project template: args:%i", argc); - - if((rc = _parse_cfg(app,argc,argv)) != kOkRC ) - goto errLabel; - - if((rc = create( app.ioH, app.io_cfg, _io_callback, &app, appIdMapA, appIdMapN ) ) != kOkRC ) - { - rc = cwLogError(rc,"IO create failed."); - goto errLabel; - } - - - // start the IO framework instance - if((rc = io::start(app.ioH)) != kOkRC ) - { - rc = cwLogError(rc,"Preset-select app start failed."); - goto errLabel; - } - - //io::uiReport(app.ioH); - - - // execute the io framework - while( !io::isShuttingDown(app.ioH)) - { - // This call will block on the websocket handle - // for up to io_cfg->ui.websockTimeOutMs milliseconds - io::exec(app.ioH); - - - } - - // stop the io framework - if((rc = io::stop(app.ioH)) != kOkRC ) - { - rc = cwLogError(rc,"IO API stop failed."); - goto errLabel; - } - - -errLabel: - destroy(app.ioH); - if( app.cfg != nullptr ) - app.cfg->free(); - cw::log::destroyGlobal(); - - return 0; -}