#include "cwCommon.h" #include "cwLog.h" #include "cwCommonImpl.h" #include "cwMem.h" #include "cwText.h" #include "cwObject.h" #include "cwTime.h" #include "cwPresetSel.h" namespace cw { namespace preset_sel { typedef struct preset_label_str { char* label; } preset_label_t; typedef struct preset_sel_str { preset_label_t* presetLabelA; unsigned presetLabelN; double defaultGain; double defaultWetDryGain; double defaultFadeOutMs; struct frag_str* fragL; frag_t* last_ts_frag; } preset_sel_t; preset_sel_t* _handleToPtr( handle_t h ) { return handleToPtr(h); } rc_t _delete_fragment( preset_sel_t* p, unsigned fragId ) { frag_t* f0 = nullptr; frag_t* f1 = p->fragL; for(; f1!=nullptr; f1=f1->link) { if( f1->fragId == fragId ) { if( f0 == nullptr ) p->fragL = f1->link; else f0->link = f1->link; // release the fragment mem::release(f1->presetA); mem::release(f1); return kOkRC; } f0 = f1; } return kOkRC; } rc_t _destroy( preset_sel_t* p ) { while( p->fragL != nullptr ) _delete_fragment(p, p->fragL->fragId ); for(unsigned i=0; ipresetLabelN; ++i) mem::release( p->presetLabelA[i].label ); mem::release(p); return kOkRC; } frag_t* _find_frag( preset_sel_t* p, unsigned fragId ) { frag_t* f; for(f=p->fragL; f!=nullptr; f=f->link) if( f->fragId == fragId ) return f; return nullptr; } rc_t _find_frag( preset_sel_t* p, unsigned fragId, frag_t*& fragPtrRef ) { rc_t rc = kOkRC; if((fragPtrRef = _find_frag(p,fragId )) == nullptr ) rc = cwLogError(kInvalidIdRC,"'%i' is not a valid fragment id.",fragId); return rc; } frag_t* _index_to_frag( preset_sel_t* p, unsigned frag_idx ) { frag_t* f; unsigned i = 0; for(f=p->fragL; f!=nullptr; f=f->link,++i) if( i == frag_idx ) break; if( f == nullptr ) cwLogError(kInvalidArgRC,"'%i' is not a valid fragment index.",frag_idx); return f; } frag_t* _loc_to_frag( preset_sel_t* p, unsigned loc ) { frag_t* f; for(f=p->fragL; f!=nullptr; f=f->link) if( f->endLoc == loc ) return f; return nullptr; } bool _ts_is_in_frag( const frag_t* f, const time::spec_t& ts ) { // if f is the earliest fragment if( f->prev == nullptr ) return time::isLTE(ts,f->endTimestamp); // else f->prev->end_ts < ts && ts <= f->end_ts return time::isLT(f->prev->endTimestamp,ts) && time::isLTE(ts,f->endTimestamp); } bool _ts_is_before_frag( const frag_t* f, const time::spec_t& ts ) { // if ts is past f if( time::isGT(ts,f->endTimestamp ) ) return false; // ts may now only be inside or before f // if f is the first frag then ts must be inside it if( f->prev == nullptr ) return false; // is ts before f return time::isLTE(ts,f->prev->endTimestamp); } bool _ts_is_after_frag( const frag_t* f, const time::spec_t& ts ) { return time::isGT(ts,f->endTimestamp); } // Scan from through the fragment list to find the fragment containing ts. frag_t* _timestamp_to_frag( preset_sel_t* p, const time::spec_t& ts, frag_t* init_frag=nullptr ) { frag_t* f = init_frag==nullptr ? p->fragL : init_frag; for(; f!=nullptr; f=f->link) if( _ts_is_in_frag(f,ts) ) return f; return nullptr; } rc_t _validate_preset_id( const frag_t* frag, unsigned preset_id ) { bool fl = (preset_id < frag->presetN) && (frag->presetA[ preset_id ].preset_idx == preset_id); return fl ? kOkRC : cwLogError(kInvalidIdRC,"The preset id '%i' is invalid on the fragment at loc:%i.",preset_id,frag->endLoc); } template< typename T > rc_t _set_value( handle_t h, unsigned fragId, unsigned varId, unsigned presetId, const T& value ) { rc_t rc = kOkRC; preset_sel_t* p = _handleToPtr(h); frag_t* f = nullptr; // locate the requested fragment if((rc = _find_frag(p,fragId,f)) != kOkRC ) goto errLabel; switch( varId ) { case kDryFlVarId: f->dryFl = value; break; case kPresetSelectVarId: for(unsigned i=0; ipresetN; ++i) f->presetA[i].playFl = f->presetA[i].preset_idx == presetId ? value : false; break; case kPresetOrderVarId: if((rc = _validate_preset_id(f, presetId )) == kOkRC ) f->presetA[ presetId ].order = value; break; case kGainVarId: f->gain = value; break; case kFadeOutMsVarId: f->fadeOutMs = value; break; case kWetGainVarId: f->wetDryGain = value; break; default: rc = cwLogError(kInvalidIdRC,"There is no preset variable with var id:%i.",varId); goto errLabel; } errLabel: if(rc != kOkRC ) rc = cwLogError(rc,"Variable value assignment failed on fragment '%i' variable:%i preset:%i",fragId,varId,presetId); return rc; } template< typename T > rc_t _get_value( handle_t h, unsigned fragId, unsigned varId, unsigned presetId, T& valueRef ) { rc_t rc = kOkRC; preset_sel_t* p = _handleToPtr(h); frag_t* f = nullptr; // locate the requested fragment if((rc = _find_frag(p,fragId,f)) != kOkRC ) goto errLabel; switch( varId ) { case kEndLocVarId: valueRef = f->endLoc; break; case kDryFlVarId: valueRef = f->dryFl; break; case kPresetSelectVarId: if((rc = _validate_preset_id(f, presetId )) == kOkRC ) valueRef = f->presetA[ presetId ].playFl; break; case kPresetOrderVarId: if((rc = _validate_preset_id(f, presetId )) == kOkRC ) valueRef = f->presetA[ presetId ].order; break; case kGainVarId: valueRef = f->gain; break; case kFadeOutMsVarId: valueRef = f->fadeOutMs; break; case kWetGainVarId: valueRef = f->wetDryGain; break; default: rc = cwLogError(kInvalidIdRC,"There is no preset variable with var id:%i.",varId); goto errLabel; } errLabel: if(rc != kOkRC ) rc = cwLogError(rc,"Variable value access failed on fragment '%i' variable:%i preset:%i",fragId,varId,presetId); return rc; } } } cw::rc_t cw::preset_sel::create( handle_t& hRef, const object_t* cfg ) { rc_t rc = kOkRC; preset_sel_t* p = nullptr; const object_t* labelL = nullptr; if((rc = destroy(hRef)) != kOkRC ) return rc; p = mem::allocZ(); // parse the cfg if((rc = cfg->getv( "preset_labelL", labelL, "default_gain", p->defaultGain, "default_wet_dry_gain", p->defaultWetDryGain, "default_fade_ms", p->defaultFadeOutMs)) != kOkRC ) { rc = cwLogError(rc,"The preset configuration parse failed."); goto errLabel; } // allocate the label array p->presetLabelN = labelL->child_count(); p->presetLabelA = mem::allocZ(p->presetLabelN); // get the preset labels for(unsigned i=0; ipresetLabelN; ++i) { const char* label = nullptr; const object_t* labelNode = labelL->child_ele(i); if( labelNode!=nullptr ) rc = labelNode->value(label); if( rc != kOkRC || label == nullptr || textLength(label) == 0 ) { rc = cwLogError(kInvalidStateRC,"A empty preset label was encountered while reading the preset label list."); goto errLabel; } p->presetLabelA[i].label = mem::duplStr(label); } hRef.set(p); errLabel: return rc; } cw::rc_t cw::preset_sel::destroy( handle_t& hRef ) { rc_t rc = kOkRC; preset_sel_t* p = nullptr; if( !hRef.isValid() ) return rc; p = _handleToPtr(hRef); if((rc = _destroy(p)) != kOkRC ) return rc; hRef.clear(); return rc; } unsigned cw::preset_sel::preset_count( handle_t h ) { preset_sel_t* p = _handleToPtr(h); return p->presetLabelN; } const char* cw::preset_sel::preset_label( handle_t h, unsigned preset_idx ) { preset_sel_t* p = _handleToPtr(h); return p->presetLabelA[ preset_idx].label; } unsigned cw::preset_sel::fragment_count( handle_t h ) { preset_sel_t* p = _handleToPtr(h); unsigned n = 0; for(const frag_t* f=p->fragL; f!=nullptr; f=f->link) ++n; return n; } const cw::preset_sel::frag_t* cw::preset_sel::get_fragment_base( handle_t h ) { preset_sel_t* p = _handleToPtr(h); return p->fragL; } const cw::preset_sel::frag_t* cw::preset_sel::get_fragment( handle_t h, unsigned fragId ) { preset_sel_t* p = _handleToPtr(h); return _find_frag(p,fragId); } cw::rc_t cw::preset_sel::create_fragment( handle_t h, unsigned fragId, unsigned end_loc, time::spec_t end_timestamp ) { preset_sel_t* p = _handleToPtr(h); frag_t* f = mem::allocZ(); f->endLoc = end_loc; f->endTimestamp = end_timestamp; f->fragId = fragId; f->dryFl = false; f->gain = p->defaultGain; f->wetDryGain = p->defaultWetDryGain; f->fadeOutMs = p->defaultFadeOutMs; f->presetA = mem::allocZ(p->presetLabelN); f->presetN = p->presetLabelN; for(unsigned i=0; ipresetLabelN; ++i) f->presetA[i].preset_idx = i; // if the list is empty if( p->fragL == nullptr ) { p->fragL = f; return kOkRC; } frag_t* f0 = p->fragL; for(; f0->link!=nullptr; f0 = f0->link) if( end_loc < f0->endLoc ) break; // assert( f0 != nullptr ); // if f is after the last current fragment ... if( f0->link == nullptr ) { // ... insert f at the end of the list f0->link = f; f->prev = f0; } else { // Insert f before f0 f->link = f0; f->prev = f0->prev; // if f0 was first on the list if( f0->prev == nullptr ) { assert( p->fragL == f0 ); p->fragL = f; } else { f0->prev->link = f; } f0->prev = f; } return kOkRC; } cw::rc_t cw::preset_sel::delete_fragment( handle_t h, unsigned fragId ) { preset_sel_t* p = _handleToPtr(h); frag_t* f0 = nullptr; frag_t* f1 = p->fragL; for(; f1!=nullptr; f1=f1->link) { if( f1->fragId == fragId ) { if( f0 == nullptr ) p->fragL = f1->link; else f0->link = f1->link; // release the fragment mem::release(f1->presetA); mem::release(f1); return kOkRC; } f0 = f1; } return kOkRC; } bool cw::preset_sel::is_fragment_loc( handle_t h, unsigned loc ) { preset_sel_t* p = _handleToPtr(h); return _loc_to_frag(p,loc) != nullptr; } unsigned cw::preset_sel::ui_select_fragment_id( handle_t h ) { preset_sel_t* p = _handleToPtr(h); for(frag_t* f = p->fragL; f!= nullptr; f=f->link) if( f->uiSelectFl ) return f->fragId; return kInvalidId; } void cw::preset_sel::ui_select_fragment( handle_t h, unsigned fragId, bool selectFl ) { preset_sel_t* p = _handleToPtr(h); frag_t* f = p->fragL; for(; f!= nullptr; f=f->link) if( f->fragId == fragId ) f->uiSelectFl = selectFl; else f->uiSelectFl = false; } cw::rc_t cw::preset_sel::set_value( handle_t h, unsigned fragId, unsigned varId, unsigned presetId, bool value ) { return _set_value(h,fragId,varId,presetId,value); } cw::rc_t cw::preset_sel::set_value( handle_t h, unsigned fragId, unsigned varId, unsigned presetId, unsigned value ) { return _set_value(h,fragId,varId,presetId,value); } cw::rc_t cw::preset_sel::set_value( handle_t h, unsigned fragId, unsigned varId, unsigned presetId, double value ) { return _set_value(h,fragId,varId,presetId,value); } cw::rc_t cw::preset_sel::get_value( handle_t h, unsigned fragId, unsigned varId, unsigned presetId, bool& valueRef ) { return _get_value(h,fragId,varId,presetId,valueRef); } cw::rc_t cw::preset_sel::get_value( handle_t h, unsigned fragId, unsigned varId, unsigned presetId, unsigned& valueRef ) { return _get_value(h,fragId,varId,presetId,valueRef); } cw::rc_t cw::preset_sel::get_value( handle_t h, unsigned fragId, unsigned varId, unsigned presetId, double& valueRef ) { return _get_value(h,fragId,varId,presetId,valueRef); } bool cw::preset_sel::track_timestamp( handle_t h, const time::spec_t& ts, const cw::preset_sel::frag_t*& frag_Ref ) { preset_sel_t* p = _handleToPtr(h); frag_t* f = nullptr; bool frag_changed_fl = false; // if this is the first call to 'track_timestamp()'. if( p->last_ts_frag == nullptr ) f = _timestamp_to_frag(p,ts); else // if the 'ts' is in the same frag as previous call. if( _ts_is_in_frag(p->last_ts_frag,ts) ) f = p->last_ts_frag; else // if 'ts' is in a later frag if( _ts_is_after_frag(p->last_ts_frag,ts) ) f = _timestamp_to_frag(p,ts,p->last_ts_frag); else // ts is prior to 'last_ts_frag' f = _timestamp_to_frag(p,ts); // 'f' will be null at this point if 'ts' is past the last preset. // In this case we should leave 'last_ts_frag' unchanged. // if 'f' is valid but different from 'last_ts_frag' if( f != nullptr && f != p->last_ts_frag ) { p->last_ts_frag = f; frag_changed_fl = true; } frag_Ref = p->last_ts_frag; return frag_changed_fl; } unsigned cw::preset_sel::fragment_play_preset_index( const frag_t* frag ) { for(unsigned i=0; ipresetN; ++i) if( frag->presetA[i].playFl ) return frag->presetA[i].preset_idx; return kInvalidIdx; } cw::rc_t cw::preset_sel::write( handle_t h, const char* fn ) { rc_t rc = kOkRC; return rc; } cw::rc_t cw::preset_sel::read( handle_t h, const char* fn ) { rc_t rc = kOkRC; return rc; } cw::rc_t cw::preset_sel::report( handle_t h ) { rc_t rc = kOkRC; preset_sel_t* p = _handleToPtr(h); unsigned i = 0; time::spec_t t0; time::setZero(t0); for(frag_t* f=p->fragL; f!=nullptr; f=f->link,++i) { unsigned elapsedMs = time::elapsedMs(t0,f->endTimestamp); double mins = elapsedMs / 60000.0; cwLogInfo("%3i id:%3i end loc:%3i end min:%7.2f",i,f->fragId,f->endLoc, mins); } return rc; }