//| Copyright: (C) 2020-2024 Kevin Larke //| License: GNU GPL version 3.0 or above. See the accompanying LICENSE file. #include "cwCommon.h" #include "cwLog.h" #include "cwCommonImpl.h" #include "cwTest.h" #include "cwMem.h" #include "cwText.h" #include "cwNumericConvert.h" #include "cwObject.h" #include "cwAudioFile.h" #include "cwVectOps.h" #include "cwMtx.h" #include "cwDspTypes.h" // coeff_t, sample_t, srate_t ... #include "cwTime.h" #include "cwMidiDecls.h" #include "cwFlowDecl.h" #include "cwFlow.h" #include "cwFlowTypes.h" #include "cwFlowNet.h" #include "cwFlowProc.h" namespace cw { namespace flow { typedef struct library_str { const char* label; class_members_t* members; } library_t; library_t g_library[] = { { "user_def_proc", &user_def_proc::members }, { "poly", &poly::members }, { "midi_in", &midi_in::members }, { "midi_out", &midi_out::members }, { "audio_in", &audio_in::members }, { "audio_out", &audio_out::members }, { "audio_file_in", &audio_file_in::members }, { "audio_file_out", &audio_file_out::members }, { "audio_gain", &audio_gain::members }, { "audio_split", &audio_split::members }, { "audio_duplicate", &audio_duplicate::members }, { "audio_merge", &audio_merge::members }, { "audio_mix", &audio_mix::members }, { "audio_silence", &audio_silence::members }, { "sine_tone", &sine_tone::members }, { "pv_analysis", &pv_analysis::members }, { "pv_synthesis", &pv_synthesis::members }, { "spec_dist", &spec_dist::members }, { "compressor", &compressor::members }, { "limiter", &limiter::members }, { "audio_delay", &audio_delay::members }, { "dc_filter", &dc_filter::members }, { "balance", &balance::members }, { "audio_meter", &audio_meter::members }, { "audio_marker", &audio_marker::members }, { "xfade_ctl", &xfade_ctl::members }, { "poly_voice_ctl", &poly_voice_ctl::members }, { "midi_voice", &midi_voice::members }, { "piano_voice", &piano_voice::members }, { "sample_hold", &sample_hold::members }, { "number", &number::members }, { "reg", ®::members }, { "timer", &timer::members }, { "counter", &counter::members }, { "list", &list::members }, { "add", &add::members }, { "preset", &preset::members }, { "print", &print::members }, { "halt", &halt::members }, { "midi_msg", &midi_msg::members }, { "midi_split", &midi_split::members }, { "midi_file", &midi_file::members }, { "midi_merge", &midi_merge::members }, { nullptr, nullptr } }; class_members_t* _find_library_record( const char* label ) { for(library_t* l = g_library; l->label != nullptr; ++l) if( textCompare(l->label,label) == 0) return l->members; return nullptr; } flow_t* _handleToPtr(handle_t h) { return handleToPtr(h); } rc_t _is_var_flag_set( const object_t* var_flags_obj, const char* flag_label, const char* classLabel, const char* varLabel, bool&is_set_flag_ref ) { rc_t rc = kOkRC; is_set_flag_ref = false; if( var_flags_obj != nullptr ) { for(unsigned k=0; kchild_count(); ++k) { const object_t* tag_obj = var_flags_obj->child_ele(k); const char* tag = nullptr; if( tag_obj != nullptr && tag_obj->is_string() && (rc=tag_obj->value(tag))==kOkRC && tag != nullptr ) { if( strcmp(tag,flag_label) == 0 ) is_set_flag_ref = true; } else { rc = cwLogError(kSyntaxErrorRC,"An invalid or non-string value was found in a flow class '%s' variable:'%s' 'flags' field.",classLabel,varLabel); } } } return rc; } rc_t _parse_udp_var_proxy_string( const char* proxyStr, var_desc_t* var_desc ) { rc_t rc = kOkRC; const char* period; // find the separating period if((period = firstMatchChar(proxyStr,'.')) == nullptr ) { rc = cwLogError(kSyntaxErrorRC,"The separating '.' could not be found in the proxy string '%s'.",cwStringNullGuard(proxyStr)); goto errLabel; } // validate the length of the proc inst label if( period-proxyStr == 0 ) { rc = cwLogError(kSyntaxErrorRC,"No proxy proc instance was found in the proxy string '%s'.",cwStringNullGuard(proxyStr)); goto errLabel; } // validate the length of the var label if( textLength(period+1) == 0 ) { rc = cwLogError(kSyntaxErrorRC,"No proxy var was found in the proxy string '%s'.",cwStringNullGuard(proxyStr)); goto errLabel; } var_desc->proxyProcLabel = mem::duplStr(proxyStr,period-proxyStr); var_desc->proxyVarLabel = mem::duplStr(period+1); errLabel: return rc; } rc_t _parse_class_var_attribute_flags(const object_t* var_flags_obj, unsigned& flags_ref) { rc_t rc = kOkRC; unsigned result_flags = 0; flags_ref = 0; if( !var_flags_obj->is_list() ) { rc = cwLogError(kSyntaxErrorRC,"The variable description 'flags' field must be a list."); goto errLabel; } for(unsigned i=0; ichild_count(); ++i) { const object_t* flag_obj = var_flags_obj->child_ele(i); const char* flag_label = nullptr; unsigned flag = 0; // validate the flag syntax if( flag_obj == nullptr || !flag_obj->is_string() || flag_obj->value(flag_label)!=kOkRC ) { rc = cwLogError(kSyntaxErrorRC,"Invalid variable description flag syntax on flag index %i.",i); goto errLabel; } // parse the flag if((flag = var_desc_attr_label_to_flag(flag_label)) == kInvalidVarDescFl ) { rc = cwLogError(kInvalidArgRC,"The variable description flag ('%s') at flag index %i is not valid.",cwStringNullGuard(flag_label),i); goto errLabel; } result_flags |= flag; } flags_ref = result_flags; errLabel: return rc; } rc_t _parse_class_var_cfg(flow_t* p, class_desc_t* class_desc, const object_t* var_desc_pair, var_desc_t*& var_desc_ref ) { rc_t rc = kOkRC; const object_t* var_flags_obj = nullptr; const char* var_value_type_str = nullptr; const char* var_label = nullptr; const char* proxy_string = nullptr; var_desc_t* vd = nullptr; var_desc_ref = nullptr; if(var_desc_pair==nullptr || !var_desc_pair->is_pair() || var_desc_pair->pair_label()==nullptr || (var_label = var_desc_pair->pair_label())==nullptr || var_desc_pair->pair_value()==nullptr || !var_desc_pair->pair_value()->is_dict() ) { rc = cwLogError(kSyntaxErrorRC,"An invalid variable description syntax was encountered."); goto errLabel; } // Allocate the var. desc record if((vd = var_desc_create( var_label, var_desc_pair->pair_value())) == nullptr ) { rc = cwLogError(kObjAllocFailRC,"Variable description allocation failed."); goto errLabel; } // get the variable description if((rc = vd->cfg->getv("doc", vd->docText)) != kOkRC ) { rc = cwLogError(rc,"Parsing failed on class:%s variable: '%s'.", class_desc->label, vd->label ); goto errLabel; } // get the variable description if((rc = vd->cfg->getv_opt("flags", var_flags_obj, "type", var_value_type_str, "value", vd->val_cfg, "proxy", proxy_string )) != kOkRC ) { rc = cwLogError(rc,"Parsing optional fields failed."); goto errLabel; } // convert the type string to a numeric type flag if( var_value_type_str != nullptr ) if( (vd->type = value_type_label_to_flag( var_value_type_str )) == kInvalidTId ) { rc = cwLogError(kSyntaxErrorRC,"Invalid variable description type flag: '%s' was encountered.", var_value_type_str ); goto errLabel; } // parse the proxy string into it's two parts: . if( proxy_string != nullptr ) { if((rc = _parse_udp_var_proxy_string( proxy_string, vd )) != kOkRC ) goto errLabel; } // parse the var desc attribute flags if( var_flags_obj != nullptr ) { vd->flags = 0; if((rc = _parse_class_var_attribute_flags(var_flags_obj, vd->flags)) != kOkRC ) goto errLabel; } var_desc_ref = vd; errLabel: if( rc != kOkRC ) { rc = cwLogError(rc,"A variable description create failed on class desc:'%s' var:'%s'.",cwStringNullGuard(class_desc->label),cwStringNullGuard(var_label)); var_desc_destroy(vd); } return rc; } rc_t _create_class_ui_desc( class_desc_t* desc ) { desc->ui = mem::allocZ(); desc->ui->label = desc->label; for(class_preset_t* p0 = desc->presetL; p0!=nullptr; p0=p0->link) desc->ui->presetN += 1; desc->ui->presetA = mem::allocZ(desc->ui->presetN); unsigned i=0; for(class_preset_t* p0 = desc->presetL; iui->presetN; ++i,p0=p0->link) { desc->ui->presetA[i].label = p0->label; desc->ui->presetA[i].preset_idx = i; } return kOkRC; } rc_t _parse_class_cfg(flow_t* p, const object_t* classCfg) { rc_t rc = kOkRC; if( !classCfg->is_dict() ) return cwLogError(kSyntaxErrorRC,"The class description dictionary does not have dictionary syntax."); p->classDescN = classCfg->child_count(); p->classDescA = mem::allocZ( p->classDescN ); // for each class description for(unsigned i=0; iclassDescN; ++i) { const object_t* class_obj = classCfg->child_ele(i); const object_t* varD = nullptr; const object_t* presetD = nullptr; class_desc_t* cd = p->classDescA + i; cd->cfg = class_obj->pair_value(); cd->label = class_obj->pair_label(); // get the variable description if((rc = cd->cfg->getv_opt("vars", varD, "presets", presetD, "poly_limit_cnt", cd->polyLimitN)) != kOkRC ) { rc = cwLogError(rc,"Parsing failed while parsing class desc:'%s'", cwStringNullGuard(cd->label) ); goto errLabel; } // parse the preset dictionary if( presetD != nullptr ) { if( !presetD->is_dict() ) { rc = cwLogError(rc,"The preset dictionary is not a dictionary on class desc:'%s'", cwStringNullGuard(cd->label) ); goto errLabel; } // for each preset in the class desc. for(unsigned j=0; jchild_count(); ++j) { const object_t* pair = presetD->child_ele(j); if( !pair->pair_value()->is_dict() ) { rc = cwLogError(kSyntaxErrorRC,"The preset '%s' in class desc '%s' is not a dictionary.", cwStringNullGuard(pair->pair_label()), cwStringNullGuard(cd->label)); goto errLabel; } class_preset_t* preset = mem::allocZ< class_preset_t >(); preset->label = pair->pair_label(); preset->cfg = pair->pair_value(); preset->link = cd->presetL; cd->presetL = preset; } } // create the class descripiton if((rc = _create_class_ui_desc(cd)) != kOkRC ) { cwLogError(rc,"Class desc UI record create failed on '%s'.",cwStringNullGuard(cd->label)); goto errLabel; } // parse the variable dictionary if( varD != nullptr ) { if( !varD->is_dict() ) { rc = cwLogError(rc,"The value dictionary is not a dictionary on class desc:'%s'", cwStringNullGuard(cd->label) ); goto errLabel; } // get the class member functions if((cd->members = _find_library_record(cd->label)) == nullptr ) { rc = cwLogError(kSyntaxErrorRC,"The '%s' class member function record could not be found.", cd->label ); goto errLabel; } // for each class value description for(unsigned j=0; jchild_count(); ++j) { const object_t* var_obj = varD->child_ele(j); var_desc_t* vd = nullptr; if((rc = _parse_class_var_cfg(p, cd, var_obj, vd )) != kOkRC ) { rc = cwLogError(rc,"Variable description created failed on the class desc '%s' on the variable description at index '%i'.",cwStringNullGuard(cd->label),j); goto errLabel; } if( vd->type == kInvalidTFl ) { rc = cwLogError(rc,"The variable description '%s' in class description '%s' does not have a valid 'type' field.",cwStringNullGuard(vd->label),cwStringNullGuard(cd->label)); goto errLabel; } if( vd->proxyProcLabel != nullptr || vd->proxyVarLabel != nullptr ) { cwLogWarning("The 'proxy' field in the variable description '%s' on class description '%s' will be ignored because the variable is not part of a UDP definition.",cwStringNullGuard(vd->label),cwStringNullGuard(cd->label)); } if( cwIsFlag(vd->flags,kUdpOutVarDescFl ) ) { cwLogWarning("The 'out' flag in the variable description '%s' on class description '%s' will be ignored because the variable is not part of a UDP definition.",cwStringNullGuard(vd->label),cwStringNullGuard(cd->label)); } vd->link = cd->varDescL; cd->varDescL = vd; } } } errLabel: return rc; } rc_t _find_udp_proc_class_desc( flow_t* p, const object_t* udpProcD, const char* procInstLabel, const class_desc_t*& class_desc_ref ) { rc_t rc = kOkRC; const object_t* procInstD = nullptr; const object_t* classStr = nullptr; const char* class_label = nullptr; class_desc_ref = nullptr; // find the proc inst dict in the UDP if((procInstD = udpProcD->find_child(procInstLabel)) == nullptr ) { rc = cwLogError(kSyntaxErrorRC,"The proc instance '%s' from the proxy var list could not be foud in the UDP.",cwStringNullGuard(procInstLabel)); goto errLabel; } // find the proc class label of the proc inst if((classStr = procInstD->find_child("class")) == nullptr || (rc = classStr->value(class_label))!=kOkRC) { rc = cwLogError(kSyntaxErrorRC,"The 'class' field could not be found in the '%s' proc instance record.", cwStringNullGuard(procInstLabel)); goto errLabel; } // find the associated class desc record if((class_desc_ref = class_desc_find(p,class_label)) == nullptr) { rc = cwLogError(kEleNotFoundRC,"The class desc record '%s' for the proc instance '%s' could not be found.",class_label,cwStringNullGuard(procInstLabel)); goto errLabel; } errLabel: return rc; } rc_t _create_udp_var_desc( flow_t* p, class_desc_t* udpClassDesc, const object_t* udpProcD, const object_t* varDescPair, var_desc_t*& vd_ref ) { rc_t rc = kOkRC; const class_desc_t* proxy_class_desc = nullptr; const var_desc_t* proxy_var_desc = nullptr; var_desc_t* var_desc = nullptr; vd_ref = nullptr; // parse the variable descripiton and create a var_desc_t record if((rc = _parse_class_var_cfg(p, udpClassDesc, varDescPair, var_desc )) != kOkRC ) { goto errLabel; } if( var_desc->type != 0 ) { cwLogWarning("The 'type' field int the variable description '%s' on the class description '%s' will be ignored because the variable is proxied.",cwStringNullGuard(udpClassDesc->label),cwStringNullGuard(var_desc->label)); } // verify that a proxy-proc-label and proxy-var-label were specified in the variable descripiton if( var_desc->proxyProcLabel == nullptr || var_desc->proxyVarLabel == nullptr ) { rc = cwLogError(kSyntaxErrorRC,"The UDP variable description '%s' in the UDP '%s' must have a valid 'proxy' field.",cwStringNullGuard(var_desc->label),cwStringNullGuard(udpClassDesc->label)); goto errLabel; } // locate the class desc associated with proxy proc if((rc = _find_udp_proc_class_desc( p, udpProcD, var_desc->proxyProcLabel, proxy_class_desc )) != kOkRC ) { goto errLabel; } // locate the var desc associated with the proxy proc var if((proxy_var_desc = var_desc_find( proxy_class_desc, var_desc->proxyVarLabel)) == nullptr ) { rc = cwLogError(kEleNotFoundRC,"The UDP proxied variable desc '%s.%s' could not be found in UDP '%s'.",cwStringNullGuard(var_desc->proxyProcLabel),cwStringNullGuard(var_desc->proxyVarLabel),cwStringNullGuard(udpClassDesc->label)); goto errLabel; } // get the UDP var_desc type from the proxied var_desc var_desc->type = proxy_var_desc->type; // augment the udp var_desc flags from the proxied var_desc var_desc->flags |= proxy_var_desc->flags; // if no default value was given to the UDP var desc then get it from the proxied var desc if( var_desc->val_cfg == nullptr ) var_desc->val_cfg = proxy_var_desc->val_cfg; vd_ref = var_desc; errLabel: if( rc != kOkRC ) { rc = cwLogError(rc,"The creation of proxy var '%s' failed.",var_desc==nullptr ? "" : cwStringNullGuard(var_desc->label)); var_desc_destroy(var_desc); } return rc; } rc_t _parse_udp_vars( flow_t* p, class_desc_t* class_desc, const object_t* udpProcD, const object_t* varD ) { rc_t rc = kOkRC; unsigned varN = 0; if( !varD->is_dict() ) { rc = cwLogError(kSyntaxErrorRC,"The proxy variable dictionary is invalid."); goto errLabel; } varN = varD->child_count(); // Fill the class_Desc.varDescL list from the UDP 'vars' dictioanry for(unsigned i=0; ichild_ele(i); var_desc_t* var_desc = nullptr; if((rc = _create_udp_var_desc( p, class_desc, udpProcD, child_pair, var_desc )) != kOkRC ) goto errLabel; var_desc->link = class_desc->varDescL; class_desc->varDescL = var_desc; //printf("Wrapper var-desc created: %i of %i : %s:%s proxy:%s:%s flags:%i.\n", i, varN, class_desc->label, var_desc->label, var_desc->proxyProcLabel,var_desc->proxyVarLabel,var_desc->flags); } errLabel: return rc; } rc_t _create_udp_class_desc( flow_t* p, const object_t* class_obj, class_desc_t* class_desc ) { rc_t rc = kOkRC; const object_t* varD = nullptr; const object_t* udpD = nullptr; const object_t* udpProcD = nullptr; const object_t* udpPresetD = nullptr; const char* udpProcDescLabel = nullptr; // Validate the UDP proc desc label and value if( class_obj==nullptr || !class_obj->is_pair() || class_obj->pair_value()==nullptr || !class_obj->pair_value()->is_dict() || (udpProcDescLabel = class_obj->pair_label()) == nullptr ) { rc = cwLogError(kInvalidArgRC,"An invalid UDP description '%s' was encountered.",cwStringNullGuard(udpProcDescLabel)); goto errLabel; } // verify that another UDP with the same name does not already exist if( class_desc_find(p,udpProcDescLabel) != nullptr ) { rc = cwLogError(kInvalidStateRC,"A UDP named '%s' already exists.",udpProcDescLabel); goto errLabel; } class_desc->cfg = class_obj->pair_value(); class_desc->label = class_obj->pair_label(); // get the 'UDP' members record if((class_desc->members = _find_library_record("user_def_proc")) == nullptr ) { rc = cwLogError(kSyntaxErrorRC,"The 'UDP' class member function record could not be found." ); goto errLabel; } // get the variable description if((rc = class_desc->cfg->getv_opt("vars", varD, "network", udpD)) != kOkRC ) { rc = cwLogError(rc,"Parse failed while parsing UDP desc:'%s'", cwStringNullGuard(class_desc->label) ); goto errLabel; } // get the UDP proc and preset dictionaries if((rc = udpD->getv("procs", udpProcD, "presets", udpPresetD)) != kOkRC ) { rc = cwLogError(rc,"Parse failed on the 'network' element."); goto errLabel; } // fill class_desc.varDescL from the UDP vars dictionary if((rc = _parse_udp_vars( p, class_desc, udpProcD, varD )) != kOkRC ) { rc = cwLogError(rc,"UDP 'vars' processing failed."); goto errLabel; } errLabel: if(rc != kOkRC ) rc = cwLogError(rc,"'proc' class description creation failed for the UDP '%s'. ",cwStringNullGuard(udpProcDescLabel)); return rc; } rc_t _parse_udp_cfg(flow_t* p, const object_t* udpCfg) { rc_t rc = kOkRC; if( !udpCfg->is_dict() ) return cwLogError(kSyntaxErrorRC,"The UDP class description dictionary does not have dictionary syntax."); unsigned udpDescN = udpCfg->child_count(); p->udpDescA = mem::allocZ( udpDescN ); // for each UDP description for(unsigned i=0; ichild_ele(i); if((rc = _create_udp_class_desc(p, udp_obj, p->udpDescA + i )) != kOkRC ) { rc = cwLogError(rc,"UDP class description create failed on the UDP at index:%i.",i); goto errLabel; } // We have to update the size of the UDP class array // as we go because we may want be able to search p->udpDescA[] // aand to do that we must now the current length. p->udpDescN += 1; } assert( udpDescN == p->udpDescN ); errLabel: if( rc != kOkRC ) { rc = cwLogError(rc,"UDP processing failed."); } return rc; } rc_t _parse_preset_array(flow_t* p, const object_t* netCfg ) { rc_t rc = kOkRC; unsigned presetAllocN = 0; const object_t* presetD = nullptr; if((rc = netCfg->getv_opt("presets",presetD)) != kOkRC ) { rc = cwLogError(rc,"An error ocurred while locating the network 'presets' configuration."); goto errLabel; } // if this network does not have any presets if( presetD == nullptr ) return rc; // the 'preset' cfg must be a dictionary if( !presetD->is_dict() ) { rc = cwLogError(kSyntaxErrorRC,"The network preset list is not a dictionary."); goto errLabel; } presetAllocN = presetD->child_count(); p->presetA = mem::allocZ(presetAllocN); p->presetN = 0; // parse each preset_label pair for(unsigned i=0; ichild_ele(i); network_preset_t& network_preset = p->presetA[ p->presetN ]; // validate the network preset pair if( preset_pair_cfg==nullptr || !preset_pair_cfg->is_pair() || preset_pair_cfg->pair_label()==nullptr || preset_pair_cfg->pair_value()==nullptr ) { rc = cwLogError(kSyntaxErrorRC,"Invalid syntax encountered on a network preset."); goto errLabel; } // get the preset type id switch( preset_pair_cfg->pair_value()->type_id() ) { case kDictTId: // 'value-list' preset network_preset.tid = kPresetVListTId; break; case kListTId: // dual preset network_preset.tid = kPresetDualTId; break; default: rc = cwLogError(kAssertFailRC,"Unknown preset type on network preset: '%s'.",cwStringNullGuard(network_preset.label)); goto errLabel; } network_preset.label = preset_pair_cfg->pair_label(); p->presetN += 1; } errLabel: if(rc != kOkRC ) { mem::release(p->presetA); p->presetN = 0; } return rc; } void _release_class_desc_array( class_desc_t*& classDescA, unsigned classDescN ) { // release the class records for(unsigned i=0; inet); _release_class_desc_array(p->classDescA,p->classDescN); _release_class_desc_array(p->udpDescA,p->udpDescN); mem::release(p->presetA); p->presetN = 0; p->classDescN = 0; p->udpDescN = 0; mem::release(p); return rc; } void _make_flow_to_ui_callback( flow_t* p ) { // There is no concurrent contention for the linked list when // this function is called and so all accesses use relaxed memory order. // Get the first variable to send to the UI variable_t* var = p->ui_var_tail->ui_var_link.load(std::memory_order_relaxed); while( var!=nullptr) { // Send the var to the UI if( p->ui_callback != nullptr ) p->ui_callback( p->ui_callback_arg, var->ui_var ); // Get the next var to send to the UI variable_t* var0 = var->ui_var_link.load(std::memory_order_relaxed); // Nullify the list links as they are used var->ui_var_link.store(nullptr,std::memory_order_relaxed); var = var0; } // Empty the UI message list. p->ui_var_head.store(&p->ui_var_stub,std::memory_order_relaxed); p->ui_var_tail = &p->ui_var_stub; p->ui_var_stub.ui_var_link.store(nullptr,std::memory_order_relaxed); } } } void cw::flow::print_abuf( const abuf_t* abuf ) { printf("Abuf: sr:%7.1f chs:%3i frameN:%4i %p",abuf->srate,abuf->chN,abuf->frameN,abuf->buf); } void cw::flow::print_external_device( const external_device_t* dev ) { cwLogPrint("Dev: %10s type:%3i fl:0x%x : ", cwStringNullGuard(dev->devLabel),dev->typeId,dev->flags); if( dev->typeId == kAudioDevTypeId ) print_abuf(dev->u.a.abuf); cwLogPrint("\n"); } cw::rc_t cw::flow::create( handle_t& hRef, const object_t* classCfg, const object_t* pgmCfg, const object_t* udpCfg, const char* proj_dir, ui_callback_t ui_callback, void* ui_callback_arg) { rc_t rc = kOkRC; bool printClassDictFl = false; unsigned maxCycleCount = kInvalidCnt; double durLimitSecs = 0; if(( rc = destroy(hRef)) != kOkRC ) return rc; flow_t* p = mem::allocZ(); // parse the class description array if((rc = _parse_class_cfg(p,classCfg)) != kOkRC ) { rc = cwLogError(kSyntaxErrorRC,"Error parsing the class description list."); goto errLabel; } // parse the UDP descriptions if( udpCfg != nullptr ) if((rc = _parse_udp_cfg(p,udpCfg)) != kOkRC ) { rc = cwLogError(kSyntaxErrorRC,"Error parsing the UDP list."); goto errLabel; } p->pgmCfg = pgmCfg; p->framesPerCycle = kDefaultFramesPerCycle; p->sample_rate = kDefaultSampleRate; p->maxCycleCount = kInvalidCnt; p->proj_dir = proj_dir; p->printLogHdrFl = true; p->ui_callback = ui_callback; p->ui_callback_arg= ui_callback_arg; p->ui_var_head.store(&p->ui_var_stub); p->ui_var_tail = &p->ui_var_stub; // parse the optional args if((rc = pgmCfg->readv("network", 0, p->networkCfg, "non_real_time_fl", kOptFl, p->non_real_time_fl, "frames_per_cycle", kOptFl, p->framesPerCycle, "sample_rate", kOptFl, p->sample_rate, "max_cycle_count", kOptFl, maxCycleCount, "dur_limit_secs", kOptFl, durLimitSecs, "preset", kOptFl, p->init_net_preset_label, "print_class_dict_fl", kOptFl, printClassDictFl, "print_network_fl", kOptFl, p->printNetworkFl, "multiPriPresetProbFl", kOptFl, p->multiPriPresetProbFl, "multiSecPresetProbFl", kOptFl, p->multiSecPresetProbFl, "multiPresetInterpFl", kOptFl, p->multiPresetInterpFl)) != kOkRC ) { rc = cwLogError(kSyntaxErrorRC,"Error parsing the network system parameters."); goto errLabel; } if((rc = _parse_preset_array(p, p->networkCfg )) != kOkRC ) { rc = cwLogError(rc,"Preset dictionary parsing failed."); goto errLabel; } // if a maxCycle count was given if( maxCycleCount != kInvalidCnt ) p->maxCycleCount = maxCycleCount; else { // if a durLimitSecs was given - use it to setMaxCycleCount if( durLimitSecs != 0.0 ) p->maxCycleCount = (unsigned)((durLimitSecs * p->sample_rate) / p->framesPerCycle); } // print the class dict if( printClassDictFl ) class_dict_print( p ); hRef.set(p); errLabel: if( rc != kOkRC ) _destroy(p); return rc; } bool cw::flow::is_non_real_time( handle_t h ) { flow_t* p = _handleToPtr(h); return p->non_real_time_fl; } double cw::flow::sample_rate( handle_t h ) { flow_t* p = _handleToPtr(h); return p->sample_rate; } unsigned cw::flow::frames_per_cycle( handle_t h ) { flow_t* p = _handleToPtr(h); return p->framesPerCycle; } unsigned cw::flow::preset_count( handle_t h ) { flow_t* p = _handleToPtr(h); return p->presetN; } const char* cw::flow::preset_label( handle_t h, unsigned preset_idx ) { flow_t* p = _handleToPtr(h); if( preset_idx >= p->presetN ) { cwLogError(kInvalidArgRC,"The preset index %i is invalid.",preset_idx); return nullptr; } return p->presetA[ preset_idx ].label; } unsigned cw::flow::preset_cfg_flags( handle_t h ) { flow_t* p = _handleToPtr(h); unsigned flags = 0; if( p->multiPriPresetProbFl ) flags |= kPriPresetProbFl; if( p->multiSecPresetProbFl ) flags |= kSecPresetProbFl; if( p->multiPresetInterpFl ) flags |= kInterpPresetFl; return flags; } cw::rc_t cw::flow::initialize( handle_t h, external_device_t* deviceA, unsigned deviceN, unsigned preset_idx ) { rc_t rc = kOkRC; variable_t* proxyVarL = nullptr; flow_t* p = _handleToPtr(h); p->deviceA = deviceA; p->deviceN = deviceN; for(unsigned i=0; iframeN != p->framesPerCycle ) cwLogWarning("The audio frame count (%i) for audio device '%s' does not match the Flow framesPerCycle (%i).",deviceA[i].u.a.abuf->frameN,p->framesPerCycle); } // if an initialization preset was given if( preset_idx != kInvalidIdx ) { const char* preset_label_str; if((preset_label_str = preset_label(h,preset_idx)) == nullptr ) { cwLogError(kInvalidArgRC,"The preset index '%i' could not be resolved to an existing preset."); goto errLabel; } // override the program assigned 'preset' p->init_net_preset_label = preset_label_str; } // instantiate the network if((rc = network_create(p,&p->networkCfg,1,proxyVarL,1,p->init_net_preset_label,p->net)) != kOkRC ) { rc = cwLogError(rc,"Network creation failed."); goto errLabel; } if( p->printNetworkFl && p->net != nullptr ) network_print(*p->net); // The network preset may have been applied as each proc. was instantiated. // This way any 'init' only preset values will have been applied and the // proc's custom create is more likely to see the values from the preset. // Now that the network is fully instantiated however we will apply it again // to be sure that the final state of the network is determined by selected preset. if( p->init_net_preset_label != nullptr && p->net != nullptr ) network_apply_preset( *p->net, p->init_net_preset_label ); // form the UI description if((rc = create_net_ui_desc(p)) != kOkRC ) { rc = cwLogError(rc,"UI description formation failed."); goto errLabel; } p->isInRuntimeFl = true; cwLogInfo("Entering runtime."); errLabel: return rc; } cw::rc_t cw::flow::destroy( handle_t& hRef ) { rc_t rc = kOkRC; flow_t* p = nullptr;; if( !hRef.isValid() ) return rc; p = _handleToPtr(hRef); _destroy(p); hRef.clear(); return rc; } const cw::flow::ui_net_t* cw::flow::ui_net( handle_t h ) { flow_t* p = _handleToPtr(h); if( p->net == nullptr ) { cwLogError(kInvalidStateRC,"No UI net exists because the no net exists."); return nullptr; } if( p->net->ui_net == nullptr ) return nullptr; return p->net->ui_net; } cw::rc_t cw::flow::exec_cycle( handle_t h ) { rc_t rc = kOkRC;; flow_t* p = _handleToPtr(h); if( p->maxCycleCount!=kInvalidCnt && p->cycleIndex >= p->maxCycleCount ) { rc = kEofRC; cwLogInfo("'maxCycleCnt' reached: %i. Shutting down flow.",p->maxCycleCount); } else { // Execute one cycle of the network if((rc = exec_cycle(*p->net)) == kOkRC ) { // During network execution variables which need to update the UI // are collected in a linked list based on p->ui_var_tail. // Callback to the UI with those variables here. _make_flow_to_ui_callback(p); } p->cycleIndex += 1; } return rc; } cw::rc_t cw::flow::exec( handle_t h ) { rc_t rc = kOkRC; //flow_t* p = _handleToPtr(h); while( rc == kOkRC ) rc = exec_cycle(h); return rc; } cw::rc_t cw::flow::apply_preset( handle_t h, const char* presetLabel ) { flow_t* p = _handleToPtr(h); return network_apply_preset(*p->net,presetLabel); } cw::rc_t cw::flow::apply_dual_preset( handle_t h, const char* presetLabel_0, const char* presetLabel_1, double coeff ) { flow_t* p = _handleToPtr(h); return network_apply_dual_preset(*p->net,presetLabel_0, presetLabel_1, coeff ); } cw::rc_t cw::flow::apply_preset( handle_t h, const multi_preset_selector_t& mps ) { flow_t* p = _handleToPtr(h); return network_apply_preset(*p->net,mps); } cw::rc_t cw::flow::set_variable_user_id( handle_t h, const ui_var_t* ui_var, unsigned user_id ) { return set_variable_user_id( *_handleToPtr(h)->net, ui_var, user_id ); } cw::rc_t cw::flow::set_variable_value( handle_t h, const char* inst_label, const char* var_label, unsigned chIdx, bool value ) { return set_variable_value( *_handleToPtr(h)->net, inst_label, var_label, chIdx, value ); } cw::rc_t cw::flow::set_variable_value( handle_t h, const char* inst_label, const char* var_label, unsigned chIdx, int value ) { return set_variable_value( *_handleToPtr(h)->net, inst_label, var_label, chIdx, value ); } cw::rc_t cw::flow::set_variable_value( handle_t h, const char* inst_label, const char* var_label, unsigned chIdx, unsigned value ) { return set_variable_value( *_handleToPtr(h)->net, inst_label, var_label, chIdx, value ); } cw::rc_t cw::flow::set_variable_value( handle_t h, const char* inst_label, const char* var_label, unsigned chIdx, float value ) { return set_variable_value( *_handleToPtr(h)->net, inst_label, var_label, chIdx, value ); } cw::rc_t cw::flow::set_variable_value( handle_t h, const char* inst_label, const char* var_label, unsigned chIdx, double value ) { return set_variable_value( *_handleToPtr(h)->net, inst_label, var_label, chIdx, value ); } cw::rc_t cw::flow::get_variable_value( handle_t h, const char* inst_label, const char* var_label, unsigned chIdx, bool& valueRef ) { return get_variable_value( *_handleToPtr(h)->net, inst_label, var_label, chIdx, valueRef ); } cw::rc_t cw::flow::get_variable_value( handle_t h, const char* inst_label, const char* var_label, unsigned chIdx, int& valueRef ) { return get_variable_value( *_handleToPtr(h)->net, inst_label, var_label, chIdx, valueRef ); } cw::rc_t cw::flow::get_variable_value( handle_t h, const char* inst_label, const char* var_label, unsigned chIdx, unsigned& valueRef ) { return get_variable_value( *_handleToPtr(h)->net, inst_label, var_label, chIdx, valueRef ); } cw::rc_t cw::flow::get_variable_value( handle_t h, const char* inst_label, const char* var_label, unsigned chIdx, float& valueRef ) { return get_variable_value( *_handleToPtr(h)->net, inst_label, var_label, chIdx, valueRef ); } cw::rc_t cw::flow::get_variable_value( handle_t h, const char* inst_label, const char* var_label, unsigned chIdx, double& valueRef ) { return get_variable_value( *_handleToPtr(h)->net, inst_label, var_label, chIdx, valueRef ); } cw::rc_t cw::flow::set_variable_value( handle_t h, const ui_var_t* ui_var, bool value ) { return set_variable_value( *_handleToPtr(h)->net, ui_var, value ); } cw::rc_t cw::flow::set_variable_value( handle_t h, const ui_var_t* ui_var, int value ) { return set_variable_value( *_handleToPtr(h)->net, ui_var, value ); } cw::rc_t cw::flow::set_variable_value( handle_t h, const ui_var_t* ui_var, unsigned value ) { return set_variable_value( *_handleToPtr(h)->net, ui_var, value ); } cw::rc_t cw::flow::set_variable_value( handle_t h, const ui_var_t* ui_var, float value ) { return set_variable_value( *_handleToPtr(h)->net, ui_var, value ); } cw::rc_t cw::flow::set_variable_value( handle_t h, const ui_var_t* ui_var, double value ) { return set_variable_value( *_handleToPtr(h)->net, ui_var, value ); } cw::rc_t cw::flow::set_variable_value( handle_t h, const ui_var_t* ui_var, const char* value ) { return set_variable_value( *_handleToPtr(h)->net, ui_var, value ); } cw::rc_t cw::flow::get_variable_value( handle_t h, const ui_var_t* ui_var, bool& value_ref ) { return get_variable_value( *_handleToPtr(h)->net, ui_var, value_ref ); } cw::rc_t cw::flow::get_variable_value( handle_t h, const ui_var_t* ui_var, int& value_ref ) { return get_variable_value( *_handleToPtr(h)->net, ui_var, value_ref ); } cw::rc_t cw::flow::get_variable_value( handle_t h, const ui_var_t* ui_var, unsigned& value_ref ) { return get_variable_value( *_handleToPtr(h)->net, ui_var, value_ref ); } cw::rc_t cw::flow::get_variable_value( handle_t h, const ui_var_t* ui_var, float& value_ref ) { return get_variable_value( *_handleToPtr(h)->net, ui_var, value_ref ); } cw::rc_t cw::flow::get_variable_value( handle_t h, const ui_var_t* ui_var, double& value_ref ) { return get_variable_value( *_handleToPtr(h)->net, ui_var, value_ref ); } cw::rc_t cw::flow::get_variable_value( handle_t h, const ui_var_t* ui_var, const char*& value_ref ) { return get_variable_value( *_handleToPtr(h)->net, ui_var, value_ref ); } void cw::flow::print_class_list( handle_t h ) { flow_t* p = _handleToPtr(h); class_dict_print(p); } void cw::flow::print_network( handle_t h ) { flow_t* p = _handleToPtr(h); for(unsigned i=0; ideviceN; ++i) print_external_device( p->deviceA + i ); network_print(*p->net); }