From 99871c5bef05829aa5f187fd105050591abab03d Mon Sep 17 00:00:00 2001 From: kevin Date: Mon, 25 Mar 2024 13:31:58 -0400 Subject: [PATCH 001/101] cwNbMpScQueue.h/cpp : 1. Added peek(). 2. blockL is now made from individual allocated blocks to ease memory alignment. 3. node_t and blob length is now padded out to a multiple of 8 bytes to guarantee memory alignment. 4. advance() now returns the next blob. --- cwNbMpScQueue.cpp | 152 ++++++++++++++++++++++++++++++++++++---------- cwNbMpScQueue.h | 20 ++++-- 2 files changed, 136 insertions(+), 36 deletions(-) diff --git a/cwNbMpScQueue.cpp b/cwNbMpScQueue.cpp index 6708965..2940b8f 100644 --- a/cwNbMpScQueue.cpp +++ b/cwNbMpScQueue.cpp @@ -30,12 +30,15 @@ namespace cw typedef struct node_str { - std::atomic next; - block_t* block; - unsigned blobByteN; + std::atomic 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. @@ -51,6 +54,8 @@ namespace cw std::atomic head; // last-in node_t* tail; // first-out + + node_t* peek; } nbmpscq_t; @@ -62,8 +67,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 +113,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 { @@ -142,7 +173,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; @@ -151,11 +181,31 @@ cw::rc_t cw::nbmpscq::create( handle_t& hRef, unsigned initBlkN, unsigned blkByt p->stub = mem::allocZ(); p->head = p->stub; // last-in - p->tail = p->stub; // first-out + p->tail = p->stub; // first-out + p->peek = nullptr; p->cleanBlkN = 0; p->blkN = initBlkN; p->blkByteN = blkByteN; + + for(unsigned i=0; i(); + b->buf = mem::allocZ(blkByteN); + + b->bufByteN = blkByteN; + + b->full_flag.store(false); + b->index.store(0); + b->eleN.store(0); + + b->link = p->blockL; + p->blockL = b; + + } + + + /* byteN = initBlkN * (sizeof(block_t) + blkByteN ); p->mem = mem::allocZ(byteN); @@ -172,7 +222,8 @@ cw::rc_t cw::nbmpscq::create( handle_t& hRef, unsigned initBlkN, unsigned blkByt b->link = p->blockL; p->blockL = b; } - + */ + hRef.set(p); errLabel: @@ -215,6 +266,15 @@ cw::rc_t cw::nbmpscq::push( handle_t h, const void* blob, unsigned blobByteN ) // Note that this case will immediately overflow the queue. 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 ); + for(; b!=nullptr; b=b->link) { @@ -274,33 +334,22 @@ cw::rc_t cw::nbmpscq::push( handle_t h, const void* blob, unsigned blobByteN ) 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 - - if( n == nullptr ) - { - blob.blob = nullptr; - blob.blobByteN = 0; - } - else - { - blob.blob = (uint8_t*)(n+1); - blob.blobByteN = n->blobByteN; - } - - return blob; + blob_t blob; + nbmpscq_t* p = _handleToPtr(h); + node_t* t = p->tail; + node_t* node = t->next.load(std::memory_order_acquire); // ACQUIRE 'next' from producer + + _init_blob( blob, node ); + return blob; } -cw::rc_t cw::nbmpscq::advance( handle_t h ) -{ - nbmpscq_t* p = _handleToPtr(h); - rc_t rc = kOkRC; - node_t* t = p->tail; - node_t* next = t->next.load(std::memory_order_acquire); // ACQUIRE 'next' from producer +cw::nbmpscq::blob_t cw::nbmpscq::advance( handle_t h ) +{ + blob_t blob; + nbmpscq_t* p = _handleToPtr(h); + node_t* t = p->tail; + node_t* next = t->next.load(std::memory_order_acquire); // ACQUIRE 'next' from producer if( next != nullptr ) { @@ -317,7 +366,46 @@ cw::rc_t cw::nbmpscq::advance( handle_t h ) 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; + +} + + + +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; } cw::rc_t cw::nbmpscq::test( const object_t* cfg ) diff --git a/cwNbMpScQueue.h b/cwNbMpScQueue.h index 2e775da..ac3dbf2 100644 --- a/cwNbMpScQueue.h +++ b/cwNbMpScQueue.h @@ -34,7 +34,7 @@ namespace cw namespace nbmpscq { typedef handle handle_t; - + rc_t create( handle_t& hRef, unsigned initBlkN, unsigned blkByteN ); rc_t destroy( handle_t& hRef ); @@ -46,18 +46,30 @@ namespace cw 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 ); + + bool is_empty( handle_t h ); rc_t test( const object_t* cfg ); From 693c87d5981b4f7e8f69c76753772426d8d4f0b5 Mon Sep 17 00:00:00 2001 From: kevin Date: Mon, 25 Mar 2024 13:32:46 -0400 Subject: [PATCH 002/101] cwWebSock.cpp : Now uses cwNbMpScQueue to cache outgoing messages. --- cwWebSock.cpp | 238 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 141 insertions(+), 97 deletions(-) diff --git a/cwWebSock.cpp b/cwWebSock.cpp index 3d4bdfc..156dfca 100644 --- a/cwWebSock.cpp +++ b/cwWebSock.cpp @@ -1,13 +1,16 @@ + #include "cwCommon.h" #include "cwLog.h" #include "cwCommonImpl.h" #include "cwMem.h" #include "cwFileSys.h" +#include "cwObject.h" #include "cwWebSock.h" -#include "cwMpScNbQueue.h" +#include "cwNbMpScQueue.h" #include "cwTime.h" #include +#include namespace cw { @@ -31,15 +34,18 @@ namespace cw // Internal outgoing msg structure. typedef struct msg_str { - unsigned protocolId; // Protocol associated with this msg. - unsigned sessionId; // Session Id that this msg should be sent to or kInvalidId if it should be sent to all sessions. - unsigned char* msg; // Msg data array. - unsigned msgByteN; // Count of bytes in msg[]. - unsigned msgId; // The msgId assigned when this msg is addded to the protocol state msg queue. - unsigned sessionN; // Count of sessions to which this msg has been sent. - struct msg_str* link; // Pointer to next message or nullptr if this is the last msg in the queue. + unsigned protocolId; // 0 Protocol associated with this msg. + unsigned sessionId; // 4 Session Id that this msg should be sent to or kInvalidId if it should be sent to all sessions. + unsigned char* msg; // 8 Msg data array. + unsigned msgByteN; // 16 Count of bytes in msg[]. + unsigned msgId; // 20 The msgId assigned when this msg is addded to the protocol state msg queue. + unsigned sessionN; // 24 Count of sessions to which this msg has been sent. + struct msg_str* link; // 28 Pointer to next message or nullptr if this is the last msg in the queue. + unsigned pad; // 36-40 (make size of msg_t a multiple of 8) } msg_t; + static_assert( sizeof(msg_t) % 8 == 0 ); + typedef struct websock_str { cbFunc_t _cbFunc; // @@ -50,7 +56,7 @@ namespace cw unsigned _nextSessionId = 0; // Next session id. unsigned _connSessionN = 0; // Count of connected sessions. struct lws_http_mount* _mount = nullptr; // - MpScNbQueue* _q; // Thread safe, non-blocking, protocol independent msg queue. + nbmpscq::handle_t _qH; // Thread safe, non-blocking, protocol independent msg queue. lws_pollfd* _pollfdA; // socket handle array used by poll() int _pollfdMaxN; @@ -184,14 +190,13 @@ namespace cw case LWS_CALLBACK_SERVER_WRITEABLE: { - msg_t* m1 = protoState->endMsg; - cwAssert(m1 != nullptr); + msg_t* m = protoState->endMsg; - // for each possible msg - while( m1->link != nullptr ) + // for each possible outgoing msg in this protocol state record + while( m != nullptr ) { - msg_t* m = m1->link; - + bool msg_written_fl = false; + // if this msg has not already been sent to this session if( m->msgId >= sess->nextMsgId ) { @@ -199,7 +204,8 @@ namespace cw // if the msg sessiond id is not valid or matches this session id ... if( m->sessionId == kInvalidId || m->sessionId == sess->id ) { - // ... then send the msg to this session + // ... then send the msg to this session + // Note: msgByteN should not include LWS_PRE int lws_result = lws_write(wsi, m->msg + LWS_PRE , m->msgByteN, LWS_WRITE_TEXT); @@ -209,17 +215,25 @@ namespace cw cwLogError(kWriteFailRC,"Websocket error: %d on write", lws_result); return -1; } + + msg_written_fl = true; } // at this point the write succeeded or this session was skipped sess->nextMsgId = m->msgId + 1; - m->sessionN += 1; - - break; + + // incr the msg session count - once m->sessionN >= protoState->sessionN this msg will be deleted in _cleanProtocoStateList() + m->sessionN += 1; + + // If a record was actually sent then we are done since only one + // call to lws_write() per LWS_CALLBACK_SERVER_WRITEABLE callback + // is permitted + if(msg_written_fl) + break; } - m1 = m1->link; + m = m->link; } } @@ -255,40 +269,41 @@ namespace cw return nullptr; } - - void _cleanProtocolStateList( protocolState_t* ps ) + // Remove message from the send queue (p->_qH), and the protocol msg list, + // which have already been sent to all relevant sessions. + rc_t _cleanProtocolStateList( websock_t* p, protocolState_t* ps ) { - msg_t* m0 = nullptr; - msg_t* m1 = ps->endMsg; + rc_t rc = kOkRC; + nbmpscq::blob_t b = get(p->_qH); - while( m1->link != nullptr ) + msg_t* oldest_msg = (msg_t*)b.blob; + + // if the last protocol end msg has been sent to all sessions ... + while( oldest_msg != nullptr && ps->endMsg != nullptr && ps->endMsg->sessionN >= ps->sessionN ) { - if( m1->link->sessionN >= ps->sessionN ) + // ... and this msg is also the oldest msg in the send queue + if( oldest_msg->msgId == ps->endMsg->msgId ) { - if( m0 == nullptr ) - ps->endMsg = m1->link; - else - m0->link = m1->link; - - msg_t* t = m1->link; - - //mem::free(m1->msg); - mem::free(m1); - - m1 = t; - - continue; + ps->endMsg = ps->endMsg->link; + b = advance(p->_qH); // ... then pop the msg off the send queue + oldest_msg = (msg_t*)b.blob; + } + else // ... otherwise the no further progress can be made + { + break; } - - m0 = m1; - m1 = m1->link; } + + if( ps->endMsg == nullptr ) + ps->begMsg = nullptr; + + return rc; } - rc_t _destroy( websock_t* p ) { - msg_t* m; + rc_t rc = kOkRC; + //msg_t* m; cwLogInfo("Websock: sent: msgs:%i largest msg:%i - recv: msgs:%i largest msg:%i - LWS_PRE:%i",p->_sendMsgCnt,p->_sendMaxByteN,p->_recvMsgCnt,p->_recvMaxByteN,LWS_PRE); cwLogInfo("Exec Time: %i avg ms, %i total ms, %i cnt", p->_execN==0 ? 0 : p->_execSumMs/p->_execN, p->_execSumMs, p->_execN ); @@ -298,39 +313,20 @@ namespace cw lws_context_destroy(p->_ctx); p->_ctx = nullptr; } - - if( p->_q != nullptr ) - { - while((m = p->_q->pop()) != nullptr) - { - //mem::free(m->msg); - mem::free(m); - } - - delete p->_q; - } - for(int i=0; p->_protocolA!=nullptr and p->_protocolA[i].callback != nullptr; ++i) { mem::free(const_cast(p->_protocolA[i].name)); // TODO: delete any msgs in the protocol state here auto ps = static_cast(p->_protocolA[i].user); - - m = ps->endMsg; - while( m != nullptr ) - { - msg_t* tmp = m->link; - - //mem::free(m->msg); - mem::free(m); - m = tmp; - } - + mem::free(ps); } - + + if((rc = nbmpscq::destroy(p->_qH)) != kOkRC ) + cwLogError(rc,"Websock queue destroy failed."); + mem::release(p->_protocolA); p->_protocolN = 0; @@ -573,11 +569,11 @@ cw::rc_t cw::websock::create( { // Allocate the application protocol state array where this application can keep protocol related info auto protocolState = mem::allocZ(1); - auto dummy = mem::allocZ(1); + //auto dummy = mem::allocZ(1); protocolState->thisPtr = p; - protocolState->begMsg = dummy; - protocolState->endMsg = dummy; + protocolState->begMsg = nullptr; + protocolState->endMsg = nullptr; // Setup the interal lws_protocols record struct lws_protocols* pr = p->_protocolA + i; @@ -607,7 +603,12 @@ cw::rc_t cw::websock::create( foreign_loops[0] = p; // pass in the custom poll object as the foreign loop object we will bind to info.foreign_loops = foreign_loops; - p->_q = new MpScNbQueue(); + if((rc = nbmpscq::create(p->_qH,3,4*4096)) != kOkRC ) + { + rc = cwLogError(rc,"Websock queue create failed."); + goto errLabel; + } + p->_cbFunc = cbFunc; p->_cbArg = cbArg; @@ -651,23 +652,33 @@ cw::rc_t cw::websock::destroy( handle_t& h ) cw::rc_t cw::websock::send(handle_t h, unsigned protocolId, unsigned sessionId, const void* msg, unsigned byteN ) { rc_t rc = kOkRC; - - uint8_t* mem = mem::allocZ( sizeof(msg_t) + LWS_PRE + byteN ); - - msg_t* m = (msg_t*)mem; - m->msg = mem + sizeof(msg_t); - - memcpy(m->msg+LWS_PRE,msg,byteN); - m->msgByteN = byteN; - m->protocolId = protocolId; - m->sessionId = sessionId; - websock_t* p = _handleToPtr(h); - p->_q->push(m); + + unsigned dataByteN = sizeof(msg_t) + LWS_PRE + byteN; + uint8_t mem[ dataByteN ]; + msg_t* m = (msg_t*)mem; + + memcpy(mem + sizeof(msg_t) + LWS_PRE,msg,byteN); + m->protocolId = protocolId; + m->sessionId = sessionId; + m->msg = nullptr; + m->msgByteN = byteN; // length of msg w/o LWS_PRE + m->msgId = kInvalidId; + m->sessionN = 0; + m->link = nullptr; + + + // put the outgoing msgs on the queue + if((rc = nbmpscq::push(p->_qH,mem,dataByteN)) != kOkRC ) + { + rc = cwLogError(rc,"Websock queue push failed."); + goto errLabel; + } p->_sendMsgCnt += 1; p->_sendMaxByteN = std::max(p->_sendMaxByteN,byteN); +errLabel: return rc; } @@ -704,28 +715,61 @@ cw::rc_t cw::websock::exec( handle_t h, unsigned timeOutMs ) time::spec_t t0 = time::current_time(); - // service any pending websocket activity - with no-timeout + // service any pending websocket activity - with no timeout lws_service_tsi(p->_ctx, -1, 0 ); - msg_t* m; - - // Get the next pending outgoing message. - while((m = p->_q->pop()) != nullptr ) + // clean already sent messages from each protocol outgoing msg list + for(unsigned i=0; i_protocolN-1; ++i) { - auto protocol = _idToProtocol(p,m->protocolId); + protocolState_t* ps = static_cast(p->_protocolA[i].user); + _cleanProtocolStateList( p, ps ); + } - // Get the application protcol record for this message + // Get the next pending outgoing message. + while(1) + { + nbmpscq::blob_t b = nbmpscq::peek(p->_qH); + + // if the outgoing queue is empty + if( b.blob == nullptr ) + break; + + msg_t* m = (msg_t*)b.blob; + + // if msg data ptr is not null then this msg has been seen in an earlier call to this function. + if( m->msg != nullptr ) + break; + + m->msg = (unsigned char*)(m+1); + + // Get the protocol record for this msg. + struct lws_protocols* protocol = _idToProtocol(p,m->protocolId); + + // Get the application protcol state record from the protocol 'user' field protocolState_t* ps = static_cast(protocol->user); // remove messages from the protocol message queue which have already been sent - _cleanProtocolStateList( ps ); - + //_cleanProtocolStateList( p, ps ); + + // Put the msg in the front of the protocal state list (msg's are removed from the back of the list) m->msgId = ps->nextNewMsgId; // set the msg id - ps->begMsg->link = m; // put the msg on the front of the outgoing queue - ps->begMsg = m; // + m->link = nullptr; ps->nextNewMsgId += 1; - - lws_callback_on_writable_all_protocol(p->_ctx,protocol); + + if( ps->begMsg == nullptr ) + { + ps->begMsg = m; + ps->endMsg = m; + } + else + { + ps->begMsg->link = m; + ps->begMsg = m; + } + + // we want one callback for each session + for(unsigned i=0; isessionN; ++i) + lws_callback_on_writable_all_protocol(p->_ctx,protocol); lws_service_tsi(p->_ctx, -1, 0 ); } From c9281603de4b1516dea0bea8593856d4f731aa79 Mon Sep 17 00:00:00 2001 From: kevin Date: Mon, 25 Mar 2024 14:29:25 -0400 Subject: [PATCH 003/101] cwNbMpScQueue.h/cpp: Added count(). --- cwNbMpScQueue.cpp | 12 ++++++++++++ cwNbMpScQueue.h | 2 ++ 2 files changed, 14 insertions(+) diff --git a/cwNbMpScQueue.cpp b/cwNbMpScQueue.cpp index 2940b8f..70fbb1d 100644 --- a/cwNbMpScQueue.cpp +++ b/cwNbMpScQueue.cpp @@ -408,6 +408,18 @@ bool cw::nbmpscq::is_empty( handle_t h ) 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 ) { rc_t rc=kOkRC,rc0,rc1; diff --git a/cwNbMpScQueue.h b/cwNbMpScQueue.h index ac3dbf2..fe6c13e 100644 --- a/cwNbMpScQueue.h +++ b/cwNbMpScQueue.h @@ -71,6 +71,8 @@ namespace cw bool is_empty( handle_t h ); + unsigned count( handle_t h ); + rc_t test( const object_t* cfg ); } From 55aed815e199123295138aec51c949fea9826559 Mon Sep 17 00:00:00 2001 From: kevin Date: Mon, 25 Mar 2024 14:29:58 -0400 Subject: [PATCH 004/101] cwUi.h/cpp : Added ui::ws::realTimeReport(). --- cwUi.cpp | 8 ++++++++ cwUi.h | 3 +++ 2 files changed, 11 insertions(+) diff --git a/cwUi.cpp b/cwUi.cpp index 5792db7..62427c4 100644 --- a/cwUi.cpp +++ b/cwUi.cpp @@ -2210,6 +2210,7 @@ void cw::ui::realTimeReport( handle_t h ) { ui_t* p = _handleToPtr(h); printf("UI msg count: recv:%i send:%i\n",p->recvMsgN,p->sentMsgN); + } @@ -2498,6 +2499,12 @@ cw::ui::handle_t cw::ui::ws::uiHandle( handle_t h ) return p->uiH; } +void cw::ui::ws::realTimeReport( handle_t h ) +{ + ui_ws_t* p = _handleToPtr(h); + report(p->wsH); + realTimeReport(p->uiH); +} namespace cw @@ -2668,3 +2675,4 @@ cw::ui::handle_t cw::ui::srv::uiHandle( handle_t h ) ui_ws_srv_t* p = _handleToPtr(h); return ws::uiHandle(p->wsUiH); } + diff --git a/cwUi.h b/cwUi.h index 7bbf535..e4077e4 100644 --- a/cwUi.h +++ b/cwUi.h @@ -236,6 +236,9 @@ namespace cw websock::handle_t websockHandle( handle_t h ); ui::handle_t uiHandle( handle_t h ); + + void realTimeReport( handle_t h ); + } namespace srv From 230f3841d750edbbbc2382a3512c1414ce26d30f Mon Sep 17 00:00:00 2001 From: kevin Date: Mon, 25 Mar 2024 14:30:47 -0400 Subject: [PATCH 005/101] cwIo.cpp: Added call to uiRealTimeReport() in call to io::realTimeReport(). --- cwIo.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cwIo.cpp b/cwIo.cpp index 9d23d5d..bb5fae2 100644 --- a/cwIo.cpp +++ b/cwIo.cpp @@ -2497,7 +2497,7 @@ void cw::io::realTimeReport( handle_t h ) { io_t* p = _handleToPtr(h); audio::device::realTimeReport(p->audioH); - + uiRealTimeReport(h); } @@ -3957,8 +3957,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); } From 488648a9675281d4860fa6349b828303e33da34d Mon Sep 17 00:00:00 2001 From: kevin Date: Mon, 25 Mar 2024 14:31:03 -0400 Subject: [PATCH 006/101] cwWebSock.cpp : Added report(). --- cwWebSock.cpp | 6 ++++++ cwWebSock.h | 2 ++ 2 files changed, 8 insertions(+) diff --git a/cwWebSock.cpp b/cwWebSock.cpp index 156dfca..4d5650d 100644 --- a/cwWebSock.cpp +++ b/cwWebSock.cpp @@ -783,3 +783,9 @@ cw::rc_t cw::websock::exec( handle_t h, unsigned timeOutMs ) return rc; } + +void cw::websock::report( handle_t h ) +{ + websock_t* p = _handleToPtr(h); + printf("Websock: msgs sent:%i recvd:%i que count:%i\n",p->_sendMsgCnt,p->_recvMsgCnt,count(p->_qH)); +} diff --git a/cwWebSock.h b/cwWebSock.h index e87fb04..de6dece 100644 --- a/cwWebSock.h +++ b/cwWebSock.h @@ -56,6 +56,8 @@ namespace cw // Call periodically from the same thread to send/recv messages. rc_t exec( handle_t h, unsigned timeOutMs ); + + void report( handle_t h ); } From 43c5be958a957c7041e6a28593d21bac3ec8796b Mon Sep 17 00:00:00 2001 From: kevin Date: Mon, 25 Mar 2024 22:14:42 -0400 Subject: [PATCH 007/101] cwFileSys.cpp : Fixed failure to free expanded path on path not existing in isFile(),isDir(),isLink(). --- cwFileSys.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cwFileSys.cpp b/cwFileSys.cpp index 32264cc..6661082 100644 --- a/cwFileSys.cpp +++ b/cwFileSys.cpp @@ -75,7 +75,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 +106,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 +137,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; From 89a4b83c455a315795f5fa7ecb2e6c780e379167 Mon Sep 17 00:00:00 2001 From: kevin Date: Mon, 25 Mar 2024 22:17:40 -0400 Subject: [PATCH 008/101] cwUi.cpp : 1. Add one to hashA[] to prevent a hash index of 0xffff from going past the end of the array. 2. Fix bug where hash table buckets were not released. --- cwUi.cpp | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/cwUi.cpp b/cwUi.cpp index a744f31..3ba352f 100644 --- a/cwUi.cpp +++ b/cwUi.cpp @@ -117,7 +117,7 @@ namespace cw unsigned sentMsgN; unsigned recvMsgN; - bucket_t hashA[ hashN ]; + bucket_t hashA[ hashN+1 ]; } ui_t; @@ -129,7 +129,11 @@ namespace cw assert( parentUuId != kInvalidId && appId != kInvalidId ); unsigned hc = parentUuId + cwSwap32(appId); - return (unsigned short)(((hc & 0xffff0000)>>16) + (hc & 0x0000ffff)); + unsigned short hash_idx = (unsigned short)(((hc & 0xffff0000)>>16) + (hc & 0x0000ffff)); + + assert( hash_idx <= hashN ); + + return hash_idx; } @@ -225,6 +229,18 @@ namespace cw m = m0; } + // Note: hashA[] has hashN+1 elements + for(unsigned i=0; i<=hashN; ++i) + { + bucket_t* b = p->hashA[i].link; + while( b!=nullptr ) + { + bucket_t* b0 = b->link; + mem::release(b); + b = b0; + } + } + mem::release(p->sessA); mem::release(p->eleA); mem::release(p->buf); From 73b433feae89c6d420583202c9951613808b65de Mon Sep 17 00:00:00 2001 From: kevin Date: Wed, 27 Mar 2024 20:19:06 -0400 Subject: [PATCH 009/101] cwPresetSel.cpp : Fix problem where there were two version of delete_fragment() - one of which allowed a memory leak. --- cwPresetSel.cpp | 99 ++++++++++++++++++++++--------------------------- 1 file changed, 45 insertions(+), 54 deletions(-) diff --git a/cwPresetSel.cpp b/cwPresetSel.cpp index 0324269..0f3a420 100644 --- a/cwPresetSel.cpp +++ b/cwPresetSel.cpp @@ -102,38 +102,52 @@ namespace cw return kInvalidIdx; } - - 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; - for(unsigned i=0; ipresetN; ++i) - mem::release(f1->presetA[i].alt_str); + rc_t _delete_fragment( preset_sel_t* p, unsigned fragId ) + { + rc_t rc = kOkRC; + frag_t* f = p->fragL; + + for(; f!=nullptr; f=f->link) + { + if( f->fragId == fragId ) + { + // if this is the first frag in the list + if( f->prev == nullptr ) + p->fragL = f->link; + else + { + + // 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; ipresetN; ++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; } - - return kOkRC; - } + rc = cwLogError(kEleNotFoundRC,"The fragment with id %i was not found.",fragId); + + errLabel: + return rc; + } + void _destroy_all_frags( preset_sel_t* p ) { while( p->fragL != nullptr) @@ -242,7 +256,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 @@ -257,6 +271,9 @@ namespace cw assert( asi < alt_strN ); 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); @@ -1145,33 +1162,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 ) From 475cf5e541d91c380b2bed5651c8f4ed47d03f67 Mon Sep 17 00:00:00 2001 From: kevin Date: Thu, 28 Mar 2024 19:43:40 -0400 Subject: [PATCH 010/101] cwNbMpScQueue.h/c : Fixed bug where the tail was not correctly updated in advance(). Added peek_reset(). --- cwNbMpScQueue.cpp | 71 ++++++++++++++++++++++++++++------------------- cwNbMpScQueue.h | 16 ++++++++++- 2 files changed, 57 insertions(+), 30 deletions(-) diff --git a/cwNbMpScQueue.cpp b/cwNbMpScQueue.cpp index 70fbb1d..2f21581 100644 --- a/cwNbMpScQueue.cpp +++ b/cwNbMpScQueue.cpp @@ -164,6 +164,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); + } + } } @@ -204,26 +217,6 @@ cw::rc_t cw::nbmpscq::create( handle_t& hRef, unsigned initBlkN, unsigned blkByt } - - /* - byteN = initBlkN * (sizeof(block_t) + blkByteN ); - p->mem = mem::allocZ(byteN); - - for(unsigned i=0; imem+i); - b->buf = (uint8_t*)(b + 1); - b->bufByteN = blkByteN; - - b->full_flag.store(false); - b->index.store(0); - b->eleN.store(0); - - b->link = p->blockL; - p->blockL = b; - } - */ - hRef.set(p); errLabel: @@ -275,6 +268,8 @@ cw::rc_t cw::nbmpscq::push( handle_t h, const void* blob, unsigned blobByteN ) // 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) { @@ -325,7 +320,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; @@ -335,9 +336,10 @@ cw::rc_t cw::nbmpscq::push( handle_t h, const void* blob, unsigned blobByteN ) 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* node = t->next.load(std::memory_order_acquire); // ACQUIRE 'next' from producer + nbmpscq_t* p = _handleToPtr(h); + + // We always access the tail element through tail->next. + node_t* node = p->tail->next.load(std::memory_order_acquire); // ACQUIRE 'next' from producer _init_blob( blob, node ); @@ -349,17 +351,23 @@ cw::nbmpscq::blob_t cw::nbmpscq::advance( handle_t h ) blob_t blob; nbmpscq_t* p = _handleToPtr(h); node_t* t = p->tail; - node_t* next = t->next.load(std::memory_order_acquire); // ACQUIRE 'next' from producer + // 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 ); + // next was valid and so eleN must be >= 1 + assert( eleN >= 1 ); + } } @@ -393,7 +401,12 @@ cw::nbmpscq::blob_t cw::nbmpscq::peek( handle_t h ) 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; } diff --git a/cwNbMpScQueue.h b/cwNbMpScQueue.h index fe6c13e..591d6bf 100644 --- a/cwNbMpScQueue.h +++ b/cwNbMpScQueue.h @@ -39,11 +39,20 @@ 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; @@ -68,9 +77,14 @@ namespace cw // 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 ); From 60e6c2f772fb4f3880ba72b2c96382f67d851917 Mon Sep 17 00:00:00 2001 From: kevin Date: Thu, 28 Mar 2024 19:45:40 -0400 Subject: [PATCH 011/101] cwWebSock.h/cpp : Optimize exec() to decrease cwIoPresetSelApp GUI startup time. Added _timed_exec_0/1/2() --- cwWebSock.cpp | 106 ++++++++++++++++++++++++++++++++++++++++++++++---- cwWebSock.h | 4 +- 2 files changed, 101 insertions(+), 9 deletions(-) diff --git a/cwWebSock.cpp b/cwWebSock.cpp index 7411b07..e8a682d 100644 --- a/cwWebSock.cpp +++ b/cwWebSock.cpp @@ -462,12 +462,15 @@ namespace cw } - rc_t _exec( websock_t* p, unsigned timeOutMs ) + rc_t _exec( websock_t* p, unsigned timeOutMs, bool* msg_fl_ref=nullptr ) { rc_t rc = kOkRC; int adjTimeOut = lws_service_adjust_timeout(p->_ctx, timeOutMs, 0); int sysRC = 0; + if( msg_fl_ref != nullptr ) + *msg_fl_ref = false; + if( p->_pollfdN > 0 ) sysRC = poll(p->_pollfdA, p->_pollfdN, adjTimeOut); @@ -509,11 +512,76 @@ namespace cw { // check if it is an fd owned by the application } + + if( msg_fl_ref != nullptr ) + *msg_fl_ref = true; + } errLabel: return rc; } + + // Call _exec() repeatedly for at least timeOutMs-5 milliseconds. + rc_t _timed_exec_0( websock_t* p, unsigned timeOutMs ) + { + rc_t rc = kOkRC; + time::spec_t t0 = time::current_time(); + unsigned dMs = 0; + unsigned adjTimeOutMs; + + do { + + adjTimeOutMs = timeOutMs - dMs; + + if((rc = _exec(p,adjTimeOutMs)) != kOkRC ) + break; + + dMs = time::elapsedMs(t0); + + }while( dMs+5 < timeOutMs ); + + return rc; + } + + // Call _exec() as long as incoming messages are received. + rc_t _timed_exec_1( websock_t* p, unsigned timeOutMs ) + { + rc_t rc = kOkRC; + bool fl = false; + + do { + + fl = false; + if((rc = _exec(p,5,&fl)) != kOkRC ) + break; + + }while( fl ); + + return rc; + } + + // Call _exec() as long as incoming messages are received. + // or 'timeOutMs' time elapses. + rc_t _timed_exec_2( websock_t* p, unsigned timeOutMs ) + { + rc_t rc = kOkRC; + bool fl = false; + time::spec_t t0 = time::current_time(); + unsigned dMs = 0; + + do { + + fl = false; + if((rc = _exec(p,5,&fl)) != kOkRC ) + break; + + dMs = time::elapsedMs(t0); + + }while( fl && (dMs+5 < timeOutMs) ); + + return rc; + } } } @@ -526,7 +594,9 @@ cw::rc_t cw::websock::create( const char* dfltHtmlPageFn, int port, const protocol_t* protocolArgA, - unsigned protocolN ) + unsigned protocolN, + unsigned queueBlkCnt, + unsigned queueBlkByteCnt ) { rc_t rc; struct lws_context_creation_info info; @@ -571,7 +641,6 @@ cw::rc_t cw::websock::create( { // Allocate the application protocol state array where this application can keep protocol related info auto protocolState = mem::allocZ(1); - //auto dummy = mem::allocZ(1); protocolState->thisPtr = p; protocolState->begMsg = nullptr; @@ -605,7 +674,7 @@ cw::rc_t cw::websock::create( foreign_loops[0] = p; // pass in the custom poll object as the foreign loop object we will bind to info.foreign_loops = foreign_loops; - if((rc = nbmpscq::create(p->_qH,3,4*4096)) != kOkRC ) + if((rc = nbmpscq::create(p->_qH,queueBlkCnt,queueBlkByteCnt)) != kOkRC ) { rc = cwLogError(rc,"Websock queue create failed."); goto errLabel; @@ -718,8 +787,10 @@ cw::rc_t cw::websock::exec( handle_t h, unsigned timeOutMs ) time::spec_t t0 = time::current_time(); // service any pending websocket activity - with no timeout - lws_service_tsi(p->_ctx, -1, 0 ); + //_timed_exec_1(p,timeOutMs/2); + _exec(p,0); + // clean already sent messages from each protocol outgoing msg list for(unsigned i=0; i_protocolN-1; ++i) { @@ -727,6 +798,8 @@ cw::rc_t cw::websock::exec( handle_t h, unsigned timeOutMs ) _cleanProtocolStateList( p, ps ); } + nbmpscq::peek_reset(p->_qH); + // Get the next pending outgoing message. while(1) { @@ -751,7 +824,7 @@ cw::rc_t cw::websock::exec( handle_t h, unsigned timeOutMs ) protocolState_t* ps = static_cast(protocol->user); // remove messages from the protocol message queue which have already been sent - //_cleanProtocolStateList( p, ps ); + _cleanProtocolStateList( p, ps ); // Put the msg in the front of the protocal state list (msg's are removed from the back of the list) m->msgId = ps->nextNewMsgId; // set the msg id @@ -771,13 +844,16 @@ cw::rc_t cw::websock::exec( handle_t h, unsigned timeOutMs ) // we want one callback for each session for(unsigned i=0; isessionN; ++i) + { lws_callback_on_writable_all_protocol(p->_ctx,protocol); - lws_service_tsi(p->_ctx, -1, 0 ); + _exec(p,0); + + } } // block waiting for incoming messages - _exec(p,timeOutMs); + _timed_exec_2(p,timeOutMs); p->_execSumMs += time::elapsedMs(t0); p->_execN += 1; @@ -789,5 +865,19 @@ cw::rc_t cw::websock::exec( handle_t h, unsigned timeOutMs ) void cw::websock::report( handle_t h ) { websock_t* p = _handleToPtr(h); + printf("Websock: msgs sent:%i recvd:%i que count:%i\n",p->_sendMsgCnt,p->_recvMsgCnt,count(p->_qH)); + + // clean already sent messages from each protocol outgoing msg list + for(unsigned i=0; i_protocolN-1; ++i) + { + protocolState_t* ps = static_cast(p->_protocolA[i].user); + + const msg_t* m; + unsigned cnt = 0; + for(m=ps->endMsg; m!=nullptr; m=m->link) + cnt += 1; + + printf("Protocol:%i %i\n",i,cnt); + } } diff --git a/cwWebSock.h b/cwWebSock.h index de6dece..b7884ed 100644 --- a/cwWebSock.h +++ b/cwWebSock.h @@ -45,7 +45,9 @@ namespace cw const char* dfltHtmlPageFn, int port, const protocol_t* protocolA, - unsigned protocolN ); + unsigned protocolN, + unsigned queueBlkCnt, + unsigned queueBlkByteCnt ); rc_t destroy( handle_t& h ); From 98ed33d2cc3f5a9268edd16e73b5237b63c8f53d Mon Sep 17 00:00:00 2001 From: kevin Date: Thu, 28 Mar 2024 19:47:29 -0400 Subject: [PATCH 012/101] cwUi.h/cpp : Add queueBlkCnt and queueBlkByteCnt to websock based API's. Removed websockTimeOutMs from UI cfg. and replace it as an argument to ui::ws::exec(). --- cwUi.cpp | 123 +++++++++++++++++++++++++++++++------------------------ cwUi.h | 58 ++++++++++++++------------ 2 files changed, 100 insertions(+), 81 deletions(-) diff --git a/cwUi.cpp b/cwUi.cpp index 3ba352f..722ca9f 100644 --- a/cwUi.cpp +++ b/cwUi.cpp @@ -166,7 +166,7 @@ namespace cw bucket_t* b = p->hashA + hash_idx; for(; b!=nullptr; b=b->link) - if( b->ele->appId==appId && b->ele->logical_parent->uuId==parentUuId && (chanId==kInvalidId || b->ele->chanId==chanId) ) + if( b->ele != nullptr && b->ele->logical_parent!=nullptr && b->ele->appId==appId && b->ele->logical_parent->uuId==parentUuId && (chanId==kInvalidId || b->ele->chanId==chanId) ) return b->ele->uuId; } @@ -2249,7 +2249,6 @@ namespace cw void* cbArg; uiCallback_t uiCbFunc; websock::cbFunc_t wsCbFunc; - unsigned wsTimeOutMs; unsigned idleMsgPeriodMs; time::spec_t lastRecvMsgTimeStamp; } ui_ws_t; @@ -2303,7 +2302,7 @@ namespace cw ui_ws_t* p = static_cast(cbArg); return websock::send( p->wsH, kUiProtocolId, wsSessId, msg, msgByteN ); } - + } } } @@ -2331,13 +2330,18 @@ cw::rc_t cw::ui::ws::parseArgs( const object_t& o, args_t& args, const char* ob "rcvBufByteN", args.rcvBufByteN, "xmtBufByteN", args.xmtBufByteN, "fmtBufByteN", args.fmtBufByteN, - "websockTimeOutMs", args.wsTimeOutMs, "idleMsgPeriodMs", args.idleMsgPeriodMs, + "queueBlkCnt", args.queueBlkCnt, + "queueBlkByteCnt", args.queueBlkByteCnt, "uiCfgFn", uiCfgFn )) != kOkRC ) { rc = cwLogError(rc,"'ui' cfg. parse failed."); } + if( op->find_child("websockTimeOutMs") != nullptr ) + cwLogWarning("The UI parameter 'websockTimeOutMs' is obsolete and ignored."); + + // expand the physical root directory if((physRootDir = filesys::expandPath( args.physRootDir)) == nullptr ) { @@ -2388,11 +2392,12 @@ cw::rc_t cw::ui::ws::create( handle_t& h, appIdMapN, wsCbFunc, args.dfltPageFn, - args.wsTimeOutMs, args.idleMsgPeriodMs, args.rcvBufByteN, args.xmtBufByteN, - args.fmtBufByteN ); + args.fmtBufByteN, + args.queueBlkCnt, + args.queueBlkByteCnt); } @@ -2406,11 +2411,12 @@ cw::rc_t cw::ui::ws::create( handle_t& h, unsigned appIdMapN, websock::cbFunc_t wsCbFunc, const char* dfltPageFn, - unsigned websockTimeOutMs, unsigned idleMsgPeriodMs, unsigned rcvBufByteN, unsigned xmtBufByteN, - unsigned fmtBufByteN ) + unsigned fmtBufByteN, + unsigned queueBlkCnt, + unsigned queueBlkByteCnt ) { rc_t rc = kOkRC; @@ -2430,7 +2436,7 @@ cw::rc_t cw::ui::ws::create( handle_t& h, void* wsCbA = wsCbFunc==nullptr ? p : cbArg; // create the websocket - if((rc = websock::create(p->wsH, wsCbF, wsCbA, physRootDir, dfltPageFn, port, protocolA, protocolN )) != kOkRC ) + if((rc = websock::create(p->wsH, wsCbF, wsCbA, physRootDir, dfltPageFn, port, protocolA, protocolN, queueBlkCnt, queueBlkByteCnt )) != kOkRC ) { cwLogError(rc,"UI Websock create failed."); goto errLabel; @@ -2446,7 +2452,6 @@ cw::rc_t cw::ui::ws::create( handle_t& h, p->cbArg = cbArg; p->uiCbFunc = uiCbFunc; p->wsCbFunc = wsCbFunc; - p->wsTimeOutMs = websockTimeOutMs; p->idleMsgPeriodMs = idleMsgPeriodMs; // initialize the last received msg time::get(p->lastRecvMsgTimeStamp); @@ -2479,13 +2484,13 @@ cw::rc_t cw::ui::ws::destroy( handle_t& h ) return rc; } -cw::rc_t cw::ui::ws::exec( handle_t h ) +cw::rc_t cw::ui::ws::exec( handle_t h, unsigned wsTimeOutMs ) { - rc_t rc = kOkRC; ui_ws_t* p = _handleToPtr(h); + rc_t rc = kOkRC; time::spec_t t; - if((rc = websock::exec( p->wsH, p->wsTimeOutMs )) != kOkRC) + if((rc = websock::exec( p->wsH, wsTimeOutMs )) != kOkRC) cwLogError(rc,"The UI websock execution failed."); // make the idle callback @@ -2500,6 +2505,7 @@ cw::rc_t cw::ui::ws::exec( handle_t h ) return rc; } + cw::rc_t cw::ui::ws::onReceive( handle_t h, unsigned protocolId, unsigned sessionId, websock::msgTypeId_t msg_type, const void* msg, unsigned byteN ) { ui_ws_t* p = _handleToPtr(h); @@ -2521,6 +2527,7 @@ cw::ui::handle_t cw::ui::ws::uiHandle( handle_t h ) return p->uiH; } + void cw::ui::ws::realTimeReport( handle_t h ) { ui_ws_t* p = _handleToPtr(h); @@ -2539,6 +2546,7 @@ namespace cw { ws::handle_t wsUiH; thread::handle_t thH; + unsigned wsTimeOutMs; } ui_ws_srv_t; ui_ws_srv_t* _handleToPtr(handle_t h ) @@ -2563,7 +2571,7 @@ namespace cw ui_ws_srv_t* p = static_cast(arg); rc_t rc; - if((rc = ws::exec(p->wsUiH)) != kOkRC ) + if((rc = ws::exec(p->wsUiH,p->wsTimeOutMs)) != kOkRC ) { cwLogError(rc,"Websocket UI exec failed."); } @@ -2574,20 +2582,51 @@ namespace cw } } +cw::rc_t cw::ui::srv::create( handle_t& h, + const ws::args_t& args, + void* cbArg, + uiCallback_t uiCbFunc, + const appIdMap_t* appIdMapA, + unsigned appIdMapN, + unsigned wsTimeOutMs, + websock::cbFunc_t wsCbFunc ) +{ + return create(h, + args.port, + args.physRootDir, + cbArg, + uiCbFunc, + args.uiRsrc, + appIdMapA, + appIdMapN, + wsTimeOutMs, + wsCbFunc, + args.dfltPageFn, + args.idleMsgPeriodMs, + args.rcvBufByteN, + args.xmtBufByteN, + args.fmtBufByteN, + args.queueBlkCnt, + args.queueBlkByteCnt ); +} + cw::rc_t cw::ui::srv::create( handle_t& h, - unsigned port, - const char* physRootDir, - void* cbArg, - uiCallback_t uiCbFunc, - const object_t* uiRsrc, - const appIdMap_t* appIdMapA, - unsigned appIdMapN, - websock::cbFunc_t wsCbFunc, - const char* dfltPageFn, - unsigned websockTimeOutMs, - unsigned rcvBufByteN, - unsigned xmtBufByteN, - unsigned fmtBufByteN ) + unsigned port, + const char* physRootDir, + void* cbArg, + uiCallback_t uiCbFunc, + const object_t* uiRsrc, + const appIdMap_t* appIdMapA, + unsigned appIdMapN, + unsigned wsTimeOutMs, + websock::cbFunc_t wsCbFunc, + const char* dfltPageFn, + unsigned idleMsgPeriodMs, + unsigned rcvBufByteN, + unsigned xmtBufByteN, + unsigned fmtBufByteN, + unsigned queueBlkCnt, + unsigned queueBlkByteCnt ) { rc_t rc = kOkRC; if((rc = destroy(h)) != kOkRC ) @@ -2595,7 +2634,7 @@ cw::rc_t cw::ui::srv::create( handle_t& h, ui_ws_srv_t* p = mem::allocZ(); - if((rc = ws::create(p->wsUiH, port, physRootDir, cbArg, uiCbFunc, uiRsrc,appIdMapA, appIdMapN, wsCbFunc, dfltPageFn, websockTimeOutMs, rcvBufByteN, xmtBufByteN, fmtBufByteN )) != kOkRC ) + if((rc = ws::create(p->wsUiH, port, physRootDir, cbArg, uiCbFunc, uiRsrc,appIdMapA, appIdMapN, wsCbFunc, dfltPageFn, idleMsgPeriodMs, rcvBufByteN, xmtBufByteN, fmtBufByteN, queueBlkCnt, queueBlkByteCnt )) != kOkRC ) { cwLogError(rc,"The websock UI creationg failed."); goto errLabel; @@ -2607,6 +2646,8 @@ cw::rc_t cw::ui::srv::create( handle_t& h, goto errLabel; } + p->wsTimeOutMs = wsTimeOutMs; + h.set(p); errLabel: @@ -2616,32 +2657,6 @@ cw::rc_t cw::ui::srv::create( handle_t& h, return rc; } -cw::rc_t cw::ui::srv::create( handle_t& h, - const ws::args_t& args, - void* cbArg, - uiCallback_t uiCbFunc, - const appIdMap_t* appIdMapA, - unsigned appIdMapN, - websock::cbFunc_t wsCbFunc ) -{ - return create(h, - args.port, - args.physRootDir, - cbArg, - uiCbFunc, - args.uiRsrc, - appIdMapA, - appIdMapN, - wsCbFunc, - args.dfltPageFn, - args.wsTimeOutMs, - args.rcvBufByteN, - args.xmtBufByteN, - args.fmtBufByteN ); -} - - - cw::rc_t cw::ui::srv::destroy( handle_t& h ) { rc_t rc = kOkRC; diff --git a/cwUi.h b/cwUi.h index e4077e4..fa685f9 100644 --- a/cwUi.h +++ b/cwUi.h @@ -188,8 +188,9 @@ namespace cw unsigned rcvBufByteN; unsigned xmtBufByteN; unsigned fmtBufByteN; - unsigned wsTimeOutMs; unsigned idleMsgPeriodMs; // min period without messages before an idle message is generated + unsigned queueBlkCnt; + unsigned queueBlkByteCnt; } args_t; rc_t parseArgs( const object_t& o, args_t& args, const char* object_label="ui" ); @@ -214,20 +215,20 @@ namespace cw unsigned appIdMapN = 0, websock::cbFunc_t wsCbFunc = nullptr, const char* dfltPageFn = "index.html", - unsigned websockTimeOutMs = 50, unsigned idleMsgPeriodMs = 50, unsigned rcvBufByteN = 1024, unsigned xmtBufByteN = 1024, - unsigned fmtBufByteN = 4096 - ); + unsigned fmtBufByteN = 4096, + unsigned queueBlkCnt = 4, + unsigned queueBlkByteCnt = 4096 ); rc_t destroy( handle_t& h ); // This function should be called periodically to send and receive // queued messages to and from the websocket. // Note that this call may block for up to 'wsTimeOutMs' milliseconds - // on the websocket handle. - rc_t exec( handle_t h ); + // while waiting for incoming websocket messages. + rc_t exec( handle_t h, unsigned wsTimeOutMs ); // This function executes the internal default websock callback function. // It is useful if the user provides a custom websock callback function @@ -236,8 +237,7 @@ namespace cw websock::handle_t websockHandle( handle_t h ); ui::handle_t uiHandle( handle_t h ); - - void realTimeReport( handle_t h ); + void realTimeReport( handle_t h ); } @@ -247,27 +247,31 @@ namespace cw typedef handle handle_t; rc_t create( handle_t& h, - const ws::args_t& args, - void* cbArg, - uiCallback_t uiCbFunc, - const appIdMap_t* appIdMapA = nullptr, - unsigned appIdMapN = 0, - websock::cbFunc_t wsCbFunc = nullptr ); + const ws::args_t& args, + void* cbArg, + uiCallback_t uiCbFunc, + const appIdMap_t* appIdMapA = nullptr, + unsigned appIdMapN = 0, + unsigned wsTimeOutMs = 50, + websock::cbFunc_t wsCbFunc = nullptr ); rc_t create( handle_t& h, - unsigned port, - const char* physRootDir, - void* cbArg, - uiCallback_t uiCbFunc, - const object_t* uiRsrc = nullptr, - const appIdMap_t* appIdMapA = nullptr, - unsigned appIdMapN = 0, - websock::cbFunc_t wsCbFunc = nullptr, - const char* dfltPageFn = "index.html", - unsigned websockTimeOutMs = 50, - unsigned rcvBufByteN = 1024, - unsigned xmtBufByteN = 1024, - unsigned fmtBufByteN = 4096 ); + unsigned port, + const char* physRootDir, + void* cbArg, + uiCallback_t uiCbFunc, + const object_t* uiRsrc = nullptr, + const appIdMap_t* appIdMapA = nullptr, + unsigned appIdMapN = 0, + unsigned wsTimeOutMs = 50, + websock::cbFunc_t wsCbFunc = nullptr, + const char* dfltPageFn = "index.html", + unsigned idleMsgPeriodMs = 50, + unsigned rcvBufByteN = 1024, + unsigned xmtBufByteN = 1024, + unsigned fmtBufByteN = 4096, + unsigned queueBlkCnt = 4, + unsigned queueBlkByteCnt = 4096 ); rc_t destroy( handle_t& h ); From d5f75f961c6e67e986c4c15d7b9c1225538b520e Mon Sep 17 00:00:00 2001 From: kevin Date: Thu, 28 Mar 2024 19:48:46 -0400 Subject: [PATCH 013/101] Added 'timeOutMs' arg. to all calls to ui::ws::exec(). --- cwIo.cpp | 8 +++++--- cwIo.h | 7 ++++--- cwIoAudioMidi.cpp | 12 ++++++++++-- cwIoAudioMidiApp.cpp | 12 ++++++++++-- cwIoMinTest.cpp | 12 ++++++++++-- cwIoPresetSelApp.cpp | 46 +++++++++++++++++++++++++++++--------------- cwIoTest.cpp | 12 ++++++++++-- cwUiTest.cpp | 7 ++++--- cwWebSockSvr.cpp | 10 +++++++--- cwWebSockSvr.h | 4 +++- 10 files changed, 94 insertions(+), 36 deletions(-) diff --git a/cwIo.cpp b/cwIo.cpp index c00f79a..5a0ebc5 100644 --- a/cwIo.cpp +++ b/cwIo.cpp @@ -2439,7 +2439,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 +2448,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,7 +2492,7 @@ void cw::io::report( handle_t h ) } for(unsigned i=0; ibeg_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, @@ -553,8 +555,8 @@ namespace cw { 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); } @@ -711,6 +713,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 ) { @@ -782,8 +785,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 ) @@ -1813,7 +1819,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; } @@ -1900,7 +1906,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; } @@ -3019,7 +3025,7 @@ rc_t _on_ui_play_loc(app_t* app, unsigned appId, unsigned loc); break; case kIoReportBtnId: - io::report( app->ioH ); + io::realTimeReport( app->ioH ); break; case kNetPrintBtnId: @@ -3586,6 +3592,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 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 diff --git a/cwIoTest.cpp b/cwIoTest.cpp index 298736b..94c27ed 100644 --- a/cwIoTest.cpp +++ b/cwIoTest.cpp @@ -308,8 +308,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: diff --git a/cwUiTest.cpp b/cwUiTest.cpp index cefc571..48054b9 100644 --- a/cwUiTest.cpp +++ b/cwUiTest.cpp @@ -378,8 +378,9 @@ namespace cw cw::rc_t cw::ui::test( const object_t* cfg ) { - rc_t rc = kOkRC; - ui::ws::args_t args = {}; + rc_t rc = kOkRC; + unsigned wsTimeOutMs = 50; + ui::ws::args_t args = {}; // Application Id's for the resource based UI elements. appIdMap_t mapA[] = @@ -437,7 +438,7 @@ cw::rc_t cw::ui::test( const object_t* cfg ) //app->uiCfgFn = "/home/kevin/src/cwtest/src/libcw/html/uiTest/ui.cfg"; // create the UI server - if((rc = srv::create(app->wsUiSrvH, args, app, _uiTestCallback, mapA, mapN, nullptr )) != kOkRC ) + if((rc = srv::create(app->wsUiSrvH, args, app, _uiTestCallback, mapA, mapN, wsTimeOutMs, nullptr )) != kOkRC ) return rc; if((rc = uiTestCreateUi( app )) != kOkRC ) diff --git a/cwWebSockSvr.cpp b/cwWebSockSvr.cpp index e55dbc4..9c083c8 100644 --- a/cwWebSockSvr.cpp +++ b/cwWebSockSvr.cpp @@ -56,7 +56,9 @@ cw::rc_t cw::websockSrv::create( int port, const websock::protocol_t* protocolA, unsigned protocolN, - unsigned timeOutMs ) + unsigned timeOutMs, + unsigned queueBlkCnt, + unsigned queueBlkByteCnt ) { rc_t rc; if((rc = destroy(h)) != kOkRC ) @@ -64,7 +66,7 @@ cw::rc_t cw::websockSrv::create( websockSrv_t* p = mem::allocZ(); - if((rc = websock::create( p->_websockH, cbFunc, cbArg, physRootDir, dfltHtmlPageFn, port, protocolA, protocolN )) != kOkRC ) + if((rc = websock::create( p->_websockH, cbFunc, cbArg, physRootDir, dfltHtmlPageFn, port, protocolA, protocolN, queueBlkCnt, queueBlkByteCnt )) != kOkRC ) goto errLabel; @@ -176,6 +178,8 @@ cw::rc_t cw::websockSrvTest( const object_t* cfg ) int port = 5687; unsigned rcvBufByteN = 128; unsigned xmtBufByteN = 128; + unsigned queueBlkCnt = 3; + unsigned queueBlkByteCnt= 4096; appCtx_t appCtx; enum @@ -198,7 +202,7 @@ cw::rc_t cw::websockSrvTest( const object_t* cfg ) unsigned protocolN = sizeof(protocolA)/sizeof(protocolA[0]); - if((rc = websockSrv::create( h, websockCb, &appCtx, physRootDir, dfltHtmlPageFn, port, protocolA, protocolN, timeOutMs )) != kOkRC ) + if((rc = websockSrv::create( h, websockCb, &appCtx, physRootDir, dfltHtmlPageFn, port, protocolA, protocolN, timeOutMs, queueBlkCnt, queueBlkByteCnt )) != kOkRC ) return rc; appCtx.wsH = websockSrv::websockHandle(h); diff --git a/cwWebSockSvr.h b/cwWebSockSvr.h index fdab325..2be27e4 100644 --- a/cwWebSockSvr.h +++ b/cwWebSockSvr.h @@ -16,7 +16,9 @@ namespace cw { int port, const websock::protocol_t* protocolA, unsigned protocolN, - unsigned websockTimeOutMs ); + unsigned websockTimeOutMs, + unsigned queueBlkCnt, + unsigned queueBlkByteCnt); rc_t destroy( handle_t& h ); From 97da53650415a24afef2b2dd62886968e6b8c90b Mon Sep 17 00:00:00 2001 From: kevin Date: Sat, 30 Mar 2024 18:01:08 -0400 Subject: [PATCH 014/101] cwIoPresetSelApp.cpp : beg_loc,end_loc is now applied to midi player from meta.cfg on file load. --- cwIoPresetSelApp.cpp | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/cwIoPresetSelApp.cpp b/cwIoPresetSelApp.cpp index b5c28c3..d33c970 100644 --- a/cwIoPresetSelApp.cpp +++ b/cwIoPresetSelApp.cpp @@ -293,7 +293,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; @@ -388,13 +388,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 ); @@ -436,7 +436,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, @@ -470,11 +470,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 ) { @@ -587,7 +589,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); @@ -1135,7 +1137,6 @@ namespace cw { if( preset_sel::track_loc( app->psH, loc, f ) ) { - _apply_preset( app, loc, (const perf_score::event_t*)msg_arg, f ); if( f != nullptr ) @@ -2131,7 +2132,7 @@ rc_t _on_ui_play_loc(app_t* app, unsigned appId, unsigned loc); m[i].id = e->uid; m[i].loc = e->loc; m[i].arg = e; - + app->locMap[i].loc = e->loc; app->locMap[i].timestamp = m[i].timestamp; @@ -2222,7 +2223,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; @@ -2267,8 +2268,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 @@ -2334,7 +2335,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; From 7d3be5ebc6ed6017676d6381f36a5061bf33ecbe Mon Sep 17 00:00:00 2001 From: kevin Date: Sat, 6 Apr 2024 15:41:20 -0400 Subject: [PATCH 015/101] cwIoPresetSelApp.cpp, ui.cfg : Add 'RT' and 'Hw' reports buttons. --- cwIoPresetSelApp.cpp | 12 ++++++++++++ html/preset_sel/ui.cfg | 2 ++ 2 files changed, 14 insertions(+) diff --git a/cwIoPresetSelApp.cpp b/cwIoPresetSelApp.cpp index b5c28c3..e717df3 100644 --- a/cwIoPresetSelApp.cpp +++ b/cwIoPresetSelApp.cpp @@ -47,6 +47,8 @@ namespace cw kPanelDivId = 1000, kQuitBtnId, kIoReportBtnId, + kIoHwReportBtnId, + kIoRtReportBtnId, kNetPrintBtnId, kReportBtnId, kLatencyBtnId, @@ -155,6 +157,8 @@ namespace cw { ui::kRootAppId, kPanelDivId, "panelDivId" }, { kPanelDivId, kQuitBtnId, "quitBtnId" }, { kPanelDivId, kIoReportBtnId, "ioReportBtnId" }, + { kPanelDivId, kIoHwReportBtnId, "ioHwReportBtnId" }, + { kPanelDivId, kIoRtReportBtnId, "ioRtReportBtnId" }, { kPanelDivId, kNetPrintBtnId, "netPrintBtnId" }, { kPanelDivId, kReportBtnId, "reportBtnId" }, { kPanelDivId, kLatencyBtnId, "latencyBtnId" }, @@ -3025,6 +3029,14 @@ rc_t _on_ui_play_loc(app_t* app, unsigned appId, unsigned loc); break; case kIoReportBtnId: + io::report( app->ioH ); + break; + + case kIoHwReportBtnId: + io::hardwareReport( app->ioH ); + break; + + case kIoRtReportBtnId: io::realTimeReport( app->ioH ); break; diff --git a/html/preset_sel/ui.cfg b/html/preset_sel/ui.cfg index 0b722b6..95de588 100644 --- a/html/preset_sel/ui.cfg +++ b/html/preset_sel/ui.cfg @@ -10,6 +10,8 @@ row: { button:{ name: quitBtnId, title:"Quit" }, button:{ name: ioReportBtnId, title:"IO Report" }, + button:{ name: ioRtReportBtnId, title: "IO RT Report" }, + button:{ name: ioHwReportBtnId, title: "IO Hw Report" }, button:{ name: netPrintBtnId, title:"Print Network" } button:{ name: reportBtnId, title:"App Report" }, button:{ name: latencyBtnId, title:"Latency Reset"}, From a6f518ed54c98cf93368ebf4755b84b77da076db Mon Sep 17 00:00:00 2001 From: kevin Date: Sat, 6 Apr 2024 15:44:43 -0400 Subject: [PATCH 016/101] cwMidiDevice.h/cpp,cwMidiDecls.h : Added getBuffer(),clearBuffer(),maxBufferMsgCount() to cwMidiDevice. Changed the MIDI device callback function to move the user arg outside of the 'packet_t'. Added midi::ch_msg_t --- cwMidiDecls.h | 17 +++++-- cwMidiDevice.cpp | 127 +++++++++++++++++++++++++++++++++++++++++++---- cwMidiDevice.h | 10 +++- 3 files changed, 140 insertions(+), 14 deletions(-) diff --git a/cwMidiDecls.h b/cwMidiDecls.h index 9746385..b282ccc 100644 --- a/cwMidiDecls.h +++ b/cwMidiDecls.h @@ -14,10 +14,10 @@ namespace cw uint8_t d0; // midi data byte 0 uint8_t d1; // midi data byte 1 } msg_t; - + 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 diff --git a/cwMidiDevice.cpp b/cwMidiDevice.cpp index d487eb9..f36845d 100644 --- a/cwMidiDevice.cpp +++ b/cwMidiDevice.cpp @@ -29,7 +29,7 @@ namespace cw kPausedStateId, kPlayingStateId } transportStateId_t; - + typedef struct device_str { cbFunc_t cbFunc; @@ -54,6 +54,11 @@ namespace cw unsigned long long offset_micros; unsigned long long last_posn_micros; time::spec_t start_time; + + ch_msg_t* buf; + unsigned bufN; + std::atomic buf_ii; + std::atomic buf_oi; } device_t; @@ -101,9 +106,12 @@ namespace cw rc = cwLogError(rc,"MIDI port thread destroy failed."); goto errLabel; } + destroy(p->alsaDevH); destroy(p->fileDevH); + + mem::release(p->buf); mem::release(p); errLabel: @@ -173,6 +181,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; imsgArray != nullptr ) + { + unsigned ii = p->buf_ii.load(); + unsigned oi = p->buf_oi.load(); + for(unsigned j=0; jmsgCnt; ++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 +235,9 @@ 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 ) { rc_t rc = kOkRC; rc_t rc1 = kOkRC; @@ -198,7 +247,11 @@ cw::rc_t cw::midi::device::create( handle_t& hRef, device_t* p = mem::allocZ(); - if((rc = create( p->alsaDevH, cbFunc, cbArg, parserBufByteCnt, appNameStr )) != kOkRC ) + if((rc = create( p->alsaDevH, + enableBufFl ? _callback : cbFunc, + enableBufFl ? p : cbArg, + parserBufByteCnt, + appNameStr )) != kOkRC ) { rc = cwLogError(rc,"ALSA MIDI device create failed."); goto errLabel; @@ -206,16 +259,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( bufferMsgCnt ); + p->bufN = bufferMsgCnt; + p->buf_ii.store(0); + p->buf_oi.store(0); if((rc = thread::create(p->threadH, _thread_func, @@ -257,6 +323,8 @@ 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; @@ -264,6 +332,8 @@ cw::rc_t cw::midi::device::create( handle_t& h, "fileDevName",fileDevName, "fileDevReadAheadMicros",fileDevReadAheadMicros, "parseBufByteCnt",parseBufByteCnt, + "enableBufFl",enableBufFl, + "bufferMsgCnt",bufMsgCnt, "file_ports",file_ports)) != kOkRC ) { rc = cwLogError(rc,"MIDI port parse args. failed."); @@ -290,7 +360,7 @@ 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); } @@ -437,15 +507,15 @@ const char* cw::midi::device::portName( handle_t h, unsigned devIdx, unsigned const char* name = nullptr; if((alsaDevIdx = _devIdxToAlsaDevIdx(p,devIdx)) != kInvalidIdx ) - name = portName(p->alsaDevH,alsaDevIdx,flags,portIdx); + name = portName(p->alsaDevH,alsaDevIdx,flags,portIdx); else if((fileDevIdx = _devIdxToFileDevIdx(p,devIdx)) != kInvalidIdx ) - name = portName(p->fileDevH,fileDevIdx,flags,portIdx); + name = portName(p->fileDevH,fileDevIdx,flags,portIdx); else 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; } @@ -573,7 +643,7 @@ errLabel: cw::rc_t cw::midi::device::setEndMsg( handle_t h, unsigned devIdx, unsigned portIdx, unsigned msgIdx ) { rc_t rc = kOkRC; - device_t* p = _handleToPtr(h); + device_t* p = _handleToPtr(h); if(_devIdxToFileDevIdx(p,devIdx) == kInvalidIdx ) { @@ -591,6 +661,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; @@ -668,6 +775,8 @@ cw::rc_t cw::midi::device::report( handle_t h ) goto errLabel; report(h,tbH); + + printf("%s\n",text(tbH)); errLabel: destroy(tbH); diff --git a/cwMidiDevice.h b/cwMidiDevice.h index 6c8b16d..237052f 100644 --- a/cwMidiDevice.h +++ b/cwMidiDevice.h @@ -30,7 +30,9 @@ 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. rc_t create( handle_t& h, cbFunc_t cbFunc, @@ -47,7 +49,7 @@ namespace cw const char* portName( handle_t h, unsigned devIdx, unsigned flags, unsigned portIdx ); unsigned portNameToIndex( handle_t h, unsigned devIdx, unsigned flags, const char* portName ); rc_t portEnable( handle_t h, unsigned devIdx, unsigned flags, unsigned portIdx, bool enableFl ); - + rc_t send( handle_t h, unsigned devIdx, unsigned portIdx, uint8_t st, uint8_t d0, uint8_t d1 ); rc_t sendData( handle_t h, unsigned devIdx, unsigned portIdx, const uint8_t* dataPtr, unsigned byteCnt ); @@ -57,6 +59,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 ); From 3526b2c8074e9519342a3672d56335c82136aeab Mon Sep 17 00:00:00 2001 From: kevin Date: Sat, 6 Apr 2024 16:02:52 -0400 Subject: [PATCH 017/101] cwMidiAlsa.cpp : Ports that do not qualify based on READ/WRITE attributes are now dropped. Ports without names are now given the default name ''. --- cwMidiAlsa.cpp | 45 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 39 insertions(+), 6 deletions(-) diff --git a/cwMidiAlsa.cpp b/cwMidiAlsa.cpp index cc969a7..bc33b7f 100644 --- a/cwMidiAlsa.cpp +++ b/cwMidiAlsa.cpp @@ -283,6 +283,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 +440,7 @@ namespace cw { assert(jdevArray[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 ? "" : 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 +462,7 @@ namespace cw { assert(kdevArray[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 ? "" : 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 +476,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: @@ -736,7 +742,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; } @@ -782,7 +788,7 @@ cw::rc_t cw::midi::device::alsa::send( handle_t h, unsigned devIdx, unsigned po rc_t rc = kOkRC; snd_seq_event_t ev; int arc; - alsa_device_t* p = _handleToPtr(h); + alsa_device_t* p = _handleToPtr(h); assert( p!=NULL && devIdx < p->devCnt && portIdx < p->devArray[devIdx].oPortCnt ); @@ -796,8 +802,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 +863,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; } From 374d3ad8a8f5a1e88ccd3110842257e4575697f0 Mon Sep 17 00:00:00 2001 From: kevin Date: Sat, 6 Apr 2024 16:04:42 -0400 Subject: [PATCH 018/101] cwMidiDeviceTest.cpp,cwMidiFileDev.cpp,cwMidiParser.cpp : Updates to account for change of MIDI callback signature in cwMidiDecls.h. --- cwMidiDeviceTest.cpp | 4 ++-- cwMidiFileDev.cpp | 3 +-- cwMidiParser.cpp | 17 +++++++++++++---- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/cwMidiDeviceTest.cpp b/cwMidiDeviceTest.cpp index 75049e4..174dcd3 100644 --- a/cwMidiDeviceTest.cpp +++ b/cwMidiDeviceTest.cpp @@ -86,7 +86,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 +95,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; jmsgCnt; ++j) if( p->msgArray != NULL ) diff --git a/cwMidiFileDev.cpp b/cwMidiFileDev.cpp index ba4a064..9978e87 100644 --- a/cwMidiFileDev.cpp +++ b/cwMidiFileDev.cpp @@ -428,13 +428,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 ); } } diff --git a/cwMidiParser.cpp b/cwMidiParser.cpp index cfb0bb0..0ea317d 100644 --- a/cwMidiParser.cpp +++ b/cwMidiParser.cpp @@ -58,8 +58,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 +102,19 @@ namespace cw // get a pointer to the next msg in the buffer msg_t* msgPtr = (msg_t*)(p->buf + p->bufIdx); + 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->status = p->status & 0xf0; - msgPtr->ch = p->status & 0x0f; msgPtr->uid = kInvalidId; switch( p->dataCnt ) From c4d518db47d0a4d51f97deead98e5ae180356bb3 Mon Sep 17 00:00:00 2001 From: kevin Date: Sat, 6 Apr 2024 16:06:17 -0400 Subject: [PATCH 019/101] cwIo.h,cpp : Updates to include cwMidiDevice input device buffer. Added hardwareReport(). Updates to account for change to midi::cbFunc_t. --- cwIo.cpp | 37 +++++++++++++++++++++++++++++++++---- cwIo.h | 7 +++++++ 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/cwIo.cpp b/cwIo.cpp index 5a0ebc5..fa2d66e 100644 --- a/cwIo.cpp +++ b/cwIo.cpp @@ -611,7 +611,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(pkt->cbArg); + io_t* p = reinterpret_cast(cbArg); rc_t rc = kOkRC; @@ -2114,10 +2114,12 @@ namespace cw rc = cwLogError(rc,"Audio device configuration failed."); goto errLabel; } - - audio::device::report( p->audioH ); errLabel: + + if( rc != kOkRC && p->audioH.isValid() ) + audio::device::report( p->audioH ); + return rc; } @@ -2495,6 +2497,15 @@ void cw::io::report( handle_t h ) 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); @@ -2749,6 +2760,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 ); diff --git a/cwIo.h b/cwIo.h index ec4a8e1..9251dac 100644 --- a/cwIo.h +++ b/cwIo.h @@ -172,7 +172,9 @@ namespace cw bool isShuttingDown( handle_t h ); void report( handle_t h ); + void hardwareReport( handle_t h ); void realTimeReport( handle_t h ); + //---------------------------------------------------------------------------------------------------------- // @@ -229,6 +231,11 @@ namespace cw const char* midiDevicePortName( handle_t h, unsigned devIdx, bool inputFl, unsigned portIdx ); 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 From e49df496e6d861fe54c486ef94a34d427050fc12 Mon Sep 17 00:00:00 2001 From: kevin Date: Sat, 6 Apr 2024 16:07:46 -0400 Subject: [PATCH 020/101] cwFlow* : Added MIDI input and output devices to 'flow' framework. --- cwFlow.cpp | 24 ++++- cwFlow.h | 31 +++++- cwFlowCross.cpp | 66 ++++++++++--- cwFlowProc.cpp | 251 ++++++++++++++++++++++++++++++++++++++++++++++++ cwFlowProc.h | 2 + cwFlowTypes.cpp | 135 +++++++++++++++++++++++--- cwFlowTypes.h | 60 +++++++----- cwIoFlow.cpp | 80 ++++++++++++--- 8 files changed, 577 insertions(+), 72 deletions(-) diff --git a/cwFlow.cpp b/cwFlow.cpp index 09a1e6e..8f61939 100644 --- a/cwFlow.cpp +++ b/cwFlow.cpp @@ -8,6 +8,8 @@ #include "cwVectOps.h" #include "cwMtx.h" #include "cwDspTypes.h" // real_t, sample_t +#include "cwTime.h" +#include "cwMidiDecls.h" #include "cwFlowDecl.h" #include "cwFlow.h" #include "cwFlowTypes.h" @@ -24,6 +26,8 @@ namespace cw } library_t; library_t g_library[] = { + { "midi_in", &midi_in::members }, + { "midi_out", &midi_out::members }, { "audio_in", &audio_in::members }, { "audio_out", &audio_out::members }, { "audioFileIn", &audioFileIn::members }, @@ -292,7 +296,7 @@ namespace cw if( src_var->value == nullptr ) { - rc = cwLogError(kSyntaxErrorRC,"The source value is null on the connection input:%s %s source:%s %s .", in_inst->label, in_var_label, src_inst->label, suffix); + rc = cwLogError(kSyntaxErrorRC,"The source value is null on the connection input:'%s' %s source:'%s' '%s' .", in_inst->label, in_var_label, src_inst->label, suffix); goto errLabel; } @@ -1549,7 +1553,7 @@ void cw::flow::print_abuf( const abuf_t* abuf ) void cw::flow::print_external_device( const external_device_t* dev ) { - printf("Dev: %10s id:%3i type:%3i fl:0x%x : ", cwStringNullGuard(dev->label),dev->ioDevId,dev->typeId,dev->flags); + printf("Dev: %10s type:%3i fl:0x%x : ", cwStringNullGuard(dev->devLabel),dev->typeId,dev->flags); if( dev->typeId == kAudioDevTypeId ) print_abuf(dev->u.a.abuf); printf("\n"); @@ -1603,6 +1607,19 @@ cw::rc_t cw::flow::create( handle_t& hRef, goto errLabel; } + 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); + } + // print the class dict if( printClassDictFl ) class_dict_print( p ); @@ -1692,7 +1709,10 @@ cw::rc_t cw::flow::exec( handle_t h ) p->cycleIndex += 1; if( p->maxCycleCount > 0 && p->cycleIndex >= p->maxCycleCount ) + { + cwLogInfo("'maxCycleCnt' reached: %i. Shutting down flow.",p->maxCycleCount); break; + } } return rc; diff --git a/cwFlow.h b/cwFlow.h index d45df47..945840b 100644 --- a/cwFlow.h +++ b/cwFlow.h @@ -31,17 +31,38 @@ namespace cw // 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 { - const char* label; // IO framework device label - unsigned ioDevId; // IO framework device id - unsigned typeId; // see ???DevTypeId above - unsigned flags; // see ???Fl above + 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 include this additional record + audio_dev_cfg_t a; // audio devices use this record + midi_dev_cfg_t m; // MIDI " " " " } u; } external_device_t; diff --git a/cwFlowCross.cpp b/cwFlowCross.cpp index 2839273..c47afc7 100644 --- a/cwFlowCross.cpp +++ b/cwFlowCross.cpp @@ -9,6 +9,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,14 +30,15 @@ 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; - + flow::handle_t flowH; + unsigned stateId; // inactive, fade-in, fade-out double fadeGain; // 0 0->1 1->0 unsigned fadeSmpN; // @@ -47,7 +50,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 +109,21 @@ namespace cw memcpy(devA,srcDevA,devN * sizeof(flow::external_device_t)); for(unsigned i=0; istateId == 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 +243,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,13 +406,27 @@ cw::rc_t cw::flow_cross::exec_cycle( handle_t h ) { flow_network_t* net = p->netA + i; - // 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; jdeviceN; ++j) - if( p->deviceA[j].typeId == flow::kAudioDevTypeId && cwIsFlag(p->deviceA[j].flags, flow::kInFl ) ) - _update_audio_input( p, p->netA + i, 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 + + // 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; jdeviceN; ++j) if( p->deviceA[j].typeId == flow::kAudioDevTypeId && cwIsFlag(p->deviceA[j].flags, flow::kOutFl ) ) diff --git a/cwFlowProc.cpp b/cwFlowProc.cpp index 0e25254..5eff84e 100644 --- a/cwFlowProc.cpp +++ b/cwFlowProc.cpp @@ -2,12 +2,18 @@ #include "cwLog.h" #include "cwCommonImpl.h" #include "cwMem.h" +#include "cwText.h" #include "cwObject.h" #include "cwAudioFile.h" #include "cwVectOps.h" #include "cwMtx.h" #include "cwDspTypes.h" // real_t, sample_t + +#include "cwTime.h" +#include "cwMidiDecls.h" + + #include "cwFlowDecl.h" #include "cwFlow.h" #include "cwFlowTypes.h" @@ -164,6 +170,251 @@ namespace cw } + + //------------------------------------------------------------------------------------------------------------------ + // + // midi_in + // + + namespace midi_in + { + enum + { + kDevLabelPId, + kPortLabelPId, + kOutPId + }; + + typedef struct + { + midi::ch_msg_t* buf; + unsigned bufN; + bool dev_filt_fl; + bool port_filt_fl; + external_device_t* ext_dev; + } inst_t; + + rc_t create( instance_t* ctx ) + { + rc_t rc = kOkRC; + const char* dev_label = nullptr; + const char* port_label = nullptr; + inst_t* inst = mem::allocZ(); + + ctx->userPtr = inst; + + // Register variable and get their current value + if((rc = var_register_and_get( ctx, kAnyChIdx, + kDevLabelPId, "dev_label", dev_label, + kPortLabelPId, "port_label", port_label )) != kOkRC ) + + { + goto errLabel; + } + + if((rc = var_register( ctx, kAnyChIdx,kOutPId, "out")) != kOkRC ) + { + goto errLabel; + } + + + inst->dev_filt_fl = true; + inst->port_filt_fl = true; + + if( textIsEqual(dev_label,"") ) + { + inst->dev_filt_fl = false; + dev_label = nullptr; + } + + if( textIsEqual(dev_label,"") ) + { + inst->port_filt_fl = false; + port_label = nullptr; + } + + + + if((inst->ext_dev = external_device_find( ctx->ctx, dev_label, kMidiDevTypeId, kInFl, port_label )) == nullptr ) + { + rc = cwLogError(kOpFailRC,"The MIDI input device '%s' port '%s' could not be found.", cwStringNullGuard(dev_label), cwStringNullGuard(port_label)); + goto errLabel; + } + + // Allocate a buffer large enough to hold the max. number of messages arriving on a single call to exec(). + inst->bufN = inst->ext_dev->u.m.maxMsgCnt; + inst->buf = mem::allocZ( inst->bufN ); + + // create one output audio buffer + rc = var_register_and_set( ctx, "out", kOutPId, kAnyChIdx, nullptr, 0 ); + + + errLabel: + return rc; + } + + rc_t destroy( instance_t* ctx ) + { + rc_t rc = kOkRC; + + inst_t* inst = (inst_t*)ctx->userPtr; + mem::release(inst->buf); + + mem::release(inst); + + return rc; + } + + rc_t value( instance_t* ctx, variable_t* var ) + { return kOkRC; } + + rc_t exec( instance_t* ctx ) + { + rc_t rc = kOkRC; + + inst_t* inst = (inst_t*)ctx->userPtr; + mbuf_t* mbuf = nullptr; + + + // get the output variable + if((rc = var_get(ctx,kOutPId,kAnyChIdx,mbuf)) != kOkRC ) + { + rc = cwLogError(kInvalidStateRC,"The MIDI file instance '%s' does not have a valid MIDI output buffer.",ctx->label); + } + else + { + // if the device filter is not set + if( !inst->dev_filt_fl) + { + mbuf->msgA = inst->ext_dev->u.m.msgArray; + mbuf->msgN = inst->ext_dev->u.m.msgCnt; + } + else // the device filter is set + { + const midi::ch_msg_t* m = inst->ext_dev->u.m.msgArray; + unsigned j = 0; + for(unsigned i=0; iext_dev->u.m.msgCnt && jbufN; ++i) + if( m->devIdx == inst->ext_dev->ioDevIdx && (!inst->port_filt_fl || m->portIdx == inst->ext_dev->ioPortIdx) ) + inst->buf[j++] = m[i]; + + mbuf->msgN = j; + mbuf->msgA = inst->buf; + } + + + } + + return rc; + } + + class_members_t members = { + .create = create, + .destroy = destroy, + .value = value, + .exec = exec, + .report = nullptr + }; + + } + + + //------------------------------------------------------------------------------------------------------------------ + // + // midi_out + // + + namespace midi_out + { + enum + { + kInPId, + kDevLabelPId, + kPortLabelPId + }; + + typedef struct + { + external_device_t* ext_dev; + } inst_t; + + rc_t create( instance_t* ctx ) + { + rc_t rc = kOkRC; // + inst_t* inst = mem::allocZ(); // + const char* dev_label = nullptr; + const char* port_label = nullptr; + mbuf_t* mbuf = nullptr; + + ctx->userPtr = inst; + + // Register variables and get their current value + if((rc = var_register_and_get( ctx, kAnyChIdx, + kDevLabelPId, "dev_label", dev_label, + kPortLabelPId,"port_label", port_label, + kInPId, "in", mbuf)) != kOkRC ) + { + goto errLabel; + } + + if((inst->ext_dev = external_device_find( ctx->ctx, dev_label, kMidiDevTypeId, kOutFl, port_label )) == nullptr ) + { + rc = cwLogError(kOpFailRC,"The audio output device description '%s' could not be found.", cwStringNullGuard(dev_label)); + goto errLabel; + } + + errLabel: + return rc; + } + + rc_t destroy( instance_t* ctx ) + { + rc_t rc = kOkRC; + inst_t* inst = (inst_t*)ctx->userPtr; + + mem::release(inst); + + return rc; + } + + rc_t value( instance_t* ctx, variable_t* var ) + { + rc_t rc = kOkRC; + return rc; + } + + rc_t exec( instance_t* ctx ) + { + rc_t rc = kOkRC; + inst_t* inst = (inst_t*)ctx->userPtr; + const mbuf_t* src_mbuf = nullptr; + + if((rc = var_get(ctx,kInPId,kAnyChIdx,src_mbuf)) != kOkRC ) + rc = cwLogError(kInvalidStateRC,"The MIDI output instance '%s' does not have a valid input connection.",ctx->label); + else + { + for(unsigned i=0; imsgN; ++i) + { + const midi::ch_msg_t* m = src_mbuf->msgA + i; + inst->ext_dev->u.m.sendTripleFunc( inst->ext_dev, m->ch, m->status, m->d0, m->d1 ); + } + } + + return rc; + } + + class_members_t members = { + .create = create, + .destroy = destroy, + .value = value, + .exec = exec, + .report = nullptr + }; + + } + + + + //------------------------------------------------------------------------------------------------------------------ // diff --git a/cwFlowProc.h b/cwFlowProc.h index 15fee86..026bbed 100644 --- a/cwFlowProc.h +++ b/cwFlowProc.h @@ -2,6 +2,8 @@ namespace cw { namespace flow { + 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; } diff --git a/cwFlowTypes.cpp b/cwFlowTypes.cpp index 02ccf1f..477b5f1 100644 --- a/cwFlowTypes.cpp +++ b/cwFlowTypes.cpp @@ -7,6 +7,8 @@ #include "cwVectOps.h" #include "cwMtx.h" #include "cwDspTypes.h" // real_t, sample_t +#include "cwTime.h" +#include "cwMidiDecls.h" #include "cwFlowDecl.h" #include "cwFlow.h" #include "cwFlowTypes.h" @@ -18,21 +20,22 @@ namespace cw { idLabelPair_t typeLabelFlagsA[] = { - { kBoolTFl, "bool" }, - { kUIntTFl, "uint" }, - { kIntTFl, "int", }, - { kFloatTFl, "float"}, - { kRealTFl, "real"}, - { kDoubleTFl, "double"}, + { kBoolTFl, "bool" }, + { kUIntTFl, "uint" }, + { kIntTFl, "int", }, + { kFloatTFl, "float"}, + { kRealTFl, "real"}, + { kDoubleTFl,"double"}, - { kBoolMtxTFl, "bool_mtx" }, - { kUIntMtxTFl, "uint_mtx" }, - { kIntMtxTFl, "int_mtx" }, - { kFloatMtxTFl, "float_mtx" }, - { kDoubleMtxTFl, "double_mtx" }, + { kBoolMtxTFl, "bool_mtx" }, + { kUIntMtxTFl, "uint_mtx" }, + { kIntMtxTFl, "int_mtx" }, + { kFloatMtxTFl, "float_mtx" }, + { kDoubleMtxTFl,"double_mtx" }, { kABufTFl, "audio" }, { kFBufTFl, "spectrum" }, + { kMBufTFl, "midi" }, { kStringTFl, "string" }, { kTimeTFl, "time" }, { kInvalidTFl, nullptr } @@ -73,6 +76,9 @@ namespace cw fbuf_destroy( v->u.fbuf ); break; + case kMBufTFl: + mbuf_destroy( v->u.mbuf ); + break; case kBoolMtxTFl: case kUIntMtxTFl: @@ -99,8 +105,7 @@ namespace cw } void _value_duplicate( value_t& dst, const value_t& src ) - { - + { switch( src.flags & kTypeMask ) { case kInvalidTFl: @@ -125,6 +130,10 @@ namespace cw dst.flags = src.flags; break; + case kMBufTFl: + dst.u.mbuf = src.u.mbuf == nullptr ? nullptr : mbuf_duplicate(src.u.mbuf); + dst.flags = src.flags; + break; case kBoolMtxTFl: case kUIntMtxTFl: @@ -182,6 +191,15 @@ namespace cw printf("(binN:%i hopSmpN:%i) ", v->u.fbuf->binN_V[i], v->u.fbuf->hopSmpN_V[i] ); } break; + + case kMBufTFl: + if( v->u.mbuf == nullptr ) + printf("mbuf: "); + else + { + printf("mbuf: cnt: %i", v->u.mbuf->msgN ); + } + break; case kBoolMtxTFl: case kUIntMtxTFl: @@ -345,6 +363,28 @@ namespace cw return rc; } + rc_t _val_get( value_t* val, mbuf_t*& valRef ) + { + rc_t rc = kOkRC; + if( cwIsFlag(val->flags & kTypeMask, kMBufTFl) ) + valRef = val->u.mbuf; + else + { + valRef = nullptr; + rc = cwLogError(kTypeMismatchRC,"The type 0x%x could not be converted to an mbuf_t.",val->flags); + } + return rc; + } + + rc_t _val_get( value_t* val, const mbuf_t*& valRef ) + { + mbuf_t* non_const_val; + rc_t rc = kOkRC; + if((rc = _val_get(val,non_const_val)) == kOkRC ) + valRef = non_const_val; + return rc; + } + template< typename T > rc_t _val_get_driver( const variable_t* var, T& valRef ) @@ -492,6 +532,14 @@ namespace cw var->local_value[ local_value_idx ].flags = kABufTFl; cwLogMod("%s.%s ch:%i %s (abuf).",var->inst->label,var->label,var->chIdx,abuf==nullptr ? "null" : "valid"); } + + template<> + void _var_setter( variable_t* var, unsigned local_value_idx, mbuf_t* val ) + { + var->local_value[ local_value_idx ].u.mbuf = val; + var->local_value[ local_value_idx ].flags = kMBufTFl; + cwLogMod("%s.%s ch:%i %s (abuf).",var->inst->label,var->label,var->chIdx,mbuf==nullptr ? "null" : "valid"); + } template<> void _var_setter( variable_t* var, unsigned local_value_idx, fbuf_t* val ) @@ -609,6 +657,19 @@ namespace cw return rc; } + rc_t _var_register_and_set( instance_t* inst, const char* var_label, unsigned vid, unsigned chIdx, mbuf_t* mbuf ) + { + rc_t rc; + variable_t* var = nullptr; + if((rc = var_register_and_set( inst, var_label, vid, chIdx, var)) != kOkRC ) + return rc; + + if( var != nullptr ) + _var_set_driver( var, kMBufTFl, mbuf ); + + return rc; + } + rc_t _var_register_and_set( instance_t* inst, const char* var_label, unsigned vid, unsigned chIdx, fbuf_t* fbuf ) { rc_t rc; @@ -1010,6 +1071,25 @@ cw::flow::fbuf_t* cw::flow::fbuf_duplicate( const fbuf_t* src ) } +cw::flow::mbuf_t* cw::flow::mbuf_create( const midi::ch_msg_t* msgA, unsigned msgN ) +{ + mbuf_t* m = mem::allocZ(); + m->msgA = msgA; + m->msgN = msgN; + return m; +} + +void cw::flow::mbuf_destroy( mbuf_t*& buf ) +{ + mem::release(buf); +} + +cw::flow::mbuf_t* cw::flow::mbuf_duplicate( const mbuf_t* src ) +{ + return mbuf_create(src->msgA,src->msgN); +} + + unsigned cw::flow::value_type_label_to_flag( const char* s ) { unsigned flags = labelToId(typeLabelFlagsA,s,kInvalidTFl); @@ -1089,10 +1169,13 @@ cw::rc_t cw::flow::instance_find( flow_t* p, const char* inst_label, instance_t* return cwLogError(kInvalidArgRC,"The instance '%s' was not found.", inst_label ); } -cw::flow::external_device_t* cw::flow::external_device_find( flow_t* p, const char* device_label, unsigned typeId, unsigned inOrOutFl ) +cw::flow::external_device_t* cw::flow::external_device_find( flow_t* p, const char* device_label, unsigned typeId, unsigned inOrOutFl, const char* midiPortLabel ) { for(unsigned i=0; ideviceN; ++i) - if( cw::textIsEqual(p->deviceA[i].label,device_label) && p->deviceA[i].typeId==typeId && cwIsFlag(p->deviceA[i].flags,inOrOutFl )) + if( (device_label==nullptr || cw::textIsEqual(p->deviceA[i].devLabel,device_label)) + && p->deviceA[i].typeId==typeId + && cwIsFlag(p->deviceA[i].flags,inOrOutFl) + && (midiPortLabel==nullptr || cw::textIsEqual(p->deviceA[i].portLabel,midiPortLabel)) ) return p->deviceA + i; cwLogError(kInvalidArgRC,"The %s device named '%s' could not be found.", cwIsFlag(inOrOutFl,kInFl) ? "in" : "out", device_label ); @@ -1381,6 +1464,21 @@ cw::rc_t cw::flow::var_register_and_set( instance_t* inst, const char* var_label return rc; } +cw::rc_t cw::flow::var_register_and_set( instance_t* inst, const char* var_label, unsigned vid, unsigned chIdx, midi::ch_msg_t* msgA, unsigned msgN ) +{ + rc_t rc = kOkRC; + mbuf_t* mbuf; + + if((mbuf = mbuf_create(msgA,msgN)) == nullptr ) + return cwLogError(kOpFailRC,"mbuf create failed on instance:'%s' variable:'%s'.", inst->label, var_label); + + if((rc = _var_register_and_set( inst, var_label, vid, chIdx, mbuf )) != kOkRC ) + mbuf_destroy(mbuf); + + return rc; +} + + cw::rc_t cw::flow::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, const fd_real_t** phsV, const fd_real_t** hzV ) { unsigned maxBinN_V[ chN ]; @@ -1423,6 +1521,13 @@ cw::rc_t cw::flow::var_get( const variable_t* var, const fbuf_t*& valRef ) cw::rc_t cw::flow::var_get( variable_t* var, fbuf_t*& valRef ) { return _val_get_driver(var,valRef); } +cw::rc_t cw::flow::var_get( const variable_t* var, const mbuf_t*& valRef ) +{ return _val_get_driver(var,valRef); } + +cw::rc_t cw::flow::var_get( variable_t* var, mbuf_t*& valRef ) +{ return _val_get_driver(var,valRef); } + + cw::rc_t cw::flow::var_set( instance_t* inst, unsigned vid, unsigned chIdx, bool val ) { rc_t rc = kOkRC; diff --git a/cwFlowTypes.h b/cwFlowTypes.h index eb1ffbf..0fe4ce5 100644 --- a/cwFlowTypes.h +++ b/cwFlowTypes.h @@ -4,16 +4,22 @@ namespace cw { #define kRealTFl kFloatTFl - typedef dsp::real_t real_t; - typedef dsp::sample_t sample_t; + typedef dsp::real_t real_t; + typedef dsp::sample_t sample_t; typedef dsp::fd_real_t fd_real_t; - typedef dsp::srate_t srate_t; - typedef unsigned uint_t; - typedef int int_t; + typedef dsp::srate_t srate_t; + typedef unsigned uint_t; + typedef int int_t; typedef unsigned vid_t; + enum { + kFbufVectN = 3, // count of signal vectors in fbuf (mag,phs,hz) + kAnyChIdx = kInvalidIdx, + kLocalValueN = 2 + }; + typedef struct abuf_str { struct value_str* base; @@ -24,12 +30,6 @@ namespace cw } 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; @@ -39,13 +39,20 @@ namespace cw unsigned* maxBinN_V; // 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_real_t** magV; // magV[ chN ][ binN ] + fd_real_t** phsV; // phsV[ chN ][ binN ] + fd_real_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) + 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 + { + struct value_str* base; + const midi::ch_msg_t* msgA; + unsigned msgN; + } mbuf_t; + enum { kInvalidTFl = 0x00000000, @@ -64,10 +71,11 @@ namespace cw kABufTFl = 0x00000800, kFBufTFl = 0x00001000, - kStringTFl = 0x00002000, - kTimeTFl = 0x00004000, + kMBufTFl = 0x00002000, + kStringTFl = 0x00004000, + kTimeTFl = 0x00008000, - kTypeMask = 0x00007fff, + kTypeMask = 0x0000ffff, }; @@ -96,6 +104,7 @@ namespace cw abuf_t* abuf; fbuf_t* fbuf; + mbuf_t* mbuf; char* s; char* fname; @@ -115,11 +124,11 @@ namespace cw 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 ); + enum { - kSrcVarFl = 0x01, - kSrcOptVarFl = 0x02 - + kSrcVarFl = 0x01, + kSrcOptVarFl = 0x02 }; typedef struct class_members_str @@ -242,6 +251,10 @@ namespace cw 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 ); void fbuf_destroy( fbuf_t*& buf ); fbuf_t* fbuf_duplicate( 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->flags & kABufTFl; } inline bool value_is_fbuf( const value_t* v ) { return v->flags & kFBufTFl; } @@ -274,7 +287,7 @@ namespace cw 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 ); + external_device_t* external_device_find( flow_t* p, const char* device_label, unsigned typeId, unsigned inOrOutFl, const char* midiPortLabel=nullptr ); void instance_print( instance_t* inst ); @@ -374,6 +387,7 @@ namespace cw 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( 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, midi::ch_msg_t* midiA, unsigned midiN ); 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 ); @@ -427,6 +441,8 @@ 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 ); template< typename T> rc_t var_get( instance_t* inst, unsigned vid, unsigned chIdx, T& valRef) diff --git a/cwIoFlow.cpp b/cwIoFlow.cpp index 7d3bf79..2ac38b1 100644 --- a/cwIoFlow.cpp +++ b/cwIoFlow.cpp @@ -8,6 +8,8 @@ #include "cwTime.h" #include "cwVectOps.h" #include "cwMtx.h" +#include "cwTime.h" +#include "cwMidiDecls.h" #include "cwDspTypes.h" #include "cwFlowDecl.h" @@ -87,7 +89,7 @@ namespace cw mem::release(p->audioGroupA); mem::release(p); - + return kOkRC; } @@ -95,10 +97,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; iioH,i,true) + midiDevicePortCount(p->ioH,i,false); + for(unsigned i=0; iaudioGroupN; ++i) devN += p->audioGroupA[i].iDeviceN + p->audioGroupA[i].oDeviceN; @@ -146,23 +151,38 @@ 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; - d->typeId = typeId; - d->flags = flags; + 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. d->u.a.abuf = &ad->abuf; - } + } void _setup_generic_device_array( io_flow_t* p ) { @@ -175,17 +195,27 @@ namespace cw // get serial devices for(unsigned di=0; ideviceN && diioH); ++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; ideviceN && diioH); ++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; ideviceN && diioH); ++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; ideviceN && diioH); ++di) + { + // input port setup + for(unsigned pi=0; piioH,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; piioH,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; giaudioGroupN; ++gi) { @@ -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; ideviceN; ++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; } From a3012db1653b1f247b93169d2244fe5b7741c6df Mon Sep 17 00:00:00 2001 From: kevin Date: Mon, 22 Apr 2024 15:56:11 -0400 Subject: [PATCH 021/101] cwText.h/cpp : Added first/lastMatchChar() for non-const strings. --- cwText.cpp | 16 +++++++++++++--- cwText.h | 3 +++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/cwText.cpp b/cwText.cpp index f6b4e7a..2ae2f89 100644 --- a/cwText.cpp +++ b/cwText.cpp @@ -146,7 +146,7 @@ const char* cw::nextNonWhiteChar( const char* s ) const char* cw::nextNonWhiteCharEOS( const char* s ) { return _nextNonWhiteChar(s,true); } -const char* cw::firstMatchChar( const char* s, char c ) +char* cw::firstMatchChar( char* s, char c ) { if( s == nullptr ) return nullptr; @@ -157,7 +157,12 @@ const char* cw::firstMatchChar( const char* s, char c ) return nullptr; } -const char* cw::lastMatchChar( const char* s, char c ) +const char* cw::firstMatchChar( const char* s, char c ) +{ + return firstMatchChar((char*)s,c); +} + +char* cw::lastMatchChar( char* s, char c ) { unsigned sn; @@ -168,13 +173,18 @@ const char* cw::lastMatchChar( const char* s, char c ) if( sn == 0 ) return nullptr; - for(const char* s1=s+(sn-1); s<=s1; --s1) + for(char* s1=s+(sn-1); s<=s1; --s1) if( *s1 == c ) return s1; return nullptr; } +const char* cw::lastMatchChar( const char* s, char c ) +{ + return lastMatchChar((char*)s,c); +} + bool cw::isInteger( const char* s ) { for(; *s; ++s) diff --git a/cwText.h b/cwText.h index cc44f06..9c4b7de 100644 --- a/cwText.h +++ b/cwText.h @@ -65,8 +65,11 @@ namespace cw // Return a pointer to the first occurrence of 'c' in s[] or nullptr // if 'c' does not occur in s[] + char* firstMatchChar( char* s, char c ); const char* firstMatchChar( const char* s, char c ); + // Find the last occurrent of 'c' in s[]. + char* lastMatchChar( char* s, char c ); const char* lastMatchChar( const char* s, char c ); bool isInteger( const char* ); // text contains only [0-9] From 1c373f8d03db0ba2460ca29d3213a6ab335e79d9 Mon Sep 17 00:00:00 2001 From: kevin Date: Mon, 22 Apr 2024 15:57:59 -0400 Subject: [PATCH 022/101] cwObject.cpp : All non-quoted-string string are now considered identifiers in objectFromString(). --- cwObject.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cwObject.cpp b/cwObject.cpp index a84a6c8..6fabd0b 100644 --- a/cwObject.cpp +++ b/cwObject.cpp @@ -907,7 +907,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( cnp, s, "string", identFl ); } From 1b649124a6ae7094962e8f5988da5bd7ed32aa08 Mon Sep 17 00:00:00 2001 From: kevin Date: Mon, 22 Apr 2024 16:02:40 -0400 Subject: [PATCH 023/101] cwFlow*,cwPresetSel.cpp: Many changes to implement 'poly' networks. cwFlowNet.h/cpp : Initial commit. --- Makefile.am | 4 +- cwFlow.cpp | 1665 ++-------------------------------- cwFlow.h | 62 +- cwFlowDecl.h | 61 ++ cwFlowNet.cpp | 2279 +++++++++++++++++++++++++++++++++++++++++++++++ cwFlowNet.h | 82 ++ cwFlowProc.cpp | 617 ++++++++++--- cwFlowProc.h | 3 + cwFlowTypes.cpp | 220 ++--- cwFlowTypes.h | 104 ++- cwPresetSel.cpp | 3 +- 11 files changed, 3183 insertions(+), 1917 deletions(-) create mode 100644 cwFlowNet.cpp create mode 100644 cwFlowNet.h diff --git a/Makefile.am b/Makefile.am index ac568fd..a94f6e6 100644 --- a/Makefile.am +++ b/Makefile.am @@ -34,8 +34,8 @@ libcwSRC += src/libcw/cwAudioFile.cpp src/libcw/cwMidiFile.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 +libcwSRC += src/libcw/cwFlow.cpp src/libcw/cwFlowTypes.cpp src/libcw/cwFlowNet.cpp src/libcw/cwFlowProc.cpp src/libcw/cwFlowCross.cpp if cwWEBSOCK libcwHDR += src/libcw/cwWebSock.h src/libcw/cwWebSockSvr.h diff --git a/cwFlow.cpp b/cwFlow.cpp index 8f61939..ceea17f 100644 --- a/cwFlow.cpp +++ b/cwFlow.cpp @@ -3,7 +3,9 @@ #include "cwCommonImpl.h" #include "cwMem.h" #include "cwText.h" +#include "cwNumericConvert.h" #include "cwObject.h" + #include "cwAudioFile.h" #include "cwVectOps.h" #include "cwMtx.h" @@ -13,6 +15,7 @@ #include "cwFlowDecl.h" #include "cwFlow.h" #include "cwFlowTypes.h" +#include "cwFlowNet.h" #include "cwFlowProc.h" namespace cw @@ -26,6 +29,7 @@ namespace cw } library_t; library_t g_library[] = { + { "poly", &poly::members }, { "midi_in", &midi_in::members }, { "midi_out", &midi_out::members }, { "audio_in", &audio_in::members }, @@ -48,6 +52,8 @@ namespace cw { "balance", &balance::members }, { "audio_meter", &audio_meter::members }, { "audio_marker", &audio_marker::members }, + { "xfade_ctl", &xfade_ctl::members }, + { "poly_mixer", &poly_mixer::members }, { nullptr, nullptr } }; @@ -113,7 +119,8 @@ namespace cw // get the variable description if((rc = cd->cfg->getv_opt("vars", varD, - "presets", presetD )) != kOkRC ) + "presets", presetD, + "poly_limit_cnt", cd->polyLimitN)) != kOkRC ) { rc = cwLogError(rc,"Parsing failed while parsing class desc:'%s'", cwStringNullGuard(cd->label) ); goto errLabel; @@ -229,19 +236,9 @@ namespace cw return rc; } - void _connect_vars( variable_t* src_var, variable_t* in_var ) - { - // connect in_var into src_var's outgoing var chain - in_var->connect_link = src_var->connect_link; - src_var->connect_link = in_var; - assert( src_var->value != nullptr ); - - in_var->value = src_var->value; - in_var->src_var = src_var; - } - - rc_t _setup_input( flow_t* p, instance_t* in_inst, const char* in_var_label, const char* src_label_arg ) + /* + rc_t _setup_input( flow_t* p, instance_t* in_inst, const char* in_var_label, const char* src_proc_label, const char* src_var_label ) { rc_t rc = kOkRC; unsigned src_charN = textLength(src_label_arg); @@ -267,21 +264,21 @@ namespace cw } // locate source instance - if((rc = instance_find(p, sbuf, src_inst )) != kOkRC ) + if((rc = instance_find(p, sbuf, kBaseSfxId, src_inst )) != kOkRC ) { rc = cwLogError(kSyntaxErrorRC,"The source instance '%s' was not found.", cwStringNullGuard(sbuf) ); goto errLabel; } // locate source value - if((rc = var_find( src_inst, suffix, kAnyChIdx, src_var)) != kOkRC ) + if((rc = var_find( src_inst, suffix, kBaseSfxId, kAnyChIdx, src_var)) != kOkRC ) { rc = cwLogError(rc,"The source var '%s' was not found on the source instance '%s'.", cwStringNullGuard(suffix), cwStringNullGuard(sbuf)); goto errLabel; } // locate input value - if((rc = var_find( in_inst, in_var_label, kAnyChIdx, in_var )) != kOkRC ) + if((rc = var_find( in_inst, in_var_label, kBaseSfxId, kAnyChIdx, in_var )) != kOkRC ) { rc = cwLogError(rc,"The input value '%s' was not found on the instance '%s'.", cwStringNullGuard(in_var_label), cwStringNullGuard(in_inst->label)); goto errLabel; @@ -307,853 +304,12 @@ namespace cw errLabel: return rc; } + */ - void _destroy_inst( instance_t* inst ) - { - if( inst == nullptr ) - return; - - if( inst->class_desc->members->destroy != nullptr && inst->userPtr != nullptr ) - inst->class_desc->members->destroy( inst ); - // destroy the instance variables - variable_t* var0 = inst->varL; - variable_t* var1 = nullptr; - while( var0 != nullptr ) - { - var1 = var0->var_link; - _var_destroy(var0); - var0 = var1; - } - - - mem::release(inst->varMapA); - mem::release(inst); - } - - rc_t _var_map_id_to_index( instance_t* inst, unsigned vid, unsigned chIdx, unsigned& idxRef ); - - rc_t _create_instance_var_map( instance_t* inst ) - { - rc_t rc = kOkRC; - unsigned max_vid = kInvalidId; - unsigned max_chIdx = 0; - variable_t* var = inst->varL; - //variable_t* v0 = nullptr; - - // determine the max variable vid and max channel index value among all variables - for(; var!=nullptr; var = var->var_link ) - { - if( var->vid != kInvalidId ) - { - if( max_vid == kInvalidId || var->vid > max_vid ) - max_vid = var->vid; - - if( var->chIdx != kAnyChIdx && (var->chIdx+1) > max_chIdx ) - max_chIdx = (var->chIdx + 1); - - } - } - - // If there are any variables - if( max_vid != kInvalidId ) - { - // create the variable map array - inst->varMapChN = max_chIdx + 1; - inst->varMapIdN = max_vid + 1; - inst->varMapN = inst->varMapIdN * inst->varMapChN; - inst->varMapA = mem::allocZ( inst->varMapN ); - - // assign each variable to a location in the map - for(variable_t* var=inst->varL; var!=nullptr; var=var->var_link) - if( var->vid != kInvalidId ) - { - unsigned idx = kInvalidIdx; - - if((rc = _var_map_id_to_index( inst, var->vid, var->chIdx, idx )) != kOkRC ) - goto errLabel; - - - // verify that there are not multiple variables per map position - if( inst->varMapA[ idx ] != nullptr ) - { - variable_t* v0 = inst->varMapA[idx]; - rc = cwLogError(kInvalidStateRC,"The variable '%s' id:%i ch:%i and '%s' id:%i ch:%i share the same variable map position on instance: %s. This is usually cased by duplicate variable id's.", - v0->label,v0->vid,v0->chIdx, var->label,var->vid,var->chIdx,inst->label); - - goto errLabel; - } - - // assign this variable to a map position - inst->varMapA[ idx ] = var; - - if( var->chIdx != kAnyChIdx && var->value == nullptr ) - { - rc = cwLogError(kInvalidStateRC,"The value of the variable '%s' ch:%i on instance:'%s' has not been set.",var->label,var->chIdx,inst->label); - goto errLabel; - } - - } - - } - - errLabel: - return rc; - - } - - void _complete_input_connections( instance_t* inst ) - { - for(variable_t* var=inst->varL; var!=nullptr; var=var->var_link) - if(var->chIdx == kAnyChIdx && is_connected_to_external_proc(var) ) - { - - variable_t* base_src_var = var->src_var; - - // since 'var' is on the 'any' channel the 'src' var must also be on the 'any' channel - assert( base_src_var->chIdx == kAnyChIdx ); - - //printf("%s %s\n",inst->label,var->label); - - // for each var channel in the input var - for(variable_t* in_var = var->ch_link; in_var != nullptr; in_var=in_var->ch_link) - { - // locate the matching channel on the 'src' var - variable_t* svar = base_src_var; - for(; svar!=nullptr; svar=svar->ch_link) - if( svar->chIdx == in_var->chIdx ) - break; - - // connect the src->input var - _connect_vars( svar==nullptr ? base_src_var : svar, in_var); - } - } - } - rc_t _call_value_func_on_all_variables( instance_t* inst ) - { - rc_t rc = kOkRC; - rc_t rc1 = kOkRC; - - for(unsigned i=0; ivarMapN; ++i) - if( inst->varMapA[i] != nullptr && inst->varMapA[i]->vid != kInvalidId ) - { - variable_t* var = inst->varMapA[i]; - - if((rc = var->inst->class_desc->members->value( var->inst, var )) != kOkRC ) - rc1 = cwLogError(rc,"The proc instance '%s' reported an invalid valid on variable:%s chIdx:%i.", var->inst->label, var->label, var->chIdx ); - } - - return rc1; - } - - rc_t _var_channelize( instance_t* inst, const char* preset_label, const char* type_src_label, const char* value_label, const object_t* value ) - { - rc_t rc = kOkRC; - - variable_t* dummy = nullptr; - - // verify that a valid value exists - if( value == nullptr ) - { - rc = cwLogError(kSyntaxErrorRC,"Unexpected missig value on %s preset '%s' instance '%s' variable '%s'.", type_src_label, preset_label, inst->label, cwStringNullGuard(value_label) ); - goto errLabel; - } - - // if a list of values was given - if( value->is_list() ) - { - for(unsigned chIdx=0; chIdxchild_count(); ++chIdx) - if((rc = var_channelize( inst, value_label, chIdx, value->child_ele(chIdx), kInvalidId, dummy )) != kOkRC ) - goto errLabel; - } - else // otherwise a single value was given - { - if((rc = var_channelize( inst, value_label, kAnyChIdx, value, kInvalidId, dummy )) != kOkRC ) - goto errLabel; - } - - errLabel: - return rc; - } - rc_t _preset_channelize_vars( instance_t* inst, const char* type_src_label, const char* preset_label, const object_t* preset_cfg ) - { - rc_t rc = kOkRC; - - //cwLogInfo("Channelizing '%s' preset %i vars for '%s'.",type_src_label, preset_cfg==nullptr ? 0 : preset_cfg->child_count(), inst->label ); - - // validate the syntax of the preset record - if( !preset_cfg->is_dict() ) - { - rc = cwLogError(kSyntaxErrorRC,"The preset record '%s' on %s '%s' is not a dictionary.", preset_label, type_src_label, inst->class_desc->label ); - goto errLabel; - } - - - // for each preset variable - for(unsigned i=0; ichild_count(); ++i) - { - const object_t* value = preset_cfg->child_ele(i)->pair_value(); - const char* value_label = preset_cfg->child_ele(i)->pair_label(); - if((rc = _var_channelize( inst, preset_label, type_src_label, value_label, value )) != kOkRC ) - goto errLabel; - - - } - - errLabel: - if( rc != kOkRC ) - rc = cwLogError(rc,"Apply %s preset failed on instance:%s class:%s preset:%s.", type_src_label, inst->label, inst->class_desc->label, preset_label ); - - return rc; - } - - - template< typename T > - T _interp_dual_value( T v0, T v1, double coeff ) - { - T y; - if( v0 == v1 ) - y = v0; - else - y = (T)(v0 + (v1-v0)*coeff ); - - //printf("%f %f -> %f\n",(double)v0,(double)v1,(double)y); - return y; - } - - rc_t _set_var_from_dual_preset_scalar_scalar( instance_t* inst, const char* var_label, const object_t* scalar_0, const object_t* scalar_1, double coeff, unsigned chIdx ) - { - rc_t rc = kOkRC; - object_t interped_value; - variable_t* dummy = nullptr; - - // one of the input values must exist - if( scalar_0==nullptr && scalar_1==nullptr ) - { - rc = cwLogError(kInvalidArgRC,"The numeric types of both operands of a dual value are null."); - goto errLabel; - } - - // It's possible that one or the other input value does not exist - if( scalar_0 == nullptr ) - scalar_0 = scalar_1; - else - { - if( scalar_1 == nullptr ) - scalar_1 = scalar_0; - } - - // verify that the input values are the same type - if( scalar_0->type->id != scalar_1->type->id ) - { - rc = cwLogError(kInvalidArgRC,"The numeric types of both operands of a dual value preset must match. (%s != %s).",cwStringNullGuard(scalar_0->type->label),cwStringNullGuard(scalar_1->type->label)); - goto errLabel; - } - - printf("%s:%s :",inst->label,var_label); - - switch( scalar_0->type->id ) - { - case kInt32TId: - interped_value.set_value( _interp_dual_value(scalar_0->u.i32,scalar_1->u.i32,coeff) ); - break; - case kUInt32TId: - interped_value.set_value( _interp_dual_value(scalar_0->u.u32,scalar_1->u.u32,coeff) ); - break; - case kInt64TId: - assert(0); - //interped_value.set_value( _interp_dual_value(scalar_0->u.i64,scalar_1->u.i64,coeff) ); - break; - case kUInt64TId: - assert(0); - //interped_value.set_value( _interp_dual_value(scalar_0->u.u64,scalar_1->u.u64,coeff) ); - break; - case kFloatTId: - interped_value.set_value( _interp_dual_value(scalar_0->u.f,scalar_1->u.f,coeff) ); - break; - case kDoubleTId: - interped_value.set_value( _interp_dual_value(scalar_0->u.d,scalar_1->u.d,coeff) ); - break; - - default: - rc = cwLogError(kInvalidStateRC,"Preset dual values of type '%s' cannot be interpolated.",cwStringNullGuard(scalar_0->type->label)); - goto errLabel; - } - - - if((rc = var_channelize( inst, var_label, chIdx, &interped_value, kInvalidId, dummy )) != kOkRC ) - { - rc = cwLogError(kInvalidArgRC,"Dual value preset application failed."); - goto errLabel; - } - - errLabel: - return rc; - } - - rc_t _set_var_from_dual_preset_list_list( instance_t* inst, const char* var_label, const object_t* list_0, const object_t* list_1, double coeff ) - { - rc_t rc = kOkRC; - - if( list_0->child_count() != list_1->child_count() ) - return cwLogError(kInvalidArgRC,"If two lists are to be applied as a dual preset they must be the same length."); - - for(unsigned chIdx=0; chIdxchild_count(); ++chIdx) - if((rc = _set_var_from_dual_preset_scalar_scalar(inst,var_label,list_0->child_ele(chIdx),list_1->child_ele(chIdx),coeff,chIdx)) != kOkRC ) - goto errLabel; - - errLabel: - return rc; - } - - rc_t _set_var_from_dual_preset_scalar_list( instance_t* inst, const char* var_label, const object_t* scalar, const object_t* list, double coeff ) - { - rc_t rc = kOkRC; - for(unsigned chIdx=0; chIdxchild_count(); ++chIdx) - if((rc = _set_var_from_dual_preset_scalar_scalar(inst,var_label,scalar,list->child_ele(chIdx),coeff,chIdx)) != kOkRC ) - goto errLabel; - - errLabel: - return rc; - } - - rc_t _set_var_from_dual_preset_list_scalar( instance_t* inst, const char* var_label, const object_t* list, const object_t* scalar, double coeff ) - { - rc_t rc = kOkRC; - for(unsigned chIdx=0; chIdxchild_count(); ++chIdx) - if((rc = _set_var_from_dual_preset_scalar_scalar(inst,var_label,list->child_ele(chIdx),scalar,coeff,chIdx)) != kOkRC ) - goto errLabel; - - errLabel: - return rc; - } - - rc_t _set_var_from_dual_preset_scalar_scalar( instance_t* inst, const char* var_label, const object_t* scalar_0, const object_t* scalar_1, double coeff ) - { - return _set_var_from_dual_preset_scalar_scalar(inst,var_label,scalar_0,scalar_1,coeff,kAnyChIdx); - } - - - rc_t _is_legal_dual_value( const object_t* value ) - { - rc_t rc = kOkRC; - - if( value->is_list() ) - { - if( value->child_count() == 0 ) - { - rc = cwLogError(kInvalidArgRC,"Empty lists values cannot be applied as part of a dual value preset."); - goto errLabel; - } - - } - else - { - switch( value->type->id ) - { - case kInt32TId: - case kUInt32TId: - case kInt64TId: - case kUInt64TId: - case kFloatTId: - case kDoubleTId: - break; - default: - rc = cwLogError(kInvalidArgRC,"Objects of type '%s' cannot be applied as part of a dual value preset.",cwStringNullGuard(value->type->label)); - } - } - - errLabel: - return rc; - - } - - rc_t _set_var_from_dual_preset( instance_t* inst, const char* var_label, const object_t* value_0, const object_t* value_1, double coeff ) - { - rc_t rc = kOkRC; - - // dual values must be either numeric scalars or lists - if((rc = _is_legal_dual_value(value_0)) != kOkRC || (rc = _is_legal_dual_value(value_1)) != kOkRC) - goto errLabel; - - - // if both values are lists then they must be the same length - if( value_0->is_list() && value_1->is_list() ) - { - rc = _set_var_from_dual_preset_list_list( inst, var_label, value_0, value_1, coeff ); - goto errLabel; - } - else - { - // if value_0 is a list and value_1 is a scalar - if( value_0->is_list() ) - { - rc = _set_var_from_dual_preset_list_scalar( inst, var_label, value_0, value_1, coeff ); - goto errLabel; - } - else - { - // if value_1 is a list and value_0 is a scalar - if( value_1->is_list() ) - { - rc = _set_var_from_dual_preset_scalar_list( inst, var_label, value_0, value_1, coeff ); - goto errLabel; - } - else // both values are scalars - { - rc = _set_var_from_dual_preset_scalar_scalar( inst, var_label, value_0, value_1, coeff ); - goto errLabel; - } - } - } - - errLabel: - return rc; - } - - rc_t _multi_preset_channelize_vars( instance_t* inst, const char* type_src_label, const char** presetLabelA, const object_t** preset_cfgA, unsigned presetN, double coeff ) - { - rc_t rc = kOkRC; - - const char* preset_label_0 = ""; - const char* preset_label_1 = ""; - - //cwLogInfo("Channelizing '%s' preset %i vars for '%s'.",type_src_label, preset_cfg==nullptr ? 0 : preset_cfg->child_count(), inst->label ); - - if( presetN < 2 ) - { - rc = cwLogError(kInvalidArgRC,"There must be at least 2 presets selected to interpolate between preset variable dictionaries."); - goto errLabel; - } - - if( presetN > 2 ) - { - cwLogWarning("More than two presets dictionaries were specified for interpolation. Only the first two will be used."); - goto errLabel; - } - - preset_label_0 = presetLabelA[0]; - preset_label_1 = presetLabelA[1]; - - // validate each of the preset records is a dict - for(unsigned i=0; iis_dict() ) - { - rc = cwLogError(kSyntaxErrorRC,"The preset record '%s' on %s '%s' is not a dictionary.", presetLabelA[i], type_src_label, inst->class_desc->label ); - goto errLabel; - } - - - // for each preset variable in the first preset var dict - for(unsigned i=0; ichild_count(); ++i) - { - const char* var_label = preset_cfgA[0]->child_ele(i)->pair_label(); - const object_t* value_0 = preset_cfgA[0]->child_ele(i)->pair_value(); - - const object_t* value_1 = preset_cfgA[1]->find_child(var_label); - - if( value_0 == nullptr && value_1 == nullptr ) - { - rc = cwLogError(kSyntaxErrorRC,"Unexpected missig values on %s preset '%s' instance '%s' variable '%s'.", type_src_label, presetLabelA[0], inst->label, cwStringNullGuard(var_label) ); - goto errLabel; - } - - if( value_0 == nullptr ) - { - cwLogWarning("The preset variable '%s' was not found for the preset: '%s'. Falling back to single value assign.",cwStringNullGuard(var_label),cwStringNullGuard(presetLabelA[0])); - - rc = _var_channelize( inst, preset_label_1, "dual class", var_label, value_1 ); - goto errLabel; - } - - if( value_1 == nullptr ) - { - cwLogWarning("The preset variable '%s' was not found for the preset: '%s'. Falling back to single value assign.",cwStringNullGuard(var_label),cwStringNullGuard(presetLabelA[1])); - - rc = _var_channelize( inst, preset_label_0, "dual class", var_label, value_0 ); - goto errLabel; - } - - - if((rc = _set_var_from_dual_preset( inst, var_label, value_0, value_1, coeff )) != kOkRC ) - { - rc = cwLogError(rc,"Multi preset application failed on variable:%s.",cwStringNullGuard(var_label)); - goto errLabel; - } - } - - errLabel: - if( rc != kOkRC ) - rc = cwLogError(rc,"Apply %s multi-preset failed on instance:%s class:%s presetA:%s presetB:%s.", type_src_label, inst->label, inst->class_desc->label, preset_label_0, preset_label_1 ); - - return rc; - } - - - rc_t _class_multi_preset_channelize_vars(instance_t* inst, const char** class_preset_labelA, unsigned presetN, double coeff ) - { - rc_t rc = kOkRC; - const object_t* presetCfgA[ presetN ]; - const char* presetLabelA[ presetN ]; - unsigned presetCfgN = 0; - - for(unsigned i=0; iclass_desc, class_preset_labelA[i])) == nullptr ) - { - rc = cwLogError(kInvalidIdRC,"The preset '%s' could not be found for the instance '%s'.", class_preset_labelA[i], inst->label); - goto errLabel; - } - - if( pr->cfg == nullptr ) - { - rc = cwLogError(kInvalidIdRC,"The value of preset '%s' was empty in instance '%s'.", class_preset_labelA[i], inst->label); - goto errLabel; - } - - presetCfgA[ presetCfgN] = pr->cfg; - presetLabelA[presetCfgN] = class_preset_labelA[i]; - presetCfgN++; - } - } - - // dispatch based on the count of presets located - switch( presetCfgN ) - { - case 0: - rc = cwLogError(kInvalidArgRC,"No valid class preset records were found while attempting apply a multi-preset."); - break; - - case 1: - // only one valid preset was located - apply it directly - rc = _preset_channelize_vars( inst, "class", presetLabelA[0], presetCfgA[0]); - break; - - default: - // more than one preset was located - apply it's interpolated values - rc = _multi_preset_channelize_vars( inst, "class", presetLabelA, presetCfgA, presetCfgN, coeff); - } - - - errLabel: - return rc; - - } - - rc_t _class_preset_channelize_vars( instance_t* inst, const char* preset_label ) - { - rc_t rc = kOkRC; - const preset_t* pr; - - if( preset_label == nullptr ) - return kOkRC; - - // locate the requestd preset record - if((pr = class_preset_find(inst->class_desc, preset_label)) == nullptr ) - { - rc = cwLogError(kInvalidIdRC,"The preset '%s' could not be found for the instance '%s'.", preset_label, inst->label); - goto errLabel; - } - - rc = _preset_channelize_vars( inst, "class", preset_label, pr->cfg); - - errLabel: - return rc; - } - - - rc_t _class_apply_presets( instance_t* inst, const object_t* preset_labels ) - { - rc_t rc = kOkRC; - const char* s = nullptr; - - // if preset_labels is a string - if( preset_labels->is_string() && preset_labels->value(s)==kOkRC ) - return _class_preset_channelize_vars(inst,s); - - // if the preset_labels is not a list - if( !preset_labels->is_list() ) - rc = cwLogError(kSyntaxErrorRC,"The preset list on instance '%s' is neither a list nor a string.",inst->label); - else - { - // preset_labels is a list. - - // for each label listed in the preset label list - for(unsigned i=0; ichild_count(); ++i) - { - const object_t* label_obj = preset_labels->child_ele(i); - - // verify that the label is a strng - if( !label_obj->is_string() || label_obj->value(s) != kOkRC ) - { - rc = cwLogError(kSyntaxErrorRC,"The preset list does not contain string on instance '%s'.",inst->label); - goto errLabel; - } - - // apply a preset label - if((rc = _class_preset_channelize_vars( inst, s)) != kOkRC ) - goto errLabel; - } - } - - errLabel: - return rc; - } - - - - - rc_t _inst_args_channelize_vars( instance_t* inst, const char* arg_label, const object_t* arg_cfg ) - { - rc_t rc = kOkRC; - - if( arg_cfg == nullptr ) - return rc; - - return _preset_channelize_vars( inst, "instance", arg_label, arg_cfg ); - - } - - typedef struct inst_parse_vars_str - { - const char* inst_label; - const char* inst_clas_label; - const object_t* in_dict; - const char* arg_label; - const object_t* preset_labels; - const object_t* arg_cfg; - } inst_parse_vars_t; - - rc_t _parse_instance_cfg( flow_t* p, const object_t* inst_cfg, inst_parse_vars_t& pvars ) - { - rc_t rc = kOkRC; - const object_t* arg_dict = nullptr; - - // validate the syntax of the inst_cfg pair - if( inst_cfg == nullptr || !inst_cfg->is_pair() || inst_cfg->pair_label()==nullptr || inst_cfg->pair_value()==nullptr ) - { - rc = cwLogError(kSyntaxErrorRC,"The instance cfg. is not a valid pair. No instance label could be parsed."); - goto errLabel; - } - - pvars.inst_label = inst_cfg->pair_label(); - - // verify that the instance label is unique - if( instance_find(p,pvars.inst_label) != nullptr ) - { - rc = cwLogError(kSyntaxErrorRC,"The instance label '%s' has already been used.",pvars.inst_label); - goto errLabel; - } - - // get the instance class label - if((rc = inst_cfg->pair_value()->getv("class",pvars.inst_clas_label)) != kOkRC ) - { - rc = cwLogError(kSyntaxErrorRC,"The instance cfg. %s is missing: 'type'.",pvars.inst_label); - goto errLabel; - } - - // parse the optional args - if((rc = inst_cfg->pair_value()->getv_opt("args", arg_dict, - "in", pvars.in_dict, - "argLabel", pvars.arg_label, - "preset", pvars.preset_labels)) != kOkRC ) - { - rc = cwLogError(kSyntaxErrorRC,"The instance cfg. '%s' missing: 'type'.",pvars.inst_label); - goto errLabel; - } - - // if an argument dict was given in the instanec cfg - if( arg_dict != nullptr ) - { - bool rptErrFl = true; - - // verify the arg. dict is actually a dict. - if( !arg_dict->is_dict() ) - { - cwLogError(kSyntaxErrorRC,"The instance argument dictionary on instance '%s' is not a dictionary.",pvars.inst_label); - goto errLabel; - } - - // if no label was given then try 'default' - if( pvars.arg_label == nullptr) - { - pvars.arg_label = "default"; - rptErrFl = false; - } - - // locate the specified argument record - if((pvars.arg_cfg = arg_dict->find_child(pvars.arg_label)) == nullptr ) - { - - // if an explicit arg. label was given but it was not found - if( rptErrFl ) - { - rc = cwLogError(kSyntaxErrorRC,"The argument cfg. '%s' was not found on instance cfg. '%s'.",pvars.arg_label,pvars.inst_label); - goto errLabel; - } - - // no explicit arg. label was given - make arg_dict the instance arg cfg. - pvars.arg_cfg = arg_dict; - pvars.arg_label = nullptr; - } - } - - errLabel: - if( rc != kOkRC ) - rc = cwLogError(kSyntaxErrorRC,"Configuration parsing failed on instance: '%s'.", cwStringNullGuard(pvars.inst_label) ); - - return rc; - } - - rc_t _create_instance( flow_t* p, const object_t* inst_cfg ) - { - rc_t rc = kOkRC; - inst_parse_vars_t pvars = {}; - instance_t* inst = nullptr; - class_desc_t* class_desc = nullptr; - - // parse the instance configuration - if((rc = _parse_instance_cfg( p, inst_cfg, pvars )) != kOkRC ) - goto errLabel; - - // locate the class desc - if(( class_desc = class_desc_find(p,pvars.inst_clas_label)) == nullptr ) - { - rc = cwLogError(kSyntaxErrorRC,"The flow class '%s' was not found.",cwStringNullGuard(pvars.inst_clas_label)); - goto errLabel; - } - - // instantiate the instance - inst = mem::allocZ(); - - inst->ctx = p; - inst->label = pvars.inst_label; - inst->inst_cfg = inst_cfg; - inst->arg_label = pvars.arg_label; - inst->arg_cfg = pvars.arg_cfg; - inst->class_desc = class_desc; - - // Instantiate all the variables in the class description - for(var_desc_t* vd=class_desc->varDescL; vd!=nullptr; vd=vd->link) - { - variable_t* var = nullptr; - if((rc = var_create( inst, vd->label, kInvalidId, kAnyChIdx, vd->val_cfg, var )) != kOkRC ) - goto errLabel; - } - - // All the variables that can be used by this instance have now been created - // and the chIdx of each variable is set to 'any'. - - // If a 'preset' field was included in the instance cfg then apply the specified class preset - if( pvars.preset_labels != nullptr ) - if((rc = _class_apply_presets(inst, pvars.preset_labels )) != kOkRC ) - goto errLabel; - - // All the class presets values have now been set and those variables - // that were expressed with a list have numeric channel indexes assigned. - - // Apply the instance preset values. - if( pvars.arg_cfg != nullptr ) - if((rc = _inst_args_channelize_vars( inst, pvars.arg_label, pvars.arg_cfg )) != kOkRC ) - goto errLabel; - - // All the instance arg values have now been set and those variables - // that were expressed with a list have numeric channel indexes assigned. - - - // TODO: Should the 'all' variable be removed for variables that have numeric channel indexes? - - // connect the variable lists in the instance 'in' dictionary - if( pvars.in_dict != nullptr ) - { - if( !pvars.in_dict->is_dict() ) - { - cwLogError(kSyntaxErrorRC,"The 'in' dict in instance '%s' is not a valid dictionary.",inst->label); - goto errLabel; - } - - // for each input variable in the 'in' set - for(unsigned i=0; ichild_count(); ++i) - { - const object_t* in_pair = pvars.in_dict->child_ele(i); - const char* in_var_label = in_pair->pair_label(); - const char* src_label = nullptr; - const var_desc_t* vd = nullptr; - - // locate the var desc of the associated variable - if((vd = var_desc_find( class_desc, in_var_label)) == nullptr ) - { - cwLogError(kSyntaxErrorRC,"The value description for the 'in' value '%s' was not found on instance '%s'. Maybe '%s' is not marked as a 'src' attribute in the class variable descripiton.",in_var_label,inst->label,in_var_label); - goto errLabel; - } - - // Note that all variable's found by the above call to var_desc_find() should be 'src' variables. - //assert( cwIsFlag(vd->flags,kSrcVarFl) ); - - // if this value is a 'src' value then it must be setup prior to the instance being instantiated - //if( cwIsFlag(vd->flags,kSrcVarFl) ) - //{ - in_pair->pair_value()->value(src_label); - - // locate the pointer to the referenced output abuf and store it in inst->srcABuf[i] - if((rc = _setup_input( p, inst, in_var_label, src_label )) != kOkRC ) - { - rc = cwLogError(kSyntaxErrorRC,"The 'in' variable at index %i is not valid on instance '%s'.", i, inst->label ); - goto errLabel; - } - //} - } - } - - // Complete the instantiation - - // Call the custom instance create() function. - if((rc = class_desc->members->create( inst )) != kOkRC ) - { - rc = cwLogError(kInvalidArgRC,"Instantiation failed on instance '%s'.", inst->label ); - goto errLabel; - } - - // Create the instance->varMap[] lookup array - if((rc =_create_instance_var_map( inst )) != kOkRC ) - goto errLabel; - - // - _complete_input_connections(inst); - - // call the 'value()' function to inform the instance of the current value of all of it's variables. - if((rc = _call_value_func_on_all_variables( inst )) != kOkRC ) - goto errLabel; - - // insert an instance in the network - if( p->network_tail == nullptr ) - { - p->network_head = inst; - p->network_tail = inst; - } - else - { - p->network_tail->link = inst; - p->network_tail = inst; - } - - - errLabel: - if( rc != kOkRC ) - _destroy_inst(inst); - - return rc; - } - - rc_t _destroy( flow_t* p) { rc_t rc = kOkRC; @@ -1161,17 +317,8 @@ namespace cw if( p == nullptr ) return rc; - instance_t* i0=p->network_head; - instance_t* i1=nullptr; - - // destroy the instances - while(i0!=nullptr) - { - i1 = i0->link; - _destroy_inst(i0); - i0 = i1; - } - + network_destroy(p->net); + // release the class records for(unsigned i=0; iclassDescN; ++i) { @@ -1199,350 +346,14 @@ namespace cw } mem::release(p->classDescA); + + mem::release(p); return rc; } - const object_t* _find_network_preset( flow_t* p, const char* presetLabel ) - { - const object_t* preset_value = nullptr; - - if( p->presetCfg != nullptr ) - { - rc_t rc; - - if((rc = p->presetCfg->getv_opt( presetLabel, preset_value )) != kOkRC ) - cwLogError(rc,"Search for network preset named '%s' failed.", cwStringNullGuard(presetLabel)); - } - - return preset_value; - - } - - rc_t _exec_cycle( flow_t* p ) - { - rc_t rc = kOkRC; - - for(instance_t* inst = p->network_head; inst!=nullptr; inst=inst->link) - { - if((rc = inst->class_desc->members->exec(inst)) != kOkRC ) - { - break; - } - } - - return rc; - } - - rc_t _get_variable( flow_t* p, const char* inst_label, const char* var_label, unsigned chIdx, instance_t*& instPtrRef, variable_t*& varPtrRef ) - { - rc_t rc = kOkRC; - instance_t* inst = nullptr; - variable_t* var = nullptr; - - varPtrRef = nullptr; - instPtrRef = nullptr; - - // locate the proc instance - if((inst = instance_find(p,inst_label)) == nullptr ) - { - rc = cwLogError(kInvalidIdRC,"Unknown proc instance label '%s'.", cwStringNullGuard(inst_label)); - goto errLabel; - } - - // locate the variable - if((rc = var_find( inst, var_label, chIdx, var)) != kOkRC ) - { - rc = cwLogError(kInvalidArgRC,"The variable '%s' could not be found on the proc instance '%s'.",cwStringNullGuard(var_label),cwStringNullGuard(inst_label)); - goto errLabel; - } - - instPtrRef = inst; - varPtrRef = var; - - errLabel: - return rc; - } - template< typename T > - rc_t _set_variable_value( flow_t* p, const char* inst_label, const char* var_label, unsigned chIdx, T value ) - { - rc_t rc = kOkRC; - instance_t* inst = nullptr; - variable_t* var = nullptr; - - // get the variable - if((rc = _get_variable(p,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( flow_t* p, const char* inst_label, const char* var_label, unsigned chIdx, T& valueRef ) - { - rc_t rc = kOkRC; - instance_t* inst = nullptr; - variable_t* var = nullptr; - - // get the variable - if((rc = _get_variable(p,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; - } - - unsigned _select_ranked_ele_by_rank_prob( const preset_order_t* presetA, const bool* selV , unsigned presetN ) - { - - // get a count of the candidate presets - unsigned rankN = selV==nullptr ? presetN : std::count_if(selV,selV+presetN,[](const bool& x){ return x; }); - - if( rankN == 0 ) - { - cwLogWarning("All preset candidates have been eliminated."); - return kInvalidIdx; - } - - unsigned rankV[ rankN ]; - unsigned idxMapA[ rankN ]; - - // fill rankV[] with candidates 'order' value - for(unsigned i=0,j=0; i 1 ); - - unsigned threshV[ rankN ]; - unsigned uniqueRankV[ rankN ]; - unsigned uniqueRankN = 0; - unsigned sel_idx = rankN - 1; // - - // for each possible rank value - for(unsigned i=0; ichild_count(); ++i) - { - const object_t* inst_pair; - if((inst_pair = net_preset_pair->child_ele(i)) != nullptr && inst_pair->is_pair() && textIsEqual(inst_pair->pair_label(),instance_label) ) - { - - preset_val_ref = inst_pair->pair_value(); - - goto errLabel; - } - } - - rc = cwLogError(kInvalidArgRC,"The preset instance label '%s' was not found.",cwStringNullGuard(preset_label)); - - errLabel: - return rc; - } } } @@ -1560,14 +371,15 @@ void cw::flow::print_external_device( const external_device_t* dev ) } + cw::rc_t cw::flow::create( handle_t& hRef, const object_t& classCfg, - const object_t& networkCfg, + const object_t& flowCfg, external_device_t* deviceA, unsigned deviceN ) { rc_t rc = kOkRC; - const object_t* network = nullptr; + const object_t* networkCfg = nullptr; bool printClassDictFl = false; bool printNetworkFl = false; @@ -1575,7 +387,7 @@ cw::rc_t cw::flow::create( handle_t& hRef, return rc; flow_t* p = mem::allocZ(); - p->networkCfg = &networkCfg; // TODO: duplicate cfg? + p->flowCfg = &flowCfg; // TODO: duplicate cfg? p->deviceA = deviceA; p->deviceN = deviceN; @@ -1587,21 +399,20 @@ cw::rc_t cw::flow::create( handle_t& hRef, } // parse the main audio file processor cfg record - if((rc = networkCfg.getv("framesPerCycle", p->framesPerCycle, - "multiPriPresetProbFl", p->multiPriPresetProbFl, - "multiSecPresetProbFl", p->multiSecPresetProbFl, - "multiPresetInterpFl", p->multiPresetInterpFl, - "network", network)) != kOkRC ) + if((rc = flowCfg.getv("framesPerCycle", p->framesPerCycle, + "multiPriPresetProbFl", p->multiPriPresetProbFl, + "multiSecPresetProbFl", p->multiSecPresetProbFl, + "multiPresetInterpFl", p->multiPresetInterpFl, + "network", networkCfg)) != kOkRC ) { rc = cwLogError(kSyntaxErrorRC,"Error parsing the required flow configuration parameters."); goto errLabel; } // parse the optional args - if((rc = networkCfg.getv_opt("maxCycleCount", p->maxCycleCount, - "printClassDictFl", printClassDictFl, - "printNetworkFl", printNetworkFl, - "presets", p->presetCfg)) != kOkRC ) + if((rc = flowCfg.getv_opt("maxCycleCount", p->maxCycleCount, + "printClassDictFl", printClassDictFl, + "printNetworkFl", printNetworkFl)) != kOkRC ) { rc = cwLogError(kSyntaxErrorRC,"Error parsing the optional flow configuration parameters."); goto errLabel; @@ -1624,22 +435,15 @@ cw::rc_t cw::flow::create( handle_t& hRef, if( printClassDictFl ) class_dict_print( p ); - // build the network - for(unsigned i=0; ichild_count(); ++i) + // instantiate the network + if((rc = network_create(p,networkCfg,p->net)) != kOkRC ) { - const object_t* inst_cfg = network->child_ele(i); - - // create the instance - if( (rc= _create_instance( p, inst_cfg ) ) != kOkRC ) - { - rc = cwLogError(rc,"The instantiation at proc index %i is invalid.",i); - goto errLabel; - - } + rc = cwLogError(rc,"Network creation failed."); + goto errLabel; } if( printNetworkFl ) - network_print(p); + network_print(p->net); hRef.set(p); @@ -1689,7 +493,7 @@ unsigned cw::flow::preset_cfg_flags( handle_t h ) cw::rc_t cw::flow::exec_cycle( handle_t h ) { - return _exec_cycle(_handleToPtr(h)); + return exec_cycle(_handleToPtr(h)->net); } cw::rc_t cw::flow::exec( handle_t h ) @@ -1699,7 +503,7 @@ cw::rc_t cw::flow::exec( handle_t h ) while( true ) { - rc = _exec_cycle(p); + rc = exec_cycle(p->net); if( rc == kEofRC ) { @@ -1720,418 +524,53 @@ cw::rc_t cw::flow::exec( handle_t h ) cw::rc_t cw::flow::apply_preset( handle_t h, const char* presetLabel ) { - rc_t rc = kOkRC; flow_t* p = _handleToPtr(h); - const object_t* net_preset_value; - const object_t* preset_pair; - - // locate the cfg of the requested preset - if((net_preset_value = _find_network_preset(p, presetLabel )) == nullptr ) - { - rc = cwLogError(kInvalidIdRC,"The network preset '%s' could not be found.", presetLabel ); - goto errLabel; - } - - // for each instance in the preset - for(unsigned i=0; ichild_count(); ++i) - { - // get the instance label/value pair - if((preset_pair = net_preset_value->child_ele(i)) != nullptr && preset_pair->is_pair() ) - { - const char* inst_label = preset_pair->pair_label(); - const object_t* preset_value_cfg = preset_pair->pair_value(); - instance_t* inst; - - // locate the instance - if((inst = instance_find(p,inst_label)) == nullptr ) - { - rc = cwLogError(kInvalidIdRC,"The network instance '%s' refered to in network preset '%s' could not be found.",inst_label,presetLabel); - goto errLabel; - } - - // if the preset value is a string then look it up in the class dictionary - if( preset_value_cfg->is_string() ) - { - const char* class_preset_label; - preset_value_cfg->value(class_preset_label); - _class_preset_channelize_vars(inst, class_preset_label ); - } - else - { - // if the preset value is a dict then apply it directly - if( preset_value_cfg->is_dict() ) - { - if((rc = _preset_channelize_vars( inst, "network", presetLabel, preset_value_cfg )) != kOkRC ) - { - rc = cwLogError(rc,"The preset '%s' application failed on instance '%s'.", presetLabel, inst_label ); - goto errLabel; - } - - } - else - { - rc = cwLogError(kSyntaxErrorRC,"The network preset '%s' instance '%s' does not have a string or dictionary value.", presetLabel, inst_label ); - goto errLabel; - } - } - } - else - { - rc = cwLogError(kSyntaxErrorRC,"The network preset '%s' is malformed.",presetLabel); - goto errLabel; - } - } - - cwLogInfo("Activated preset:%s",presetLabel); - errLabel: - return rc; + 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 ) { - rc_t rc = kOkRC; flow_t* p = _handleToPtr(h); - const object_t* net_preset_value_0; - - cwLogInfo("*** Applying dual: %s %s : %f",presetLabel_0, presetLabel_1, coeff ); - - // locate the cfg of the requested preset - if((net_preset_value_0 = _find_network_preset(p, presetLabel_0 )) == nullptr ) - { - rc = cwLogError(kInvalidIdRC,"The network preset '%s' could not be found.", presetLabel_0 ); - goto errLabel; - } - - // for each instance in the preset - for(unsigned i=0; ichild_count(); ++i) - { - const object_t* preset_pair_0 = net_preset_value_0->child_ele(i); - const char* inst_label = preset_pair_0->pair_label(); - const object_t* preset_value_cfg_0 = preset_pair_0->pair_value(); - instance_t* inst = nullptr; - const object_t* preset_value_cfg_1 = nullptr; - const int two = 2; - const char* class_preset_labelA[two]; - - // get the instance label/value pair - if((preset_pair_0 = net_preset_value_0->child_ele(i)) == nullptr || !preset_pair_0->is_pair() ) - { - rc = cwLogError(kSyntaxErrorRC,"An invalid preset value pair was encountered in '%s'.",presetLabel_0); - goto errLabel; - } - - // verify that the preset value is a string or dict - if( preset_pair_0->pair_value()==nullptr || (!preset_value_cfg_0->is_dict() && !preset_value_cfg_0->is_string() )) - { - rc = cwLogError(kSyntaxErrorRC,"The preset value pair for instance '%s' in '%s' is not a 'dict' or 'string'.",inst_label,presetLabel_0); - goto errLabel; - } - - // locate the instance associated with the primary and secondary preset - if((inst = instance_find(p,inst_label)) == nullptr ) - { - rc = cwLogError(kInvalidIdRC,"The network instance '%s' refered to in network preset '%s' could not be found.",cwStringNullGuard(inst_label),cwStringNullGuard(presetLabel_0)); - goto errLabel; - } - - // locate the second instance/preset value pair - if((rc = _find_network_preset_instance_pair( p, presetLabel_1, inst_label, preset_value_cfg_1 )) != kOkRC ) - { - rc = cwLogError(kInvalidIdRC,"The second network instance '%s' refered to in network preset '%s' could not be found.",inst_label,presetLabel_1); - goto errLabel; - } - - // TODO: We require that the instance presets both be of the same type: string or dict. - // There's no good reason for this, as string's resolve to class dict presets anyway. - // Fix this! - if( !(preset_value_cfg_0->is_dict() == preset_value_cfg_1->is_dict() && preset_value_cfg_0->is_string() == preset_value_cfg_1->is_string()) ) - { - rc = cwLogError(kInvalidIdRC,"The value type (string or dict) of dual network presets must match. (%s != %s)",preset_value_cfg_0->type->label,preset_value_cfg_1->type->label); - goto errLabel; - } - - preset_value_cfg_0->value(class_preset_labelA[0]); - preset_value_cfg_1->value(class_preset_labelA[1]); - - - // if the preset value is a string then look it up in the class dictionary - if( preset_value_cfg_0->is_string() ) - { - rc = _class_multi_preset_channelize_vars(inst, class_preset_labelA, two, coeff ); - } - else - { - assert( preset_value_cfg_1->is_dict() ); - - const object_t* preset_value_cfgA[] = { preset_value_cfg_0, preset_value_cfg_1}; - - if((rc = _multi_preset_channelize_vars( inst, "network", class_preset_labelA, preset_value_cfgA, two, coeff )) != kOkRC ) - { - rc = cwLogError(rc,"The dual preset '%s':'%s' application failed on instance '%s'.", cwStringNullGuard(class_preset_labelA[0]), cwStringNullGuard(class_preset_labelA[1]), inst_label ); - goto errLabel; - } - } - } - - - errLabel: - - if( rc != kOkRC ) - rc = cwLogError(rc,"The dual preset '%s':'%s' application failed.", cwStringNullGuard(presetLabel_0), cwStringNullGuard(presetLabel_1) ); - - return rc; + 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 ) { - rc_t rc = kOkRC; - const char* label0 = nullptr; - const char* label1 = nullptr; - bool priProbFl = cwIsFlag(mps.flags, kPriPresetProbFl ); - bool secProbFl = cwIsFlag(mps.flags, kSecPresetProbFl ); - bool interpFl = cwIsFlag(mps.flags, kInterpPresetFl ); - - //printf("preset flags: pri:%i sec:%i interp:%i\n",priProbFl,secProbFl,interpFl); - - // verify that the set of candidate presets is not empty - if( mps.presetN == 0 ) - { - cwLogError(kInvalidArgRC,"A multi-preset application was requested but no presets were provided."); - goto errLabel; - } - - // if only a single candidate preset exists or needs to be selected - if( interpFl==false || mps.presetN==1 ) - { - // if only a single candidate preset is available or pri. probablity is not enabled - if( mps.presetN == 1 || priProbFl==false ) - label0 = mps.presetA[0].preset_label; - else - { - if( priProbFl ) - label0 = _select_ranked_ele_label_by_rank_prob( mps.presetA, nullptr, mps.presetN ); - else - label0 = mps.presetA[0].preset_label; - } - } - else // interpolation has been selected and at least 2 presets exist - { - unsigned pri_sel_idx = 0; - - // select the primary preset - if( priProbFl ) - pri_sel_idx = _select_ranked_ele_by_rank_prob( mps.presetA, nullptr, mps.presetN ); - else - { - // select all presets assigned to order == 1 - bool selA[ mps.presetN ]; - for(unsigned i=0; inet,mps); } -/* -cw::rc_t cw::flow::apply_preset( handle_t h, const multi_preset_selector_t& multi_preset_sel ) -{ - rc_t rc = kOkRC; - const char* label0 = nullptr; - const char* label1 = nullptr; - const char* prob_label = nullptr; - bool multiPriPresetProbFl = cwIsFlag(multi_preset_sel.flags, kPriPresetProbFl ); - bool multiSecPresetProbFl = cwIsFlag(multi_preset_sel.flags, kSecPresetProbFl ); - bool multiPresetInterpFl = cwIsFlag(multi_preset_sel.flags, kInterpPresetFl ); - - // verify that the set of presets to select from is not empty - if( multi_preset_sel.presetN == 0 ) - { - cwLogError(kInvalidArgRC,"A multi-preset application was requested but no presets were provided."); - goto errLabel; - } - - // if probabistic selection was requested and is possible - if( multiPresetProbFl && multi_preset_sel.presetN > 1 ) - { - auto presetA = multi_preset_sel.presetA; - auto presetN = multi_preset_sel.presetN; - - // if we are interpolating then the base preset is always the first one in presetA[] - // so do not include it as a candidate for probabilistic selection - if( multiPresetInterpFl ) - { - presetA += 1; - presetN -= 1; - - // if only one preset remains in the list then prob. selection is not possible - if( presetN == 1 ) - prob_label = presetA[0].preset_label; - } - - // select a preset based using the ranked-prob. algorithm. - if( prob_label == nullptr ) - { - unsigned prob_sel_idx; - - if((prob_sel_idx = _select_ranked_ele_by_rank_prob( presetA, presetN )) == kInvalidIdx ) - rc = cwLogWarning("The multi-preset select function failed. Selecting preset 0."); - else - { - prob_label = presetA[prob_sel_idx].preset_label; - - cwLogInfo("Multi-preset prob. select:%s : %i from %i", - cwStringNullGuard(prob_label), - prob_sel_idx, - multi_preset_sel.presetN ); - - } - } - } - - // prob_label now holds a probablistically selected preset label - // or null if prob. sel. was not requested or failed - - switch( multi_preset_sel.presetN ) - { - case 0: - assert(0); // we avoided this case at the top of the function - break; - - case 1: - // if there is only one preset to select from - label0 = multi_preset_sel.presetA[0].preset_label; - break; - - default: - // There are at least two presets ... - // ... and prob. select was not requested or failed - if( prob_label == nullptr ) - { - label0 = multi_preset_sel.presetA[0].preset_label; - label1 = multiPresetInterpFl ? multi_preset_sel.presetA[1].preset_label : nullptr; - } - else // ... and a prob. selection exists - { - // if we need two presets - if( multiPresetInterpFl ) - { - label0 = multi_preset_sel.presetA[0].preset_label; - label1 = prob_label; - } - else // otherwise we need only one - { - label0 = prob_label; - label1 = nullptr; - } - } - } - - if( label0 == nullptr ) - { - rc = cwLogError(kInvalidStateRC,"The selected multi-preset label is empty."); - goto errLabel; - } - - if( label1 == nullptr ) - { - rc = apply_preset( h, label0 ); - } - else - { - double coeff = _calc_multi_preset_dual_coeff(multi_preset_sel); - rc = apply_dual_preset( h, label0, label1, coeff ); - } - -errLabel: - return rc; -} -*/ 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), inst_label, var_label, chIdx, 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), inst_label, var_label, chIdx, 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), inst_label, var_label, chIdx, 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), inst_label, var_label, chIdx, 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), inst_label, var_label, chIdx, 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), inst_label, var_label, chIdx, 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), inst_label, var_label, chIdx, 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), inst_label, var_label, chIdx, 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), inst_label, var_label, chIdx, 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), inst_label, var_label, chIdx, valueRef ); } +{ return get_variable_value( _handleToPtr(h)->net, inst_label, var_label, chIdx, valueRef ); } @@ -2148,7 +587,7 @@ void cw::flow::print_network( handle_t h ) for(unsigned i=0; ideviceN; ++i) print_external_device( p->deviceA + i ); - network_print(p); + network_print(p->net); } diff --git a/cwFlow.h b/cwFlow.h index 945840b..51a767c 100644 --- a/cwFlow.h +++ b/cwFlow.h @@ -8,64 +8,6 @@ namespace cw typedef handle 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; - - 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; void print_abuf( const struct abuf_str* abuf ); @@ -86,11 +28,11 @@ namespace cw rc_t exec_cycle( handle_t h ); // Run the network to completion. - rc_t exec( handle_t h ); + 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 ); diff --git a/cwFlowDecl.h b/cwFlowDecl.h index 7303f99..044cf24 100644 --- a/cwFlowDecl.h +++ b/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, diff --git a/cwFlowNet.cpp b/cwFlowNet.cpp new file mode 100644 index 0000000..19be4ec --- /dev/null +++ b/cwFlowNet.cpp @@ -0,0 +1,2279 @@ +#include "cwCommon.h" +#include "cwLog.h" +#include "cwCommonImpl.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" // real_t, sample_t +#include "cwTime.h" +#include "cwMidiDecls.h" +#include "cwFlowDecl.h" +#include "cwFlowTypes.h" +#include "cwFlowNet.h" +#include "cwFlowProc.h" + +namespace cw +{ + namespace flow + { + + void _destroy_inst( instance_t* inst ) + { + if( inst == nullptr ) + return; + + if( inst->class_desc->members->destroy != nullptr && inst->userPtr != nullptr ) + inst->class_desc->members->destroy( inst ); + + // destroy the instance variables + variable_t* var0 = inst->varL; + variable_t* var1 = nullptr; + while( var0 != nullptr ) + { + var1 = var0->var_link; + _var_destroy(var0); + var0 = var1; + } + + inst->varL = nullptr; + + mem::release(inst->label); + mem::release(inst->varMapA); + mem::release(inst); + } + + rc_t _network_destroy( network_t& net ) + { + rc_t rc = kOkRC; + + for(unsigned i=0; iconnect_link = src_var->connect_link; + src_var->connect_link = in_var; + + assert( src_var->value != nullptr ); + + in_var->value = src_var->value; + in_var->src_var = src_var; + } + + rc_t _create_instance_var_map( instance_t* inst ) + { + rc_t rc = kOkRC; + unsigned max_vid = kInvalidId; + unsigned max_chIdx = 0; + variable_t* var = inst->varL; + //variable_t* v0 = nullptr; + + // determine the max variable vid and max channel index value among all variables + for(; var!=nullptr; var = var->var_link ) + { + if( var->vid != kInvalidId ) + { + if( max_vid == kInvalidId || var->vid > max_vid ) + max_vid = var->vid; + + if( var->chIdx != kAnyChIdx && (var->chIdx+1) > max_chIdx ) + max_chIdx = (var->chIdx + 1); + + } + } + + // If there are any variables + if( max_vid != kInvalidId ) + { + // create the variable map array + inst->varMapChN = max_chIdx + 1; + inst->varMapIdN = max_vid + 1; + inst->varMapN = inst->varMapIdN * inst->varMapChN; + inst->varMapA = mem::allocZ( inst->varMapN ); + + // assign each variable to a location in the map + for(variable_t* var=inst->varL; var!=nullptr; var=var->var_link) + if( var->vid != kInvalidId ) + { + unsigned idx = kInvalidIdx; + + if((rc = _var_map_id_to_index( inst, var->vid, var->chIdx, idx )) != kOkRC ) + goto errLabel; + + + // verify that there are not multiple variables per map position + if( inst->varMapA[ idx ] != nullptr ) + { + variable_t* v0 = inst->varMapA[idx]; + rc = cwLogError(kInvalidStateRC,"The variable '%s' id:%i ch:%i and '%s' id:%i ch:%i share the same variable map position on instance: %s. This is usually cased by duplicate variable id's.", + v0->label,v0->vid,v0->chIdx, var->label,var->vid,var->chIdx,inst->label); + + goto errLabel; + } + + // assign this variable to a map position + inst->varMapA[ idx ] = var; + + if( var->chIdx != kAnyChIdx && var->value == nullptr ) + { + rc = cwLogError(kInvalidStateRC,"The value of the variable '%s' ch:%i on instance:'%s' has not been set.",var->label,var->chIdx,inst->label); + goto errLabel; + } + + } + + } + + errLabel: + return rc; + + } + + void _complete_input_connections( instance_t* inst ) + { + for(variable_t* var=inst->varL; var!=nullptr; var=var->var_link) + if(var->chIdx == kAnyChIdx && is_connected_to_external_proc(var) ) + { + + variable_t* base_src_var = var->src_var; + + // since 'var' is on the 'any' channel the 'src' var must also be on the 'any' channel + assert( base_src_var->chIdx == kAnyChIdx ); + + //printf("%s %s\n",inst->label,var->label); + + // for each var channel in the input var + for(variable_t* in_var = var->ch_link; in_var != nullptr; in_var=in_var->ch_link) + { + // locate the matching channel on the 'src' var + variable_t* svar = base_src_var; + for(; svar!=nullptr; svar=svar->ch_link) + if( svar->chIdx == in_var->chIdx ) + break; + + // connect the src->input var + _connect_vars( svar==nullptr ? base_src_var : svar, in_var); + } + } + } + + rc_t _call_value_func_on_all_variables( instance_t* inst ) + { + rc_t rc = kOkRC; + rc_t rc1 = kOkRC; + + for(unsigned i=0; ivarMapN; ++i) + if( inst->varMapA[i] != nullptr && inst->varMapA[i]->vid != kInvalidId ) + { + variable_t* var = inst->varMapA[i]; + + if((rc = var->inst->class_desc->members->value( var->inst, var )) != kOkRC ) + rc1 = cwLogError(rc,"The proc instance '%s:%i' reported an invalid valid on variable:%s chIdx:%i.", var->inst->label, var->inst->label_sfx_id, var->label, var->chIdx ); + } + + return rc1; + } + + rc_t _var_channelize( instance_t* inst, const char* preset_label, const char* type_src_label, const char* value_label, const object_t* value ) + { + rc_t rc = kOkRC; + + variable_t* dummy = nullptr; + + // verify that a valid value exists + if( value == nullptr ) + { + rc = cwLogError(kSyntaxErrorRC,"Unexpected missig value on %s preset '%s' instance '%s' variable '%s'.", type_src_label, preset_label, inst->label, cwStringNullGuard(value_label) ); + goto errLabel; + } + + // if a list of values was given + if( value->is_list() ) + { + for(unsigned chIdx=0; chIdxchild_count(); ++chIdx) + if((rc = var_channelize( inst, value_label, kBaseSfxId, chIdx, value->child_ele(chIdx), kInvalidId, dummy )) != kOkRC ) + goto errLabel; + } + else // otherwise a single value was given + { + if((rc = var_channelize( inst, value_label, kBaseSfxId, kAnyChIdx, value, kInvalidId, dummy )) != kOkRC ) + goto errLabel; + } + + errLabel: + return rc; + } + + rc_t _preset_channelize_vars( instance_t* inst, const char* type_src_label, const char* preset_label, const object_t* preset_cfg ) + { + rc_t rc = kOkRC; + + //cwLogInfo("Channelizing '%s' preset %i vars for '%s'.",type_src_label, preset_cfg==nullptr ? 0 : preset_cfg->child_count(), inst->label ); + + // validate the syntax of the preset record + if( !preset_cfg->is_dict() ) + { + rc = cwLogError(kSyntaxErrorRC,"The preset record '%s' on %s '%s' is not a dictionary.", preset_label, type_src_label, inst->class_desc->label ); + goto errLabel; + } + + + // for each preset variable + for(unsigned i=0; ichild_count(); ++i) + { + const object_t* value = preset_cfg->child_ele(i)->pair_value(); + const char* value_label = preset_cfg->child_ele(i)->pair_label(); + if((rc = _var_channelize( inst, preset_label, type_src_label, value_label, value )) != kOkRC ) + goto errLabel; + + + } + + errLabel: + if( rc != kOkRC ) + rc = cwLogError(rc,"Apply %s preset failed on instance:%s class:%s preset:%s.", type_src_label, inst->label, inst->class_desc->label, preset_label ); + + return rc; + } + + + template< typename T > + T _interp_dual_value( T v0, T v1, double coeff ) + { + T y; + if( v0 == v1 ) + y = v0; + else + y = (T)(v0 + (v1-v0)*coeff ); + + //printf("%f %f -> %f\n",(double)v0,(double)v1,(double)y); + return y; + } + + rc_t _set_var_from_dual_preset_scalar_scalar( instance_t* inst, const char* var_label, const object_t* scalar_0, const object_t* scalar_1, double coeff, unsigned chIdx ) + { + rc_t rc = kOkRC; + object_t interped_value; + variable_t* dummy = nullptr; + + // one of the input values must exist + if( scalar_0==nullptr && scalar_1==nullptr ) + { + rc = cwLogError(kInvalidArgRC,"The numeric types of both operands of a dual value are null."); + goto errLabel; + } + + // It's possible that one or the other input value does not exist + if( scalar_0 == nullptr ) + scalar_0 = scalar_1; + else + { + if( scalar_1 == nullptr ) + scalar_1 = scalar_0; + } + + // verify that the input values are the same type + if( scalar_0->type->id != scalar_1->type->id ) + { + rc = cwLogError(kInvalidArgRC,"The numeric types of both operands of a dual value preset must match. (%s != %s).",cwStringNullGuard(scalar_0->type->label),cwStringNullGuard(scalar_1->type->label)); + goto errLabel; + } + + printf("%s:%s :",inst->label,var_label); + + switch( scalar_0->type->id ) + { + case kInt32TId: + interped_value.set_value( _interp_dual_value(scalar_0->u.i32,scalar_1->u.i32,coeff) ); + break; + case kUInt32TId: + interped_value.set_value( _interp_dual_value(scalar_0->u.u32,scalar_1->u.u32,coeff) ); + break; + case kInt64TId: + assert(0); + //interped_value.set_value( _interp_dual_value(scalar_0->u.i64,scalar_1->u.i64,coeff) ); + break; + case kUInt64TId: + assert(0); + //interped_value.set_value( _interp_dual_value(scalar_0->u.u64,scalar_1->u.u64,coeff) ); + break; + case kFloatTId: + interped_value.set_value( _interp_dual_value(scalar_0->u.f,scalar_1->u.f,coeff) ); + break; + case kDoubleTId: + interped_value.set_value( _interp_dual_value(scalar_0->u.d,scalar_1->u.d,coeff) ); + break; + + default: + rc = cwLogError(kInvalidStateRC,"Preset dual values of type '%s' cannot be interpolated.",cwStringNullGuard(scalar_0->type->label)); + goto errLabel; + } + + + if((rc = var_channelize( inst, var_label, kBaseSfxId, chIdx, &interped_value, kInvalidId, dummy )) != kOkRC ) + { + rc = cwLogError(kInvalidArgRC,"Dual value preset application failed."); + goto errLabel; + } + + errLabel: + return rc; + } + + rc_t _set_var_from_dual_preset_list_list( instance_t* inst, const char* var_label, const object_t* list_0, const object_t* list_1, double coeff ) + { + rc_t rc = kOkRC; + + if( list_0->child_count() != list_1->child_count() ) + return cwLogError(kInvalidArgRC,"If two lists are to be applied as a dual preset they must be the same length."); + + for(unsigned chIdx=0; chIdxchild_count(); ++chIdx) + if((rc = _set_var_from_dual_preset_scalar_scalar(inst,var_label,list_0->child_ele(chIdx),list_1->child_ele(chIdx),coeff,chIdx)) != kOkRC ) + goto errLabel; + + errLabel: + return rc; + } + + rc_t _set_var_from_dual_preset_scalar_list( instance_t* inst, const char* var_label, const object_t* scalar, const object_t* list, double coeff ) + { + rc_t rc = kOkRC; + for(unsigned chIdx=0; chIdxchild_count(); ++chIdx) + if((rc = _set_var_from_dual_preset_scalar_scalar(inst,var_label,scalar,list->child_ele(chIdx),coeff,chIdx)) != kOkRC ) + goto errLabel; + + errLabel: + return rc; + } + + rc_t _set_var_from_dual_preset_list_scalar( instance_t* inst, const char* var_label, const object_t* list, const object_t* scalar, double coeff ) + { + rc_t rc = kOkRC; + for(unsigned chIdx=0; chIdxchild_count(); ++chIdx) + if((rc = _set_var_from_dual_preset_scalar_scalar(inst,var_label,list->child_ele(chIdx),scalar,coeff,chIdx)) != kOkRC ) + goto errLabel; + + errLabel: + return rc; + } + + rc_t _set_var_from_dual_preset_scalar_scalar( instance_t* inst, const char* var_label, const object_t* scalar_0, const object_t* scalar_1, double coeff ) + { + return _set_var_from_dual_preset_scalar_scalar(inst,var_label,scalar_0,scalar_1,coeff,kAnyChIdx); + } + + + rc_t _is_legal_dual_value( const object_t* value ) + { + rc_t rc = kOkRC; + + if( value->is_list() ) + { + if( value->child_count() == 0 ) + { + rc = cwLogError(kInvalidArgRC,"Empty lists values cannot be applied as part of a dual value preset."); + goto errLabel; + } + + } + else + { + switch( value->type->id ) + { + case kInt32TId: + case kUInt32TId: + case kInt64TId: + case kUInt64TId: + case kFloatTId: + case kDoubleTId: + break; + default: + rc = cwLogError(kInvalidArgRC,"Objects of type '%s' cannot be applied as part of a dual value preset.",cwStringNullGuard(value->type->label)); + } + } + + errLabel: + return rc; + + } + + rc_t _set_var_from_dual_preset( instance_t* inst, const char* var_label, const object_t* value_0, const object_t* value_1, double coeff ) + { + rc_t rc = kOkRC; + + // dual values must be either numeric scalars or lists + if((rc = _is_legal_dual_value(value_0)) != kOkRC || (rc = _is_legal_dual_value(value_1)) != kOkRC) + goto errLabel; + + + // if both values are lists then they must be the same length + if( value_0->is_list() && value_1->is_list() ) + { + rc = _set_var_from_dual_preset_list_list( inst, var_label, value_0, value_1, coeff ); + goto errLabel; + } + else + { + // if value_0 is a list and value_1 is a scalar + if( value_0->is_list() ) + { + rc = _set_var_from_dual_preset_list_scalar( inst, var_label, value_0, value_1, coeff ); + goto errLabel; + } + else + { + // if value_1 is a list and value_0 is a scalar + if( value_1->is_list() ) + { + rc = _set_var_from_dual_preset_scalar_list( inst, var_label, value_0, value_1, coeff ); + goto errLabel; + } + else // both values are scalars + { + rc = _set_var_from_dual_preset_scalar_scalar( inst, var_label, value_0, value_1, coeff ); + goto errLabel; + } + } + } + + errLabel: + return rc; + } + + rc_t _multi_preset_channelize_vars( instance_t* inst, const char* type_src_label, const char** presetLabelA, const object_t** preset_cfgA, unsigned presetN, double coeff ) + { + rc_t rc = kOkRC; + + const char* preset_label_0 = ""; + const char* preset_label_1 = ""; + + //cwLogInfo("Channelizing '%s' preset %i vars for '%s'.",type_src_label, preset_cfg==nullptr ? 0 : preset_cfg->child_count(), inst->label ); + + if( presetN < 2 ) + { + rc = cwLogError(kInvalidArgRC,"There must be at least 2 presets selected to interpolate between preset variable dictionaries."); + goto errLabel; + } + + if( presetN > 2 ) + { + cwLogWarning("More than two presets dictionaries were specified for interpolation. Only the first two will be used."); + goto errLabel; + } + + preset_label_0 = presetLabelA[0]; + preset_label_1 = presetLabelA[1]; + + // validate each of the preset records is a dict + for(unsigned i=0; iis_dict() ) + { + rc = cwLogError(kSyntaxErrorRC,"The preset record '%s' on %s '%s' is not a dictionary.", presetLabelA[i], type_src_label, inst->class_desc->label ); + goto errLabel; + } + + + // for each preset variable in the first preset var dict + for(unsigned i=0; ichild_count(); ++i) + { + const char* var_label = preset_cfgA[0]->child_ele(i)->pair_label(); + const object_t* value_0 = preset_cfgA[0]->child_ele(i)->pair_value(); + + const object_t* value_1 = preset_cfgA[1]->find_child(var_label); + + if( value_0 == nullptr && value_1 == nullptr ) + { + rc = cwLogError(kSyntaxErrorRC,"Unexpected missig values on %s preset '%s' instance '%s' variable '%s'.", type_src_label, presetLabelA[0], inst->label, cwStringNullGuard(var_label) ); + goto errLabel; + } + + if( value_0 == nullptr ) + { + cwLogWarning("The preset variable '%s' was not found for the preset: '%s'. Falling back to single value assign.",cwStringNullGuard(var_label),cwStringNullGuard(presetLabelA[0])); + + rc = _var_channelize( inst, preset_label_1, "dual class", var_label, value_1 ); + goto errLabel; + } + + if( value_1 == nullptr ) + { + cwLogWarning("The preset variable '%s' was not found for the preset: '%s'. Falling back to single value assign.",cwStringNullGuard(var_label),cwStringNullGuard(presetLabelA[1])); + + rc = _var_channelize( inst, preset_label_0, "dual class", var_label, value_0 ); + goto errLabel; + } + + + if((rc = _set_var_from_dual_preset( inst, var_label, value_0, value_1, coeff )) != kOkRC ) + { + rc = cwLogError(rc,"Multi preset application failed on variable:%s.",cwStringNullGuard(var_label)); + goto errLabel; + } + } + + errLabel: + if( rc != kOkRC ) + rc = cwLogError(rc,"Apply %s multi-preset failed on instance:%s class:%s presetA:%s presetB:%s.", type_src_label, inst->label, inst->class_desc->label, preset_label_0, preset_label_1 ); + + return rc; + } + + + rc_t _class_multi_preset_channelize_vars(instance_t* inst, const char** class_preset_labelA, unsigned presetN, double coeff ) + { + rc_t rc = kOkRC; + const object_t* presetCfgA[ presetN ]; + const char* presetLabelA[ presetN ]; + unsigned presetCfgN = 0; + + for(unsigned i=0; iclass_desc, class_preset_labelA[i])) == nullptr ) + { + rc = cwLogError(kInvalidIdRC,"The preset '%s' could not be found for the instance '%s'.", class_preset_labelA[i], inst->label); + goto errLabel; + } + + if( pr->cfg == nullptr ) + { + rc = cwLogError(kInvalidIdRC,"The value of preset '%s' was empty in instance '%s'.", class_preset_labelA[i], inst->label); + goto errLabel; + } + + presetCfgA[ presetCfgN] = pr->cfg; + presetLabelA[presetCfgN] = class_preset_labelA[i]; + presetCfgN++; + } + } + + // dispatch based on the count of presets located + switch( presetCfgN ) + { + case 0: + rc = cwLogError(kInvalidArgRC,"No valid class preset records were found while attempting apply a multi-preset."); + break; + + case 1: + // only one valid preset was located - apply it directly + rc = _preset_channelize_vars( inst, "class", presetLabelA[0], presetCfgA[0]); + break; + + default: + // more than one preset was located - apply it's interpolated values + rc = _multi_preset_channelize_vars( inst, "class", presetLabelA, presetCfgA, presetCfgN, coeff); + } + + + errLabel: + return rc; + + } + + rc_t _class_preset_channelize_vars( instance_t* inst, const char* preset_label ) + { + rc_t rc = kOkRC; + const preset_t* pr; + + if( preset_label == nullptr ) + return kOkRC; + + // locate the requestd preset record + if((pr = class_preset_find(inst->class_desc, preset_label)) == nullptr ) + { + rc = cwLogError(kInvalidIdRC,"The preset '%s' could not be found for the instance '%s'.", preset_label, inst->label); + goto errLabel; + } + + rc = _preset_channelize_vars( inst, "class", preset_label, pr->cfg); + + errLabel: + return rc; + } + + + rc_t _class_apply_presets( instance_t* inst, const object_t* preset_labels ) + { + rc_t rc = kOkRC; + const char* s = nullptr; + + // if preset_labels is a string + if( preset_labels->is_string() && preset_labels->value(s)==kOkRC ) + return _class_preset_channelize_vars(inst,s); + + // if the preset_labels is not a list + if( !preset_labels->is_list() ) + rc = cwLogError(kSyntaxErrorRC,"The preset list on instance '%s' is neither a list nor a string.",inst->label); + else + { + // preset_labels is a list. + + // for each label listed in the preset label list + for(unsigned i=0; ichild_count(); ++i) + { + const object_t* label_obj = preset_labels->child_ele(i); + + // verify that the label is a strng + if( !label_obj->is_string() || label_obj->value(s) != kOkRC ) + { + rc = cwLogError(kSyntaxErrorRC,"The preset list does not contain string on instance '%s'.",inst->label); + goto errLabel; + } + + // apply a preset label + if((rc = _class_preset_channelize_vars( inst, s)) != kOkRC ) + goto errLabel; + } + } + + errLabel: + return rc; + } + + + + + rc_t _proc_inst_args_channelize_vars( instance_t* inst, const char* arg_label, const object_t* arg_cfg ) + { + rc_t rc = kOkRC; + + if( arg_cfg == nullptr ) + return rc; + + return _preset_channelize_vars( inst, "instance", arg_label, arg_cfg ); + + } + + + //======================================================================================================= + // + // network creation + // + + enum { + kInVarTypeId = 0x01, + kSrcProcTypeId = 0x02, + kSrcVarTypeId = 0x04 + }; + + typedef struct in_ele_str + { + unsigned typeId; // See k???_InFl above + char* label; // label of in or src id + unsigned base_sfx_id; // literal base_sfx_id or kInvalidId if the base_sfx_id was not given or 'is_iter_fl' is false + unsigned sfx_id_count; // literal sfx_id_count or kInvalidCnt if not given + unsigned is_iter_fl; // this id included an '_' + } in_ele_t; + + typedef struct in_stmt_str + { + in_ele_t in_var_ele; // in-var element + char* src_net_label; // src-net label (null=in-var net, '_'=root net, string=named net) + network_t* src_net; // network containing the src-proc + in_ele_t src_proc_ele; // src-proc element + in_ele_t src_var_ele; // src-var element + var_desc_t* in_var_desc; // Pointer to the in-var var_desc. + bool create_in_fl; // True if the in_var needs to be created with an sfx_id, false create the var by the default process (w/o sfx_id) + in_ele_t* iter_cnt_ctl_ele; // Pointer to the ele which is controlling the iteration count (or null if in-var is non-iterating) + unsigned iter_cnt; // Count of iterations or 0 if in-var is non-iterating. + } in_stmt_t; + + typedef struct proc_inst_parse_statestr + { + const char* inst_label; // + const char* inst_clas_label; // + const char* arg_label; // + const object_t* preset_labels; // + const object_t* arg_cfg; // + const object_t* in_dict; // cfg. node to the in-list + in_stmt_t* in_array; // in_array[ in_arrayN ] in-stmt array + unsigned in_arrayN; // count of in-stmt's in the in-list. + } proc_inst_parse_state_t; + + bool _is_non_null_pair( const object_t* cfg ) + { return cfg != nullptr && cfg->is_pair() && cfg->pair_label()!=nullptr && cfg->pair_value()!=nullptr; } + + + rc_t _parse_in_ele( const char* id_str, in_ele_t& r ) + { + rc_t rc = kOkRC; + unsigned bufN; + + r.base_sfx_id = kInvalidId; + r.sfx_id_count = kInvalidCnt; + + if((bufN = textLength(id_str)) == 0 ) + { + rc = cwLogError(kSyntaxErrorRC,"A blank connection id string was encountered."); + goto errLabel; + } + else + { + char* underscore = nullptr; + char* digit = nullptr; + char buf[ bufN+1 ]; + + // copy the id string into a non-const scratch buffer + textCopy(buf,bufN+1,id_str); + + // locate the last underscore + if((underscore = lastMatchChar(buf,'_')) != nullptr ) + { + *underscore = 0; // terminate the string prior to the underscore + + for(digit = underscore + 1; *digit; digit++) + if( !isdigit(*digit) ) + break; + + // if the underscore was followed by a number + // or if the underscore was the last char + // in the string - then digit will point to + // the terminating zero - otherwise the + // underscore did not indicate an iterating id + if( *digit != 0 ) + { + *underscore = '_'; // replace the underscore - its part of the label + underscore = nullptr; + } + else + { + r.is_iter_fl = true; + + // if there is a number following the underscore then this is the secInt + if( textLength(underscore + 1) ) + { + // a literal iteration count was given - parse it into an integer + if((rc = string_to_number(underscore + 1,r.sfx_id_count)) != kOkRC ) + { + rc = cwLogError(rc,"Unable to parse the secondary integer in the connection label '%s'.",cwStringNullGuard(id_str)); + goto errLabel; + } + } + } + } + + // verify that some content remains in the id string + if( textLength(buf) == 0 ) + { + rc = cwLogError(kSyntaxErrorRC,"Unable to parse the connection id string '%s'.",cwStringNullGuard(id_str)); + goto errLabel; + } + + // go backward from the last char until the begin-of-string or a non-digit is found + for(digit=buf + textLength(buf)-1; digit>buf; --digit) + if(!isdigit(*digit) ) + { + ++digit; // advance to the first digit in the number + break; + } + + // if a digit was found then this is the 'priInt' + if( textLength(digit) ) + { + assert( buf <= digit-1 && digit-1 <= buf + bufN ); + + // a literal base-sfx-id was given - parse it into an integer + if((rc = string_to_number(digit,r.base_sfx_id)) != kOkRC ) + { + rc = cwLogError(rc,"Unable to parse the primary integer in the connection label '%s'.",cwStringNullGuard(id_str)); + goto errLabel; + } + + *digit = 0; // zero terminate the label + + } + + // verify that some content remains in the id string + if( textLength(buf) == 0 ) + { + rc = cwLogError(kSyntaxErrorRC,"Unexpected invalid connection id string '%s'.",cwStringNullGuard(id_str)); + goto errLabel; + + } + else + { + // store the label + r.label = mem::duplStr(buf); + } + } + + + errLabel: + return rc; + } + + rc_t _calc_src_proc_ele_count(network_t& net, in_ele_t& src_proc_ele, unsigned& cnt_ref) + { + rc_t rc = kOkRC; + cnt_ref = 0; + + // if a literal proc sfx_id was given then use it otherwise use the default base-sfx-id (0) + unsigned sfx_id = src_proc_ele.base_sfx_id==kInvalidCnt ? kBaseSfxId : src_proc_ele.base_sfx_id; + unsigned n; + for(n=0; instance_find(net, src_proc_ele.label, sfx_id ) != nullptr; ++n ) + sfx_id += 1; + + if( n == 0 ) + { + rc = cwLogError(kSyntaxErrorRC,"The src-proc '%s:%i' was not found.",cwStringNullGuard(src_proc_ele.label),sfx_id); + goto errLabel; + } + + cnt_ref = n; + errLabel: + return rc; + } + + rc_t _calc_src_var_ele_count(network_t& net, const in_ele_t& src_proc_ele, const in_ele_t& src_var_ele, unsigned& cnt_ref) + { + rc_t rc = kOkRC; + instance_t* src_proc = nullptr; + unsigned proc_sfx_id = src_proc_ele.base_sfx_id==kInvalidCnt ? kBaseSfxId : src_proc_ele.base_sfx_id; + + cnt_ref = 0; + + // locate the parent proc of this var + if((src_proc = instance_find(net,src_proc_ele.label,proc_sfx_id)) == nullptr ) + { + cwLogError(kSyntaxErrorRC,"The src-proc instance '%s:%i' could not be found.",cwStringNullGuard(src_proc_ele.label),proc_sfx_id); + goto errLabel; + } + else + { + // if a starting var sfx_id was given by the id then use it otherwise use the default base-sfx-id (0) + unsigned sfx_id = src_var_ele.base_sfx_id==kInvalidCnt ? kBaseSfxId : src_var_ele.base_sfx_id; + unsigned n; + for(n=0; var_exists(src_proc,src_var_ele.label, sfx_id, kAnyChIdx ); ++n ) + sfx_id += 1; + + + if( n == 0 ) + { + cwLogError(kSyntaxErrorRC,"The src-var '%s:%i' was not found.",cwStringNullGuard(src_var_ele.label),sfx_id); + goto errLabel; + } + + cnt_ref = n; + + } + + errLabel: + return rc; + } + + // If the in-var is iterating then the count of iterations must be controlled by exactly one + // of the 3 parts of the in-stmt: in-var,src_proc, or src_var. This function determines + // which element is used to determine the iteration count. + rc_t _determine_in_stmt_iter_count_ctl_ele(network_t& net, instance_t* inst, in_stmt_t& in_stmt ) + { + assert( in_stmt.in_var_ele.is_iter_fl ); + rc_t rc = kOkRC; + + in_ele_t* iter_cnt_ctl_ele = nullptr; + + // if the in-var gives a literal count - then it determines the count + if( in_stmt.in_var_ele.sfx_id_count != kInvalidCnt ) + { + // if the in-var gives a literal count then the src-proc cannot give one + if( in_stmt.src_proc_ele.sfx_id_count != kInvalidCnt ) + { + rc = cwLogError(kSyntaxErrorRC,"The in-var provided a literal iteration count therefore the src-proc cannot."); + goto errLabel; + } + + // if the in-var gives a literal count then the src-var cannot give one + if( in_stmt.src_var_ele.sfx_id_count != kInvalidCnt ) + { + rc = cwLogError(kSyntaxErrorRC,"The in-var provided a literal iteration count therefore the src-var cannot."); + goto errLabel; + } + + iter_cnt_ctl_ele = &in_stmt.in_var_ele; + + } + else // the src-proc or src-var must control the iter count + { + // if the src-proc gives a literal count - then it determines th count + if( in_stmt.src_proc_ele.sfx_id_count != kInvalidCnt ) + { + // then the src-var cannot give a literal count + if( in_stmt.src_var_ele.sfx_id_count != kInvalidCnt ) + { + rc = cwLogError(kSyntaxErrorRC,"The src-proc provided a literal iteration count therefore the src-var cannot."); + goto errLabel; + } + + iter_cnt_ctl_ele = &in_stmt.src_proc_ele; + + } + else + { + // if the src-var gives a literal count - then it determines the count + if( in_stmt.src_var_ele.sfx_id_count != kInvalidCnt ) + { + iter_cnt_ctl_ele = &in_stmt.src_var_ele; + } + else // no literal count was given - we need to get the implied count + { + // if the src-proc is iterating then it will provide the count + if( in_stmt.src_proc_ele.is_iter_fl ) + { + // the src-var cannot be iterating if the src-proc is iterating + if( in_stmt.src_var_ele.is_iter_fl ) + { + rc = cwLogError(kSyntaxErrorRC,"The src-proc is iterating therefore the src-var cannot."); + goto errLabel; + } + + iter_cnt_ctl_ele = &in_stmt.src_proc_ele; + } + else // the src-proc isn't iterating check the src-var + { + if( in_stmt.src_var_ele.is_iter_fl ) + { + iter_cnt_ctl_ele = &in_stmt.src_var_ele; + } + else // no iteration count control was found + { + rc = cwLogError(kSyntaxErrorRC,"No iteration count control was specified."); + goto errLabel; + } + } + } + } + } + + errLabel: + + if( rc == kOkRC ) + in_stmt.iter_cnt_ctl_ele = iter_cnt_ctl_ele; + + return rc; + } + + rc_t _determine_in_stmt_iter_count( network_t& net,instance_t* inst, in_stmt_t& in_stmt ) + { + rc_t rc = kOkRC; + + // it has already been determined that this an iterating in-stmt + // and a iteration count control element has been identified. + assert( in_stmt.in_var_ele.is_iter_fl ); + assert( in_stmt.iter_cnt_ctl_ele != nullptr ); + + switch( in_stmt.iter_cnt_ctl_ele->typeId ) + { + case kInVarTypeId: + + assert( in_stmt.iter_cnt_ctl_ele->sfx_id_count != kInvalidCnt ); + + if((in_stmt.iter_cnt = in_stmt.iter_cnt_ctl_ele->sfx_id_count) == 0 ) + rc = cwLogError(rc,"The literal in-var iteration count on '%s:%i' must be greater than zero.", cwStringNullGuard(in_stmt.iter_cnt_ctl_ele->label),in_stmt.iter_cnt_ctl_ele->base_sfx_id); + break; + + case kSrcProcTypeId: + if((rc = _calc_src_proc_ele_count( *in_stmt.src_net, in_stmt.src_proc_ele, in_stmt.iter_cnt )) != kOkRC ) + rc = cwLogError(rc,"Unable to determine the in-stmt iteration count based on the iteration control src-proc '%s'.",cwStringNullGuard(in_stmt.src_proc_ele.label)); + break; + + case kSrcVarTypeId: + if((rc = _calc_src_var_ele_count( *in_stmt.src_net, in_stmt.src_proc_ele, in_stmt.src_var_ele, in_stmt.iter_cnt )) != kOkRC ) + rc = cwLogError(rc,"Unable to determine the in-stmt iteration count based on the iteration control src-var '%s'.",cwStringNullGuard(in_stmt.src_var_ele.label)); + + break; + + default: + rc = cwLogError(kInvalidStateRC,"An unknown in-stmt element type was encountered."); + } + + return rc; + } + + void _destroy_in_stmt( in_stmt_t& s ) + { + mem::release(s.in_var_ele.label); + mem::release(s.src_net_label); + mem::release(s.src_proc_ele.label); + mem::release(s.src_var_ele.label); + } + + rc_t _parse_in_stmt_src_net_proc_var_string( char* str, char*& src_net_label, const char*& src_proc_label, const char*& src_var_label ) + { + rc_t rc = kOkRC; + char* period0 = nullptr; + char* period1 = nullptr; + + // locate the separator period on the src proc/var id + if((period0 = firstMatchChar(str,'.')) == nullptr ) + { + cwLogError(kSyntaxErrorRC,"No period separator was found in the src net/proc/var for the src specifier:%s.",str); + goto errLabel; + } + + *period0 = 0; + + if((period1 = firstMatchChar(period0+1,'.')) != nullptr ) + { + *period1 = 0; + src_var_label = period1 + 1; // Set a pointer to the src var label + src_proc_label = period0 + 1; + src_net_label = mem::duplStr(str); + } + else + { + src_var_label = period0 + 1; + src_proc_label = str; + src_net_label = nullptr; + } + + if( textLength(src_var_label) == 0 ) + rc = cwLogError(kSyntaxErrorRC,"The 'src-var' label has length 0."); + + if( textLength(src_proc_label) == 0 ) + rc = cwLogError(kSyntaxErrorRC,"The 'src-proc' label has length 0."); + + + errLabel: + return rc; + } + + + // Recursively search the tree of networks rooted on 'net' for the + // network named 'net_inst_label'. + network_t* _find_labeled_network( network_t& net, const char* net_inst_label ) + { + network_t* labeled_net = nullptr; + + // for each instance in the network + for(unsigned i=0; iinternal_net != nullptr ) + { + // if the name of the network matches the key ... + if( textIsEqual(inst->label,net_inst_label) ) + labeled_net = inst->internal_net; // .. we are done + else + { + // ... otherwise recurse + labeled_net = _find_labeled_network(*inst->internal_net,net_inst_label); + } + } + + } + return labeled_net; + } + + // Set 'in_stmt.src_net' based on 'in_stmt.src_net_label' + rc_t _locate_src_net(network_t& net,instance_t* inst, in_stmt_t& in_stmt) + { + rc_t rc = kOkRC; + network_t* src_net = nullptr; + + in_stmt.src_net = nullptr; + + if( in_stmt.src_net_label == nullptr ) + src_net = &net; + else + { + if( textIsEqual(in_stmt.src_net_label,"_") ) + src_net = &inst->ctx->net; + else + { + if((src_net = _find_labeled_network(inst->ctx->net,in_stmt.src_net_label)) == nullptr ) + { + rc = cwLogError(kSyntaxErrorRC,"The source net '%s' was not found.",cwStringNullGuard(in_stmt.src_net_label)); + goto errLabel; + } + } + } + errLabel: + in_stmt.src_net = src_net; + + if( in_stmt.src_net == nullptr ) + rc = cwLogError(kSyntaxErrorRC,"No source net was found."); + + return rc; + } + + + rc_t _create_in_stmt( network_t& net, instance_t* inst, in_stmt_t& in_stmt, const char* in_var_str, const char* src_proc_var_str ) + { + rc_t rc = kOkRC; + unsigned src_char_cnt = 0; + + in_stmt.in_var_ele.typeId = kInVarTypeId; + in_stmt.src_proc_ele.typeId = kSrcProcTypeId; + in_stmt.src_var_ele.typeId = kSrcVarTypeId; + + // verify the src proc/var string is valid + if( (src_char_cnt = textLength(src_proc_var_str)) == 0 ) + { + cwLogError(kSyntaxErrorRC,"No source variable was found for the input variable '%s'.",cwStringNullGuard(in_var_str)); + goto errLabel; + } + else + { + const char* src_proc_label = nullptr; + const char* src_var_label = nullptr; + + char str[ src_char_cnt+1 ]; + + // put the src proc/var string into a non-const scratch buffer + textCopy(str,src_char_cnt+1,src_proc_var_str); + + // parse the src part into it's 3 parts + if((rc = _parse_in_stmt_src_net_proc_var_string(str, in_stmt.src_net_label, src_proc_label, src_var_label )) != kOkRC ) + { + cwLogError(rc,"Unable to parse the 'src' part of an 'in-stmt'."); + goto errLabel; + } + + // parse the in-var + if((rc = _parse_in_ele( in_var_str, in_stmt.in_var_ele )) != kOkRC ) + { + rc = cwLogError(rc,"Unable to parse the in-var from '%s'.",cwStringNullGuard(in_var_str)); + goto errLabel; + } + + // parse the src-proc + if((rc = _parse_in_ele( src_proc_label, in_stmt.src_proc_ele )) != kOkRC ) + { + rc = cwLogError(rc,"Unable to parse the in-var from '%s'.",cwStringNullGuard(in_var_str)); + goto errLabel; + } + + // parse the src-var + if((rc = _parse_in_ele( src_var_label, in_stmt.src_var_ele )) != kOkRC ) + { + rc = cwLogError(rc,"Unable to parse the in-var from '%s'.",cwStringNullGuard(in_var_str)); + goto errLabel; + } + + // get the var class desc. for the in-var + if(( in_stmt.in_var_desc = var_desc_find(inst->class_desc,in_stmt.in_var_ele.label)) == nullptr ) + { + rc = cwLogError(rc,"Unable to locate the var class desc for the in-var from '%s'.",cwStringNullGuard(in_stmt.in_var_ele.label)); + goto errLabel; + } + + // get the src net + if((rc = _locate_src_net(net,inst,in_stmt)) != kOkRC ) + { + rc = cwLogError(rc,"Unable to locate the src-net '%s'.",cwStringNullGuard(in_stmt.src_net_label)); + goto errLabel; + } + + // if the in-var has an sfx_id, or is iterating, then the var needs to be created (the dflt creation process assumes no sfx id) + if( in_stmt.in_var_ele.base_sfx_id != kInvalidId || in_stmt.in_var_ele.is_iter_fl ) + { + in_stmt.create_in_fl = true; + if( in_stmt.in_var_ele.base_sfx_id == kInvalidId ) + in_stmt.in_var_ele.base_sfx_id = kBaseSfxId; + } + + // if the src-proc is not iterating and the src-proc was not given a literal sfx-id + if( in_stmt.src_proc_ele.is_iter_fl==false && in_stmt.src_proc_ele.base_sfx_id==kInvalidId && in_stmt.src_net==&net) + in_stmt.src_proc_ele.base_sfx_id = inst->label_sfx_id; + + // if this is not an iterating in-stmt ... + if( !in_stmt.in_var_ele.is_iter_fl ) + { + in_stmt.iter_cnt = 1; // ... then it must be a simple 1:1 connection + } + else + { + // if the in-stmt is iterating then determine the in-stmt element which controls the iteration count + if((rc = _determine_in_stmt_iter_count_ctl_ele(net,inst,in_stmt)) != kOkRC || in_stmt.iter_cnt_ctl_ele==nullptr) + { + rc = cwLogError(rc,"Unable to determine the iter count control ele."); + goto errLabel; + } + + // if the in-stmt is iterating then determine the iteration count + if((rc = _determine_in_stmt_iter_count(net,inst,in_stmt)) != kOkRC ) + { + cwLogError(rc,"Unable to determine the in-stmt iteration count."); + goto errLabel; + } + } + } + + errLabel: + if( rc != kOkRC ) + _destroy_in_stmt(in_stmt); + + return rc; + } + + rc_t _parse_in_list( network_t& net, instance_t* inst, proc_inst_parse_state_t& pstate ) + { + rc_t rc = kOkRC; + + if( pstate.in_dict == nullptr ) + goto errLabel; + + if( !pstate.in_dict->is_dict() ) + { + cwLogError(kSyntaxErrorRC,"The 'in' dict in instance '%s' is not a valid dictionary.",inst->label); + goto errLabel; + } + + if( pstate.in_dict->child_count() == 0 ) + goto errLabel; + + pstate.in_arrayN = pstate.in_dict->child_count(); + pstate.in_array = mem::allocZ(pstate.in_arrayN); + + // for each input variable in the 'in' set + for(unsigned i=0; ichild_ele(i); // in:src pair + const char* in_var_str = in_pair->pair_label(); // 'in' var string + const char* src_proc_var_str = nullptr; + + // get the src net/proc/var string + if((rc = in_pair->pair_value()->value(src_proc_var_str)) != kOkRC ) + { + cwLogError(rc,"Unable to access the source proc/var string for the input var '%s'.",cwStringNullGuard(in_var_str)); + goto errLabel; + } + + // + if((rc= _create_in_stmt(net, inst, in_stmt, in_var_str, src_proc_var_str )) != kOkRC ) + { + cwLogError(rc,"Parse failed on the in-connection '%s:%s'.",cwStringNullGuard(in_var_str),cwStringNullGuard(src_proc_var_str)); + goto errLabel; + } + + // create the var + if( in_stmt.create_in_fl ) + { + for(unsigned i=0; ilabel, + in_stmt.in_var_ele.base_sfx_id + i, + kInvalidId, + kAnyChIdx, + in_stmt.in_var_desc->val_cfg, + dum )) != kOkRC ) + { + rc = cwLogError(rc,"in-stmt var create failed on '%s:%s'.",cwStringNullGuard(in_var_str),cwStringNullGuard(src_proc_var_str)); + goto errLabel; + } + } + } + + } + + errLabel: + + return rc; + + } + + bool _is_var_inst_already_created( const char* var_label, const proc_inst_parse_state_t& pstate ) + { + for(unsigned i=0; ivarDesc->type, src_var->varDesc->type) ) + { + rc = cwLogError(kSyntaxErrorRC,"The type flags don't match on input:%s:%i source:%s:%i.%s:%i .", in_var_label, in_var_sfx_id, src_proc_label, src_proc_sfx_id, src_var_label, src_var_sfx_id); + goto errLabel; + } + + // verify that the source exists + if( src_var->value == nullptr ) + { + rc = cwLogError(kSyntaxErrorRC,"The source value is null on the connection input::%s:%i source:%s:%i.%s:%i .", in_var_label, in_var_sfx_id, src_proc_label, src_proc_sfx_id, src_var_label, src_var_sfx_id); + goto errLabel; + } + + // + _connect_vars( src_var, in_var ); + } + } + + errLabel: + if( rc != kOkRC ) + rc = cwLogError(rc,"Connection failed on proc '%s:%i'.",inst->label,inst->label_sfx_id); + return rc; + } + + + rc_t _parse_proc_inst_cfg( network_t& net, const object_t* proc_inst_cfg, unsigned sfx_id, proc_inst_parse_state_t& pstate ) + { + rc_t rc = kOkRC; + const object_t* arg_dict = nullptr; + + // validate the syntax of the proc_inst_cfg pair + if( !_is_non_null_pair(proc_inst_cfg)) + { + rc = cwLogError(kSyntaxErrorRC,"The instance cfg. is not a valid pair. No instance label could be parsed."); + goto errLabel; + } + + pstate.inst_label = proc_inst_cfg->pair_label(); + + // verify that the instance label is unique + if( instance_find(net,pstate.inst_label,sfx_id) != nullptr ) + { + rc = cwLogError(kSyntaxErrorRC,"The proc instance label '%s:%i' has already been used.",pstate.inst_label,sfx_id); + goto errLabel; + } + + // get the instance class label + if((rc = proc_inst_cfg->pair_value()->getv("class",pstate.inst_clas_label)) != kOkRC ) + { + rc = cwLogError(kSyntaxErrorRC,"The instance cfg. %s is missing: 'type'.",pstate.inst_label); + goto errLabel; + } + + // parse the optional args + if((rc = proc_inst_cfg->pair_value()->getv_opt("args", arg_dict, + "in", pstate.in_dict, + "argLabel", pstate.arg_label, + "preset", pstate.preset_labels)) != kOkRC ) + { + rc = cwLogError(kSyntaxErrorRC,"The instance cfg. '%s' missing: 'type'.",pstate.inst_label); + goto errLabel; + } + + // if an argument dict was given in the instance cfg + if( arg_dict != nullptr ) + { + bool rptErrFl = true; + + // verify the arg. dict is actually a dict. + if( !arg_dict->is_dict() ) + { + cwLogError(kSyntaxErrorRC,"The instance argument dictionary on instance '%s' is not a dictionary.",pstate.inst_label); + goto errLabel; + } + + // if no label was given then try 'default' + if( pstate.arg_label == nullptr) + { + pstate.arg_label = "default"; + rptErrFl = false; + } + + // locate the specified argument record + if((pstate.arg_cfg = arg_dict->find_child(pstate.arg_label)) == nullptr ) + { + + // if an explicit arg. label was given but it was not found + if( rptErrFl ) + { + rc = cwLogError(kSyntaxErrorRC,"The argument cfg. '%s' was not found on instance cfg. '%s'.",pstate.arg_label,pstate.inst_label); + goto errLabel; + } + + // no explicit arg. label was given - make arg_dict the instance arg cfg. + pstate.arg_cfg = arg_dict; + pstate.arg_label = nullptr; + } + } + + errLabel: + if( rc != kOkRC ) + rc = cwLogError(kSyntaxErrorRC,"Configuration parsing failed on instance: '%s'.", cwStringNullGuard(pstate.inst_label) ); + + return rc; + } + + void _destroy_pstate( proc_inst_parse_state_t pstate ) + { + for(unsigned i=0; iclass_desc->label,proc_clas_label) ) + ++n; + return n; + } + + rc_t _create_instance( flow_t* p, const object_t* proc_inst_cfg, unsigned sfx_id, network_t& net, instance_t*& inst_ref ) + { + rc_t rc = kOkRC; + proc_inst_parse_state_t pstate = {}; + instance_t* inst = nullptr; + class_desc_t* class_desc = nullptr; + + inst_ref = nullptr; + + // parse the instance configuration + if((rc = _parse_proc_inst_cfg( net, proc_inst_cfg, sfx_id, pstate )) != kOkRC ) + goto errLabel; + + // locate the class desc + if(( class_desc = class_desc_find(p,pstate.inst_clas_label)) == nullptr ) + { + rc = cwLogError(kSyntaxErrorRC,"The flow class '%s' was not found.",cwStringNullGuard(pstate.inst_clas_label)); + goto errLabel; + } + + // if the poly instance count has been exceeded for this proc inst class ... + if(class_desc->polyLimitN > 0 && _poly_copy_count(net,pstate.inst_clas_label) >= class_desc->polyLimitN ) + { + // ... then silently skip this instantiation + cwLogDebug("The poly class copy count has been exceeded for '%s' - skipping instantiation of sfx_id:%i.",pstate.inst_label,sfx_id); + goto errLabel; + } + + // instantiate the instance + inst = mem::allocZ(); + + inst->ctx = p; + inst->label = mem::duplStr(pstate.inst_label); + inst->label_sfx_id = sfx_id; + inst->proc_cfg = proc_inst_cfg->pair_value(); + inst->arg_label = pstate.arg_label; + inst->arg_cfg = pstate.arg_cfg; + inst->class_desc = class_desc; + inst->net = &net; + + // parse the in-list ,fill in pstate.in_array, and create var instances for var's referenced by in-list + if((rc = _parse_in_list( net, inst, pstate )) != kOkRC ) + { + rc = cwLogError(rc,"in-list parse failed on proc instance '%s:%i'.",cwStringNullGuard(inst->label),sfx_id); + goto errLabel; + } + + // Instantiate all the variables in the class description - that were not already created in _parse_in_list() + for(var_desc_t* vd=class_desc->varDescL; vd!=nullptr; vd=vd->link) + if( !_is_var_inst_already_created( vd->label, pstate ) ) + { + variable_t* var = nullptr; + if((rc = var_create( inst, vd->label, kBaseSfxId, kInvalidId, kAnyChIdx, vd->val_cfg, var )) != kOkRC ) + goto errLabel; + } + + // All the variables that can be used by this instance have now been created + // and the chIdx of each variable is set to 'any'. + + // If a 'preset' field was included in the class cfg then apply the specified class preset + if( pstate.preset_labels != nullptr ) + if((rc = _class_apply_presets(inst, pstate.preset_labels )) != kOkRC ) + goto errLabel; + + // All the class presets values have now been set and those variables + // that were expressed with a list have numeric channel indexes assigned. + + // Apply the proc instance preset values. + if( pstate.arg_cfg != nullptr ) + if((rc = _proc_inst_args_channelize_vars( inst, pstate.arg_label, pstate.arg_cfg )) != kOkRC ) + goto errLabel; + + // All the instance arg values have now been set and those variables + // that were expressed with a list have numeric channel indexes assigned. + + + // TODO: Should the 'all' variable be removed for variables that have numeric channel indexes? + + // Connect the in-list variables to their sources. + if((rc = _connect_in_vars(net, inst, pstate)) != kOkRC ) + { + rc = cwLogError(rc,"Creation of the proc instance '%s:%i' failed during input connection processing.",cwStringNullGuard(inst->label),inst->label_sfx_id); + goto errLabel; + } + + // Complete the instantiation of the proc instance by calling the custom instance creation function. + + // Call the custom instance create() function. + if((rc = class_desc->members->create( inst )) != kOkRC ) + { + rc = cwLogError(kInvalidArgRC,"Instantiation failed on instance '%s:%i'.", inst->label,inst->label_sfx_id ); + goto errLabel; + } + + // Create the instance->varMap[] lookup array + if((rc =_create_instance_var_map( inst )) != kOkRC ) + goto errLabel; + + // the custom creation function may have added channels to in-list vars fix up those connections here. + _complete_input_connections(inst); + + // call the 'value()' function to inform the instance of the current value of all of it's variables. + if((rc = _call_value_func_on_all_variables( inst )) != kOkRC ) + goto errLabel; + + + inst_ref = inst; + + /* + // insert an instance in the network + if( net.network_tail == nullptr ) + { + net.network_head = inst; + net.network_tail = inst; + } + else + { + net.network_tail->link = inst; + net.network_tail = inst; + } + */ + + errLabel: + if( rc != kOkRC ) + _destroy_inst(inst); + _destroy_pstate(pstate); + + return rc; + } + + //======================================================================================================= + // + // Apply presets + // + + + unsigned _select_ranked_ele_by_rank_prob( const preset_order_t* presetA, const bool* selV , unsigned presetN ) + { + + // get a count of the candidate presets + unsigned rankN = selV==nullptr ? presetN : std::count_if(selV,selV+presetN,[](const bool& x){ return x; }); + + if( rankN == 0 ) + { + cwLogWarning("All preset candidates have been eliminated."); + return kInvalidIdx; + } + + unsigned rankV[ rankN ]; + unsigned idxMapA[ rankN ]; + + // fill rankV[] with candidates 'order' value + for(unsigned i=0,j=0; i 1 ); + + unsigned threshV[ rankN ]; + unsigned uniqueRankV[ rankN ]; + unsigned uniqueRankN = 0; + unsigned sel_idx = rankN - 1; // + + // for each possible rank value + for(unsigned i=0; ichild_count(); ++i) + { + const object_t* inst_pair; + if((inst_pair = net_preset_pair->child_ele(i)) != nullptr && inst_pair->is_pair() && textIsEqual(inst_pair->pair_label(),instance_label) ) + { + + preset_val_ref = inst_pair->pair_value(); + + goto errLabel; + } + } + + rc = cwLogError(kInvalidArgRC,"The preset instance label '%s' was not found.",cwStringNullGuard(preset_label)); + + errLabel: + return rc; + } + + + } +} + +cw::rc_t cw::flow::network_create( flow_t* p, + const object_t* networkCfg, + network_t& net, + unsigned polyCnt, + network_order_id_t orderId ) +{ + rc_t rc = kOkRC; + + // default to kNetFirstPolyOrderId + unsigned outerN = polyCnt; + unsigned innerN = 1; + + if((rc = networkCfg->getv("procs",net.procsCfg)) != kOkRC ) + { + rc = cwLogError(rc,"Failed on parsing required network cfg. elements."); + goto errLabel; + } + + if((rc = networkCfg->getv_opt("presets",net.presetsCfg)) != kOkRC ) + { + rc = cwLogError(rc,"Failed on parsing optional network cfg. elements."); + goto errLabel; + } + + + if( orderId == kProcFirstPolyOrderId ) + { + outerN = 1; + innerN = polyCnt; + } + + net.proc_arrayAllocN = polyCnt * net.procsCfg->child_count(); + net.proc_array = mem::allocZ(net.proc_arrayAllocN); + net.proc_arrayN = 0; + + for(unsigned i=0; ichild_count(); ++j) + { + const object_t* proc_cfg = net.procsCfg->child_ele(j); + + for(unsigned k=0; kgetv_opt( presetLabel, preset_value )) != kOkRC ) + cwLogError(rc,"Search for network preset named '%s' failed.", cwStringNullGuard(presetLabel)); + } + + return preset_value; + +} + +cw::rc_t cw::flow::exec_cycle( network_t& net ) +{ + rc_t rc = kOkRC; + + for(unsigned i=0; iclass_desc->members->exec(net.proc_array[i])) != kOkRC ) + { + break; + } + } + + return rc; +} + +cw::rc_t cw::flow::get_variable( network_t& net, const char* inst_label, const char* var_label, unsigned chIdx, instance_t*& instPtrRef, variable_t*& varPtrRef ) +{ + rc_t rc = kOkRC; + instance_t* inst = nullptr; + variable_t* var = nullptr; + + varPtrRef = nullptr; + instPtrRef = nullptr; + + // locate the proc instance + if((inst = instance_find(net,inst_label,kBaseSfxId)) == nullptr ) + { + rc = cwLogError(kInvalidIdRC,"Unknown proc instance label '%s'.", cwStringNullGuard(inst_label)); + goto errLabel; + } + + // locate the variable + if((rc = var_find( inst, var_label, kBaseSfxId, chIdx, var)) != kOkRC ) + { + rc = cwLogError(kInvalidArgRC,"The variable '%s' could not be found on the proc instance '%s'.",cwStringNullGuard(var_label),cwStringNullGuard(inst_label)); + goto errLabel; + } + + instPtrRef = inst; + varPtrRef = var; + +errLabel: + return rc; +} + + +cw::rc_t cw::flow::network_apply_preset( network_t& net, const char* presetLabel ) +{ + rc_t rc = kOkRC; + const object_t* net_preset_value; + const object_t* preset_pair; + + // locate the cfg of the requested preset + if((net_preset_value = find_network_preset(net, presetLabel )) == nullptr ) + { + rc = cwLogError(kInvalidIdRC,"The network preset '%s' could not be found.", presetLabel ); + goto errLabel; + } + + // for each instance in the preset + for(unsigned i=0; ichild_count(); ++i) + { + // get the instance label/value pair + if((preset_pair = net_preset_value->child_ele(i)) != nullptr && preset_pair->is_pair() ) + { + const char* inst_label = preset_pair->pair_label(); + const object_t* preset_value_cfg = preset_pair->pair_value(); + instance_t* inst; + + // locate the instance + if((inst = instance_find(net,inst_label,kBaseSfxId)) == nullptr ) + { + rc = cwLogError(kInvalidIdRC,"The network instance '%s' refered to in network preset '%s' could not be found.",inst_label,presetLabel); + goto errLabel; + } + + // if the preset value is a string then look it up in the class preset dictionary + if( preset_value_cfg->is_string() ) + { + const char* class_preset_label; + preset_value_cfg->value(class_preset_label); + _class_preset_channelize_vars(inst, class_preset_label ); + } + else + { + // if the preset value is a dict then apply it directly + if( preset_value_cfg->is_dict() ) + { + if((rc = _preset_channelize_vars( inst, "network", presetLabel, preset_value_cfg )) != kOkRC ) + { + rc = cwLogError(rc,"The preset '%s' application failed on instance '%s'.", presetLabel, inst_label ); + goto errLabel; + } + + } + else + { + rc = cwLogError(kSyntaxErrorRC,"The network preset '%s' instance '%s' does not have a string or dictionary value.", presetLabel, inst_label ); + goto errLabel; + } + } + } + else + { + rc = cwLogError(kSyntaxErrorRC,"The network preset '%s' is malformed.",presetLabel); + goto errLabel; + } + } + + cwLogInfo("Activated preset:%s",presetLabel); +errLabel: + return rc; +} + +cw::rc_t cw::flow::network_apply_dual_preset( network_t& net, const char* presetLabel_0, const char* presetLabel_1, double coeff ) +{ + rc_t rc = kOkRC; + + const object_t* net_preset_value_0; + + cwLogInfo("*** Applying dual: %s %s : %f",presetLabel_0, presetLabel_1, coeff ); + + // locate the cfg of the requested preset + if((net_preset_value_0 = find_network_preset(net, presetLabel_0 )) == nullptr ) + { + rc = cwLogError(kInvalidIdRC,"The network preset '%s' could not be found.", presetLabel_0 ); + goto errLabel; + } + + // for each instance in the preset + for(unsigned i=0; ichild_count(); ++i) + { + const object_t* preset_pair_0 = net_preset_value_0->child_ele(i); + const char* inst_label = preset_pair_0->pair_label(); + const object_t* preset_value_cfg_0 = preset_pair_0->pair_value(); + instance_t* inst = nullptr; + const object_t* preset_value_cfg_1 = nullptr; + const int two = 2; + const char* class_preset_labelA[two]; + + // get the instance label/value pair + if((preset_pair_0 = net_preset_value_0->child_ele(i)) == nullptr || !preset_pair_0->is_pair() ) + { + rc = cwLogError(kSyntaxErrorRC,"An invalid preset value pair was encountered in '%s'.",presetLabel_0); + goto errLabel; + } + + // verify that the preset value is a string or dict + if( preset_pair_0->pair_value()==nullptr || (!preset_value_cfg_0->is_dict() && !preset_value_cfg_0->is_string() )) + { + rc = cwLogError(kSyntaxErrorRC,"The preset value pair for instance '%s' in '%s' is not a 'dict' or 'string'.",inst_label,presetLabel_0); + goto errLabel; + } + + // locate the instance associated with the primary and secondary preset + if((inst = instance_find(net,inst_label,kBaseSfxId)) == nullptr ) + { + rc = cwLogError(kInvalidIdRC,"The network instance '%s' refered to in network preset '%s' could not be found.",cwStringNullGuard(inst_label),cwStringNullGuard(presetLabel_0)); + goto errLabel; + } + + // locate the second instance/preset value pair + if((rc = _find_network_preset_instance_pair(net, presetLabel_1, inst_label, preset_value_cfg_1 )) != kOkRC ) + { + rc = cwLogError(kInvalidIdRC,"The second network instance '%s' refered to in network preset '%s' could not be found.",inst_label,presetLabel_1); + goto errLabel; + } + + // TODO: We require that the instance presets both be of the same type: string or dict. + // There's no good reason for this, as string's resolve to class dict presets anyway. + // Fix this! + if( !(preset_value_cfg_0->is_dict() == preset_value_cfg_1->is_dict() && preset_value_cfg_0->is_string() == preset_value_cfg_1->is_string()) ) + { + rc = cwLogError(kInvalidIdRC,"The value type (string or dict) of dual network presets must match. (%s != %s)",preset_value_cfg_0->type->label,preset_value_cfg_1->type->label); + goto errLabel; + } + + preset_value_cfg_0->value(class_preset_labelA[0]); + preset_value_cfg_1->value(class_preset_labelA[1]); + + + // if the preset value is a string then look it up in the class dictionary + if( preset_value_cfg_0->is_string() ) + { + rc = _class_multi_preset_channelize_vars(inst, class_preset_labelA, two, coeff ); + } + else + { + assert( preset_value_cfg_1->is_dict() ); + + const object_t* preset_value_cfgA[] = { preset_value_cfg_0, preset_value_cfg_1}; + + if((rc = _multi_preset_channelize_vars( inst, "network", class_preset_labelA, preset_value_cfgA, two, coeff )) != kOkRC ) + { + rc = cwLogError(rc,"The dual preset '%s':'%s' application failed on instance '%s'.", cwStringNullGuard(class_preset_labelA[0]), cwStringNullGuard(class_preset_labelA[1]), inst_label ); + goto errLabel; + } + } + } + + +errLabel: + + if( rc != kOkRC ) + rc = cwLogError(rc,"The dual preset '%s':'%s' application failed.", cwStringNullGuard(presetLabel_0), cwStringNullGuard(presetLabel_1) ); + + return rc; +} + +cw::rc_t cw::flow::network_apply_preset( network_t& net, const multi_preset_selector_t& mps ) +{ + rc_t rc = kOkRC; + const char* label0 = nullptr; + const char* label1 = nullptr; + bool priProbFl = cwIsFlag(mps.flags, kPriPresetProbFl ); + bool secProbFl = cwIsFlag(mps.flags, kSecPresetProbFl ); + bool interpFl = cwIsFlag(mps.flags, kInterpPresetFl ); + + //printf("preset flags: pri:%i sec:%i interp:%i\n",priProbFl,secProbFl,interpFl); + + // verify that the set of candidate presets is not empty + if( mps.presetN == 0 ) + { + cwLogError(kInvalidArgRC,"A multi-preset application was requested but no presets were provided."); + goto errLabel; + } + + // if only a single candidate preset exists or needs to be selected + if( interpFl==false || mps.presetN==1 ) + { + // if only a single candidate preset is available or pri. probablity is not enabled + if( mps.presetN == 1 || priProbFl==false ) + label0 = mps.presetA[0].preset_label; + else + { + if( priProbFl ) + label0 = _select_ranked_ele_label_by_rank_prob( mps.presetA, nullptr, mps.presetN ); + else + label0 = mps.presetA[0].preset_label; + } + } + else // interpolation has been selected and at least 2 presets exist + { + unsigned pri_sel_idx = 0; + + // select the primary preset + if( priProbFl ) + pri_sel_idx = _select_ranked_ele_by_rank_prob( mps.presetA, nullptr, mps.presetN ); + else + { + // select all presets assigned to order == 1 + bool selA[ mps.presetN ]; + for(unsigned i=0; i + rc_t set_variable_value( network_t& net, const char* inst_label, const char* var_label, unsigned chIdx, T value ) + { + rc_t rc = kOkRC; + instance_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; + instance_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; + } + + rc_t network_apply_preset( network_t& net, const char* presetLabel ); + rc_t network_apply_dual_preset( network_t& net, const char* presetLabel_0, const char* presetLabel_1, double coeff ); + rc_t network_apply_preset( network_t& net, const multi_preset_selector_t& mps ); + + + } +} + + +#endif diff --git a/cwFlowProc.cpp b/cwFlowProc.cpp index 5eff84e..bb23ca1 100644 --- a/cwFlowProc.cpp +++ b/cwFlowProc.cpp @@ -17,6 +17,7 @@ #include "cwFlowDecl.h" #include "cwFlow.h" #include "cwFlowTypes.h" +#include "cwFlowNet.h" #include "cwFlowProc.h" #include "cwFile.h" @@ -89,6 +90,119 @@ namespace cw }; } + + //------------------------------------------------------------------------------------------------------------------ + // + // poly + // + namespace poly + { + enum + { + kCountPId, + kOrderPId, + }; + + typedef struct + { + unsigned count; + network_t net; + network_order_id_t orderId; + } inst_t; + + + rc_t create( instance_t* proc ) + { + rc_t rc = kOkRC; + inst_t* inst = mem::allocZ(); + const object_t* networkCfg = nullptr; + const char* order_label = nullptr; + + proc->userPtr = inst; + + if((rc = var_register_and_get( proc, kAnyChIdx, + kCountPId, "count", kBaseSfxId, inst->count, + kOrderPId, "order", kBaseSfxId, order_label )) != kOkRC ) + goto errLabel; + + if( inst->count == 0 ) + { + cwLogWarning("The 'poly' %s:%i was given a count of 0.",proc->label,proc->label_sfx_id); + goto errLabel; + } + + if((rc = proc->proc_cfg->getv("network",networkCfg)) != kOkRC ) + { + rc = cwLogError(rc,"The 'network' cfg. was not found."); + goto errLabel; + } + + // get the network exec. order type + if( textIsEqual(order_label,"net") ) + inst->orderId = kNetFirstPolyOrderId; + else + { + if( textIsEqual(order_label,"proc") ) + inst->orderId = kProcFirstPolyOrderId; + else + { + rc = cwLogError(kInvalidArgRC,"'%s' is not one of the valid order types (i.e. 'net','proc').",order_label); + goto errLabel; + } + } + + if((rc = network_create(proc->ctx,networkCfg,inst->net,inst->count )) != kOkRC ) + { + rc = cwLogError(rc,"Creation failed on the internal network."); + goto errLabel; + } + + + // Set the internal net pointer in the base proc instance + // so that network based utilities can scan it + proc->internal_net = &inst->net; + + errLabel: + return rc; + } + + rc_t destroy( instance_t* proc ) + { + inst_t* p = (inst_t*)proc->userPtr; + network_destroy(p->net); + + + mem::release( proc->userPtr ); + return kOkRC; + } + + rc_t value( instance_t* ctx, variable_t* var ) + { + return kOkRC; + } + + rc_t exec( instance_t* ctx ) + { + inst_t* p = (inst_t*)ctx->userPtr; + rc_t rc = kOkRC; + + if((rc = exec_cycle(p->net)) != kOkRC ) + { + rc = cwLogError(rc,"poly internal network exec failed."); + } + + return rc; + } + + class_members_t members = { + .create = create, + .destroy = destroy, + .value = value, + .exec = exec, + .report = nullptr + }; + + } //------------------------------------------------------------------------------------------------------------------ // @@ -115,12 +229,12 @@ namespace cw real_t in_value = 0.5; ctx->userPtr = mem::allocZ(); - if((rc = var_register_and_get( ctx, kAnyChIdx, kInPId, "in", in_value )) != kOkRC ) + if((rc = var_register_and_get( ctx, kAnyChIdx, kInPId, "in", kBaseSfxId, in_value )) != kOkRC ) goto errLabel; if((rc = var_register_and_set( ctx, kAnyChIdx, - kOutPId, "out", in_value, - kInvOutPId, "inv_out", (real_t)(1.0-in_value) )) != kOkRC ) + kOutPId, "out", kBaseSfxId, in_value, + kInvOutPId, "inv_out", kBaseSfxId, (real_t)(1.0-in_value) )) != kOkRC ) { goto errLabel; } @@ -205,14 +319,14 @@ namespace cw // Register variable and get their current value if((rc = var_register_and_get( ctx, kAnyChIdx, - kDevLabelPId, "dev_label", dev_label, - kPortLabelPId, "port_label", port_label )) != kOkRC ) + kDevLabelPId, "dev_label", kBaseSfxId, dev_label, + kPortLabelPId, "port_label", kBaseSfxId, port_label )) != kOkRC ) { goto errLabel; } - if((rc = var_register( ctx, kAnyChIdx,kOutPId, "out")) != kOkRC ) + if((rc = var_register( ctx, kAnyChIdx, kOutPId, "out", kBaseSfxId)) != kOkRC ) { goto errLabel; } @@ -246,7 +360,7 @@ namespace cw inst->buf = mem::allocZ( inst->bufN ); // create one output audio buffer - rc = var_register_and_set( ctx, "out", kOutPId, kAnyChIdx, nullptr, 0 ); + rc = var_register_and_set( ctx, "out", kBaseSfxId, kOutPId, kAnyChIdx, nullptr, 0 ); errLabel: @@ -349,9 +463,9 @@ namespace cw // Register variables and get their current value if((rc = var_register_and_get( ctx, kAnyChIdx, - kDevLabelPId, "dev_label", dev_label, - kPortLabelPId,"port_label", port_label, - kInPId, "in", mbuf)) != kOkRC ) + kDevLabelPId, "dev_label", kBaseSfxId, dev_label, + kPortLabelPId,"port_label", kBaseSfxId, port_label, + kInPId, "in", kBaseSfxId, mbuf)) != kOkRC ) { goto errLabel; } @@ -444,7 +558,7 @@ namespace cw ctx->userPtr = inst; // Register variable and get their current value - if((rc = var_register_and_get( ctx, kAnyChIdx, kDevLabelPId, "dev_label", inst->dev_label )) != kOkRC ) + if((rc = var_register_and_get( ctx, kAnyChIdx, kDevLabelPId, "dev_label", kBaseSfxId, inst->dev_label )) != kOkRC ) { goto errLabel; } @@ -457,7 +571,7 @@ namespace cw // create one output audio buffer - rc = var_register_and_set( ctx, "out", kOutPId, kAnyChIdx, inst->ext_dev->u.a.abuf->srate, inst->ext_dev->u.a.abuf->chN, ctx->ctx->framesPerCycle ); + rc = var_register_and_set( ctx, "out", kBaseSfxId, kOutPId, kAnyChIdx, inst->ext_dev->u.a.abuf->srate, inst->ext_dev->u.a.abuf->chN, ctx->ctx->framesPerCycle ); errLabel: return rc; @@ -542,8 +656,8 @@ namespace cw // Register variables and get their current value if((rc = var_register_and_get( ctx, kAnyChIdx, - kDevLabelPId, "dev_label", inst->dev_label, - kInPId, "in", src_abuf)) != kOkRC ) + kDevLabelPId, "dev_label", kBaseSfxId, inst->dev_label, + kInPId, "in", kBaseSfxId, src_abuf)) != kOkRC ) { goto errLabel; } @@ -638,16 +752,16 @@ namespace cw inst_t* inst = mem::allocZ(); ctx->userPtr = inst; - if((rc = var_register( ctx, kAnyChIdx, kOnOffFlPId, "on_off" )) != kOkRC ) + if((rc = var_register( ctx, kAnyChIdx, kOnOffFlPId, "on_off", kBaseSfxId)) != kOkRC ) { goto errLabel; } // Register variable and get their current value if((rc = var_register_and_get( ctx, kAnyChIdx, - kFnamePId, "fname", inst->filename, - kSeekSecsPId, "seekSecs", seekSecs, - kEofFlPId, "eofFl", inst->eofFl )) != kOkRC ) + kFnamePId, "fname", kBaseSfxId, inst->filename, + kSeekSecsPId, "seekSecs", kBaseSfxId, seekSecs, + kEofFlPId, "eofFl", kBaseSfxId, inst->eofFl )) != kOkRC ) { goto errLabel; } @@ -669,7 +783,7 @@ namespace cw cwLogInfo("Audio '%s' srate:%f chs:%i frames:%i %f seconds.",inst->filename,info.srate,info.chCnt,info.frameCnt, info.frameCnt/info.srate ); // create one output audio buffer - with the same configuration as the source audio file - rc = var_register_and_set( ctx, "out", kOutPId, kAnyChIdx, info.srate, info.chCnt, ctx->ctx->framesPerCycle ); + rc = var_register_and_set( ctx, "out", kBaseSfxId, kOutPId, kAnyChIdx, info.srate, info.chCnt, ctx->ctx->framesPerCycle ); errLabel: return rc; @@ -795,9 +909,9 @@ namespace cw // Register variables and get their current value if((rc = var_register_and_get( ctx, kAnyChIdx, - kFnamePId, "fname", inst->filename, - kBitsPId, "bits", audioFileBits, - kInPId, "in", src_abuf )) != kOkRC ) + kFnamePId, "fname", kBaseSfxId, inst->filename, + kBitsPId, "bits", kBaseSfxId, audioFileBits, + kInPId, "in", kBaseSfxId, src_abuf )) != kOkRC ) { goto errLabel; } @@ -902,16 +1016,16 @@ namespace cw ctx->userPtr = mem::allocZ(); // get the source audio buffer - if((rc = var_register_and_get(ctx, kAnyChIdx,kInPId,"in",abuf )) != kOkRC ) + if((rc = var_register_and_get(ctx, kAnyChIdx,kInPId,"in",kBaseSfxId,abuf )) != kOkRC ) goto errLabel; // register the gain for(unsigned i=0; ichN; ++i) - if((rc = var_register( ctx, i, kGainPId, "gain" )) != kOkRC ) + if((rc = var_register( ctx, i, kGainPId, "gain", kBaseSfxId )) != kOkRC ) goto errLabel; // create the output audio buffer - rc = var_register_and_set( ctx, "out", kOutPId, kAnyChIdx, abuf->srate, abuf->chN, abuf->frameN ); + rc = var_register_and_set( ctx, "out", kBaseSfxId, kOutPId, kAnyChIdx, abuf->srate, abuf->chN, abuf->frameN ); errLabel: @@ -1021,7 +1135,7 @@ namespace cw ctx->userPtr = inst; // get the source audio buffer - if((rc = var_register_and_get(ctx, kAnyChIdx,kInPId,"in",abuf )) != kOkRC ) + if((rc = var_register_and_get(ctx, kAnyChIdx,kInPId,"in",kBaseSfxId, abuf )) != kOkRC ) goto errLabel; if( abuf->chN ) @@ -1030,20 +1144,20 @@ namespace cw inst->chSelMap = mem::allocZ(abuf->chN); - if((rc = var_channel_count(ctx,"select",selChN)) != kOkRC ) + if((rc = var_channel_count(ctx,"select",kBaseSfxId,selChN)) != kOkRC ) goto errLabel; // register the gain for(unsigned i=0; ichN; ++i) { if( i < selChN ) - if((rc = var_register_and_get( ctx, i, kSelectPId, "select", inst->chSelMap[i] )) != kOkRC ) + if((rc = var_register_and_get( ctx, i, kSelectPId, "select", kBaseSfxId, inst->chSelMap[i] )) != kOkRC ) goto errLabel; if( inst->chSelMap[i] ) { // register an output gain control - if((rc = var_register( ctx, inst->outChN, kGainPId, "gain")) != kOkRC ) + if((rc = var_register( ctx, inst->outChN, kGainPId, "gain", kBaseSfxId)) != kOkRC ) goto errLabel; // count the number of selected channels to determine the count of output channels @@ -1055,7 +1169,7 @@ namespace cw if( inst->outChN == 0 ) cwLogWarning("The audio split instance '%s' has no selected channels.",ctx->label); else - rc = var_register_and_set( ctx, "out", kOutPId, kAnyChIdx, abuf->srate, inst->outChN, abuf->frameN ); + rc = var_register_and_set( ctx, "out", kBaseSfxId, kOutPId, kAnyChIdx, abuf->srate, inst->outChN, abuf->frameN ); } errLabel: @@ -1162,7 +1276,7 @@ namespace cw ctx->userPtr = inst; // get the source audio buffer - if((rc = var_register_and_get(ctx, kAnyChIdx,kInPId,"in",abuf )) != kOkRC ) + if((rc = var_register_and_get(ctx, kAnyChIdx,kInPId,"in",kBaseSfxId,abuf )) != kOkRC ) goto errLabel; if( abuf->chN ) @@ -1172,13 +1286,13 @@ namespace cw // register the gain for(unsigned i=0; ichN; ++i) { - if((rc = var_register_and_get( ctx, i, kDuplicatePId, "duplicate", inst->chDuplMap[i] )) != kOkRC ) + if((rc = var_register_and_get( ctx, i, kDuplicatePId, "duplicate", kBaseSfxId, inst->chDuplMap[i] )) != kOkRC ) goto errLabel; if( inst->chDuplMap[i] ) { // register an input gain control - if((rc = var_register( ctx, inst->outChN, kGainPId, "gain")) != kOkRC ) + if((rc = var_register( ctx, inst->outChN, kGainPId, "gain", kBaseSfxId)) != kOkRC ) goto errLabel; // count the number of selected channels to determine the count of output channels @@ -1190,7 +1304,7 @@ namespace cw if( inst->outChN == 0 ) cwLogWarning("The audio split instance '%s' has no selected channels.",ctx->label); else - rc = var_register_and_set( ctx, "out", kOutPId, kAnyChIdx, abuf->srate, inst->outChN, abuf->frameN ); + rc = var_register_and_set( ctx, "out", kBaseSfxId, kOutPId, kAnyChIdx, abuf->srate, inst->outChN, abuf->frameN ); } errLabel: @@ -1309,11 +1423,11 @@ namespace cw // TODO: allow non-contiguous source labels // the source labels must be contiguous - if( !var_has_value( ctx, label, kAnyChIdx ) ) + if( !var_has_value( ctx, label, kBaseSfxId, kAnyChIdx ) ) break; // get the source audio buffer - if((rc = var_register_and_get(ctx, kAnyChIdx,kInBasePId+i,label,abuf )) != kOkRC ) + if((rc = var_register_and_get(ctx, kAnyChIdx,kInBasePId+i,label,kBaseSfxId, abuf )) != kOkRC ) { goto errLabel; } @@ -1337,11 +1451,11 @@ namespace cw // register the gain for(unsigned i=0; ichN, abuf1->chN); // register the gain - var_register_and_get( ctx, kAnyChIdx, kGain0PId, "gain0", dum ); - var_register_and_get( ctx, kAnyChIdx, kGain1PId, "gain1", dum ); + var_register_and_get( ctx, kAnyChIdx, kGain0PId, "gain0", kBaseSfxId, dum ); + var_register_and_get( ctx, kAnyChIdx, kGain1PId, "gain1", kBaseSfxId, dum ); // create the output audio buffer - rc = var_register_and_set( ctx, "out", kOutPId, kAnyChIdx, abuf0->srate, outChN, abuf0->frameN ); + rc = var_register_and_set( ctx, "out", kBaseSfxId, kOutPId, kAnyChIdx, abuf0->srate, outChN, abuf0->frameN ); errLabel: return rc; @@ -1601,7 +1715,7 @@ namespace cw ctx->userPtr = inst; // Register variables and get their current value - if((rc = var_register_and_get( ctx, kAnyChIdx, kChCntPid, "chCnt", chCnt)) != kOkRC ) + if((rc = var_register_and_get( ctx, kAnyChIdx, kChCntPid, "chCnt", kBaseSfxId, chCnt)) != kOkRC ) { goto errLabel; } @@ -1609,15 +1723,15 @@ namespace cw // register each oscillator variable for(unsigned i=0; ictx->framesPerCycle ); + rc = var_register_and_set( ctx, "out", kBaseSfxId, kOutPId, kAnyChIdx, srate, chCnt, ctx->ctx->framesPerCycle ); inst->phaseA = mem::allocZ( chCnt ); @@ -1719,7 +1833,7 @@ namespace cw inst_t* inst = mem::allocZ(); ctx->userPtr = inst; - if((rc = var_register_and_get( ctx, kAnyChIdx,kInPId, "in", srcBuf )) != kOkRC ) + if((rc = var_register_and_get( ctx, kAnyChIdx,kInPId, "in", kBaseSfxId, srcBuf )) != kOkRC ) { cwLogError(kInvalidArgRC,"Unable to access the 'src' buffer."); } @@ -1746,10 +1860,10 @@ namespace cw bool hzFl = false; if((rc = var_register_and_get( ctx, i, - kMaxWndSmpNPId, "maxWndSmpN", maxWndSmpN, - kWndSmpNPId, "wndSmpN", wndSmpN, - kHopSmpNPId, "hopSmpN", hopSmpN, - kHzFlPId, "hzFl", hzFl )) != kOkRC ) + kMaxWndSmpNPId, "maxWndSmpN", kBaseSfxId, maxWndSmpN, + kWndSmpNPId, "wndSmpN", kBaseSfxId, wndSmpN, + kHopSmpNPId, "hopSmpN", kBaseSfxId, hopSmpN, + kHzFlPId, "hzFl", kBaseSfxId, hzFl )) != kOkRC ) { goto errLabel; } @@ -1771,7 +1885,7 @@ namespace cw // create the fbuf 'out' - if((rc = var_register_and_set(ctx, "out", kOutPId, kAnyChIdx, srcBuf->srate, srcBuf->chN, maxBinNV, binNV, hopNV, magV, phsV, hzV )) != kOkRC ) + if((rc = var_register_and_set(ctx, "out", kBaseSfxId, kOutPId, kAnyChIdx, srcBuf->srate, srcBuf->chN, maxBinNV, binNV, hopNV, magV, phsV, hzV )) != kOkRC ) { cwLogError(kOpFailRC,"The output freq. buffer could not be created."); goto errLabel; @@ -1902,7 +2016,7 @@ namespace cw inst_t* inst = mem::allocZ(); ctx->userPtr = inst; - if((rc = var_register_and_get( ctx, kAnyChIdx,kInPId, "in", srcBuf)) != kOkRC ) + if((rc = var_register_and_get( ctx, kAnyChIdx,kInPId, "in", kBaseSfxId, srcBuf)) != kOkRC ) { goto errLabel; } @@ -1925,11 +2039,11 @@ namespace cw } } - if((rc = var_register( ctx, kAnyChIdx, kInPId, "in" )) != kOkRC ) + if((rc = var_register( ctx, kAnyChIdx, kInPId, "in", kBaseSfxId)) != kOkRC ) goto errLabel; // create the abuf 'out' - rc = var_register_and_set( ctx, "out", kOutPId, kAnyChIdx, srcBuf->srate, srcBuf->chN, ctx->ctx->framesPerCycle ); + rc = var_register_and_set( ctx, "out", kBaseSfxId, kOutPId, kAnyChIdx, srcBuf->srate, srcBuf->chN, ctx->ctx->framesPerCycle ); } errLabel: @@ -2035,7 +2149,7 @@ namespace cw ctx->userPtr = inst; // verify that a source buffer exists - if((rc = var_register_and_get(ctx, kAnyChIdx,kInPId,"in",srcBuf )) != kOkRC ) + if((rc = var_register_and_get(ctx, kAnyChIdx,kInPId,"in",kBaseSfxId,srcBuf )) != kOkRC ) { rc = cwLogError(rc,"The instance '%s' does not have a valid input connection.",ctx->label); goto errLabel; @@ -2070,13 +2184,13 @@ namespace cw spec_dist_t* sd = inst->sdA[i]; if((rc = var_register_and_get( ctx, i, - kBypassPId, "bypass", sd->bypassFl, - kCeilingPId, "ceiling", sd->ceiling, - kExpoPId, "expo", sd->expo, - kThreshPId, "thresh", sd->thresh, - kUprSlopePId, "upr", sd->uprSlope, - kLwrSlopePId, "lwr", sd->lwrSlope, - kMixPId, "mix", sd->mix )) != kOkRC ) + kBypassPId, "bypass", kBaseSfxId, sd->bypassFl, + kCeilingPId, "ceiling", kBaseSfxId, sd->ceiling, + kExpoPId, "expo", kBaseSfxId, sd->expo, + kThreshPId, "thresh", kBaseSfxId, sd->thresh, + kUprSlopePId, "upr", kBaseSfxId, sd->uprSlope, + kLwrSlopePId, "lwr", kBaseSfxId, sd->lwrSlope, + kMixPId, "mix", kBaseSfxId, sd->mix )) != kOkRC ) { goto errLabel; } @@ -2084,7 +2198,7 @@ namespace cw } // create the output buffer - if((rc = var_register_and_set( ctx, "out", kOutPId, kAnyChIdx, srcBuf->srate, srcBuf->chN, srcBuf->maxBinN_V, srcBuf->binN_V, srcBuf->hopSmpN_V, magV, phsV, hzV )) != kOkRC ) + if((rc = var_register_and_set( ctx, "out", kBaseSfxId, kOutPId, kAnyChIdx, srcBuf->srate, srcBuf->chN, srcBuf->maxBinN_V, srcBuf->binN_V, srcBuf->hopSmpN_V, magV, phsV, hzV )) != kOkRC ) goto errLabel; } @@ -2223,7 +2337,7 @@ namespace cw ctx->userPtr = inst; // verify that a source buffer exists - if((rc = var_register_and_get(ctx, kAnyChIdx,kInPId,"in",srcBuf )) != kOkRC ) + if((rc = var_register_and_get(ctx, kAnyChIdx,kInPId,"in",kBaseSfxId,srcBuf )) != kOkRC ) { rc = cwLogError(rc,"The instance '%s' does not have a valid input connection.",ctx->label); goto errLabel; @@ -2243,15 +2357,15 @@ namespace cw // get the compressor variable values if((rc = var_register_and_get( ctx, i, - kBypassPId, "bypass", bypassFl, - kInGainPId, "igain", igain, - kThreshPId, "thresh", thresh, - kRatioPId, "ratio", ratio, - kAtkMsPId, "atk_ms", atk_ms, - kRlsMsPId, "rls_ms", rls_ms, - kWndMsPId, "wnd_ms", wnd_ms, - kMaxWndMsPId, "maxWnd_ms", maxWnd_ms, - kOutGainPId, "ogain", ogain )) != kOkRC ) + kBypassPId, "bypass", kBaseSfxId, bypassFl, + kInGainPId, "igain", kBaseSfxId, igain, + kThreshPId, "thresh", kBaseSfxId, thresh, + kRatioPId, "ratio", kBaseSfxId, ratio, + kAtkMsPId, "atk_ms", kBaseSfxId, atk_ms, + kRlsMsPId, "rls_ms", kBaseSfxId, rls_ms, + kWndMsPId, "wnd_ms", kBaseSfxId, wnd_ms, + kMaxWndMsPId, "maxWnd_ms", kBaseSfxId, maxWnd_ms, + kOutGainPId, "ogain", kBaseSfxId, ogain )) != kOkRC ) { goto errLabel; } @@ -2266,7 +2380,7 @@ namespace cw } // create the output audio buffer - if((rc = var_register_and_set( ctx, "out", kOutPId, kAnyChIdx, srcBuf->srate, srcBuf->chN, srcBuf->frameN )) != kOkRC ) + if((rc = var_register_and_set( ctx, "out", kBaseSfxId, kOutPId, kAnyChIdx, srcBuf->srate, srcBuf->chN, srcBuf->frameN )) != kOkRC ) goto errLabel; } @@ -2411,7 +2525,7 @@ namespace cw ctx->userPtr = inst; // verify that a source buffer exists - if((rc = var_register_and_get(ctx, kAnyChIdx,kInPId,"in",srcBuf )) != kOkRC ) + if((rc = var_register_and_get(ctx, kAnyChIdx,kInPId,"in",kBaseSfxId,srcBuf )) != kOkRC ) { rc = cwLogError(rc,"The instance '%s' does not have a valid input connection.",ctx->label); goto errLabel; @@ -2431,10 +2545,10 @@ namespace cw // get the limiter variable values if((rc = var_register_and_get( ctx, i, - kBypassPId, "bypass", bypassFl, - kInGainPId, "igain", igain, - kThreshPId, "thresh", thresh, - kOutGainPId, "ogain", ogain )) != kOkRC ) + kBypassPId, "bypass", kBaseSfxId, bypassFl, + kInGainPId, "igain", kBaseSfxId, igain, + kThreshPId, "thresh", kBaseSfxId, thresh, + kOutGainPId, "ogain", kBaseSfxId, ogain )) != kOkRC ) { goto errLabel; } @@ -2449,7 +2563,7 @@ namespace cw } // create the output audio buffer - if((rc = var_register_and_set( ctx, "out", kOutPId, kAnyChIdx, srcBuf->srate, srcBuf->chN, srcBuf->frameN )) != kOkRC ) + if((rc = var_register_and_set( ctx, "out", kBaseSfxId, kOutPId, kAnyChIdx, srcBuf->srate, srcBuf->chN, srcBuf->frameN )) != kOkRC ) goto errLabel; } @@ -2585,7 +2699,7 @@ namespace cw ctx->userPtr = inst; // get the source audio buffer - if((rc = var_register_and_get(ctx, kAnyChIdx,kInPId,"in",abuf )) != kOkRC ) + if((rc = var_register_and_get(ctx, kAnyChIdx,kInPId,"in",kBaseSfxId,abuf )) != kOkRC ) goto errLabel; @@ -2596,8 +2710,8 @@ namespace cw for(unsigned i=0; ichN; ++i) { if((rc = var_register_and_get( ctx, i, - kMaxDelayMsPId, "maxDelayMs", maxDelayMs, - kDelayMsPId, "delayMs", delayMs)) != kOkRC ) + kMaxDelayMsPId, "maxDelayMs", kBaseSfxId, maxDelayMs, + kDelayMsPId, "delayMs", kBaseSfxId, delayMs)) != kOkRC ) { goto errLabel; } @@ -2617,7 +2731,7 @@ namespace cw inst->delayBuf = abuf_create( abuf->srate, abuf->chN, inst->maxDelayFrameN ); // create the output audio buffer - rc = var_register_and_set( ctx, "out", kOutPId, kAnyChIdx, abuf->srate, abuf->chN, abuf->frameN ); + rc = var_register_and_set( ctx, "out", kBaseSfxId, kOutPId, kAnyChIdx, abuf->srate, abuf->chN, abuf->frameN ); errLabel: @@ -2775,7 +2889,7 @@ namespace cw ctx->userPtr = inst; // verify that a source buffer exists - if((rc = var_register_and_get(ctx, kAnyChIdx,kInPId,"in",srcBuf )) != kOkRC ) + if((rc = var_register_and_get(ctx, kAnyChIdx,kInPId,"in",kBaseSfxId,srcBuf )) != kOkRC ) { rc = cwLogError(rc,"The instance '%s' does not have a valid input connection.",ctx->label); goto errLabel; @@ -2795,8 +2909,8 @@ namespace cw // get the dc_filter variable values if((rc = var_register_and_get( ctx, i, - kBypassPId, "bypass", bypassFl, - kGainPId, "gain", gain )) != kOkRC ) + kBypassPId, "bypass", kBaseSfxId, bypassFl, + kGainPId, "gain", kBaseSfxId, gain )) != kOkRC ) { goto errLabel; } @@ -2811,7 +2925,7 @@ namespace cw } // create the output audio buffer - if((rc = var_register_and_set( ctx, "out", kOutPId, kAnyChIdx, srcBuf->srate, srcBuf->chN, srcBuf->frameN )) != kOkRC ) + if((rc = var_register_and_set( ctx, "out", kBaseSfxId, kOutPId, kAnyChIdx, srcBuf->srate, srcBuf->chN, srcBuf->frameN )) != kOkRC ) goto errLabel; } @@ -2931,7 +3045,7 @@ namespace cw ctx->userPtr = inst; // verify that a source buffer exists - if((rc = var_register_and_get(ctx, kAnyChIdx,kInPId,"in",srcBuf )) != kOkRC ) + if((rc = var_register_and_get(ctx, kAnyChIdx,kInPId,"in",kBaseSfxId,srcBuf )) != kOkRC ) { rc = cwLogError(rc,"The instance '%s' does not have a valid input connection.",ctx->label); goto errLabel; @@ -2950,18 +3064,18 @@ namespace cw // get the audio_meter variable values if((rc = var_register_and_get( ctx, i, - kDbFlPId, "dbFl", dbFl, - kWndMsPId, "wndMs", wndMs, - kPeakDbPId, "peakDb", peakThreshDb )) != kOkRC ) + kDbFlPId, "dbFl", kBaseSfxId, dbFl, + kWndMsPId, "wndMs", kBaseSfxId, wndMs, + kPeakDbPId, "peakDb", kBaseSfxId, peakThreshDb )) != kOkRC ) { goto errLabel; } // get the audio_meter variable values if((rc = var_register( ctx, i, - kOutPId, "out", - kPeakFlPId, "peakFl", - kClipFlPId, "clipFl" )) != kOkRC ) + kOutPId, "out", kBaseSfxId, + kPeakFlPId, "peakFl", kBaseSfxId, + kClipFlPId, "clipFl", kBaseSfxId )) != kOkRC ) { goto errLabel; } @@ -3075,15 +3189,15 @@ namespace cw ctx->userPtr = mem::allocZ(); // get the source audio buffer - if((rc = var_register_and_get(ctx, kAnyChIdx,kInPId,"in",abuf )) != kOkRC ) + if((rc = var_register_and_get(ctx, kAnyChIdx,kInPId,"in",kBaseSfxId,abuf )) != kOkRC ) goto errLabel; // register the marker input - if((rc = var_register_and_set( ctx, kAnyChIdx, kMarkPId, "mark", 0.0f )) != kOkRC ) + if((rc = var_register_and_set( ctx, kAnyChIdx, kMarkPId, "mark", kBaseSfxId, 0.0f )) != kOkRC ) goto errLabel; // create the output audio buffer - rc = var_register_and_set( ctx, "out", kOutPId, kAnyChIdx, abuf->srate, abuf->chN, abuf->frameN ); + rc = var_register_and_set( ctx, "out", kBaseSfxId, kOutPId, kAnyChIdx, abuf->srate, abuf->chN, abuf->frameN ); errLabel: return rc; @@ -3147,6 +3261,307 @@ namespace cw }; } + + //------------------------------------------------------------------------------------------------------------------ + // + // xfade_ctl + // + namespace xfade_ctl + { + enum { + kNetLabelPId, + kNetLabelSfxPId, + kSrateRefPId, + kDurMsPId, + kTriggerPId, + kGainPId, + }; + + typedef struct + { + unsigned xfadeDurMs; // crossfade duration in milliseconds + instance_t* net_proc; // source 'poly' network + network_t net; // internal proxy network + unsigned poly_ch_cnt; // set to 2 (one for 'cur' poly-ch., one for 'next' poly-ch.) + unsigned net_proc_cnt; // count of proc's in a single poly-channel (net_proc->proc_arrayN/poly_cnt) + unsigned cur_poly_ch_idx; // + unsigned next_poly_ch_idx; // + float* target_gainA; // target_gainA[net_proc->poly_cnt] + float* cur_gainA; // cur_gainA[net_proc->poly_cnt] + double srate; + + } inst_t; + + void _trigger_xfade( inst_t* p ) + { + // begin fading out the cur channel + p->target_gainA[ p->cur_poly_ch_idx ] = 0; + + // the next poly-ch become the cur poly-ch + p->cur_poly_ch_idx = p->next_poly_ch_idx; + + // the next poly-ch advances + p->next_poly_ch_idx = p->next_poly_ch_idx+1 >= p->poly_ch_cnt ? 0 : p->next_poly_ch_idx+1; + + // j selects a block of 'net_proc_cnt' slots in the proxy network which will become the 'next' channel + unsigned j = p->next_poly_ch_idx * p->net_proc_cnt; + + // set the [j:j+poly_proc_cnt] pointers in the proxy net to the actual proc instances in the source net + for(unsigned i=0; inet_proc->internal_net->proc_arrayN; ++i) + if( p->net_proc->internal_net->proc_array[i]->label_sfx_id == p->next_poly_ch_idx ) + { + assert( p->next_poly_ch_idx * p->net_proc_cnt <= j + && j < p->next_poly_ch_idx * p->net_proc_cnt + p->net_proc_cnt + && j < p->net.proc_arrayN ); + + p->net.proc_array[j++] = p->net_proc->internal_net->proc_array[i]; + } + + // begin fading in the new cur channel + p->target_gainA[ p->cur_poly_ch_idx ] = 1; + + // if the next channel is not already at 0 send it in that direction + p->target_gainA[ p->next_poly_ch_idx ] = 0; + + } + + + + rc_t create( instance_t* ctx ) + { + rc_t rc = kOkRC; + const char* netLabel = nullptr; + unsigned netLabelSfxId = kBaseSfxId; + bool trigFl = false; + variable_t* gainVar = nullptr; + abuf_t* srateSrc = nullptr; + double dum; + + inst_t* p = mem::allocZ(); + + ctx->userPtr = p; + + p->poly_ch_cnt = 2; + + if((rc = var_register_and_get(ctx,kAnyChIdx, + kNetLabelPId, "net", kBaseSfxId, netLabel, + kNetLabelSfxPId, "netSfxId", kBaseSfxId, netLabelSfxId, + kSrateRefPId, "srateSrc", kBaseSfxId, srateSrc, + kDurMsPId, "durMs", kBaseSfxId, p->xfadeDurMs, + kTriggerPId, "trigger", kBaseSfxId, trigFl, + kGainPId, "gain", kBaseSfxId, dum)) != kOkRC ) + { + goto errLabel; + } + + // locate the source poly-network for this xfad-ctl + if((rc = instance_find(*ctx->net,netLabel,netLabelSfxId,p->net_proc)) != kOkRC ) + { + cwLogError(rc,"The xfade_ctl source network proc instance '%s:%i' was not found.",cwStringNullGuard(netLabel),netLabelSfxId); + goto errLabel; + } + + if( p->net_proc->internal_net->poly_cnt < 3 ) + { + cwLogError(rc,"The xfade_ctl source network must have at least 3 poly channels. %i < 3",p->net_proc->internal_net->poly_cnt); + goto errLabel; + } + + + // create the gain output variables - one output for each poly-channel + for(unsigned i=1; inet_proc->internal_net->poly_cnt; ++i) + { + variable_t* dum; + if((rc = var_create(ctx, "gain", i, kGainPId+i, kAnyChIdx, nullptr, dum )) != kOkRC ) + { + cwLogError(rc,"'gain:%i' create failed.",i); + goto errLabel; + } + } + + // count of proc's in one poly-ch of the poly network + p->net_proc_cnt = p->net_proc->internal_net->proc_arrayN / p->net_proc->internal_net->poly_cnt; + + // create the proxy network + p->net.proc_arrayAllocN = p->net_proc_cnt * p->poly_ch_cnt; + p->net.proc_arrayN = p->net.proc_arrayAllocN; + p->net.proc_array = mem::allocZ(p->net.proc_arrayAllocN); + p->target_gainA = mem::allocZ(p->net_proc->internal_net->poly_cnt); + p->cur_gainA = mem::allocZ(p->net_proc->internal_net->poly_cnt); + p->srate = srateSrc->srate; + + // make the proxy network public - xfad_ctl now looks like the source network + // because it has the same proc instances + ctx->internal_net = &p->net; + + // setup the channels such that the first active channel after _trigger_xfade() + // will be channel 0 + p->cur_poly_ch_idx = 1; + p->next_poly_ch_idx = 2; + _trigger_xfade(p); // cur=2 nxt=0 initialize inst ptrs in range: p->net[0:net_proc_cnt] + _trigger_xfade(p); // cur=0 nxt=1 initialize inst ptrs in range: p->net[net_proc_cnt:2*net_proc_cnt] + + + + errLabel: + return rc; + } + + rc_t destroy( instance_t* ctx ) + { + inst_t* p = (inst_t*)ctx->userPtr; + mem::release(p->net.proc_array); + mem::release(p->target_gainA); + mem::release(p->cur_gainA); + mem::release(ctx->userPtr); + + return kOkRC; + } + + rc_t value( instance_t* ctx, variable_t* var ) + { return kOkRC; } + + // return sign of expression as a float + float _signum( float v ) { return (0.0f < v) - (v < 0.0f); } + + rc_t exec( instance_t* ctx ) + { + rc_t rc = kOkRC; + inst_t* p = (inst_t*)ctx->userPtr; + bool trigFl = false; + + // check if a cross-fade has been triggered + if((rc = var_get(ctx,kTriggerPId,kAnyChIdx,trigFl)) == kOkRC ) + { + _trigger_xfade(p); + + var_set(ctx,kTriggerPId,kAnyChIdx,false); + } + + // time in sample frames to complete a xfade + double xfade_dur_smp = p->xfadeDurMs * p->srate / 1000.0; + + // fraction of a xfade which will be completed in on exec() cycle + float delta_gain_per_cycle = (float)(ctx->ctx->framesPerCycle / xfade_dur_smp); + + // update the cross-fade gain outputs + for(unsigned i=0; inet_proc->internal_net->poly_cnt; ++i) + { + p->cur_gainA[i] += _signum(p->target_gainA[i] - p->cur_gainA[i]) * delta_gain_per_cycle; + + p->cur_gainA[i] = std::min(1.0f, std::max(0.0f, p->cur_gainA[i])); + + var_set(ctx,kGainPId+i,kAnyChIdx,p->cur_gainA[i]); + } + + errLabel: + return rc; + } + + class_members_t members = { + .create = create, + .destroy = destroy, + .value = value, + .exec = exec, + .report = nullptr + }; + + } + + //------------------------------------------------------------------------------------------------------------------ + // + // poly_mixer + // + namespace poly_mixer + { + enum { + kOutGainPId, + kOutPId, + + }; + + typedef struct + { + unsigned inBaseVId; + unsigned gainBaseVId; + } inst_t; + + + rc_t create( instance_t* ctx ) + { + rc_t rc = kOkRC; + /* + const abuf_t* abuf0 = nullptr; // + const abuf_t* abuf1 = nullptr; + unsigned outChN = 0; + double dum; + + // get the source audio buffer + if((rc = var_register_and_get(ctx, kAnyChIdx, + kIn0PId,"in0",kBaseSfxId,abuf0, + kIn1PId,"in1",kBaseSfxId,abuf1 )) != kOkRC ) + { + goto errLabel; + } + + assert( abuf0->frameN == abuf1->frameN ); + + outChN = std::max(abuf0->chN, abuf1->chN); + + // register the gain + var_register_and_get( ctx, kAnyChIdx, kGain0PId, "gain0", kBaseSfxId, dum ); + var_register_and_get( ctx, kAnyChIdx, kGain1PId, "gain1", kBaseSfxId, dum ); + + // create the output audio buffer + rc = var_register_and_set( ctx, "out", kBaseSfxId, kOutPId, kAnyChIdx, abuf0->srate, outChN, abuf0->frameN ); + */ + errLabel: + return rc; + } + + rc_t destroy( instance_t* ctx ) + { return kOkRC; } + + rc_t value( instance_t* ctx, variable_t* var ) + { return kOkRC; } + + + rc_t exec( instance_t* ctx ) + { + rc_t rc = kOkRC; + /* + abuf_t* obuf = nullptr; + //const abuf_t* ibuf0 = nullptr; + //const abuf_t* ibuf1 = nullptr; + + if((rc = var_get(ctx,kOutPId, kAnyChIdx, obuf)) != kOkRC ) + goto errLabel; + + //if((rc = var_get(ctx,kIn0PId, kAnyChIdx, ibuf0 )) != kOkRC ) + // goto errLabel; + + //if((rc = var_get(ctx,kIn1PId, kAnyChIdx, ibuf1 )) != kOkRC ) + // goto errLabel; + + vop::zero(obuf->buf, obuf->frameN*obuf->chN ); + + _mix( ctx, kIn0PId, kGain0PId, obuf ); + _mix( ctx, kIn1PId, kGain1PId, obuf ); + */ + + errLabel: + return rc; + } + + class_members_t members = { + .create = create, + .destroy = destroy, + .value = value, + .exec = exec, + .report = nullptr + }; + + } } // flow diff --git a/cwFlowProc.h b/cwFlowProc.h index 026bbed..3898cb1 100644 --- a/cwFlowProc.h +++ b/cwFlowProc.h @@ -2,6 +2,7 @@ namespace cw { namespace flow { + 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; } @@ -24,5 +25,7 @@ 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 poly_mixer { extern class_members_t members; } } } diff --git a/cwFlowTypes.cpp b/cwFlowTypes.cpp index 477b5f1..0788edc 100644 --- a/cwFlowTypes.cpp +++ b/cwFlowTypes.cpp @@ -393,7 +393,7 @@ namespace cw return cwLogError(kInvalidArgRC,"Cannnot get the value of a non-existent variable."); if( var->value == nullptr ) - return cwLogError(kInvalidStateRC,"No value has been assigned to the variable: %s.%s ch:%i.",cwStringNullGuard(var->inst->label),cwStringNullGuard(var->label),var->chIdx); + return cwLogError(kInvalidStateRC,"No value has been assigned to the variable: %s:%i.%s:%i ch:%i.",cwStringNullGuard(var->inst->label),var->inst->label_sfx_id,cwStringNullGuard(var->label),var->label_sfx_id,var->chIdx); return _val_get(var->value,valRef); } @@ -408,7 +408,7 @@ namespace cw { // validate the type of the variable against the description if( !cwIsFlag(varRef->varDesc->type,typeFl ) ) - rc = cwLogError(kTypeMismatchRC,"Type mismatch. Instance:%s variable:%s with type 0x%x does not match requested type:0x%x.",varRef->inst->label,varRef->label,varRef->varDesc->type,typeFl); + rc = cwLogError(kTypeMismatchRC,"Type mismatch. Instance:%s:%i variable:%s:%i with type 0x%x does not match requested type:0x%x.",varRef->inst->label,varRef->inst->label_sfx_id,varRef->label,varRef->label_sfx_id,varRef->varDesc->type,typeFl); } @@ -430,16 +430,16 @@ namespace cw return kOkRC; } } - return cwLogError(kInvalidIdRC,"The variable matching id:%i ch:%i on instance '%s' could not be found.", vid, chIdx, inst->label); + return cwLogError(kInvalidIdRC,"The variable matching id:%i ch:%i on instance '%s:%i' could not be found.", vid, chIdx, inst->label, inst->label_sfx_id); } // Variable lookup: Exact match on label and chIdx - variable_t* _var_find_on_label_and_ch( instance_t* inst, const char* var_label, unsigned chIdx ) + variable_t* _var_find_on_label_and_ch( instance_t* inst, const char* var_label, unsigned sfx_id, unsigned chIdx ) { for(variable_t* var = inst->varL; var!=nullptr; var=var->var_link) { // the variable vid and chIdx should form a unique pair - if( textCompare(var->label,var_label)==0 && var->chIdx == chIdx ) + if( var->label_sfx_id==sfx_id && textCompare(var->label,var_label)==0 && var->chIdx == chIdx ) return var; } @@ -450,10 +450,10 @@ namespace cw rc_t _validate_var_assignment( variable_t* var, unsigned typeFl ) { if( cwIsFlag(var->varDesc->flags, kSrcVarFl ) ) - return cwLogError(kInvalidStateRC, "The variable '%s' on instance '%s' cannot be set because it is a 'src' variable.", var->label, var->inst->label); + return cwLogError(kInvalidStateRC, "The variable '%s:%i' on instance '%s:%i' cannot be set because it is a 'src' variable.", var->label, var->label_sfx_id, var->inst->label,var->inst->label_sfx_id); if( !cwIsFlag(var->varDesc->type, typeFl ) ) - return cwLogError(kTypeMismatchRC, "The variable '%s' on instance '%s' is not a '%s'.", var->label, var->inst->label, _typeFlagToLabel( typeFl )); + return cwLogError(kTypeMismatchRC, "The variable '%s:%i' on instance '%s:%i' is not a '%s'.", var->label, var->label_sfx_id, var->inst->label, var->inst->label_sfx_id, _typeFlagToLabel( typeFl )); return kOkRC; } @@ -482,7 +482,7 @@ namespace cw { var->local_value[ local_value_idx ].u.b = val; var->local_value[ local_value_idx ].flags = kBoolTFl; - cwLogMod("%s.%s ch:%i %i (bool).",var->inst->label,var->label,var->chIdx,val); + cwLogMod("%s:%i.%s:%i ch:%i %i (bool).",var->inst->label,var->inst->label_sfx_id,var->label,var->label_sfx_id,var->chIdx,val); } template<> @@ -490,7 +490,7 @@ namespace cw { var->local_value[ local_value_idx ].u.u = val; var->local_value[ local_value_idx ].flags = kUIntTFl; - cwLogMod("%s.%s ch:%i %i (uint).",var->inst->label,var->label,var->chIdx,val); + cwLogMod("%s:%i.%s:%i ch:%i %i (uint).",var->inst->label,var->inst->label_sfx_id,var->label,var->label_sfx_id,var->chIdx,val); } template<> @@ -498,7 +498,7 @@ namespace cw { var->local_value[ local_value_idx ].u.i = val; var->local_value[ local_value_idx ].flags = kIntTFl; - cwLogMod("%s.%s ch:%i %i (int).",var->inst->label,var->label,var->chIdx,val); + cwLogMod("%s:%i.%s:%i ch:%i %i (int).",var->inst->label,var->inst->label_sfx_id,var->label,var->label_sfx_id,var->chIdx,val); } template<> @@ -506,7 +506,7 @@ namespace cw { var->local_value[ local_value_idx ].u.f = val; var->local_value[ local_value_idx ].flags = kFloatTFl; - cwLogMod("%s.%s ch:%i %f (float).",var->inst->label,var->label,var->chIdx,val); + cwLogMod("%s:%i.%s:%i ch:%i %f (float).",var->inst->label,var->inst->label_sfx_id,var->label,var->label_sfx_id,var->chIdx,val); } template<> @@ -514,7 +514,7 @@ namespace cw { var->local_value[ local_value_idx ].u.d = val; var->local_value[ local_value_idx ].flags = kDoubleTFl; - cwLogMod("%s.%s ch:%i %f (double).",var->inst->label,var->label,var->chIdx,val); + cwLogMod("%s:%i.%s:%i ch:%i %f (double).",var->inst->label,var->inst->label_sfx_id,var->label,var->label_sfx_id,var->chIdx,val); } template<> @@ -522,7 +522,7 @@ namespace cw { var->local_value[ local_value_idx ].u.s = mem::duplStr(val); var->local_value[ local_value_idx ].flags = kStringTFl; - cwLogMod("%s.%s ch:%i %s (string).",var->inst->label,var->label,var->chIdx,val); + cwLogMod("%s:%i.%s:%i ch:%i %s (string).",var->inst->label,var->inst->label_sfx_id,var->label,var->label_sfx_id,var->chIdx,val); } template<> @@ -530,7 +530,7 @@ namespace cw { var->local_value[ local_value_idx ].u.abuf = val; var->local_value[ local_value_idx ].flags = kABufTFl; - cwLogMod("%s.%s ch:%i %s (abuf).",var->inst->label,var->label,var->chIdx,abuf==nullptr ? "null" : "valid"); + cwLogMod("%s:%i.%s:%i ch:%i %s (abuf).",var->inst->label,var->inst->label_sfx_id,var->label,var->label_sfx_id,var->chIdx,abuf==nullptr ? "null" : "valid"); } template<> @@ -538,7 +538,7 @@ namespace cw { var->local_value[ local_value_idx ].u.mbuf = val; var->local_value[ local_value_idx ].flags = kMBufTFl; - cwLogMod("%s.%s ch:%i %s (abuf).",var->inst->label,var->label,var->chIdx,mbuf==nullptr ? "null" : "valid"); + cwLogMod("%s:%i.%s:%i ch:%i %s (abuf).",var->inst->label,var->inst->label_sfx_id,var->label,var->label_sfx_id,var->chIdx,mbuf==nullptr ? "null" : "valid"); } template<> @@ -546,7 +546,7 @@ namespace cw { var->local_value[ local_value_idx ].u.fbuf = val; var->local_value[ local_value_idx ].flags = kFBufTFl; - cwLogMod("%s.%s ch:%i %s (fbuf).",var->inst->label,var->label,var->chIdx,fbuf==nullptr ? "null" : "valid"); + cwLogMod("%s:%i.%s:%i ch:%i %s (fbuf).",var->inst->label,var->inst->label_sfx_id,var->label,var->label_sfx_id,var->chIdx,fbuf==nullptr ? "null" : "valid"); } template< typename T > @@ -644,11 +644,11 @@ namespace cw } - rc_t _var_register_and_set( instance_t* inst, const char* var_label, unsigned vid, unsigned chIdx, abuf_t* abuf ) + rc_t _var_register_and_set( instance_t* inst, const char* var_label, unsigned sfx_id, unsigned vid, unsigned chIdx, abuf_t* abuf ) { 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( inst, var_label, sfx_id, vid, chIdx, var)) != kOkRC ) return rc; if( var != nullptr ) @@ -657,11 +657,11 @@ namespace cw return rc; } - rc_t _var_register_and_set( instance_t* inst, const char* var_label, unsigned vid, unsigned chIdx, mbuf_t* mbuf ) + rc_t _var_register_and_set( instance_t* inst, const char* var_label, unsigned sfx_id, unsigned vid, unsigned chIdx, mbuf_t* mbuf ) { 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( inst, var_label, sfx_id, vid, chIdx, var)) != kOkRC ) return rc; if( var != nullptr ) @@ -670,11 +670,11 @@ namespace cw return rc; } - rc_t _var_register_and_set( instance_t* inst, const char* var_label, unsigned vid, unsigned chIdx, fbuf_t* fbuf ) + rc_t _var_register_and_set( instance_t* inst, const char* var_label, unsigned sfx_id, unsigned vid, unsigned chIdx, fbuf_t* fbuf ) { 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( inst, var_label, sfx_id, vid, chIdx, var)) != kOkRC ) return rc; if( var != nullptr ) @@ -746,7 +746,7 @@ namespace cw errLabel: if( rc != kOkRC ) - rc = cwLogError(kSyntaxErrorRC,"The %s.%s could not extract a type:%s from a configuration value.",var->inst->label,var->label,_typeFlagToLabel(var->varDesc->type & kTypeMask)); + rc = cwLogError(kSyntaxErrorRC,"The %s:%i.%s:%i could not extract a type:%s from a configuration value.",var->inst->label,var->inst->label_sfx_id,var->label,var->label_sfx_id,_typeFlagToLabel(var->varDesc->type & kTypeMask)); return rc; } @@ -757,20 +757,20 @@ namespace cw // verify that the map idx is valid if( idx >= inst->varMapN ) - return cwLogError(kAssertFailRC,"The variable map positioning location %i is out of the range %i on instance '%s' vid:%i ch:%i.", idx, inst->varMapN, inst->label,vid,chIdx); + return cwLogError(kAssertFailRC,"The variable map positioning location %i is out of the range %i on instance '%s:%i' vid:%i ch:%i.", idx, inst->varMapN, inst->label,inst->label_sfx_id,vid,chIdx); idxRef = idx; return kOkRC; } - rc_t _var_map_label_to_index( instance_t* inst, const char* var_label, unsigned chIdx, unsigned& idxRef ) + rc_t _var_map_label_to_index( instance_t* inst, const char* var_label, unsigned sfx_id, unsigned chIdx, unsigned& idxRef ) { rc_t rc = kOkRC; variable_t* var = nullptr; idxRef = kInvalidIdx; - if((rc = var_find(inst, var_label, chIdx, var )) == kOkRC) + if((rc = var_find(inst, var_label, sfx_id, chIdx, var )) == kOkRC) rc = _var_map_id_to_index( inst, var->vid, chIdx, idxRef ); return rc; @@ -787,9 +787,9 @@ namespace cw if( new_var->chIdx == kAnyChIdx ) return kOkRC; - if((base_var = _var_find_on_label_and_ch( inst, new_var->label, kAnyChIdx )) == nullptr ) + if((base_var = _var_find_on_label_and_ch( inst, new_var->label, new_var->label_sfx_id, kAnyChIdx )) == nullptr ) { - rc = cwLogError(kInvalidStateRC,"The base channel variable does not exist for '%s.%s'. This is an illegal state.", inst->label, new_var->label ); + rc = cwLogError(kInvalidStateRC,"The base channel variable does not exist for '%s:%i.%s:%i'. This is an illegal state.", inst->label, inst->label_sfx_id, new_var->label, new_var->label_sfx_id ); goto errLabel; } @@ -815,7 +815,7 @@ namespace cw // Create a variable and set it's value from 'value_cfg'. // If 'value_cfg' is null then use the value from var->varDesc->val_cfg. - rc_t _var_create( instance_t* inst, const char* var_label, unsigned id, unsigned chIdx, const object_t* value_cfg, variable_t*& varRef ) + rc_t _var_create( instance_t* inst, const char* var_label, unsigned sfx_id, unsigned id, unsigned chIdx, const object_t* value_cfg, variable_t*& varRef ) { rc_t rc = kOkRC; variable_t* var = nullptr; @@ -824,28 +824,29 @@ namespace cw varRef = nullptr; // if this var already exists - it can't be created again - if((var = _var_find_on_label_and_ch(inst,var_label,chIdx)) != nullptr ) + if((var = _var_find_on_label_and_ch(inst,var_label, sfx_id, chIdx)) != nullptr ) { - rc = cwLogError(kInvalidStateRC,"The variable '%s' ch:%i has already been created on the instance: '%s'.",var_label,chIdx,inst->label); + rc = cwLogError(kInvalidStateRC,"The variable '%s:%i' ch:%i has already been created on the instance: '%s:%i'.",var_label,sfx_id,chIdx,inst->label,inst->label_sfx_id); goto errLabel; } // locate the var desc if((vd = var_desc_find( inst->class_desc, var_label)) == nullptr ) { - rc = cwLogError(kInvalidIdRC,"Unable to locate the variable '%s' in class '%s'.", var_label, inst->class_desc->label ); + rc = cwLogError(kInvalidIdRC,"Unable to locate the variable '%s:%i' in class '%s'.", var_label, sfx_id, inst->class_desc->label ); goto errLabel; } // create the var var = mem::allocZ(); - var->varDesc = vd; - var->inst = inst; - var->label = mem::duplStr(var_label); - var->vid = id; - var->chIdx = chIdx; - var->value = nullptr; + var->varDesc = vd; + var->inst = inst; + var->label = mem::duplStr(var_label); + var->label_sfx_id = sfx_id; + var->vid = id; + var->chIdx = chIdx; + var->value = nullptr; // if no value was given then set the value to the value given in the class if( value_cfg == nullptr ) @@ -868,12 +869,12 @@ namespace cw if( rc != kOkRC ) { _var_destroy(var); - cwLogError(kOpFailRC,"Variable creation failed on '%s.%s' ch:%i.", inst->label, var_label, chIdx ); + cwLogError(kOpFailRC,"Variable creation failed on '%s:%i.%s:%i' ch:%i.", inst->label, inst->label_sfx_id, var_label, sfx_id, chIdx ); } else { varRef = var; - cwLogMod("Created var: %s.%s ch:%i.", inst->label, var_label, chIdx ); + cwLogMod("Created var: %s:%i.%s:%i ch:%i.", inst->label, inst->label_sfx_id, var_label, sfx_id, chIdx ); } return rc; @@ -883,31 +884,34 @@ namespace cw { const char* conn_label = is_connected_to_external_proc(var) ? "extern" : " "; - printf(" %20s id:%4i ch:%3i : %s : ", var->label, var->vid, var->chIdx, conn_label ); + printf(" %20s:%5i id:%4i ch:%3i : %s : ", var->label, var->label_sfx_id, var->vid, var->chIdx, conn_label ); if( var->value == nullptr ) _value_print( &var->local_value[0] ); else _value_print( var->value ); + if( var->src_var != nullptr ) + printf(" src:%s:%i.%s:%i",var->src_var->inst->label,var->src_var->inst->label_sfx_id,var->src_var->label,var->src_var->label_sfx_id); + printf("\n"); } - rc_t _preset_set_var_value( instance_t* inst, const char* var_label, unsigned chIdx, const object_t* value ) + rc_t _preset_set_var_value( instance_t* inst, const char* var_label, unsigned sfx_id, unsigned chIdx, const object_t* value ) { rc_t rc = kOkRC; variable_t* var = nullptr; // get the variable - if((rc = var_find( inst, var_label, chIdx, var )) != kOkRC ) + if((rc = var_find( inst, var_label, sfx_id, chIdx, var )) != kOkRC ) goto errLabel; rc = _set_var_value_from_cfg( var, value ); errLabel: if( rc != kOkRC ) - rc = cwLogError(rc,"The value of instance:%s variable:%s could not be set via a preset.", inst->label, var_label ); + rc = cwLogError(rc,"The value of instance:%s:%i variable:%s:%i could not be set via a preset.", inst->label, inst->label_sfx_id, var_label, sfx_id ); return rc; } @@ -1136,37 +1140,52 @@ void cw::flow::class_dict_print( flow_t* p ) for(; vd!=nullptr; vd=vd->link) { - const char* srcFlStr = vd->flags & kSrcVarFl ? "src" : " "; - const char* srcOptFlStr = vd->flags & kSrcOptVarFl ? "srcOpt" : " "; + const char* srcFlStr = vd->flags & kSrcVarFl ? "src" : " "; + const char* srcOptFlStr = vd->flags & kSrcOptVarFl ? "srcOpt" : " "; + const char* plyMltFlStr = vd->flags & kMultVarFl ? "mult" : " "; - printf(" %10s 0x%08x %s %s %s\n", cwStringNullGuard(vd->label), vd->type, srcFlStr, srcOptFlStr, cwStringNullGuard(vd->docText) ); + printf(" %10s 0x%08x %s %s %s %s\n", cwStringNullGuard(vd->label), vd->type, srcFlStr, srcOptFlStr, plyMltFlStr, cwStringNullGuard(vd->docText) ); } } } -void cw::flow::network_print( flow_t* p ) + +void cw::flow::network_print( const network_t& net ) { - for(instance_t* inst = p->network_head; inst!=nullptr; inst=inst->link) + // for each instance in the network + for(unsigned i=0; iinternal_net != nullptr ) + { + printf("------- Begin Nested Network: %s -------\n",cwStringNullGuard(inst->label)); + network_print(*(inst->internal_net)); + printf("------- End Nested Network: %s -------\n",cwStringNullGuard(inst->label)); + } + + } } -cw::flow::instance_t* cw::flow::instance_find( flow_t* p, const char* inst_label ) +cw::flow::instance_t* cw::flow::instance_find( network_t& net, const char* inst_label, unsigned sfx_id ) { - for(instance_t* inst = p->network_head; inst!=nullptr; inst=inst->link ) - if( textCompare(inst_label,inst->label) == 0 ) - return inst; + for(unsigned i=0; ilabel_sfx_id==sfx_id && textIsEqual(inst_label,net.proc_array[i]->label) ) + return net.proc_array[i]; return nullptr; } -cw::rc_t cw::flow::instance_find( flow_t* p, const char* inst_label, instance_t*& instPtrRef ) +cw::rc_t cw::flow::instance_find( network_t& net, const char* inst_label, unsigned sfx_id, instance_t*& instPtrRef ) { rc_t rc = kOkRC; - if((instPtrRef = instance_find(p,inst_label)) != nullptr ) + if((instPtrRef = instance_find(net,inst_label,sfx_id)) != nullptr ) return rc; - return cwLogError(kInvalidArgRC,"The instance '%s' was not found.", inst_label ); + return cwLogError(kInvalidArgRC,"The instance '%s:%i' was not found.", inst_label, sfx_id ); } cw::flow::external_device_t* cw::flow::external_device_find( flow_t* p, const char* device_label, unsigned typeId, unsigned inOrOutFl, const char* midiPortLabel ) @@ -1185,7 +1204,7 @@ cw::flow::external_device_t* cw::flow::external_device_find( flow_t* p, const ch void cw::flow::instance_print( instance_t* inst ) { - printf("%s\n", inst->label); + printf("%s:%i\n", inst->label,inst->label_sfx_id); for(variable_t* var = inst->varL; var!=nullptr; var=var->var_link) if( var->chIdx == kAnyChIdx ) for(variable_t* v0 = var; v0!=nullptr; v0=v0->ch_link) @@ -1209,31 +1228,31 @@ void cw::flow::_var_destroy( variable_t* var ) } -cw::rc_t cw::flow::var_create( instance_t* inst, const char* var_label, unsigned id, unsigned chIdx, const object_t* value_cfg, variable_t*& varRef ) +cw::rc_t cw::flow::var_create( instance_t* inst, const char* var_label, unsigned sfx_id, unsigned id, unsigned chIdx, const object_t* value_cfg, variable_t*& varRef ) { rc_t rc = kOkRC; - rc = _var_create( inst, var_label, id, chIdx, value_cfg, varRef ); + rc = _var_create( inst, var_label, sfx_id, id, chIdx, value_cfg, varRef ); return rc; } -cw::rc_t cw::flow::var_channelize( instance_t* inst, const char* var_label, unsigned chIdx, const object_t* value_cfg, unsigned vid, variable_t*& varRef ) +cw::rc_t cw::flow::var_channelize( instance_t* inst, const char* var_label, unsigned sfx_id, unsigned chIdx, const object_t* value_cfg, unsigned vid, variable_t*& varRef ) { rc_t rc = kOkRC; variable_t* var = nullptr; variable_t* base_var = nullptr; varRef = nullptr; - if((base_var = _var_find_on_label_and_ch( inst, var_label, kAnyChIdx)) == nullptr) + if((base_var = _var_find_on_label_and_ch( inst, var_label, sfx_id, kAnyChIdx)) == nullptr) { - rc = cwLogError(kInvalidStateRC,"The base ('any') channel variable could not be located on '%s.%s'.",inst->label,var_label); + rc = cwLogError(kInvalidStateRC,"The base ('any') channel variable could not be located on '%s:%i.%s:%i'.",inst->label,inst->label_sfx_id,var_label,sfx_id); goto errLabel; } // locate the variable with the stated chIdx - var = _var_find_on_label_and_ch( inst, var_label, chIdx ); + var = _var_find_on_label_and_ch( inst, var_label, sfx_id, chIdx ); // 'src' variables cannot be channelized @@ -1247,7 +1266,7 @@ cw::rc_t cw::flow::var_channelize( instance_t* inst, const char* var_label, uns if( var == nullptr && chIdx != kAnyChIdx ) { // create the channelized var - if((rc = _var_create( inst, var_label, vid, chIdx, value_cfg, var )) != kOkRC ) + if((rc = _var_create( inst, var_label, sfx_id, vid, chIdx, value_cfg, var )) != kOkRC ) goto errLabel; // if no value was set then set the value from the 'any' channel @@ -1272,7 +1291,7 @@ cw::rc_t cw::flow::var_channelize( instance_t* inst, const char* var_label, uns } else { - cwLogWarning("An existing var (%s.%s ch:%i) was specified for channelizing but no value was provided.", inst->label, var_label, chIdx ); + cwLogWarning("An existing var (%s:i.%s:i ch:%i) was specified for channelizing but no value was provided.", inst->label, inst->label_sfx_id, var_label, sfx_id, chIdx ); } } @@ -1281,20 +1300,20 @@ cw::rc_t cw::flow::var_channelize( instance_t* inst, const char* var_label, uns errLabel: if( rc != kOkRC ) - rc = cwLogError(rc,"Channelize failed for variable '%s' on instance '%s' ch:%i.", var_label, inst->label, chIdx ); + rc = cwLogError(rc,"Channelize failed for variable '%s:%i' on instance '%s:i' ch:%i.", var_label, sfx_id, inst->label, inst->label_sfx_id, chIdx ); return rc; } -bool cw::flow::var_exists( instance_t* inst, const char* label, unsigned chIdx ) -{ return _var_find_on_label_and_ch(inst,label,chIdx) != nullptr; } +bool cw::flow::var_exists( instance_t* inst, const char* label, unsigned sfx_id, unsigned chIdx ) +{ return _var_find_on_label_and_ch(inst,label,sfx_id,chIdx) != nullptr; } -bool cw::flow::var_has_value( instance_t* inst, const char* label, unsigned chIdx ) +bool cw::flow::var_has_value( instance_t* inst, const char* label, unsigned sfx_id, unsigned chIdx ) { variable_t* varPtr = nullptr; rc_t rc; - if((rc = var_find( inst, label, chIdx, varPtr )) != kOkRC ) + if((rc = var_find( inst, label, sfx_id, chIdx, varPtr )) != kOkRC ) return false; return varPtr->value != nullptr; @@ -1322,7 +1341,7 @@ cw::rc_t cw::flow::var_find( instance_t* inst, unsigned vid, unsigned chIdx, var var = inst->varMapA[idx]; else { - rc = cwLogError(kInvalidIdRC,"The index of variable vid:%i chIdx:%i on instance '%s' could not be calculated and the variable value could not be retrieved.", vid, chIdx, inst->label); + rc = cwLogError(kInvalidIdRC,"The index of variable vid:%i chIdx:%i on instance '%s:%i' could not be calculated and the variable value could not be retrieved.", vid, chIdx, inst->label,inst->label_sfx_id); goto errLabel; } } @@ -1338,34 +1357,35 @@ cw::rc_t cw::flow::var_find( instance_t* inst, unsigned vid, unsigned chIdx, var -cw::rc_t cw::flow::var_find( instance_t* inst, const char* label, unsigned chIdx, variable_t*& vRef ) +cw::rc_t cw::flow::var_find( instance_t* inst, const char* label, unsigned sfx_id, unsigned chIdx, variable_t*& vRef ) { variable_t* var; vRef = nullptr; - if((var = _var_find_on_label_and_ch(inst,label,chIdx)) != nullptr ) + if((var = _var_find_on_label_and_ch(inst,label,sfx_id,chIdx)) != nullptr ) { vRef = var; return kOkRC; } - return cwLogError(kInvalidIdRC,"The instance '%s' does not have a variable named '%s'.", inst->label, label ); + return cwLogError(kInvalidIdRC,"The instance '%s:%i' does not have a variable named '%s:%i'.", inst->label, inst->label_sfx_id, label, sfx_id ); } -cw::rc_t cw::flow::var_find( instance_t* inst, const char* label, unsigned chIdx, const variable_t*& vRef ) +cw::rc_t cw::flow::var_find( instance_t* inst, const char* label, unsigned sfx_id, unsigned chIdx, const variable_t*& vRef ) { variable_t* v = nullptr; - rc_t rc = var_find(inst,label,chIdx,v); + rc_t rc = var_find(inst,label,sfx_id,chIdx,v); vRef = v; return rc; } -cw::rc_t cw::flow::var_channel_count( instance_t* inst, const char* label, unsigned& chCntRef ) + +cw::rc_t cw::flow::var_channel_count( instance_t* inst, const char* label, unsigned sfx_id, unsigned& chCntRef ) { rc_t rc = kOkRC; const variable_t* var= nullptr; - if((rc = var_find(inst,label,kAnyChIdx,var)) != kOkRC ) - return cwLogError(rc,"Channel count was not available because the variable '%s.%s' does not exist.",cwStringNullGuard(inst->label),cwStringNullGuard(label)); + if((rc = var_find(inst,label,sfx_id,kAnyChIdx,var)) != kOkRC ) + return cwLogError(rc,"Channel count was not available because the variable '%s:%i.%s:%i' does not exist.",cwStringNullGuard(inst->label),inst->label_sfx_id,cwStringNullGuard(label),sfx_id); return var_channel_count(var,chCntRef); } @@ -1377,9 +1397,9 @@ cw::rc_t cw::flow::var_channel_count( const variable_t* var, unsigned& chCntRef chCntRef = 0; - if((rc = var_find( var->inst, var->label, kAnyChIdx, v )) != kOkRC ) + if((rc = var_find( var->inst, var->label, var->label_sfx_id, kAnyChIdx, v )) != kOkRC ) { - rc = cwLogError(kInvalidStateRC,"The base channel variable instance could not be found for the variable '%s.%s'.",var->inst->label,var->label); + rc = cwLogError(kInvalidStateRC,"The base channel variable instance could not be found for the variable '%s:%i.%s:%i'.",var->inst->label,var->inst->label_sfx_id,var->label,var->label_sfx_id); goto errLabel; } @@ -1392,7 +1412,7 @@ cw::rc_t cw::flow::var_channel_count( const variable_t* var, unsigned& chCntRef -cw::rc_t cw::flow::var_register( instance_t* inst, const char* var_label, unsigned vid, unsigned chIdx, const object_t* value_cfg, variable_t*& varRef ) +cw::rc_t cw::flow::var_register( instance_t* inst, const char* var_label, unsigned sfx_id, unsigned vid, unsigned chIdx, const object_t* value_cfg, variable_t*& varRef ) { rc_t rc = kOkRC; variable_t* var = nullptr; @@ -1403,7 +1423,7 @@ cw::rc_t cw::flow::var_register( instance_t* inst, const char* var_label, unsign // The concatenation of 'vid' and 'chIdx' should be unique // if an exact match to label/chIdx was found - if((var = _var_find_on_label_and_ch(inst,var_label,chIdx)) != nullptr ) + if((var = _var_find_on_label_and_ch(inst,var_label,sfx_id,chIdx)) != nullptr ) { // if a value was given - then update the value if( value_cfg != nullptr ) @@ -1412,74 +1432,74 @@ cw::rc_t cw::flow::var_register( instance_t* inst, const char* var_label, unsign } else // an exact match was not found - channelize the variable { - if((rc = var_channelize(inst,var_label,chIdx,value_cfg,vid,var)) != kOkRC ) + if((rc = var_channelize(inst,var_label,sfx_id,chIdx,value_cfg,vid,var)) != kOkRC ) goto errLabel; } var->vid = vid; varRef = var; - if((var = _var_find_on_label_and_ch(inst,var_label,kAnyChIdx)) != nullptr ) + if((var = _var_find_on_label_and_ch(inst,var_label,sfx_id,kAnyChIdx)) != nullptr ) var->vid = vid; else - rc = cwLogError(kInvalidStateRC,"The variable '%s' instance '%s' has no base channel.", var_label, inst->label, chIdx); + rc = cwLogError(kInvalidStateRC,"The variable '%s:%i' instance '%s:%i' has no base channel.", var_label, sfx_id, inst->label, inst->label_sfx_id, chIdx); errLabel: if( rc != kOkRC ) - rc = cwLogError(rc,"Registration failed on variable '%s' instance '%s' ch: %i.", var_label, inst->label, chIdx); + rc = cwLogError(rc,"Registration failed on variable '%s:%i' instance '%s:%i' ch: %i.", var_label, sfx_id, inst->label, inst->label_sfx_id, chIdx); return rc; } -cw::rc_t cw::flow::var_register_and_set( instance_t* inst, const char* var_label, unsigned vid, unsigned chIdx, variable_t*& varRef ) +cw::rc_t cw::flow::var_register_and_set( instance_t* inst, const char* var_label, unsigned sfx_id, unsigned vid, unsigned chIdx, variable_t*& varRef ) { - return var_register( inst, var_label, vid, chIdx, nullptr, varRef ); + return var_register( inst, var_label, sfx_id, vid, chIdx, nullptr, varRef ); } -cw::rc_t cw::flow::var_register_and_set( instance_t* inst, const char* var_label, unsigned vid, unsigned chIdx, srate_t srate, unsigned chN, unsigned frameN ) +cw::rc_t cw::flow::var_register_and_set( instance_t* inst, const char* var_label, unsigned sfx_id, unsigned vid, unsigned chIdx, srate_t srate, unsigned chN, unsigned frameN ) { rc_t rc = kOkRC; abuf_t* abuf; if((abuf = abuf_create( srate, chN, frameN )) == nullptr ) - return cwLogError(kOpFailRC,"abuf create failed on instance:'%s' variable:'%s'.", inst->label, var_label); + return cwLogError(kOpFailRC,"abuf create failed on instance:'%s:%i' variable:'%s:%i'.", inst->label, inst->label_sfx_id, var_label,sfx_id); - if((rc = _var_register_and_set( inst, var_label, vid, chIdx, abuf )) != kOkRC ) + if((rc = _var_register_and_set( inst, var_label, sfx_id, vid, chIdx, abuf )) != kOkRC ) abuf_destroy(abuf); return rc; } -cw::rc_t cw::flow::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, const fd_real_t** phsV, const fd_real_t** hzV ) +cw::rc_t cw::flow::var_register_and_set( instance_t* inst, 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_real_t** magV, const fd_real_t** phsV, const fd_real_t** hzV ) { rc_t rc = kOkRC; fbuf_t* fbuf; if((fbuf = fbuf_create( srate, chN, maxBinN_V, binN_V, hopSmpN_V, magV, phsV, hzV )) == nullptr ) - return cwLogError(kOpFailRC,"fbuf create failed on instance:'%s' variable:'%s'.", inst->label, var_label); + return cwLogError(kOpFailRC,"fbuf create failed on instance:'%s:%i' variable:'%s:%i'.", inst->label, inst->label_sfx_id, var_label,sfx_id); - if((rc = _var_register_and_set( inst, var_label, vid, chIdx, fbuf )) != kOkRC ) + if((rc = _var_register_and_set( inst, var_label, sfx_id, vid, chIdx, fbuf )) != kOkRC ) fbuf_destroy(fbuf); return rc; } -cw::rc_t cw::flow::var_register_and_set( instance_t* inst, const char* var_label, unsigned vid, unsigned chIdx, midi::ch_msg_t* msgA, unsigned msgN ) +cw::rc_t cw::flow::var_register_and_set( instance_t* inst, const char* var_label, unsigned sfx_id, unsigned vid, unsigned chIdx, midi::ch_msg_t* msgA, unsigned msgN ) { rc_t rc = kOkRC; mbuf_t* mbuf; if((mbuf = mbuf_create(msgA,msgN)) == nullptr ) - return cwLogError(kOpFailRC,"mbuf create failed on instance:'%s' variable:'%s'.", inst->label, var_label); + return cwLogError(kOpFailRC,"mbuf create failed on instance:'%s:%i' variable:'%s:%i'.", inst->label, inst->label_sfx_id, var_label, sfx_id); - if((rc = _var_register_and_set( inst, var_label, vid, chIdx, mbuf )) != kOkRC ) + if((rc = _var_register_and_set( inst, var_label, sfx_id, vid, chIdx, mbuf )) != kOkRC ) mbuf_destroy(mbuf); return rc; } -cw::rc_t cw::flow::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, const fd_real_t** phsV, const fd_real_t** hzV ) +cw::rc_t cw::flow::var_register_and_set( instance_t* inst, const char* var_label, unsigned sfx_id, unsigned vid, unsigned chIdx, srate_t srate, unsigned chN, unsigned maxBinN, unsigned binN, unsigned hopSmpN, const fd_real_t** magV, const fd_real_t** phsV, const fd_real_t** hzV ) { unsigned maxBinN_V[ chN ]; unsigned binN_V[ chN ]; @@ -1487,7 +1507,7 @@ cw::rc_t cw::flow::var_register_and_set( instance_t* inst, const char* var_label vop::fill(maxBinN_V,chN,maxBinN); vop::fill(binN_V,chN,binN); vop::fill(hopSmpN_V,chN, hopSmpN ); - return var_register_and_set(inst,var_label,vid,chIdx,srate, chN, maxBinN_V, binN_V, hopSmpN_V, magV, phsV, hzV); + return var_register_and_set(inst,var_label,sfx_id,vid,chIdx,srate, chN, maxBinN_V, binN_V, hopSmpN_V, magV, phsV, hzV); } diff --git a/cwFlowTypes.h b/cwFlowTypes.h index 0fe4ce5..e111842 100644 --- a/cwFlowTypes.h +++ b/cwFlowTypes.h @@ -15,6 +15,7 @@ namespace cw typedef unsigned vid_t; enum { + kBaseSfxId = 0, kFbufVectN = 3, // count of signal vectors in fbuf (mag,phs,hz) kAnyChIdx = kInvalidIdx, kLocalValueN = 2 @@ -127,8 +128,10 @@ namespace cw enum { + kNoVarFl = 0x00, kSrcVarFl = 0x01, - kSrcOptVarFl = 0x02 + kSrcOptVarFl = 0x02, + kMultVarFl = 0x04 }; typedef struct class_members_str @@ -165,6 +168,7 @@ namespace cw var_desc_t* varDescL; // varDescA[varDescN] value description list preset_t* presetL; // presetA[ presetN ] 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 } class_desc_t; @@ -174,6 +178,7 @@ namespace cw { struct instance_str* inst; // pointer to this variables instance char* label; // this variables label + unsigned label_sfx_id; // the label suffix id of this variable or kInvalidIdx if this has no suffix 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 value_t local_value[ kLocalValueN ]; // the local value instance (actual value if this is not a 'src' variable) @@ -187,15 +192,33 @@ namespace cw } variable_t; + struct instance_str; + + typedef struct network_str + { + const object_t* procsCfg; // network proc list + const object_t* presetsCfg; // presets designed for this network + + unsigned poly_cnt; // count of duplicated networks in the list + + struct instance_str** proc_array; + unsigned proc_arrayAllocN; + unsigned proc_arrayN; + + } network_t; + typedef struct instance_str { struct flow_str* ctx; // global system context + network_t* 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 object_t* proc_cfg; // instance configuration + const char* arg_label; // optional args label const object_t* arg_cfg; // optional args configuration @@ -207,17 +230,16 @@ namespace cw unsigned varMapIdN; // max 'vid' among all variables on this instance unsigned varMapN; // varMapN = varMapIdN * varMapChN variable_t** varMapA; // varMapA[ varMapN ] = allows fast lookup from ('vid','chIdx) to variable + + network_t* internal_net; - struct instance_str* link; } instance_t; - typedef struct flow_str { - const object_t* networkCfg; // complete cfg used to create this network + const object_t* flowCfg; // complete cfg used to create this flow - const object_t* presetCfg; // presets designed for this network unsigned framesPerCycle; // sample frames per cycle (64) bool multiPriPresetProbFl; // If set then probability is used to choose presets on multi-preset application @@ -232,8 +254,8 @@ namespace cw 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 + network_t net; + } flow_t; //------------------------------------------------------------------------------------------------------------------------ @@ -278,15 +300,16 @@ namespace cw // // Network // - void network_print( flow_t* p ); + void network_print(const network_t& net ); //------------------------------------------------------------------------------------------------------------------------ // // Instance // - 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 ); + instance_t* instance_find( network_t& net, const char* inst_label, unsigned sfx_id ); + rc_t instance_find( network_t& net, const char* inst_label, unsigned sfx_id, instance_t*& instPtrRef ); + external_device_t* external_device_find( flow_t* p, const char* device_label, unsigned typeId, unsigned inOrOutFl, const char* midiPortLabel=nullptr ); void instance_print( instance_t* inst ); @@ -300,14 +323,14 @@ 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 ); + rc_t var_create( instance_t* inst, const char* var_label, unsigned sfx_id, unsigned vid, unsigned chIdx, const object_t* value_cfg, variable_t*& varRef ); // 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 ); + rc_t var_channelize( instance_t* inst, const char* var_label, unsigned sfx_id, unsigned chIdx, const object_t* value_cfg, unsigned vid, variable_t*& varRef ); // `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( instance_t* inst, 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 ); @@ -320,11 +343,11 @@ namespace cw inline rc_t _var_reg(cw::flow::instance_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( instance_t* inst, 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_register( inst, var_label, sfx_id, vid, chIdx, nullptr, dummy )) == kOkRC ) if((rc = _var_reg( inst, chIdx, std::forward(args)...)) != kOkRC ) return rc; return rc; @@ -332,8 +355,8 @@ namespace cw // 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)...); } + rc_t var_register( instance_t* inst, unsigned chIdx, unsigned vid, const char* var_label, unsigned sfx_id, ARGS&&... args ) + { return _var_reg( inst, chIdx, vid, var_label, sfx_id, std::forward(args)...); } @@ -345,11 +368,11 @@ namespace cw inline rc_t _var_register_and_get(cw::flow::instance_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( instance_t* inst, 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(inst,var_label,sfx_id,vid,chIdx,nullptr,var)) == kOkRC ) rc = var_get(var,valRef); return rc; } @@ -358,11 +381,11 @@ namespace cw inline rc_t _var_reg_and_get(cw::flow::instance_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( instance_t* inst, 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_register_and_get( inst, var_label, sfx_id, vid, chIdx, valRef )) == kOkRC ) if((rc = _var_reg_and_get( inst, chIdx, std::forward(args)...)) != kOkRC ) return rc; @@ -371,8 +394,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)...); } + rc_t var_register_and_get( instance_t* inst, unsigned chIdx, unsigned vid, const char* var_label, unsigned sfx_id, ARGS&&... args ) + { return _var_reg_and_get( inst, chIdx, vid, var_label, sfx_id, std::forward(args)...); } @@ -384,22 +407,22 @@ 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( instance_t* inst, 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, midi::ch_msg_t* midiA, unsigned midiN ); - 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( instance_t* inst, const char* var_label, unsigned sfx_id, 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 sfx_id, unsigned vid, unsigned chIdx, midi::ch_msg_t* midiA, unsigned midiN ); + rc_t var_register_and_set( instance_t* inst, 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_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 sfx_id, 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 ); inline rc_t _var_register_and_set(cw::flow::instance_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( instance_t* inst, 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( inst, var_label, sfx_id, vid, chIdx, var)) == kOkRC ) { var_set( inst, vid, chIdx, val ); @@ -412,22 +435,23 @@ 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)...); } + rc_t var_register_and_set( instance_t* inst, unsigned chIdx, unsigned vid, const char* var_label, unsigned sfx_id, ARGS&&... args ) + { return _var_register_and_set( inst, chIdx, vid, var_label, sfx_id, std::forward(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( instance_t* inst, const char* label, unsigned sfx_id, unsigned chIdx ); + bool var_has_value( instance_t* inst, const char* label, unsigned sfx_id, unsigned chIdx ); - 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 ); + rc_t var_find( instance_t* inst, const char* var_label, unsigned sfx_id, unsigned chIdx, const variable_t*& varRef ); + rc_t var_find( instance_t* inst, const char* var_label, unsigned sfx_id, 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( instance_t* inst, const char* label, unsigned sfx_idx, unsigned& chCntRef ); rc_t var_channel_count( const variable_t* var, unsigned& chCntRef ); diff --git a/cwPresetSel.cpp b/cwPresetSel.cpp index 0f3a420..2e07769 100644 --- a/cwPresetSel.cpp +++ b/cwPresetSel.cpp @@ -6,11 +6,12 @@ #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" From 78ca1b3e357e3584468940d6d395c840c441b211 Mon Sep 17 00:00:00 2001 From: kevin Date: Mon, 22 Apr 2024 16:02:53 -0400 Subject: [PATCH 024/101] README.md : Updates --- README.md | 381 +++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 366 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 2a8f27c..4a0dae4 100644 --- a/README.md +++ b/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,21 @@ 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. + +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. Note that all vars must be included in the class description. -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 +494,23 @@ 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 'args' record may have multiple sets of args. +If the preset instance includes an 'argsLabel' value then this record +is selected to be applied. If No 'argsLabel' is given then +the record named 'default' is selected. If there is no record +named 'default' then no record is applied. + +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. @@ -520,5 +531,345 @@ access to registered variables. +# Notes on 'poly' and 'mult': + +The 'in' statement is formed by a list of _Connect Expressions_ : + +`:` + +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-var-id`":" `src_expr` +`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-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 no "_" is 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 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 + +Var Updates and Preset Application +================================== + +Variable addresses are formed from the following parameters: +`()*,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 `` without +asking for it at runtime. For example for the cross-fader control the +application must ask for the `` 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: + +- Documentation w/ examples. + +- Compile presets: at load time the presets should be resolved + to the proc and vars to which they will be assigned. + +- 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? + +- 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. + +- '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. + +- 'poly' and 'sub' should be arbitrarily nestable. + + +- Reduce runtime over head for var get/set operations. From e42dd71b09337f3d2436d1c5789d8e824a8d7b3a Mon Sep 17 00:00:00 2001 From: kevin Date: Fri, 26 Apr 2024 16:58:01 -0400 Subject: [PATCH 025/101] cwLex.h/cpp : test() now returns an rc_t. Added test for use of floating point suffix 'f'. --- cwLex.cpp | 20 +++++++++++--------- cwLex.h | 2 +- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/cwLex.cpp b/cwLex.cpp index 5eca99b..da73cc2 100644 --- a/cwLex.cpp +++ b/cwLex.cpp @@ -853,7 +853,7 @@ namespace cw //) //( - void test() + rc_t test() { rc_t rc = kOkRC; unsigned tid = kInvalidId; @@ -866,14 +866,16 @@ namespace cw "/* block \n" "comment */" "\"quoted string\"" - "ident1" + "ident1 " + "1234.56f" + "345u" " // last line comment"; // initialize a lexer with a buffer of text if((rc = lex::create(h,buf,strlen(buf), kReturnSpaceLexFl | kReturnCommentsLexFl)) != kOkRC ) { - cwLogError(rc,"Lexer initialization failed."); - return; + rc = cwLogError(rc,"Lexer initialization failed."); + goto errLabel; } // register some additional recoginizers @@ -884,7 +886,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 +901,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 ) { @@ -912,10 +912,12 @@ namespace cw } } - + // finalize the lexer lex::destroy(h); + errLabel: + return rc; } } } diff --git a/cwLex.h b/cwLex.h index 2f25b77..c77aafd 100644 --- a/cwLex.h +++ b/cwLex.h @@ -129,7 +129,7 @@ namespace cw const char* idToLabel( handle_t h, unsigned typeId ); // Lexer testing stub. - void test( ); + rc_t test( ); } } From a7a38fa9f4862ed4b9efe240cf7b12ca369adcf3 Mon Sep 17 00:00:00 2001 From: kevin Date: Fri, 26 Apr 2024 16:59:40 -0400 Subject: [PATCH 026/101] cwObject.h/cpp : cwObject now detects and properly encodes floats and unsigned integers that are specified with the 'f' and 'u' suffix. --- cwObject.cpp | 10 ++++++++-- cwObjectTemplate.h | 6 ++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/cwObject.cpp b/cwObject.cpp index 6fabd0b..60682f8 100644 --- a/cwObject.cpp +++ b/cwObject.cpp @@ -869,11 +869,17 @@ 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: - _objCreateValueNode( cnp, lex::tokenInt(lexH), "int" ); + if( tokenIsUnsigned(lexH) ) + _objCreateValueNode( cnp, lex::tokenUInt(lexH), "uint" ); + else + _objCreateValueNode( cnp, lex::tokenInt(lexH), "int" ); break; case lex::kHexLexTId: diff --git a/cwObjectTemplate.h b/cwObjectTemplate.h index 81027b4..7fca3ec 100644 --- a/cwObjectTemplate.h +++ b/cwObjectTemplate.h @@ -74,6 +74,12 @@ namespace cw return obj; } + template<> object_t* _objSetLeafValue( object_t* obj, float value ) + { + obj->u.f = value; + obj->type = _objIdToType(kFloatTId); + return obj; + } template<> object_t* _objSetLeafValue( object_t* obj, double value ) { From a0b4e9c1205aa51d261183894eef9634c126a47d Mon Sep 17 00:00:00 2001 From: kevin Date: Fri, 26 Apr 2024 17:00:58 -0400 Subject: [PATCH 027/101] cwDspTypes.h,cwDspTransforms.h/cpp : Removed the 'real_t' and replaced it with more specific types which identify how the type is being used (e.g. coeff_t, ftime_t). --- cwDspTransforms.cpp | 32 ++++++++++----------- cwDspTransforms.h | 68 ++++++++++++++++++++++----------------------- cwDspTypes.h | 5 ++-- 3 files changed, 53 insertions(+), 52 deletions(-) diff --git a/cwDspTransforms.cpp b/cwDspTransforms.cpp index 801fea8..2195964 100644 --- a/cwDspTransforms.cpp +++ b/cwDspTransforms.cpp @@ -21,9 +21,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 +33,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(); @@ -105,8 +105,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 +147,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 +170,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(); @@ -196,7 +196,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(); @@ -255,12 +255,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(y,n,x,n,p->b0, p->b, p->a, p->d, 1 ); + vop::filter(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 +272,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(); p->srate = srate; @@ -383,7 +383,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 +475,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); diff --git a/cwDspTransforms.h b/cwDspTransforms.h index 0d361bc..4248c0e 100644 --- a/cwDspTransforms.h +++ b/cwDspTransforms.h @@ -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 ); } } diff --git a/cwDspTypes.h b/cwDspTypes.h index 9a03f69..e8cc1ab 100644 --- a/cwDspTypes.h +++ b/cwDspTypes.h @@ -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 From bdcb26ac726fd4f5d9a6369b20a65f8850708acf Mon Sep 17 00:00:00 2001 From: kevin Date: Fri, 26 Apr 2024 17:04:03 -0400 Subject: [PATCH 028/101] cwFlowTypes.h : Changes to account for changed dsp::types. Added is_connected_to_source_proc(), var_is_source() and is_source_var(). --- cwFlowTypes.cpp | 91 +++++++++++++++++++++++++++++++++++-------------- cwFlowTypes.h | 84 ++++++++++++++++++++++++--------------------- 2 files changed, 111 insertions(+), 64 deletions(-) diff --git a/cwFlowTypes.cpp b/cwFlowTypes.cpp index 0788edc..eada7df 100644 --- a/cwFlowTypes.cpp +++ b/cwFlowTypes.cpp @@ -24,7 +24,6 @@ namespace cw { kUIntTFl, "uint" }, { kIntTFl, "int", }, { kFloatTFl, "float"}, - { kRealTFl, "real"}, { kDoubleTFl,"double"}, { kBoolMtxTFl, "bool_mtx" }, @@ -38,6 +37,14 @@ namespace cw { kMBufTFl, "midi" }, { kStringTFl, "string" }, { kTimeTFl, "time" }, + { kCfgTFl, "cfg" }, + + // alias types to map to cwDspTypes.h + { kFloatTFl, "srate"}, + { kFloatTFl, "sample"}, + { kFloatTFl, "coeff"}, + { kDoubleTFl, "ftime" }, + { kInvalidTFl, nullptr } }; @@ -461,12 +468,18 @@ namespace cw rc_t _var_broadcast_new_value( variable_t* var ) { rc_t rc = kOkRC; - /* + // notify each connected var that the value has changed for(variable_t* con_var = var->connect_link; con_var!=nullptr; con_var=con_var->connect_link) - if((rc = con_var->inst->class_desc->members->value( con_var->inst, con_var )) != kOkRC ) - break; - */ + { + // the var->local_value[] slot used by the source variable may have changed - update the destination variable + // so that it points to the correct value. + con_var->value = var->value; + + //if((rc = con_var->inst->class_desc->members->value( con_var->inst, con_var )) != kOkRC ) + // break; + + } return rc; } @@ -548,7 +561,7 @@ namespace cw var->local_value[ local_value_idx ].flags = kFBufTFl; cwLogMod("%s:%i.%s:%i ch:%i %s (fbuf).",var->inst->label,var->inst->label_sfx_id,var->label,var->label_sfx_id,var->chIdx,fbuf==nullptr ? "null" : "valid"); } - + template< typename T > rc_t _var_set_template( variable_t* var, unsigned typeFlag, T val ) { @@ -604,7 +617,7 @@ namespace cw } - bool is_connected_to_external_proc( const variable_t* var ) + bool is_connected_to_source_proc( const variable_t* var ) { // if this var does not have a 'src_ptr' then it can't be connected to an external proc if( var->src_var == nullptr || var->value == nullptr ) @@ -618,13 +631,16 @@ namespace cw return true; } + bool is_a_source_var( const variable_t* var ) + { return var->connect_link != nullptr; } + template< typename T > rc_t _var_set_driver( variable_t* var, unsigned typeFlag, T value ) { rc_t rc; // if this variable is fed from the output of an external proc - then it's local value cannot be set - if(is_connected_to_external_proc(var) ) + if(is_connected_to_source_proc(var) ) return kOkRC; @@ -687,6 +703,7 @@ namespace cw { rc_t rc = kOkRC; + // get the variable type - note that the value type (value->flags) may be differnt unsigned typeFlag = var->varDesc->type & kTypeMask; switch( typeFlag ) @@ -882,7 +899,7 @@ namespace cw void _var_print( const variable_t* var ) { - const char* conn_label = is_connected_to_external_proc(var) ? "extern" : " "; + const char* conn_label = is_connected_to_source_proc(var) ? "extern" : " "; printf(" %20s:%5i id:%4i ch:%3i : %s : ", var->label, var->label_sfx_id, var->vid, var->chIdx, conn_label ); @@ -969,7 +986,7 @@ const cw::flow::sample_t* cw::flow::abuf_get_channel( abuf_t* abuf, unsigned c return abuf->buf + (chIdx*abuf->frameN); } -cw::flow::fbuf_t* cw::flow::fbuf_create( srate_t srate, unsigned chN, const unsigned* maxBinN_V, const unsigned* binN_V, const unsigned* hopSmpN_V, const fd_real_t** magV, const fd_real_t** phsV, const fd_real_t** hzV ) +cw::flow::fbuf_t* cw::flow::fbuf_create( srate_t srate, unsigned chN, const unsigned* maxBinN_V, const unsigned* binN_V, const unsigned* hopSmpN_V, const fd_sample_t** magV, const fd_sample_t** phsV, const fd_sample_t** hzV ) { for(unsigned i=0; i maxBinN_V[i] ) @@ -985,9 +1002,9 @@ cw::flow::fbuf_t* cw::flow::fbuf_create( srate_t srate, unsigned chN, const unsi f->maxBinN_V = mem::allocZ(chN); f->binN_V = mem::allocZ(chN); f->hopSmpN_V = mem::allocZ(chN); - f->magV = mem::allocZ(chN); - f->phsV = mem::allocZ(chN); - f->hzV = mem::allocZ(chN); + f->magV = mem::allocZ(chN); + f->phsV = mem::allocZ(chN); + f->hzV = mem::allocZ(chN); f->readyFlV = mem::allocZ(chN); vop::copy( f->binN_V, binN_V, chN ); @@ -998,17 +1015,17 @@ cw::flow::fbuf_t* cw::flow::fbuf_create( srate_t srate, unsigned chN, const unsi { for(unsigned chIdx=0; chIdxmagV[ chIdx ] = (fd_real_t*)magV[chIdx]; - f->phsV[ chIdx ] = (fd_real_t*)phsV[chIdx]; - f->hzV[ chIdx ] = (fd_real_t*)hzV[chIdx]; + f->magV[ chIdx ] = (fd_sample_t*)magV[chIdx]; + f->phsV[ chIdx ] = (fd_sample_t*)phsV[chIdx]; + f->hzV[ chIdx ] = (fd_sample_t*)hzV[chIdx]; } } else { unsigned maxTotalBinsN = vop::sum( maxBinN_V, chN ); - fd_real_t* buf = mem::allocZ( kFbufVectN * maxTotalBinsN ); - fd_real_t* m = buf; + fd_sample_t* buf = mem::allocZ( kFbufVectN * maxTotalBinsN ); + fd_sample_t* m = buf; for(unsigned chIdx=0; chIdxmagV[chIdx] = m + 0 * f->binN_V[chIdx]; @@ -1026,7 +1043,7 @@ cw::flow::fbuf_t* cw::flow::fbuf_create( srate_t srate, unsigned chN, const unsi } -cw::flow::fbuf_t* cw::flow::fbuf_create( srate_t srate, unsigned chN, unsigned maxBinN, unsigned binN, unsigned hopSmpN, const fd_real_t** magV, const fd_real_t** phsV, const fd_real_t** hzV ) +cw::flow::fbuf_t* cw::flow::fbuf_create( srate_t srate, unsigned chN, unsigned maxBinN, unsigned binN, unsigned hopSmpN, const fd_sample_t** magV, const fd_sample_t** phsV, const fd_sample_t** hzV ) { unsigned maxBinN_V[ chN ]; unsigned binN_V[ chN ]; @@ -1249,12 +1266,10 @@ cw::rc_t cw::flow::var_channelize( instance_t* inst, const char* var_label, uns rc = cwLogError(kInvalidStateRC,"The base ('any') channel variable could not be located on '%s:%i.%s:%i'.",inst->label,inst->label_sfx_id,var_label,sfx_id); goto errLabel; } - - + // locate the variable with the stated chIdx var = _var_find_on_label_and_ch( inst, var_label, sfx_id, chIdx ); - // 'src' variables cannot be channelized if( cwIsFlag(base_var->varDesc->flags,kSrcVarFl) ) { @@ -1287,6 +1302,7 @@ cw::rc_t cw::flow::var_channelize( instance_t* inst, const char* var_label, uns // a correctly channelized var was found - but we still may need to set the value if( value_cfg != nullptr ) { + //cwLogInfo("%s ch:%i",var_label,chIdx); rc = _set_var_value_from_cfg( var, value_cfg ); } else @@ -1300,7 +1316,7 @@ cw::rc_t cw::flow::var_channelize( instance_t* inst, const char* var_label, uns errLabel: if( rc != kOkRC ) - rc = cwLogError(rc,"Channelize failed for variable '%s:%i' on instance '%s:i' ch:%i.", var_label, sfx_id, inst->label, inst->label_sfx_id, chIdx ); + rc = cwLogError(rc,"Channelize failed for variable '%s:%i' on instance '%s:%i' ch:%i.", var_label, sfx_id, inst->label, inst->label_sfx_id, chIdx ); return rc; } @@ -1319,6 +1335,31 @@ bool cw::flow::var_has_value( instance_t* inst, const char* label, unsigned sfx_ return varPtr->value != nullptr; } +bool cw::flow::var_is_a_source( instance_t* inst, const char* label, unsigned sfx_id, unsigned chIdx ) +{ + rc_t rc; + variable_t* varPtr = nullptr; + if((rc = var_find( inst, label, sfx_id, chIdx, varPtr)) != kOkRC ) + { + cwLogError(kEleNotFoundRC,"The variable '%s:%i' was not found on proc:'%s:%i'. 'source' state query is invalid.",cwStringNullGuard(label),sfx_id,cwStringNullGuard(inst->label),inst->label_sfx_id); + return false; + } + + return is_a_source_var(varPtr); +} + +bool cw::flow::var_is_a_source( instance_t* inst, unsigned vid, unsigned chIdx ) +{ + rc_t rc; + variable_t* varPtr = nullptr; + if((rc = var_find( inst, vid, chIdx, varPtr)) != kOkRC ) + { + cwLogError(kEleNotFoundRC,"The variable with vid '%i' was not found on proc:'%s:%i'. 'source' state query is invalid.",vid,cwStringNullGuard(inst->label),inst->label_sfx_id); + return false; + } + + return is_a_source_var(varPtr); +} cw::rc_t cw::flow::var_find( instance_t* inst, unsigned vid, unsigned chIdx, variable_t*& varRef ) { @@ -1471,7 +1512,7 @@ cw::rc_t cw::flow::var_register_and_set( instance_t* inst, const char* va return rc; } -cw::rc_t cw::flow::var_register_and_set( instance_t* inst, 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_real_t** magV, const fd_real_t** phsV, const fd_real_t** hzV ) +cw::rc_t cw::flow::var_register_and_set( instance_t* inst, 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, const fd_sample_t** phsV, const fd_sample_t** hzV ) { rc_t rc = kOkRC; fbuf_t* fbuf; @@ -1499,7 +1540,7 @@ cw::rc_t cw::flow::var_register_and_set( instance_t* inst, const char* va } -cw::rc_t cw::flow::var_register_and_set( instance_t* inst, const char* var_label, unsigned sfx_id, unsigned vid, unsigned chIdx, srate_t srate, unsigned chN, unsigned maxBinN, unsigned binN, unsigned hopSmpN, const fd_real_t** magV, const fd_real_t** phsV, const fd_real_t** hzV ) +cw::rc_t cw::flow::var_register_and_set( instance_t* inst, 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, const fd_sample_t** phsV, const fd_sample_t** hzV ) { unsigned maxBinN_V[ chN ]; unsigned binN_V[ chN ]; diff --git a/cwFlowTypes.h b/cwFlowTypes.h index e111842..a0ae0c7 100644 --- a/cwFlowTypes.h +++ b/cwFlowTypes.h @@ -3,13 +3,13 @@ namespace cw namespace flow { - #define kRealTFl kFloatTFl - typedef dsp::real_t real_t; - typedef dsp::sample_t sample_t; - typedef dsp::fd_real_t fd_real_t; - typedef dsp::srate_t srate_t; - typedef unsigned uint_t; - typedef int int_t; + typedef dsp::coeff_t coeff_t; + typedef dsp::sample_t sample_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; @@ -40,11 +40,11 @@ namespace cw unsigned* maxBinN_V; // 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) + fd_sample_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 @@ -66,15 +66,15 @@ namespace cw kBoolMtxTFl = 0x00000020, kUIntMtxTFl = 0x00000040, kIntMtxTFl = 0x00000080, - kRealMtxTFl = 0x00000100, - kFloatMtxTFl = 0x00000200, - kDoubleMtxTFl= 0x00000400, + kFloatMtxTFl = 0x00000100, + kDoubleMtxTFl= 0x00000200, - kABufTFl = 0x00000800, - kFBufTFl = 0x00001000, - kMBufTFl = 0x00002000, - kStringTFl = 0x00004000, - kTimeTFl = 0x00008000, + kABufTFl = 0x00000400, + kFBufTFl = 0x00000800, + kMBufTFl = 0x00001000, + kStringTFl = 0x00002000, + kTimeTFl = 0x00004000, + kCfgTFl = 0x00008000, kTypeMask = 0x0000ffff, @@ -85,7 +85,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; @@ -95,20 +94,20 @@ namespace cw { unsigned flags; union { - bool b; - uint_t u; - int_t i; - float f; - double d; - - mtx_t* mtx; + bool b; + uint_t u; + int_t i; + float f; + double d; - abuf_t* abuf; - fbuf_t* fbuf; - mbuf_t* mbuf; + mtx_t* mtx; + abuf_t* abuf; + fbuf_t* fbuf; + mbuf_t* mbuf; - char* s; - char* fname; + char* s; + + const object_t* cfg; } u; @@ -269,8 +268,8 @@ namespace cw 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 ); @@ -332,8 +331,12 @@ namespace cw // `value_cfg` is optional. Set it to NULL to ignore rc_t var_register( instance_t* inst, 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 an source proc variable + bool is_connected_to_source_proc( 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 ); + //----------------- // @@ -411,8 +414,8 @@ namespace cw rc_t var_register_and_set( instance_t* inst, const char* var_label, unsigned sfx_id, 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 sfx_id, unsigned vid, unsigned chIdx, midi::ch_msg_t* midiA, unsigned midiN ); - rc_t var_register_and_set( instance_t* inst, 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_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 sfx_id, 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( instance_t* inst, 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( instance_t* inst, 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; } @@ -424,7 +427,8 @@ namespace cw variable_t* var = nullptr; if((rc = var_register_and_set( inst, var_label, sfx_id, vid, chIdx, var)) == kOkRC ) { - var_set( inst, vid, chIdx, val ); + if((rc = var_set( inst, vid, chIdx, val )) != kOkRC ) + return rc; if((rc = _var_register_and_set( inst, chIdx, std::forward(args)...)) != kOkRC ) return rc; @@ -444,6 +448,8 @@ namespace cw bool var_exists( instance_t* inst, const char* label, unsigned sfx_id, unsigned chIdx ); bool var_has_value( instance_t* inst, const char* label, unsigned sfx_id, unsigned chIdx ); + bool var_is_a_source( instance_t* inst, const char* label, unsigned sfx_id, unsigned chIdx ); + bool var_is_a_source( instance_t* inst, unsigned vid, unsigned chIdx ); rc_t var_find( instance_t* inst, const char* var_label, unsigned sfx_id, unsigned chIdx, const variable_t*& varRef ); rc_t var_find( instance_t* inst, const char* var_label, unsigned sfx_id, unsigned chIdx, variable_t*& varRef ); From b4529aeb3fd46a1c0a4c5f1c44506ef309ea5c06 Mon Sep 17 00:00:00 2001 From: kevin Date: Fri, 26 Apr 2024 17:05:48 -0400 Subject: [PATCH 029/101] cwFlow.h.cpp : test() now takes a command line argument to select a particular test case. 'multi???Preset???Fl' in network cfg. are now optional. --- cwFlow.cpp | 37 +++++++++++++++++++++++++++++-------- cwFlow.h | 2 +- 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/cwFlow.cpp b/cwFlow.cpp index ceea17f..cdea341 100644 --- a/cwFlow.cpp +++ b/cwFlow.cpp @@ -9,7 +9,7 @@ #include "cwAudioFile.h" #include "cwVectOps.h" #include "cwMtx.h" -#include "cwDspTypes.h" // real_t, sample_t +#include "cwDspTypes.h" // coeff_t, sample_t, srate_t ... #include "cwTime.h" #include "cwMidiDecls.h" #include "cwFlowDecl.h" @@ -54,6 +54,10 @@ namespace cw { "audio_marker", &audio_marker::members }, { "xfade_ctl", &xfade_ctl::members }, { "poly_mixer", &poly_mixer::members }, + { "sample_hold", &sample_hold::members }, + { "number", &number::members }, + { "timer", &timer::members }, + { "counter", &counter::members }, { nullptr, nullptr } }; @@ -197,7 +201,7 @@ namespace cw // convert the type string to a numeric type flag if( (type_flag = value_type_label_to_flag( type_str )) == kInvalidTId ) { - rc = cwLogError(rc,"Invalid type flag: '%s' class:'%s' value:'%s'.", type_str, cd->label, vd->label ); + rc = cwLogError(kSyntaxErrorRC,"Invalid type flag: '%s' class:'%s' value:'%s'.", type_str, cd->label, vd->label ); goto errLabel; } @@ -400,9 +404,6 @@ cw::rc_t cw::flow::create( handle_t& hRef, // parse the main audio file processor cfg record if((rc = flowCfg.getv("framesPerCycle", p->framesPerCycle, - "multiPriPresetProbFl", p->multiPriPresetProbFl, - "multiSecPresetProbFl", p->multiSecPresetProbFl, - "multiPresetInterpFl", p->multiPresetInterpFl, "network", networkCfg)) != kOkRC ) { rc = cwLogError(kSyntaxErrorRC,"Error parsing the required flow configuration parameters."); @@ -411,6 +412,9 @@ cw::rc_t cw::flow::create( handle_t& hRef, // parse the optional args if((rc = flowCfg.getv_opt("maxCycleCount", p->maxCycleCount, + "multiPriPresetProbFl", p->multiPriPresetProbFl, + "multiSecPresetProbFl", p->multiSecPresetProbFl, + "multiPresetInterpFl", p->multiPresetInterpFl, "printClassDictFl", printClassDictFl, "printNetworkFl", printNetworkFl)) != kOkRC ) { @@ -591,20 +595,37 @@ void cw::flow::print_network( handle_t h ) } -cw::rc_t cw::flow::test( const object_t* cfg ) +cw::rc_t cw::flow::test( const object_t* cfg, int argc, const char* argv[] ) { rc_t rc = kOkRC; handle_t flowH; object_t* class_cfg = nullptr; + const object_t* test_cases_cfg = nullptr; + const object_t* test_cfg = nullptr; const char* flow_proc_fname; + + if( argc < 2 || textLength(argv[1]) == 0 ) + { + rc = cwLogError(kInvalidArgRC,"No 'test-case' label was given on the command line."); + goto errLabel; + } - if((rc = cfg->getv("flow_proc_fname",flow_proc_fname)) != kOkRC ) + if((rc = cfg->getv("flow_proc_fname",flow_proc_fname, + "test_cases",test_cases_cfg)) != kOkRC ) { rc = cwLogError(rc,"The name of the flow_proc_dict file could not be parsed."); goto errLabel; } + // find the user requested test case + if((test_cfg = test_cases_cfg->find_child(argv[1])) == nullptr ) + { + rc = cwLogError(kInvalidArgRC,"The test case named '%s' was not found.",argv[0]); + goto errLabel; + } + + // parse the flow-proc-cfg if((rc = objectFromFile(flow_proc_fname,class_cfg)) != kOkRC ) { rc = cwLogError(rc,"The flow proc dict could not be read from '%s'.",cwStringNullGuard(flow_proc_fname)); @@ -612,7 +633,7 @@ cw::rc_t cw::flow::test( const object_t* cfg ) } // create the flow object - if((rc = create( flowH, *class_cfg, *cfg)) != kOkRC ) + if((rc = create( flowH, *class_cfg, *test_cfg)) != kOkRC ) { rc = cwLogError(rc,"Flow object create failed."); goto errLabel; diff --git a/cwFlow.h b/cwFlow.h index 51a767c..722775a 100644 --- a/cwFlow.h +++ b/cwFlow.h @@ -50,7 +50,7 @@ namespace cw void print_class_list( handle_t h ); void print_network( handle_t h ); - rc_t test( const object_t* cfg ); + rc_t test( const object_t* cfg, int argc, const char* argv[] ); From fc8b806d97ecb41c505f766b27b20f9d5567674f Mon Sep 17 00:00:00 2001 From: kevin Date: Fri, 26 Apr 2024 17:09:10 -0400 Subject: [PATCH 030/101] cwFlowProc.h/cpp : Added 'sample_hold','number','timer','counter'. --- cwFlowProc.cpp | 799 +++++++++++++++++++++++++++++++++++++++++++++---- cwFlowProc.h | 5 + 2 files changed, 753 insertions(+), 51 deletions(-) diff --git a/cwFlowProc.cpp b/cwFlowProc.cpp index bb23ca1..b55993c 100644 --- a/cwFlowProc.cpp +++ b/cwFlowProc.cpp @@ -8,7 +8,7 @@ #include "cwVectOps.h" #include "cwMtx.h" -#include "cwDspTypes.h" // real_t, sample_t +#include "cwDspTypes.h" // srate_t, sample_t, coeff_t, ... #include "cwTime.h" #include "cwMidiDecls.h" @@ -43,40 +43,40 @@ namespace cw } inst_t; - rc_t create( instance_t* ctx ) + rc_t create( instance_t* proc ) { rc_t rc = kOkRC; - inst_t* inst = mem::allocZ(); - ctx->userPtr = inst; + inst_t* p = mem::allocZ(); + proc->userPtr = p; // Custom create code goes here return rc; } - rc_t destroy( instance_t* ctx ) + rc_t destroy( instance_t* proc ) { rc_t rc = kOkRC; - inst_t* inst = (inst_t*)ctx->userPtr; + inst_t* p = (inst_t*)proc->userPtr; // Custom clean-up code goes here - mem::release(inst); + mem::release(p); return rc; } - rc_t value( instance_t* ctx, variable_t* var ) + rc_t value( instance_t* proc, variable_t* var ) { rc_t rc = kOkRC; return rc; } - rc_t exec( instance_t* ctx ) + rc_t exec( instance_t* proc ) { - rc_t rc = kOkRC; - //inst_t* inst = (inst_t*)ctx->userPtr; + rc_t rc = kOkRC; + //inst_t* p = (inst_t*)proc->userPtr; return rc; } @@ -91,6 +91,7 @@ namespace cw } + //------------------------------------------------------------------------------------------------------------------ // // poly @@ -219,14 +220,14 @@ namespace cw typedef struct { - real_t value; + coeff_t value; } inst_t; rc_t create( instance_t* ctx ) { rc_t rc = kOkRC; - real_t in_value = 0.5; + coeff_t in_value = 0.5; ctx->userPtr = mem::allocZ(); if((rc = var_register_and_get( ctx, kAnyChIdx, kInPId, "in", kBaseSfxId, in_value )) != kOkRC ) @@ -234,7 +235,7 @@ namespace cw if((rc = var_register_and_set( ctx, kAnyChIdx, kOutPId, "out", kBaseSfxId, in_value, - kInvOutPId, "inv_out", kBaseSfxId, (real_t)(1.0-in_value) )) != kOkRC ) + kInvOutPId, "inv_out", kBaseSfxId, (coeff_t)(1.0-in_value) )) != kOkRC ) { goto errLabel; } @@ -259,11 +260,11 @@ namespace cw rc_t rc = kOkRC; inst_t* inst = (inst_t*)(ctx->userPtr); - real_t value = 1; + coeff_t value = 1; var_get(ctx, kInPId, kAnyChIdx, value); var_set(ctx, kOutPId, kAnyChIdx, value); - var_set(ctx, kInvOutPId, kAnyChIdx, (real_t)(1.0 - value) ); + var_set(ctx, kInvOutPId, kAnyChIdx, (coeff_t)(1.0 - value) ); if( inst->value != value ) { @@ -748,7 +749,7 @@ namespace cw { rc_t rc = kOkRC; audiofile::info_t info; - real_t seekSecs; + ftime_t seekSecs; inst_t* inst = mem::allocZ(); ctx->userPtr = inst; @@ -808,7 +809,7 @@ namespace cw rc_t value( instance_t* ctx, variable_t* var ) { rc_t rc = kOkRC; - real_t seekSecs = 0; + ftime_t seekSecs = 0; inst_t* inst = (inst_t*)ctx->userPtr; if((rc = var_get(ctx,kSeekSecsPId,kAnyChIdx,seekSecs)) != kOkRC ) @@ -1005,8 +1006,8 @@ namespace cw typedef struct inst_str { unsigned n; - real_t vgain; - real_t gain; + coeff_t vgain; + coeff_t gain; } inst_t; rc_t create( instance_t* ctx ) @@ -1041,7 +1042,7 @@ namespace cw rc_t value( instance_t* ctx, variable_t* var ) { - real_t value = 0; + coeff_t value = 0; inst_t* inst = (inst_t*)ctx->userPtr; var_get(ctx,kGainPId,0,value); @@ -1631,7 +1632,7 @@ namespace cw { const sample_t* isig = ibuf->buf + i*ibuf->frameN; sample_t* osig = obuf->buf + i*obuf->frameN; - real_t gain = 1; + coeff_t gain = 1; if((rc = var_get(ctx, gainPId, kAnyChIdx, gain)) != kOkRC ) goto errLabel; @@ -1694,13 +1695,15 @@ namespace cw kSratePId, kChCntPid, kFreqHzPId, + kPhasePId, + kDcPId, kGainPId, kOutPId }; typedef struct { - real_t *phaseA; + double *phaseA; } inst_t; rc_t create( instance_t* ctx ) @@ -1709,8 +1712,10 @@ namespace cw inst_t* inst = mem::allocZ(); srate_t srate = 0; unsigned chCnt = 0; - real_t gain; - real_t hz; + coeff_t gain; + coeff_t hz; + coeff_t phase; + coeff_t dc; ctx->userPtr = inst; @@ -1725,6 +1730,8 @@ namespace cw if((rc = var_register_and_get( ctx, i, kSratePId, "srate", kBaseSfxId, srate, kFreqHzPId, "hz", kBaseSfxId, hz, + kPhasePId, "phase", kBaseSfxId, phase, + kDcPId, "dc", kBaseSfxId, dc, kGainPId, "gain", kBaseSfxId, gain)) != kOkRC ) { goto errLabel; @@ -1733,7 +1740,7 @@ namespace cw // create one output audio buffer rc = var_register_and_set( ctx, "out", kBaseSfxId, kOutPId, kAnyChIdx, srate, chCnt, ctx->ctx->framesPerCycle ); - inst->phaseA = mem::allocZ( chCnt ); + inst->phaseA = mem::allocZ( chCnt ); errLabel: @@ -1772,13 +1779,15 @@ namespace cw { for(unsigned i=0; ichN; ++i) { - real_t gain = val_get( ctx, kGainPId, i ); - real_t hz = val_get( ctx, kFreqHzPId, i ); - srate_t srate = val_get( ctx, kSratePId, i ); + coeff_t gain = val_get( ctx, kGainPId, i ); + coeff_t hz = val_get( ctx, kFreqHzPId, i ); + coeff_t phase = val_get( ctx, kPhasePId, i ); + coeff_t dc = val_get( ctx, kDcPId, i ); + srate_t srate = val_get(ctx, kSratePId, i ); sample_t* v = abuf->buf + (i*abuf->frameN); for(unsigned j=0; jframeN; ++j) - v[j] = (sample_t)(gain * sin( inst->phaseA[i] + (2.0 * M_PI * j * hz/srate))); + v[j] = (sample_t)((gain * sin( inst->phaseA[i] + phase + (2.0 * M_PI * j * hz/srate)))+dc); inst->phaseA[i] += 2.0 * M_PI * abuf->frameN * hz/srate; } @@ -1803,7 +1812,7 @@ namespace cw // namespace pv_analysis { - typedef struct dsp::pv_anl::obj_str pv_t; + typedef struct dsp::pv_anl::obj_str pv_t; enum { kInPId, @@ -1843,9 +1852,9 @@ namespace cw inst->pvN = srcBuf->chN; inst->pvA = mem::allocZ( inst->pvN ); // allocate pv channel array - const fd_real_t* magV[ srcBuf->chN ]; - const fd_real_t* phsV[ srcBuf->chN ]; - const fd_real_t* hzV[ srcBuf->chN ]; + const fd_sample_t* magV[ srcBuf->chN ]; + const fd_sample_t* phsV[ srcBuf->chN ]; + const fd_sample_t* hzV[ srcBuf->chN ]; unsigned maxBinNV[ srcBuf->chN ]; unsigned binNV[ srcBuf->chN ]; unsigned hopNV[ srcBuf->chN ]; @@ -1991,7 +2000,7 @@ namespace cw // namespace pv_synthesis { - typedef struct dsp::pv_syn::obj_str pv_t; + typedef struct dsp::pv_syn::obj_str pv_t; enum { kInPId, @@ -2117,7 +2126,7 @@ namespace cw // namespace spec_dist { - typedef struct dsp::spec_dist::obj_str spec_dist_t; + typedef struct dsp::spec_dist::obj_str spec_dist_t; enum { @@ -2160,9 +2169,9 @@ namespace cw inst->sdN = srcBuf->chN; inst->sdA = mem::allocZ( inst->sdN ); - const fd_real_t* magV[ srcBuf->chN ]; - const fd_real_t* phsV[ srcBuf->chN ]; - const fd_real_t* hzV[ srcBuf->chN ]; + const fd_sample_t* magV[ srcBuf->chN ]; + const fd_sample_t* phsV[ srcBuf->chN ]; + const fd_sample_t* hzV[ srcBuf->chN ]; //if((rc = var_register(ctx, kAnyChIdx, kInPId, "in")) != kOkRC ) // goto errLabel; @@ -2351,7 +2360,8 @@ namespace cw // create a compressor object for each input channel for(unsigned i=0; ichN; ++i) { - real_t igain, maxWnd_ms, wnd_ms, thresh, ratio, atk_ms, rls_ms, ogain; + coeff_t igain, thresh, ratio, ogain; + ftime_t maxWnd_ms, wnd_ms, atk_ms, rls_ms; bool bypassFl; @@ -2406,7 +2416,7 @@ namespace cw { rc_t rc = kOkRC; inst_t* inst = (inst_t*)ctx->userPtr; - real_t tmp; + ftime_t tmp; if( var->chIdx != kAnyChIdx && var->chIdx < inst->cmpN ) { @@ -2539,7 +2549,7 @@ namespace cw // create a limiter object for each input channel for(unsigned i=0; ichN; ++i) { - real_t igain, thresh, ogain; + coeff_t igain, thresh, ogain; bool bypassFl; @@ -2589,7 +2599,7 @@ namespace cw { rc_t rc = kOkRC; inst_t* inst = (inst_t*)ctx->userPtr; - real_t rtmp; + coeff_t rtmp; bool btmp; if( var->chIdx != kAnyChIdx && var->chIdx < inst->limN ) @@ -2693,8 +2703,8 @@ namespace cw rc_t rc = kOkRC; const abuf_t* abuf = nullptr; // inst_t* inst = mem::allocZ(); - real_t delayMs = 0; - real_t maxDelayMs = 0; + ftime_t delayMs = 0; + ftime_t maxDelayMs = 0; ctx->userPtr = inst; @@ -2755,7 +2765,7 @@ namespace cw rc_t rc = kOkRC; inst_t* inst = (inst_t*)ctx->userPtr; abuf_t* ibuf = nullptr; - real_t delayMs = 0; + ftime_t delayMs = 0; unsigned delayFrameN = 0; if((rc = var_get(ctx,kInPId, kAnyChIdx, ibuf )) != kOkRC ) @@ -2903,7 +2913,7 @@ namespace cw // create a dc_filter object for each input channel for(unsigned i=0; ichN; ++i) { - real_t gain; + coeff_t gain; bool bypassFl; @@ -2972,7 +2982,7 @@ namespace cw for(unsigned i=0; i( ctx, kGainPId, i ); + coeff_t gain = val_get( ctx, kGainPId, i ); bool bypassFl = val_get( ctx, kBypassPId, i ); dsp::dc_filter::set( inst->dcfA[i], gain, bypassFl ); @@ -3059,7 +3069,8 @@ namespace cw // create a audio_meter object for each input channel for(unsigned i=0; ichN; ++i) { - real_t wndMs, peakThreshDb; + ftime_t wndMs; + coeff_t peakThreshDb; bool dbFl; // get the audio_meter variable values @@ -3080,7 +3091,7 @@ namespace cw goto errLabel; } - unsigned maxWndMs = std::max(wndMs,1000.0f); + unsigned maxWndMs = std::max(wndMs,1000.0); // create the audio_meter instance if((rc = dsp::audio_meter::create( inst->mtrA[i], srcBuf->srate, maxWndMs, wndMs, peakThreshDb)) != kOkRC ) @@ -3179,7 +3190,7 @@ namespace cw typedef struct inst_str { - real_t mark; + sample_t mark; } inst_t; rc_t create( instance_t* ctx ) @@ -3562,8 +3573,694 @@ namespace cw }; } + + + //------------------------------------------------------------------------------------------------------------------ + // + // sample_hold + // + namespace sample_hold + { + enum + { + kInPId, + kPeriodMsPId, + kOutPId, + kMeanPId, + }; + + typedef struct inst_str + { + unsigned chN; // count of audio input channels and output sample variables. + unsigned bufAllocFrmN; // count of sample frames allocated in the sample buffer + unsigned periodFrmN; // count of sample frames in the sample period + unsigned ii; // next buf[][] frame index to receive an incoming audio sample + sample_t** buf; // buf[chN][bufSmpAllocN] + } inst_t; + + unsigned _period_ms_to_smp( srate_t srate, unsigned framesPerCycle, double periodMs ) + { + unsigned frmN = (unsigned)(srate * periodMs / 1000.0); + return std::max(framesPerCycle,frmN); + } + + unsigned _period_ms_to_smp( srate_t srate, unsigned framesPerCycle, unsigned bufSmpAllocN, double periodMs ) + { + unsigned frmN = _period_ms_to_smp(srate,framesPerCycle, periodMs ); + + // clip sample period to the max. buffer length. + return std::min(bufSmpAllocN,frmN); + } + + sample_t _mean( inst_t* p, unsigned chIdx, unsigned oi, unsigned n0, unsigned n1 ) + { + sample_t sum = 0; + + for(unsigned i=0; ibuf[chIdx][oi + i ]; + + for(unsigned i=0; ibuf[chIdx][i]; + + return n0+n1==0 ? 0 : sum/(n0+n1); + } + + void _destroy( inst_t* p ) + { + for(unsigned i=0; ichN; ++i) + mem::release(p->buf[i]); + mem::release(p->buf); + mem::release(p); + } + + rc_t create( instance_t* ctx ) + { + rc_t rc = kOkRC; + const abuf_t* abuf = nullptr; // + double periodMs = 0; + + ctx->userPtr = mem::allocZ(); + inst_t* p = (inst_t*)ctx->userPtr; + + // get the source audio buffer + if((rc = var_register_and_get(ctx, kAnyChIdx, + kInPId, "in", kBaseSfxId, abuf, + kPeriodMsPId, "period_ms",kBaseSfxId, periodMs)) != kOkRC ) + { + goto errLabel; + } + + p->chN = abuf->chN; + p->bufAllocFrmN = _period_ms_to_smp( abuf->srate, ctx->ctx->framesPerCycle, periodMs ); + p->periodFrmN = p->bufAllocFrmN; + p->buf = mem::allocZ(abuf->chN); + + for(unsigned i=0; ichN; ++i) + { + p->buf[i] = mem::allocZ(p->bufAllocFrmN); + if((rc = var_register_and_set(ctx, i, + kOutPId, "out", kBaseSfxId, 0.0f, + kMeanPId, "mean", kBaseSfxId, 0.0f)) != kOkRC ) + { + goto errLabel; + } + } + + errLabel: + if(rc != kOkRC ) + _destroy(p); + return rc; + } + + rc_t destroy( instance_t* ctx ) + { + inst_t* p = (inst_t*)(ctx->userPtr); + _destroy(p); + return kOkRC; + } + + rc_t value( instance_t* ctx, variable_t* var ) + { + rc_t rc = kOkRC; + + switch( var->vid ) + { + case kPeriodMsPId: + { + double periodMs; + const abuf_t* abuf; + inst_t* p = (inst_t*)(ctx->userPtr); + + var_get(ctx,kInPId,kAnyChIdx,abuf); + + if((rc = var_get(var,periodMs)) == kOkRC ) + { + p->periodFrmN = _period_ms_to_smp( abuf->srate, ctx->ctx->framesPerCycle, p->bufAllocFrmN, periodMs ); + } + } + break; + + default: + break; + + } + + return rc; + } + + rc_t exec( instance_t* ctx ) + { + rc_t rc = kOkRC; + const abuf_t* ibuf = nullptr; + inst_t* p = (inst_t*)(ctx->userPtr); + unsigned chN = 0; + unsigned oi = 0; + unsigned n0 = 0; + unsigned n1 = 0; + + // get the src buffer + if((rc = var_get(ctx,kInPId, kAnyChIdx, ibuf )) != kOkRC ) + goto errLabel; + + chN = std::min(ibuf->chN,p->chN); + + // Copy samples into buf. + for(unsigned i=0; ichN; ++i) + { + sample_t* isig = ibuf->buf + i*ibuf->frameN; + sample_t* obuf = p->buf[i]; + unsigned k = p->ii; + + for(unsigned j=0; jframeN; ++j) + { + obuf[k++] = isig[j]; + if( k>= p->bufAllocFrmN ) + k -= p->bufAllocFrmN; + } + } + + // advance the input index + p->ii += ibuf->frameN; + if( p->ii >= p->bufAllocFrmN ) + p->ii -= p->bufAllocFrmN; + + + // if the sampling buf is in range oi:ii + if( p->ii >= p->periodFrmN ) + { + oi = p->ii - p->periodFrmN; + n0 = p->ii - oi; + n1 = 0; + } + else // the sampling buf is in two parts: bufAllocN-ii:bufAllocN, 0:ii + { + oi = p->bufAllocFrmN - (p->periodFrmN - p->ii); + n0 = p->bufAllocFrmN - oi; + n1 = p->ii; + } + + for(unsigned i=0; ichN; ++i) + { + // the output is the first sample in the buffer + var_set(ctx,kOutPId,i, p->buf[i][oi] ); + + if( var_is_a_source(ctx,kMeanPId,i) ) + var_set(ctx,kMeanPId,i, _mean(p,i,oi,n0,n1)); + } + + errLabel: + return rc; + } + + + class_members_t members = { + .create = create, + .destroy = destroy, + .value = value, + .exec = exec, + .report = nullptr + }; + + } + + //------------------------------------------------------------------------------------------------------------------ + // + // Number + // + namespace number + { + enum { + kInPId, + kBoolPId, + kUIntPId, + kIntPId, + kFloatPId, + kOutPId + }; + + typedef struct + { + bool delta_fl; + double value; + } inst_t; + + + rc_t create( instance_t* proc ) + { + rc_t rc = kOkRC; + inst_t* p = mem::allocZ(); + proc->userPtr = p; + + if((rc = var_register_and_get(proc,kAnyChIdx, + kInPId,"in",kBaseSfxId,p->value)) != kOkRC ) + { + goto errLabel; + } + + if((rc = var_register_and_set(proc,kAnyChIdx, + kBoolPId,"bool",kBaseSfxId,p->value != 0, + kUIntPId,"uint",kBaseSfxId,(unsigned)p->value, + kIntPId,"int",kBaseSfxId,(int)p->value, + kFloatPId,"float",kBaseSfxId,(float)p->value, + kOutPId,"out",kBaseSfxId,p->value )) != kOkRC ) + { + goto errLabel; + } + + p->delta_fl = true; + + errLabel: + return rc; + } + + rc_t destroy( instance_t* proc ) + { + rc_t rc = kOkRC; + + inst_t* p = (inst_t*)proc->userPtr; + mem::release(p); + return rc; + } + + rc_t value( instance_t* proc, variable_t* var ) + { + rc_t rc = kOkRC; + if( var->vid == kInPId ) + { + double v; + if((rc = var_get(var,v)) == kOkRC ) + { + inst_t* p = (inst_t*)proc->userPtr; + if( !p->delta_fl ) + p->delta_fl = v != p->value; + p->value = v; + + } + } + return rc; + } + + rc_t exec( instance_t* proc ) + { + rc_t rc = kOkRC; + inst_t* p = (inst_t*)proc->userPtr; + + if( p->delta_fl ) + { + p->delta_fl = false; + var_set(proc,kBoolPId,kAnyChIdx,p->value!=0); + var_set(proc,kUIntPId,kAnyChIdx,(unsigned)fabs(p->value)); + var_set(proc,kIntPId,kAnyChIdx,(int)p->value); + var_set(proc,kFloatPId,kAnyChIdx,(float)p->value); + var_set(proc,kOutPId,kAnyChIdx,p->value); + } + + return rc; + } + + class_members_t members = { + .create = create, + .destroy = destroy, + .value = value, + .exec = exec, + .report = nullptr + }; + + } + + //------------------------------------------------------------------------------------------------------------------ + // + // Timer + // + namespace timer + { + enum { + kSratePId, + kPeriodMsPId, + kOutPId, + }; + + typedef struct + { + unsigned periodFrmN; + unsigned periodPhase; + } inst_t; + + unsigned _period_ms_to_frame_count( instance_t* proc, inst_t* p, srate_t srate, ftime_t periodMs ) + { + return std::max((unsigned)(srate * periodMs / 1000.0), proc->ctx->framesPerCycle); + } + + rc_t create( instance_t* proc ) + { + rc_t rc = kOkRC; + ftime_t periodMs = 0; + srate_t srate = 0; + inst_t* p = mem::allocZ(); + proc->userPtr = p; + + + if((rc = var_register_and_get(proc,kAnyChIdx, + kSratePId, "srate", kBaseSfxId,srate, + kPeriodMsPId, "period_ms",kBaseSfxId,periodMs)) != kOkRC ) + { + goto errLabel; + } + + if((rc = var_register_and_set(proc,kAnyChIdx, + kOutPId, "out", kBaseSfxId,false)) != kOkRC ) + { + goto errLabel; + } + + p->periodFrmN = _period_ms_to_frame_count(proc,p,srate,periodMs); + + errLabel: + return rc; + } + + rc_t destroy( instance_t* proc ) + { + rc_t rc = kOkRC; + inst_t* p = (inst_t*)proc->userPtr; + mem::release(p); + return rc; + } + + rc_t value( instance_t* proc, variable_t* var ) + { + rc_t rc = kOkRC; + switch( var->vid ) + { + case kPeriodMsPId: + { + double periodMs; + srate_t srate; + inst_t* p = (inst_t*)(proc->userPtr); + + var_get(proc,kSratePId,kAnyChIdx,srate); + + if((rc = var_get(var,periodMs)) == kOkRC ) + p->periodFrmN = _period_ms_to_frame_count( proc, p, srate, periodMs ); + } + break; + + default: + break; + } + return rc; + } + + rc_t exec( instance_t* proc ) + { + rc_t rc = kOkRC; + inst_t* p = (inst_t*)proc->userPtr; + + p->periodPhase += proc->ctx->framesPerCycle; + + + if( p->periodPhase >= p->periodFrmN ) + { + p->periodPhase -= p->periodFrmN; + + bool val = false; + var_get(proc,kOutPId,kAnyChIdx,val); + + //printf("%i %i %i\n",p->periodPhase,p->periodFrmN,val); + + var_set(proc,kOutPId,kAnyChIdx,!val); + } + + return rc; + } + + class_members_t members = { + .create = create, + .destroy = destroy, + .value = value, + .exec = exec, + .report = nullptr + }; + + } + //------------------------------------------------------------------------------------------------------------------ + // + // Counter + // + namespace counter + { + enum { + kTriggerPId, + kResetPId, + kInitPId, + kMinPId, + kMaxPId, + kIncPId, + kRepeatPId, + kModePId, + kOutPId + }; + + enum { + kModuloModeId, + kReverseModeId, + kClipModeId, + kInvalidModeId + }; + + typedef struct + { + unsigned mode_id; + + bool trig_val; + bool delta_fl; + + bool reset_val; + bool reset_fl; + + bool done_fl; + + double dir; + + } inst_t; + + idLabelPair_t modeArray[] = { + { kModuloModeId, "modulo" }, + { kReverseModeId, "reverse" }, + { kClipModeId, "clip" }, + { kInvalidId, ""} + }; + + + unsigned _string_to_mode_id( const char* mode_label, unsigned& mode_id_ref ) + { + rc_t rc = kOkRC; + mode_id_ref = kInvalidId; + for(unsigned i=0; modeArray[i].id != kInvalidId; ++i) + if( textIsEqual(modeArray[i].label,mode_label) ) + { + mode_id_ref = modeArray[i].id; + return kOkRC; + } + + return cwLogError(kInvalidArgRC,"'%s' is not a valid counter 'mode'.",cwStringNullGuard(mode_label)); + } + + rc_t create( instance_t* proc ) + { + rc_t rc = kOkRC; + inst_t* p = mem::allocZ(); + proc->userPtr = p; + double init_val; + const char* mode_label; + + if((rc = var_register_and_get(proc, kAnyChIdx, + kTriggerPId, "trigger", kBaseSfxId, p->trig_val, + kResetPId, "reset", kBaseSfxId, p->reset_val, + kInitPId, "init", kBaseSfxId, init_val, + kModePId, "mode", kBaseSfxId, mode_label)) != kOkRC ) + { + goto errLabel; + } + + + if((rc = var_register(proc, kAnyChIdx, + kMinPId, "min", kBaseSfxId, + kMaxPId, "max", kBaseSfxId, + kIncPId, "inc", kBaseSfxId, + kRepeatPId, "repeat_fl", kBaseSfxId)) != kOkRC ) + { + goto errLabel; + } + + if((rc = var_register_and_set(proc,kAnyChIdx, + kOutPId,"out",kBaseSfxId,init_val)) != kOkRC ) + { + goto errLabel; + } + + + if((rc = _string_to_mode_id(mode_label,p->mode_id)) != kOkRC ) + goto errLabel; + + p->dir = 1.0; + + errLabel: + return rc; + } + + rc_t destroy( instance_t* proc ) + { + rc_t rc = kOkRC; + + inst_t* p = (inst_t*)proc->userPtr; + mem::release(p); + + return rc; + } + + rc_t value( instance_t* proc, variable_t* var ) + { + rc_t rc = kOkRC; + inst_t* p = (inst_t*)proc->userPtr; + + switch( var->vid ) + { + case kTriggerPId: + { + bool v; + + if((rc = var_get(var,v)) == kOkRC ) + { + if( !p->delta_fl ) + p->delta_fl = p->trig_val != v; + + p->trig_val = v; + } + } + break; + + case kModePId: + { + const char* s; + if((rc = var_get(var,s)) == kOkRC ) + rc = _string_to_mode_id(s,p->mode_id); + } + break; + + } + + return rc; + } + + rc_t exec( instance_t* proc ) + { + rc_t rc = kOkRC; + inst_t* p = (inst_t*)proc->userPtr; + + + bool v; + if((rc = var_get(proc,kTriggerPId,kAnyChIdx,v)) != kOkRC ) + { + cwLogError(rc,"Fail!"); + goto errLabel; + } + + p->delta_fl = v != p->trig_val; + p->trig_val = v; + + if( p->delta_fl ) + { + p->delta_fl = false; + + double cnt,inc,minv,maxv; + var_get(proc,kOutPId,kAnyChIdx,cnt); + var_get(proc,kIncPId,kAnyChIdx,inc); + var_get(proc,kMinPId,kAnyChIdx,minv); + var_get(proc,kMaxPId,kAnyChIdx,maxv); + + double incr = p->dir * inc; + cnt += incr; + + if( minv > cnt || cnt > maxv ) + { + bool repeat_fl; + var_get(proc,kRepeatPId,kAnyChIdx,repeat_fl); + + if( !repeat_fl ) + p->done_fl = true; + else + { + if( cnt > maxv) + { + switch( p->mode_id ) + { + case kModuloModeId: + cnt -= (maxv-minv) + incr; + break; + + case kReverseModeId: + p->dir = -1 * p->dir; + cnt = maxv - (cnt - maxv); + break; + + case kClipModeId: + cnt = maxv; + break; + + default: + assert(0); + + } + } + + if( cnt < minv) + { + switch( p->mode_id ) + { + case kModuloModeId: + cnt += (maxv - minv) + incr; + break; + + case kReverseModeId: + p->dir = -1 * p->dir; + cnt = minv + (minv-cnt); + break; + + case kClipModeId: + cnt = minv; + break; + + default: + assert(0); + } + } + } + } + + if( !p->done_fl ) + { + printf("cnt:%f\n",cnt); + var_set(proc,kOutPId,kAnyChIdx,cnt); + } + } + + errLabel: + return rc; + } + + class_members_t members = { + .create = create, + .destroy = destroy, + .value = value, + .exec = exec, + .report = nullptr + }; + + } + } // flow } // cw diff --git a/cwFlowProc.h b/cwFlowProc.h index 3898cb1..d09b0e0 100644 --- a/cwFlowProc.h +++ b/cwFlowProc.h @@ -27,5 +27,10 @@ namespace cw namespace audio_marker { extern class_members_t members; } namespace xfade_ctl { extern class_members_t members; } namespace poly_mixer { extern class_members_t members; } + namespace sample_hold { extern class_members_t members; } + namespace number { extern class_members_t members; } + namespace timer { extern class_members_t members; } + namespace counter { extern class_members_t members; } + } } From e2a307c2accf16870bb1fd392f30139ad6026b6a Mon Sep 17 00:00:00 2001 From: kevin Date: Fri, 26 Apr 2024 17:09:45 -0400 Subject: [PATCH 031/101] cwIoPresetSelApp.cpp : Changes to reflect new cwDspTypes. --- cwIoPresetSelApp.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/cwIoPresetSelApp.cpp b/cwIoPresetSelApp.cpp index ca18f98..64227e3 100644 --- a/cwIoPresetSelApp.cpp +++ b/cwIoPresetSelApp.cpp @@ -983,9 +983,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 ); @@ -2848,7 +2848,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; @@ -2943,7 +2943,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 ); @@ -2954,7 +2954,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 ) { @@ -2974,7 +2974,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."); From 964df1dc23346950f9859d6c79a325ba86225279 Mon Sep 17 00:00:00 2001 From: kevin Date: Fri, 26 Apr 2024 17:12:18 -0400 Subject: [PATCH 032/101] cwFlowNet.cpp : Minor changes to reflect changed function names - is_connected_to_source_proc(). --- cwFlowNet.cpp | 33 +++++++++++---------------------- 1 file changed, 11 insertions(+), 22 deletions(-) diff --git a/cwFlowNet.cpp b/cwFlowNet.cpp index 19be4ec..1a4c5e2 100644 --- a/cwFlowNet.cpp +++ b/cwFlowNet.cpp @@ -147,7 +147,7 @@ namespace cw void _complete_input_connections( instance_t* inst ) { for(variable_t* var=inst->varL; var!=nullptr; var=var->var_link) - if(var->chIdx == kAnyChIdx && is_connected_to_external_proc(var) ) + if(var->chIdx == kAnyChIdx && is_connected_to_source_proc(var) ) { variable_t* base_src_var = var->src_var; @@ -238,6 +238,9 @@ namespace cw { const object_t* value = preset_cfg->child_ele(i)->pair_value(); const char* value_label = preset_cfg->child_ele(i)->pair_label(); + + //cwLogInfo("variable:%s",value_label); + if((rc = _var_channelize( inst, preset_label, type_src_label, value_label, value )) != kOkRC ) goto errLabel; @@ -1285,14 +1288,12 @@ namespace cw goto errLabel; } } - } - + } } errLabel: - return rc; - + return rc; } bool _is_var_inst_already_created( const char* var_label, const proc_inst_parse_state_t& pstate ) @@ -1368,7 +1369,7 @@ namespace cw goto errLabel; } - // locate source value + // locate source variable if((rc = var_find( src_proc, src_var_label, src_var_sfx_id, kAnyChIdx, src_var)) != kOkRC ) { rc = cwLogError(rc,"The src-var '%s:i' was not found.", in_stmt.src_var_ele.label, src_var_sfx_id); @@ -1567,11 +1568,13 @@ namespace cw // All the class presets values have now been set and those variables // that were expressed with a list have numeric channel indexes assigned. - // Apply the proc instance preset values. + // Apply the proc instance 'args:{}' values. if( pstate.arg_cfg != nullptr ) + { if((rc = _proc_inst_args_channelize_vars( inst, pstate.arg_label, pstate.arg_cfg )) != kOkRC ) goto errLabel; - + } + // All the instance arg values have now been set and those variables // that were expressed with a list have numeric channel indexes assigned. @@ -1608,20 +1611,6 @@ namespace cw inst_ref = inst; - /* - // insert an instance in the network - if( net.network_tail == nullptr ) - { - net.network_head = inst; - net.network_tail = inst; - } - else - { - net.network_tail->link = inst; - net.network_tail = inst; - } - */ - errLabel: if( rc != kOkRC ) _destroy_inst(inst); From 2437a7453988e9d13ceda4e31637bface50a5e01 Mon Sep 17 00:00:00 2001 From: kevin Date: Fri, 26 Apr 2024 17:12:45 -0400 Subject: [PATCH 033/101] flow_doc.md : initial commit. --- flow/flow_doc.md | 192 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 192 insertions(+) create mode 100644 flow/flow_doc.md diff --git a/flow/flow_doc.md b/flow/flow_doc.md new file mode 100644 index 0000000..45eac60 --- /dev/null +++ b/flow/flow_doc.md @@ -0,0 +1,192 @@ + + + +# Flow Documentation: + +Flow is an audio processing framework that is designed to +facilitate the specification of real-time and non-real-time music +and audio processing networks and the application of network state data. + + + + + + +## Building Neworks: + + +### A Simple Network. +``` +``` + + + +### Polyphonic Network. + +3. Network with sub-nets. + + +## Processing Unit Class + +type | Description +-------|------------------------------------- +string | +bool | +int | `int32_t` +uint | `uint32_t` +float | f32 +double | f64 +srate | (float) Sample rate type +sample | (float) Audio sample type +coeff | (float) Value that will be directly applied to a sample value (e.g added or multiplied) +ftime | (double) Fractional seconds + +See list in cwFlowTypes.cpp : typeLabelFlagsA[] + + +### Processing Units (proc) + +A 'process' or **proc** is a set of functions and **variables**. + +A **network** is a set of proc's with interconnected variable. +By default most proc variables can be connected to, or act as sources for, +other proc variables. A variable consists of a label, a type (e.g. int,real,audio,...), +some attribtes (more about those below), a default value, and a documentation string. + +One of the goals of 'flow' is to naturally handle multi-channel audio signals. +To this end many audio processors create multiple internal sub-processes +to handle each audio channel based on the number of input audio channels. + +For example an audio gain processor consists of three variables: an input audio variable, +an output audio variable and a gain coefficient. When the gain unit is +created it will create an independent sub-process to handle each channel. +If the input audio signal has multiple channels than the gain processor +will internally duplicate each of the three variables. This allows +independent control of the gain of each of the audio channels. +In 'flow' parlance each of the sub-processes is referred to as a **channel**. +A variable that can duplicated in this way is referred to as a **multi-channel variable**. + +'flow' has a second form of variable multiplicity. This form occurs when +a variable is duplicated based on how it is connected in the network. +For example a simple audio mixer might have an undefined number of audio inputs +and a single audio output. The number of audio inputs is only defined +once it is connected in the network. This notion of variable multiplicity +is different from a **channel** because each of the incoming audio +signals may themselves contain multiple channels - each of which should +be individually addressable. However, it should also be possible +to address each of the incoming signals as a single entity. To +accomplish this we use the concept of the **mult-variable**. A mult-variable +amounts to creating an array of variables based on a single variable description, +where the array length is determined at network compile time. mult-variables +are distinguished by labels with integer suffixes. + + + + + + + + +The functions are: + +Name | Description +---------|------------------------------------------------------------------------ +create | Implements the custom elements of the proc instantiation. +destroy | Destroy resources that were acquired in create(). +value | Variable values will pass through this function as they are assigned. +exec | Implements the custom execution functionality of this process. +report | Print the state of the process. + + + + + + + +### Var Syntax + +__label__ : { type: __type__, value: __value__, doc:"q-string" } + +Part | Description +-------|------------------------------------------------------- +label | Variable name +type | Variable type. See Data types below. +value | The default value of the variable. +doc | Documentation string for this variable. + +Notes: + +- Whenever possible default values should be provided for the +variable - even if the value is meaningless - like 0.0. This is +important because during proc instantiation, prior to the custom +create() call, variables listed in the proc instance's 'in' statement +are connected to their respective sources. If the source does not +have a valid value then the instantiation will fail. This consitutes +a failure because it is guaranteed that when the custom create() +function is called all variables in the 'in' statement will be +resolved to a source variable with a valid value. This allows the +proc instance to have the information it needs to configure itself. + +There is one subtle case where the default value becomes important. When the +variables in an 'in' statement are initially connected to their source they are +connected to the 'any-channel' source variable because they +do not have a specific channel yet. Specific channel can only be known +during or after the custom create() function is called. Since the way +a given proc. distributes channels will vary from one proc. to the +next. + +If during initial variable connection the source happens to be a +variable with channels then the values that were assigned to those +channels, in the source proc. create() function, will not show up on +the 'any-channel'. If no default value was assigned to the source +variable then the 'any-channel' will have no value, and the connection +will fail with an error message like this: + +``` +"The source value is null on the connection input:foo:0 source:blah:0.bar:0". +``` + +Note that although variables are initially connected to the +'any-channel' source variable, prior to the their proc's create() call, +after the create() call, when the variables do have a valid channel +number, they are reconnected to a specific channel on the source +variable. + + +### Preset Syntax: + + +### Data Types: + +Types | Description | +---------|-------------------------| +bool | `bool` +int | `int32_t` +uint | `uint32_t` +real | `double` +audio | multi-channel audio +spectrum | multi-channel spectrum + +### Variable Flags: + +Flag | Description +-----------------|------------------------------------------------------------------------------------------- +`init` | This variale is set at proc instantation and never changes. +`src` | This variable must be connect to a source variable in the instance 'in' statement or be set to a default value. See 1. +`no_src` | This variable may not be connected to a source variable. See 1. +`no_dflt_create` | This variable is not created automatically as part of the proc instantiation. See 2. +`mult` | This variable may be duplicated in the instance 'in' statement. See 3. + + +Notes: +1. Unless the `no_src` attribute is set any variable may be connected to a source variable +in the proc instantation 'in' statement. + +2. By default all variables are created prior to the proc `create()` function being called. +Variable with the `no_dflt_create` attribute will not be created. This is useful in cases +where information needs to be accessed in the `create()` function in order to determine +some basic parameters of the variable For example a proc that needs to create multiple output +variables based on the number of input audio channels cannot know how many output variables +it may need until it accesses the number of audio channels it has been instantiated with. + + From 6c65dec3c79c8f6b8dee6cdcbebcda7f51818e36 Mon Sep 17 00:00:00 2001 From: kevin Date: Fri, 26 Apr 2024 17:13:00 -0400 Subject: [PATCH 034/101] README.md : Updates. --- README.md | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 4a0dae4..85e121e 100644 --- a/README.md +++ b/README.md @@ -845,7 +845,7 @@ In otherwords setting 'proc-sfx-id' to kInvalidId is not resolvable without more information. - + ### TODO: @@ -872,4 +872,18 @@ ports of the internal elements. - Reduce runtime over head for var get/set operations. +- enforce var attributes +- Allow multiple types on an input. + For example 'adder' should have a single input + which can by any numeric type. + +- Allow min/max values on numeric variables. + +- 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, var_label_1 ] ` + +- The 'poly' frawework is not quite complete. + +- From 6e3b59adb61e7bed5a839eab5628b96127ec4ecf Mon Sep 17 00:00:00 2001 From: kevin Date: Fri, 26 Apr 2024 17:44:33 -0400 Subject: [PATCH 035/101] cwFlowProc.cpp : Fix bugs in 'counter'. --- cwFlowProc.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cwFlowProc.cpp b/cwFlowProc.cpp index b55993c..2e32245 100644 --- a/cwFlowProc.cpp +++ b/cwFlowProc.cpp @@ -4198,12 +4198,12 @@ namespace cw switch( p->mode_id ) { case kModuloModeId: - cnt -= (maxv-minv) + incr; + cnt = minv + (cnt-maxv); break; case kReverseModeId: p->dir = -1 * p->dir; - cnt = maxv - (cnt - maxv); + cnt = maxv - (cnt-maxv); break; case kClipModeId: @@ -4221,7 +4221,7 @@ namespace cw switch( p->mode_id ) { case kModuloModeId: - cnt += (maxv - minv) + incr; + cnt = maxv - (minv-cnt); break; case kReverseModeId: From 95ae6d0525f566f5a6f75dabe32d5fc733dc14cc Mon Sep 17 00:00:00 2001 From: kevin Date: Fri, 26 Apr 2024 17:45:12 -0400 Subject: [PATCH 036/101] README.md : Updates. --- README.md | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 85e121e..99611e6 100644 --- a/README.md +++ b/README.md @@ -850,6 +850,10 @@ resolvable without more information. ### TODO: - Documentation w/ examples. + + Write the rules for each member function. + +- value() should return a special return-code value to indicate that the +value should not be updated. - Compile presets: at load time the presets should be resolved to the proc and vars to which they will be assigned. @@ -858,6 +862,8 @@ resolvable without more information. - Variable attributes should be meaningful. e.g. src,src_opt,mult,init, .... Should we check for 'src' or 'mult' attribute on var's? + +- Enforce var attributes. - 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. @@ -867,12 +873,11 @@ 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. -- 'poly' and 'sub' should be arbitrarily nestable. +- 'poly' and 'sub' should be arbitrarily nestable. DONE? - Reduce runtime over head for var get/set operations. -- enforce var attributes - Allow multiple types on an input. For example 'adder' should have a single input @@ -882,8 +887,13 @@ ports of the internal elements. - 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, var_label_1 ] ` +Add proc instance field: `log:{ var_label_0:0, var_label_1:0 } ` -- The 'poly' frawework is not quite complete. -- +Next: + +- Implement 'preset' proc. This will involve implementing the 'cfg' datatype. + +- Finish the 'poly' frawework. We are making 'mult' var's, but do any of the procs explicitly deal with them? + +- Turn on variable 'broadcast'. Why was it turned off? ... maybe multiple updates? From c7cd952fcccd6aa5fd506b95bab79f7aa00bde8c Mon Sep 17 00:00:00 2001 From: kevin Date: Fri, 26 Apr 2024 17:47:34 -0400 Subject: [PATCH 037/101] cwFlowProc.cpp : More counter improvements. --- cwFlowProc.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/cwFlowProc.cpp b/cwFlowProc.cpp index 2e32245..a0695b0 100644 --- a/cwFlowProc.cpp +++ b/cwFlowProc.cpp @@ -4198,12 +4198,14 @@ namespace cw switch( p->mode_id ) { case kModuloModeId: - cnt = minv + (cnt-maxv); + while(cnt > maxv ) + cnt = minv + (cnt-maxv); break; case kReverseModeId: p->dir = -1 * p->dir; - cnt = maxv - (cnt-maxv); + while( cnt > maxv ) + cnt = maxv - (cnt-maxv); break; case kClipModeId: @@ -4221,12 +4223,14 @@ namespace cw switch( p->mode_id ) { case kModuloModeId: - cnt = maxv - (minv-cnt); + while( cnt < minv ) + cnt = maxv - (minv-cnt); break; case kReverseModeId: p->dir = -1 * p->dir; - cnt = minv + (minv-cnt); + while(cnt < minv ) + cnt = minv + (minv-cnt); break; case kClipModeId: From 4b08aeabde536de0526331e2ff0c244f334c400f Mon Sep 17 00:00:00 2001 From: kevin Date: Tue, 30 Apr 2024 19:52:13 -0400 Subject: [PATCH 038/101] cwObject.h : Added is_ predicate functions. --- cwObject.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cwObject.h b/cwObject.h index e86bc12..494b17c 100644 --- a/cwObject.h +++ b/cwObject.h @@ -141,6 +141,11 @@ namespace cw 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 ); From 4619fc43a1c1d7c087158a02393ad50a7cec12ad Mon Sep 17 00:00:00 2001 From: kevin Date: Tue, 30 Apr 2024 19:52:40 -0400 Subject: [PATCH 039/101] cwMath.cpp : Fixed isPowerOfTwo() to recognize 1 as a power of 2. --- cwMath.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cwMath.cpp b/cwMath.cpp index 9f855d3..d5a737a 100644 --- a/cwMath.cpp +++ b/cwMath.cpp @@ -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 ) From b57693f4e4f71813d5701367f13096214a1d5d72 Mon Sep 17 00:00:00 2001 From: kevin Date: Tue, 30 Apr 2024 19:58:10 -0400 Subject: [PATCH 040/101] cwFlow,cwFlowNet,cwFlowTypes,cwFlowProc : Many changes. Added ability to set types at proc instantiation time. Added ability to have multi-type variables which get resolved at proc. instantiation time. Added ability to proc 'log' attribute to print selected variables as they change. Values are now coerced to the variable type in var_set(). Added new proc's : list,add,preset. --- cwFlow.cpp | 9 +- cwFlowNet.cpp | 138 +++++-- cwFlowProc.cpp | 859 ++++++++++++++++++++++++++++++++++------- cwFlowProc.h | 7 +- cwFlowTypes.cpp | 965 +++++++++++++++++++++++++++++++++++++---------- cwFlowTypes.h | 143 ++++--- flow/flow_doc.md | 38 +- 7 files changed, 1745 insertions(+), 414 deletions(-) diff --git a/cwFlow.cpp b/cwFlow.cpp index cdea341..078ff79 100644 --- a/cwFlow.cpp +++ b/cwFlow.cpp @@ -34,8 +34,8 @@ namespace cw { "midi_out", &midi_out::members }, { "audio_in", &audio_in::members }, { "audio_out", &audio_out::members }, - { "audioFileIn", &audioFileIn::members }, - { "audioFileOut", &audioFileOut::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 }, @@ -58,6 +58,9 @@ namespace cw { "number", &number::members }, { "timer", &timer::members }, { "counter", &counter::members }, + { "list", &list::members }, + { "add", &add::members }, + { "preset", &preset::members }, { nullptr, nullptr } }; @@ -621,7 +624,7 @@ cw::rc_t cw::flow::test( const object_t* cfg, int argc, const char* argv[] ) // find the user requested test case if((test_cfg = test_cases_cfg->find_child(argv[1])) == nullptr ) { - rc = cwLogError(kInvalidArgRC,"The test case named '%s' was not found.",argv[0]); + rc = cwLogError(kInvalidArgRC,"The test case named '%s' was not found.",argv[1]); goto errLabel; } diff --git a/cwFlowNet.cpp b/cwFlowNet.cpp index 1a4c5e2..01f2cd8 100644 --- a/cwFlowNet.cpp +++ b/cwFlowNet.cpp @@ -66,8 +66,10 @@ namespace cw void _connect_vars( variable_t* src_var, variable_t* in_var ) { // connect in_var into src_var's outgoing var chain - in_var->connect_link = src_var->connect_link; - src_var->connect_link = in_var; + in_var->dst_link = src_var->dst_tail; + src_var->dst_tail = in_var; + if( src_var->dst_head == nullptr ) + src_var->dst_head = in_var; assert( src_var->value != nullptr ); @@ -171,7 +173,42 @@ namespace cw } } } - + + rc_t _set_log_flags(instance_t* proc, const object_t* log_labels) + { + rc_t rc = kOkRC; + + if( log_labels == nullptr ) + return rc; + + if( !log_labels->is_dict() ) + { + rc = cwLogError(kSyntaxErrorRC,"The log spec on '%s:%i' is not a dictionary.",cwStringNullGuard(proc->label),proc->label_sfx_id); + goto errLabel; + } + + for(unsigned i=0; ichild_count(); ++i) + { + const object_t* pair; + unsigned sfx_id; + + if((pair = log_labels->child_ele(i)) == nullptr || pair->pair_label()==nullptr || pair->pair_value()==nullptr || (rc=pair->pair_value()->value(sfx_id))!=kOkRC ) + { + rc = cwLogError(kSyntaxErrorRC,"Syntax error on log var identifier."); + goto errLabel; + } + + if((rc = var_set_flags( proc, kAnyChIdx, pair->pair_label(), sfx_id, kLogVarFl )) != kOkRC ) + { + rc = cwLogError(rc,"Unable to set var flags on '%s:%i' var:'%s:%i'.",cwStringNullGuard(proc->label),proc->label_sfx_id,pair->pair_label(),sfx_id); + goto errLabel; + } + } + + errLabel: + return rc; + } + rc_t _call_value_func_on_all_variables( instance_t* inst ) { rc_t rc = kOkRC; @@ -181,40 +218,49 @@ namespace cw if( inst->varMapA[i] != nullptr && inst->varMapA[i]->vid != kInvalidId ) { variable_t* var = inst->varMapA[i]; - - if((rc = var->inst->class_desc->members->value( var->inst, var )) != kOkRC ) + + if((rc = var_call_custom_value_func( var )) != kOkRC ) rc1 = cwLogError(rc,"The proc instance '%s:%i' reported an invalid valid on variable:%s chIdx:%i.", var->inst->label, var->inst->label_sfx_id, var->label, var->chIdx ); } return rc1; } - rc_t _var_channelize( instance_t* inst, const char* preset_label, const char* type_src_label, const char* value_label, const object_t* value ) + rc_t _var_channelize( instance_t* inst, const char* preset_label, const char* type_src_label, const char* var_label, const object_t* value ) { rc_t rc = kOkRC; - variable_t* dummy = nullptr; + variable_t* dummy = nullptr; + var_desc_t* vd = nullptr; // verify that a valid value exists if( value == nullptr ) { - rc = cwLogError(kSyntaxErrorRC,"Unexpected missig value on %s preset '%s' instance '%s' variable '%s'.", type_src_label, preset_label, inst->label, cwStringNullGuard(value_label) ); + rc = cwLogError(kSyntaxErrorRC,"Unexpected missig value on %s preset '%s' instance '%s' variable '%s'.", type_src_label, preset_label, inst->label, cwStringNullGuard(var_label) ); goto errLabel; } - - // if a list of values was given - if( value->is_list() ) + else { - for(unsigned chIdx=0; chIdxchild_count(); ++chIdx) - if((rc = var_channelize( inst, value_label, kBaseSfxId, chIdx, value->child_ele(chIdx), kInvalidId, dummy )) != kOkRC ) - goto errLabel; - } - else // otherwise a single value was given - { - if((rc = var_channelize( inst, value_label, kBaseSfxId, kAnyChIdx, value, kInvalidId, dummy )) != kOkRC ) - goto errLabel; - } + bool is_var_cfg_type_fl = (vd = var_desc_find( inst->class_desc, var_label ))!=nullptr && cwIsFlag(vd->type,kCfgTFl); + bool is_list_fl = value->is_list(); + bool is_list_of_list_fl = is_list_fl && value->child_count() > 0 && value->child_ele(0)->is_list(); + bool parse_list_fl = (is_list_fl && !is_var_cfg_type_fl) || (is_list_of_list_fl && is_var_cfg_type_fl); + // if a list of values was given and the var type is not a 'cfg' type or if a list of lists was given + if( parse_list_fl ) + { + // then each value in the list is assigned to the associated channel + for(unsigned chIdx=0; chIdxchild_count(); ++chIdx) + if((rc = var_channelize( inst, var_label, kBaseSfxId, chIdx, value->child_ele(chIdx), kInvalidId, dummy )) != kOkRC ) + goto errLabel; + } + else // otherwise a single value was given + { + if((rc = var_channelize( inst, var_label, kBaseSfxId, kAnyChIdx, value, kInvalidId, dummy )) != kOkRC ) + goto errLabel; + } + } + errLabel: return rc; } @@ -236,12 +282,12 @@ namespace cw // for each preset variable for(unsigned i=0; ichild_count(); ++i) { - const object_t* value = preset_cfg->child_ele(i)->pair_value(); - const char* value_label = preset_cfg->child_ele(i)->pair_label(); + const object_t* value = preset_cfg->child_ele(i)->pair_value(); + const char* var_label = preset_cfg->child_ele(i)->pair_label(); //cwLogInfo("variable:%s",value_label); - if((rc = _var_channelize( inst, preset_label, type_src_label, value_label, value )) != kOkRC ) + if((rc = _var_channelize( inst, preset_label, type_src_label, var_label, value )) != kOkRC ) goto errLabel; @@ -707,6 +753,7 @@ namespace cw const char* arg_label; // const object_t* preset_labels; // const object_t* arg_cfg; // + const object_t* log_labels; // const object_t* in_dict; // cfg. node to the in-list in_stmt_t* in_array; // in_array[ in_arrayN ] in-stmt array unsigned in_arrayN; // count of in-stmt's in the in-list. @@ -1253,9 +1300,16 @@ namespace cw { in_stmt_t& in_stmt = pstate.in_array[i]; const object_t* in_pair = pstate.in_dict->child_ele(i); // in:src pair - const char* in_var_str = in_pair->pair_label(); // 'in' var string + const char* in_var_str = nullptr; const char* src_proc_var_str = nullptr; + // validate the basic syntax + if( in_pair == nullptr || (in_var_str = in_pair->pair_label()) == nullptr || in_pair->pair_value()==nullptr ) + { + cwLogError(rc,"Malformed dictionary encountered in 'in' statement."); + goto errLabel; + } + // get the src net/proc/var string if((rc = in_pair->pair_value()->value(src_proc_var_str)) != kOkRC ) { @@ -1282,6 +1336,7 @@ namespace cw kInvalidId, kAnyChIdx, in_stmt.in_var_desc->val_cfg, + kInvalidTFl, dum )) != kOkRC ) { rc = cwLogError(rc,"in-stmt var create failed on '%s:%s'.",cwStringNullGuard(in_var_str),cwStringNullGuard(src_proc_var_str)); @@ -1432,9 +1487,10 @@ namespace cw // parse the optional args if((rc = proc_inst_cfg->pair_value()->getv_opt("args", arg_dict, - "in", pstate.in_dict, - "argLabel", pstate.arg_label, - "preset", pstate.preset_labels)) != kOkRC ) + "in", pstate.in_dict, + "argLabel", pstate.arg_label, + "preset", pstate.preset_labels, + "log", pstate.log_labels )) != kOkRC ) { rc = cwLogError(kSyntaxErrorRC,"The instance cfg. '%s' missing: 'type'.",pstate.inst_label); goto errLabel; @@ -1550,16 +1606,16 @@ namespace cw // Instantiate all the variables in the class description - that were not already created in _parse_in_list() for(var_desc_t* vd=class_desc->varDescL; vd!=nullptr; vd=vd->link) - if( !_is_var_inst_already_created( vd->label, pstate ) ) + if( !_is_var_inst_already_created( vd->label, pstate ) && cwIsNotFlag(vd->type,kRuntimeTFl) ) { variable_t* var = nullptr; - if((rc = var_create( inst, vd->label, kBaseSfxId, kInvalidId, kAnyChIdx, vd->val_cfg, var )) != kOkRC ) + if((rc = var_create( inst, vd->label, kBaseSfxId, kInvalidId, kAnyChIdx, vd->val_cfg, kInvalidTFl, var )) != kOkRC ) goto errLabel; } // All the variables that can be used by this instance have now been created // and the chIdx of each variable is set to 'any'. - + // If a 'preset' field was included in the class cfg then apply the specified class preset if( pstate.preset_labels != nullptr ) if((rc = _class_apply_presets(inst, pstate.preset_labels )) != kOkRC ) @@ -1584,36 +1640,52 @@ namespace cw // Connect the in-list variables to their sources. if((rc = _connect_in_vars(net, inst, pstate)) != kOkRC ) { - rc = cwLogError(rc,"Creation of the proc instance '%s:%i' failed during input connection processing.",cwStringNullGuard(inst->label),inst->label_sfx_id); + rc = cwLogError(rc,"Input connection processing failed."); goto errLabel; } - + // Complete the instantiation of the proc instance by calling the custom instance creation function. // Call the custom instance create() function. if((rc = class_desc->members->create( inst )) != kOkRC ) { - rc = cwLogError(kInvalidArgRC,"Instantiation failed on instance '%s:%i'.", inst->label,inst->label_sfx_id ); + rc = cwLogError(kInvalidArgRC,"Custom instantiation failed." ); goto errLabel; } // Create the instance->varMap[] lookup array if((rc =_create_instance_var_map( inst )) != kOkRC ) + { + rc = cwLogError(rc,"Variable map creation failed."); goto errLabel; + } // the custom creation function may have added channels to in-list vars fix up those connections here. _complete_input_connections(inst); + // set the log flags again so that vars created by the instance can be included in the log output + if((rc = _set_log_flags(inst,pstate.log_labels)) != kOkRC ) + goto errLabel; + // call the 'value()' function to inform the instance of the current value of all of it's variables. if((rc = _call_value_func_on_all_variables( inst )) != kOkRC ) goto errLabel; + if((rc = instance_validate(inst)) != kOkRC ) + { + rc = cwLogError(rc,"Proc instance validation failed."); + goto errLabel; + } inst_ref = inst; errLabel: if( rc != kOkRC ) + { + rc = cwLogError(rc,"Proc instantiation failed on '%s:%i'.",cwStringNullGuard(pstate.inst_label),sfx_id); _destroy_inst(inst); + } + _destroy_pstate(pstate); return rc; diff --git a/cwFlowProc.cpp b/cwFlowProc.cpp index a0695b0..e9f640a 100644 --- a/cwFlowProc.cpp +++ b/cwFlowProc.cpp @@ -724,10 +724,10 @@ namespace cw //------------------------------------------------------------------------------------------------------------------ // - // AudioFileIn + // audio_file_in // - namespace audioFileIn + namespace audio_file_in { enum { @@ -881,10 +881,10 @@ namespace cw //------------------------------------------------------------------------------------------------------------------ // - // AudioFileOut + // audio_file_out // - namespace audioFileOut + namespace audio_file_out { enum { @@ -1753,6 +1753,7 @@ namespace cw inst_t* inst = (inst_t*)ctx->userPtr; + mem::release(inst->phaseA); mem::release(inst); return rc; @@ -3346,7 +3347,7 @@ namespace cw bool trigFl = false; variable_t* gainVar = nullptr; abuf_t* srateSrc = nullptr; - double dum; + double dum_dbl; inst_t* p = mem::allocZ(); @@ -3360,7 +3361,7 @@ namespace cw kSrateRefPId, "srateSrc", kBaseSfxId, srateSrc, kDurMsPId, "durMs", kBaseSfxId, p->xfadeDurMs, kTriggerPId, "trigger", kBaseSfxId, trigFl, - kGainPId, "gain", kBaseSfxId, dum)) != kOkRC ) + kGainPId, "gain", kBaseSfxId, dum_dbl)) != kOkRC ) { goto errLabel; } @@ -3383,7 +3384,7 @@ namespace cw for(unsigned i=1; inet_proc->internal_net->poly_cnt; ++i) { variable_t* dum; - if((rc = var_create(ctx, "gain", i, kGainPId+i, kAnyChIdx, nullptr, dum )) != kOkRC ) + if((rc = var_create(ctx, "gain", i, kGainPId+i, kAnyChIdx, nullptr, kInvalidTFl, dum )) != kOkRC ) { cwLogError(rc,"'gain:%i' create failed.",i); goto errLabel; @@ -3790,44 +3791,21 @@ namespace cw namespace number { enum { - kInPId, - kBoolPId, - kUIntPId, - kIntPId, - kFloatPId, - kOutPId + kValuePId, }; - typedef struct - { - bool delta_fl; - double value; - } inst_t; rc_t create( instance_t* proc ) { - rc_t rc = kOkRC; - inst_t* p = mem::allocZ(); - proc->userPtr = p; + rc_t rc = kOkRC; + double value = 0; if((rc = var_register_and_get(proc,kAnyChIdx, - kInPId,"in",kBaseSfxId,p->value)) != kOkRC ) + kValuePId,"value",kBaseSfxId,value)) != kOkRC ) { goto errLabel; } - - if((rc = var_register_and_set(proc,kAnyChIdx, - kBoolPId,"bool",kBaseSfxId,p->value != 0, - kUIntPId,"uint",kBaseSfxId,(unsigned)p->value, - kIntPId,"int",kBaseSfxId,(int)p->value, - kFloatPId,"float",kBaseSfxId,(float)p->value, - kOutPId,"out",kBaseSfxId,p->value )) != kOkRC ) - { - goto errLabel; - } - - p->delta_fl = true; errLabel: return rc; @@ -3836,45 +3814,18 @@ namespace cw rc_t destroy( instance_t* proc ) { rc_t rc = kOkRC; - - inst_t* p = (inst_t*)proc->userPtr; - mem::release(p); return rc; } rc_t value( instance_t* proc, variable_t* var ) { rc_t rc = kOkRC; - if( var->vid == kInPId ) - { - double v; - if((rc = var_get(var,v)) == kOkRC ) - { - inst_t* p = (inst_t*)proc->userPtr; - if( !p->delta_fl ) - p->delta_fl = v != p->value; - p->value = v; - - } - } return rc; } rc_t exec( instance_t* proc ) { rc_t rc = kOkRC; - inst_t* p = (inst_t*)proc->userPtr; - - if( p->delta_fl ) - { - p->delta_fl = false; - var_set(proc,kBoolPId,kAnyChIdx,p->value!=0); - var_set(proc,kUIntPId,kAnyChIdx,(unsigned)fabs(p->value)); - var_set(proc,kIntPId,kAnyChIdx,(int)p->value); - var_set(proc,kFloatPId,kAnyChIdx,(float)p->value); - var_set(proc,kOutPId,kAnyChIdx,p->value); - } - return rc; } @@ -3978,6 +3929,7 @@ namespace cw p->periodPhase += proc->ctx->framesPerCycle; + //printf("%i %i\n",p->periodPhase,p->periodFrmN); if( p->periodPhase >= p->periodFrmN ) { @@ -4020,6 +3972,7 @@ namespace cw kIncPId, kRepeatPId, kModePId, + kOutTypePId, kOutPId }; @@ -4075,12 +4028,16 @@ namespace cw proc->userPtr = p; double init_val; const char* mode_label; + variable_t* dum = nullptr; + const char* out_type_label; + unsigned out_type_fl; if((rc = var_register_and_get(proc, kAnyChIdx, kTriggerPId, "trigger", kBaseSfxId, p->trig_val, kResetPId, "reset", kBaseSfxId, p->reset_val, kInitPId, "init", kBaseSfxId, init_val, - kModePId, "mode", kBaseSfxId, mode_label)) != kOkRC ) + kModePId, "mode", kBaseSfxId, mode_label, + kOutTypePId, "out_type",kBaseSfxId, out_type_label)) != kOkRC ) { goto errLabel; } @@ -4095,12 +4052,25 @@ namespace cw goto errLabel; } - if((rc = var_register_and_set(proc,kAnyChIdx, - kOutPId,"out",kBaseSfxId,init_val)) != kOkRC ) + // get the type of the output + if(out_type_label==nullptr || (out_type_fl = value_type_label_to_flag( out_type_label )) == kInvalidTFl ) + { + rc = cwLogError(kInvalidArgRC,"The output type '%s' is not a valid type.",cwStringNullGuard(out_type_label)); + goto errLabel; + } + + + if((rc = var_create( proc, "out", kBaseSfxId, kOutPId, kAnyChIdx, nullptr, out_type_fl, dum )) != kOkRC ) { goto errLabel; } - + + if((rc = var_set( proc, kOutPId, kAnyChIdx, 0u )) != kOkRC ) + { + rc = cwLogError(rc,"Unable to set the initial counter value to %f.",init_val); + goto errLabel; + } + if((rc = _string_to_mode_id(mode_label,p->mode_id)) != kOkRC ) goto errLabel; @@ -4131,7 +4101,6 @@ namespace cw case kTriggerPId: { bool v; - if((rc = var_get(var,v)) == kOkRC ) { if( !p->delta_fl ) @@ -4159,98 +4128,93 @@ namespace cw { rc_t rc = kOkRC; inst_t* p = (inst_t*)proc->userPtr; - - + double cnt,inc,minv,maxv; bool v; + + if( !p->delta_fl ) + return rc; + + p->delta_fl = false; if((rc = var_get(proc,kTriggerPId,kAnyChIdx,v)) != kOkRC ) { cwLogError(rc,"Fail!"); goto errLabel; } - - p->delta_fl = v != p->trig_val; + p->trig_val = v; - if( p->delta_fl ) + var_get(proc,kOutPId,kAnyChIdx,cnt); + var_get(proc,kIncPId,kAnyChIdx,inc); + var_get(proc,kMinPId,kAnyChIdx,minv); + var_get(proc,kMaxPId,kAnyChIdx,maxv); + + cnt += p->dir * inc; + + if( minv > cnt || cnt > maxv ) { - p->delta_fl = false; - - double cnt,inc,minv,maxv; - var_get(proc,kOutPId,kAnyChIdx,cnt); - var_get(proc,kIncPId,kAnyChIdx,inc); - var_get(proc,kMinPId,kAnyChIdx,minv); - var_get(proc,kMaxPId,kAnyChIdx,maxv); + bool repeat_fl; + var_get(proc,kRepeatPId,kAnyChIdx,repeat_fl); - double incr = p->dir * inc; - cnt += incr; - - if( minv > cnt || cnt > maxv ) + if( !repeat_fl ) + p->done_fl = true; + else { - bool repeat_fl; - var_get(proc,kRepeatPId,kAnyChIdx,repeat_fl); - - if( !repeat_fl ) - p->done_fl = true; - else + if( cnt > maxv) { - if( cnt > maxv) + switch( p->mode_id ) { - switch( p->mode_id ) - { - case kModuloModeId: - while(cnt > maxv ) - cnt = minv + (cnt-maxv); - break; + case kModuloModeId: + while(cnt > maxv ) + cnt = minv + (cnt-maxv); + break; - case kReverseModeId: - p->dir = -1 * p->dir; - while( cnt > maxv ) - cnt = maxv - (cnt-maxv); - break; + case kReverseModeId: + p->dir = -1 * p->dir; + while( cnt > maxv ) + cnt = maxv - (cnt-maxv); + break; - case kClipModeId: - cnt = maxv; - break; + case kClipModeId: + cnt = maxv; + break; - default: - assert(0); + default: + assert(0); - } - } - - if( cnt < minv) - { - switch( p->mode_id ) - { - case kModuloModeId: - while( cnt < minv ) - cnt = maxv - (minv-cnt); - break; - - case kReverseModeId: - p->dir = -1 * p->dir; - while(cnt < minv ) - cnt = minv + (minv-cnt); - break; - - case kClipModeId: - cnt = minv; - break; - - default: - assert(0); - } } } - } - if( !p->done_fl ) - { - printf("cnt:%f\n",cnt); - var_set(proc,kOutPId,kAnyChIdx,cnt); + if( cnt < minv) + { + switch( p->mode_id ) + { + case kModuloModeId: + while( cnt < minv ) + cnt = maxv - (minv-cnt); + break; + + case kReverseModeId: + p->dir = -1 * p->dir; + while(cnt < minv ) + cnt = minv + (minv-cnt); + break; + + case kClipModeId: + cnt = minv; + break; + + default: + assert(0); + } + } } } + // if the counter has not reached it's terminal state + if( !p->done_fl ) + var_set(proc,kOutPId,kAnyChIdx,cnt); + + errLabel: return rc; } @@ -4264,6 +4228,631 @@ namespace cw }; } + + //------------------------------------------------------------------------------------------------------------------ + // + // List + // + namespace list + { + enum + { + kInPId, + kListPId, + kOutPId + }; + + typedef struct + { + unsigned listN; // the length of the list + const object_t* list; // the list + unsigned typeFl; // the output type + unsigned index; // the last index referenced + bool deltaFl; + } inst_t; + + rc_t _determine_type( const object_t* list, unsigned& typeFl_ref ) + { + rc_t rc = kOkRC; + + typeFl_ref = kInvalidTFl; + + enum { bool_idx, uint_idx, int_idx, float_idx, double_idx, string_idx, cfg_idx, typeN }; + typedef struct type_map_str + { + unsigned idx; + unsigned typeFl; + unsigned cnt; + } type_map_t; + + type_map_t typeA[] = { + { bool_idx, kBoolTFl, 0 }, + { uint_idx, kUIntTFl, 0 }, + { int_idx, kIntTFl, 0 }, + { float_idx, kFloatTFl, 0 }, + { double_idx, kDoubleTFl, 0 }, + { string_idx, kStringTFl, 0 }, + { cfg_idx, kCfgTFl, 0 }, + }; + + + // count the number of each type of element in the list. + for(unsigned i=0; ichild_count(); ++i) + { + const object_t* c = list->child_ele(i); + + switch( c->type->id ) + { + case kCharTId: typeA[uint_idx].cnt+=1; break; + case kInt8TId: typeA[int_idx].cnt +=1; break; + case kUInt8TId: typeA[uint_idx].cnt+=1; break; + case kInt16TId: typeA[int_idx].cnt +=1; break; + case kUInt16TId: typeA[uint_idx].cnt+=1; break; + case kInt32TId: typeA[int_idx].cnt +=1; break; + case kUInt32TId: typeA[uint_idx].cnt+=1; break; + case kFloatTId: typeA[float_idx].cnt+=1; break; + case kDoubleTId: typeA[double_idx].cnt+=1; break; + case kBoolTId: typeA[bool_idx].cnt+=1; break; + case kStringTId: typeA[string_idx].cnt+=1; break; + case kCStringTId:typeA[string_idx].cnt+=1; break; + break; + + default: + switch( c->type->id ) + { + case kVectTId: + case kPairTId: + case kListTId: + case kDictTId: + typeA[cfg_idx].cnt +=1; + break; + + default: + rc = cwLogError(kSyntaxErrorRC,"The object type '0x%x' is not a valid list entry type. %i",c->type->flags,list->child_count()); + goto errLabel; + } + } + + unsigned type_flag = kInvalidTFl; // type flag of one of the reference types + unsigned type_cnt = 0; // count of types + + for(unsigned i=0; i 0 ) + { + type_cnt += 1; + type_flag = typeA[i].typeFl; + } + + // it is an error if more than one type of element was included in the list - + // and one of those types was string or cfg - having multiple numeric types + // is ok because they can be converted between each other - but string, and cfg's + // cannot be converted to numbers, nor can the be converted between each other. + if( type_cnt > 1 && (typeA[string_idx].cnt>0 || typeA[cfg_idx].cnt>0) ) + { + rc = cwLogError(kInvalidArgRC,"The list types. The list must be all numerics, all strings, or all cfg. types."); + for(unsigned i=0; i 0 ) + cwLogInfo("%i %s",typeA[i].cnt, value_type_flag_to_label(typeA[i].typeFl)); + + goto errLabel; + } + + typeFl_ref = type_flag; + } + + + errLabel: + return rc; + } + + template< typename T > + rc_t _set_out_tmpl( instance_t* proc, inst_t* p, unsigned idx, T& v ) + { + rc_t rc; + const object_t* ele; + + // get the list element to output + if((ele = p->list->child_ele(idx)) == nullptr ) + { + rc = cwLogError(kEleNotFoundRC,"The list element at index %i could not be accessed.",idx); + goto errLabel; + } + + // get the value of the list element + if((rc = ele->value(v)) != kOkRC ) + { + rc = cwLogError(rc,"List value access failed on index %i",idx); + goto errLabel; + } + + // set the output + if((rc = var_set(proc,kOutPId,kAnyChIdx,v)) != kOkRC ) + { + rc = cwLogError(rc,"List output failed on index %i",idx); + goto errLabel; + } + + errLabel: + return rc; + } + + rc_t _set_output( instance_t* proc, inst_t* p ) + { + rc_t rc; + unsigned idx; + + if((rc = var_get(proc,kInPId,kAnyChIdx,idx)) != kOkRC ) + { + rc = cwLogError(rc,"Unable to get the list index."); + goto errLabel; + } + + // if the index has not changed then there is nothing to do + if( idx == p->index ) + goto errLabel; + + switch( p->typeFl ) + { + case kUIntTFl: + { + unsigned v; + rc = _set_out_tmpl(proc,p,idx,v); + } + break; + + case kIntTFl: + { + int v; + rc = _set_out_tmpl(proc,p,idx,v); + } + break; + + case kFloatTFl: + { + float v; + rc = _set_out_tmpl(proc,p,idx,v); + } + break; + + case kDoubleTFl: + { + double v; + rc = _set_out_tmpl(proc,p,idx,v); + } + break; + + case kStringTFl: + { + const char* v; + rc = _set_out_tmpl(proc,p,idx,v); + } + break; + + case kCfgTFl: + { + const object_t* v; + rc = _set_out_tmpl(proc,p,idx,v); + } + break; + + default: + rc = cwLogError(kInvalidArgRC,"The list type flag %s (0x%x) is not valid.",value_type_flag_to_label(p->typeFl),p->typeFl); + break; + + } + + if(rc != kOkRC ) + goto errLabel; + + p->index = idx; + + errLabel: + return rc; + } + + rc_t create( instance_t* proc ) + { + rc_t rc = kOkRC; + inst_t* p = mem::allocZ(); + unsigned index; + proc->userPtr = p; + + variable_t* dum = nullptr; + + p->index = kInvalidIdx; + p->typeFl = kInvalidTFl; + p->deltaFl = false; + + if((rc = var_register_and_get(proc, kAnyChIdx, + kInPId, "in", kBaseSfxId, index, + kListPId,"list", kBaseSfxId, p->list)) != kOkRC ) + { + goto errLabel; + } + + if( !p->list->is_list() ) + { + cwLogError(kSyntaxErrorRC,"The list cfg. value is not a list."); + goto errLabel; + } + + p->listN = p->list->child_count(); + + // determine what type of element is in the list + // (all elements in the this list must be of the same type: numeric,string,cfg) + if((rc = _determine_type( p->list, p->typeFl )) != kOkRC ) + goto errLabel; + + // create the output variable + if((rc = var_create( proc, "out", kBaseSfxId, kOutPId, kAnyChIdx, nullptr, p->typeFl, dum )) != kOkRC ) + { + rc = cwLogError(rc,"'out' var create failed."); + goto errLabel; + } + + // set the initial value of the output + if((rc = _set_output(proc,p)) != kOkRC ) + goto errLabel; + + errLabel: + return rc; + } + + rc_t destroy( instance_t* proc ) + { + rc_t rc = kOkRC; + + inst_t* p = (inst_t*)proc->userPtr; + + mem::release(p); + + return rc; + } + + rc_t value( instance_t* proc, variable_t* var ) + { + rc_t rc = kOkRC; + if( var->vid == kInPId ) + { + inst_t* p = (inst_t*)proc->userPtr; + unsigned idx; + if( var_get(var,idx) == kOkRC && idx != p->index) + p->deltaFl = true; + } + return rc; + } + + rc_t exec( instance_t* proc ) + { + rc_t rc = kOkRC; + inst_t* p = (inst_t*)proc->userPtr; + + if( p->deltaFl ) + { + rc = _set_output(proc, p ); + p->deltaFl = false; + } + return rc; + } + + class_members_t members = { + .create = create, + .destroy = destroy, + .value = value, + .exec = exec, + .report = nullptr + }; + + } + + //------------------------------------------------------------------------------------------------------------------ + // + // add + // + namespace add + { + enum { + kOutPId, + kOTypePId, + kInPId + }; + + typedef struct + { + bool delta_fl; + unsigned inN; + } inst_t; + + + template< typename T > + rc_t _sum( instance_t* proc, variable_t* var ) + { + rc_t rc = kOkRC; + inst_t* p = (inst_t*)proc->userPtr; + + T sum = 0; + + // read and sum the inputs + for(unsigned i=0; iinN; ++i) + { + T val; + if((rc = var_get(proc,kInPId+i,kAnyChIdx,val)) == kOkRC ) + sum += val; + else + { + rc = cwLogError(rc,"Operand index %i read failed.",i); + goto errLabel; + } + } + + // set the output + if((rc = var_set(var,sum)) != kOkRC ) + { + rc = cwLogError(rc,"Result set failed."); + goto errLabel; + } + + errLabel: + return rc; + } + + rc_t _exec( instance_t* proc, variable_t* out_var=nullptr ) + { + rc_t rc = kOkRC; + inst_t* p = (inst_t*)(proc->userPtr); + + if( !p->delta_fl ) + return rc; + + p->delta_fl = false; + + if( out_var == nullptr ) + if((rc = var_find(proc,kOutPId,kAnyChIdx,out_var)) != kOkRC ) + { + rc = cwLogError(rc,"The output variable could not be found."); + goto errLabel; + } + + switch( out_var->varDesc->type ) + { + case kBoolTFl: rc = _sum(proc,out_var); break; + case kUIntTFl: rc = _sum(proc,out_var); break; + case kIntTFl: rc = _sum(proc,out_var); break; + case kFloatTFl: rc = _sum(proc,out_var); break; + case kDoubleTFl: rc = _sum(proc,out_var); break; + default: + rc = cwLogError(kInvalidArgRC,"The output type %s (0x%x) is not valid.",value_type_flag_to_label(out_var->value->tflag),out_var->value->tflag); + goto errLabel; + } + + if(rc != kOkRC ) + rc = cwLogError(kOpFailRC,"Sum failed."); + + errLabel: + return rc; + + } + + rc_t create( instance_t* proc ) + { + rc_t rc = kOkRC; + inst_t* p = mem::allocZ(); + proc->userPtr = p; + + variable_t* out_var = nullptr; + const char* out_type_label = nullptr; + unsigned out_type_flag = kInvalidTFl; + unsigned sfxIdAllocN = instance_var_count(proc); + unsigned sfxIdA[ sfxIdAllocN ]; + p->inN = 0; + + // get a count of the number of input variables + if((rc = var_mult_sfx_id_array(proc, "in", sfxIdA, sfxIdAllocN, p->inN )) != kOkRC ) + { + rc = cwLogError(rc,"Unable to obtain the array of mult label-sfx-id's for the variable 'in'."); + goto errLabel; + } + + // if the adder has no inputs + if( p->inN == 0 ) + { + rc = cwLogError(rc,"The 'add' unit '%s' appears to not have any inputs.",cwStringNullGuard(proc->label)); + goto errLabel; + } + + // sort the input id's in ascending order + std::sort(sfxIdA, sfxIdA + p->inN, [](unsigned& a,unsigned& b){ return ainN; ++i) + { + variable_t* dum; + if((rc = var_register(proc, "in", sfxIdA[i], kInPId+i, kAnyChIdx, nullptr, dum )) != kOkRC ) + { + rc = cwLogError(rc,"Variable registration failed for the variable 'in:%i'.",sfxIdA[i]);; + goto errLabel; + } + } + + // Get the output type label as a string + if((rc = var_register_and_get(proc,kAnyChIdx,kOTypePId,"otype",kBaseSfxId,out_type_label)) != kOkRC ) + { + rc = cwLogError(rc,"Variable registration failed for the variable 'otype:0'.");; + goto errLabel; + } + + // Convert the output type label into a flag + if((out_type_flag = value_type_label_to_flag(out_type_label)) == kInvalidTFl ) + { + rc = cwLogError(rc,"The type label '%s' does not identify a valid type.",cwStringNullGuard(out_type_label));; + goto errLabel; + } + + // Create the output var + if((rc = var_create( proc, "out", kBaseSfxId, kOutPId, kAnyChIdx, nullptr, out_type_flag, out_var )) != kOkRC ) + { + rc = cwLogError(rc,"The output variable create failed."); + goto errLabel; + } + + /* + if((rc = var_set(proc,kOutPId,kAnyChIdx,0.0)) != kOkRC ) + { + rc = cwLogError(rc,"Initial output variable set failed."); + goto errLabel; + } + */ + + p->delta_fl=true; + _exec(proc,out_var); + errLabel: + return rc; + } + + rc_t destroy( instance_t* proc ) + { + rc_t rc = kOkRC; + + inst_t* p = (inst_t*)proc->userPtr; + + mem::release(p); + + return rc; + } + + rc_t value( instance_t* proc, variable_t* var ) + { + rc_t rc = kOkRC; + inst_t* p = (inst_t*)(proc->userPtr); + + if( kInPId <= var->vid && var->vid < kInPId+p->inN ) + p->delta_fl = true; + + return rc; + } + + rc_t exec( instance_t* proc ) + { + return _exec(proc); + } + + class_members_t members = { + .create = create, + .destroy = destroy, + .value = value, + .exec = exec, + .report = nullptr + }; + } + + //------------------------------------------------------------------------------------------------------------------ + // + // preset + // + namespace preset + { + enum { kInPId }; + + enum { kPresetLabelCharN=255 }; + + typedef struct + { + char preset_label[ kPresetLabelCharN+1]; + } inst_t; + + + rc_t _set_preset( instance_t* proc, inst_t* p, const char* preset_label ) + { + rc_t rc = kOkRC; + + if( preset_label == nullptr ) + { + if((rc = var_get(proc, kInPId, kAnyChIdx, preset_label)) != kOkRC ) + { + rc = cwLogError(rc,"The variable 'in read failed."); + goto errLabel; + } + } + + if( preset_label == nullptr ) + { + rc = cwLogError(kInvalidArgRC,"Preset application failed due to blank preset label."); + goto errLabel; + } + + if((rc = network_apply_preset(*proc->net, preset_label)) != kOkRC ) + { + rc = cwLogError(rc,"Appy preset '%s' failed.",cwStringNullGuard(preset_label)); + goto errLabel; + } + + if( textLength(preset_label) >= kPresetLabelCharN ) + strncpy(p->preset_label,preset_label,kPresetLabelCharN); + else + { + rc = cwLogError(kBufTooSmallRC,"The preset label '%s' is to long.",cwStringNullGuard(preset_label)); + goto errLabel; + } + + errLabel: + return rc; + + } + + rc_t create( instance_t* proc ) + { + rc_t rc = kOkRC; + inst_t* p = mem::allocZ(); + proc->userPtr = p; + const char* label = nullptr; + + p->preset_label[0] = 0; + + if((rc = var_register_and_get(proc,kAnyChIdx,kInPId,"in",kBaseSfxId,label)) != kOkRC ) + goto errLabel; + + errLabel: + return rc; + } + + rc_t destroy( instance_t* proc ) + { + rc_t rc = kOkRC; + + inst_t* p = (inst_t*)proc->userPtr; + + // Custom clean-up code goes here + + mem::release(p); + + return rc; + } + + rc_t value( instance_t* proc, variable_t* var ) + { + rc_t rc = kOkRC; + return rc; + } + + rc_t exec( instance_t* proc ) + { + rc_t rc = kOkRC; + //inst_t* p = (inst_t*)proc->userPtr; + + return rc; + } + + class_members_t members = { + .create = create, + .destroy = destroy, + .value = value, + .exec = exec, + .report = nullptr + }; + + } + + } // flow } // cw diff --git a/cwFlowProc.h b/cwFlowProc.h index d09b0e0..30e3588 100644 --- a/cwFlowProc.h +++ b/cwFlowProc.h @@ -7,8 +7,8 @@ namespace cw 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; } @@ -31,6 +31,9 @@ namespace cw namespace number { 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; } } } diff --git a/cwFlowTypes.cpp b/cwFlowTypes.cpp index eada7df..6474233 100644 --- a/cwFlowTypes.cpp +++ b/cwFlowTypes.cpp @@ -2,6 +2,7 @@ #include "cwLog.h" #include "cwCommonImpl.h" #include "cwMem.h" +#include "cwMath.h" #include "cwText.h" #include "cwObject.h" #include "cwVectOps.h" @@ -45,6 +46,10 @@ namespace cw { kFloatTFl, "coeff"}, { kDoubleTFl, "ftime" }, + { kUIntTFl | kIntTFl | kFloatTFl | kDoubleTFl, "numeric" }, + + { kRuntimeTFl, "runtime" }, + { kInvalidTFl, nullptr } }; @@ -63,7 +68,7 @@ namespace cw if( v == nullptr ) return; - switch( v->flags & kTypeMask ) + switch( v->tflag & kTypeMask ) { case kInvalidTFl: break; @@ -98,22 +103,25 @@ namespace cw case kStringTFl: mem::release( v->u.s ); break; - + case kTimeTFl: assert(0); break; + case kCfgTFl: + break; + default: assert(0); break; } - v->flags = kInvalidTFl; + v->tflag = kInvalidTFl; } void _value_duplicate( value_t& dst, const value_t& src ) { - switch( src.flags & kTypeMask ) + switch( src.tflag & kTypeMask ) { case kInvalidTFl: break; @@ -129,17 +137,17 @@ namespace cw case kABufTFl: dst.u.abuf = src.u.abuf == nullptr ? nullptr : abuf_duplicate(src.u.abuf); - dst.flags = src.flags; + dst.tflag = src.tflag; break; case kFBufTFl: dst.u.fbuf = src.u.fbuf == nullptr ? nullptr : fbuf_duplicate(src.u.fbuf); - dst.flags = src.flags; + dst.tflag = src.tflag; break; case kMBufTFl: dst.u.mbuf = src.u.mbuf == nullptr ? nullptr : mbuf_duplicate(src.u.mbuf); - dst.flags = src.flags; + dst.tflag = src.tflag; break; case kBoolMtxTFl: @@ -152,13 +160,17 @@ namespace cw case kStringTFl: dst.u.s = mem::duplStr( dst.u.s ); - dst.flags = src.flags; + dst.tflag = src.tflag; break; case kTimeTFl: assert(0); break; + case kCfgTFl: + dst = src; + break; + default: assert(0); break; @@ -171,16 +183,16 @@ namespace cw if( v == nullptr ) return; - switch( v->flags & kTypeMask ) + switch( v->tflag & kTypeMask ) { case kInvalidTFl: break; - case kBoolTFl: printf("%s ", v->u.b ? "True" : "False" ); break; - case kUIntTFl: printf("%i ", v->u.u ); break; - case kIntTFl: printf("%i ", v->u.i ); break; - case kFloatTFl: printf("%f ", v->u.f ); break; - case kDoubleTFl:printf("%f ", v->u.d ); break; + case kBoolTFl: printf("b:%s ", v->u.b ? "true" : "false" ); break; + case kUIntTFl: printf("u:%i ", v->u.u ); break; + case kIntTFl: printf("i:%i ", v->u.i ); break; + case kFloatTFl: printf("f:%f ", v->u.f ); break; + case kDoubleTFl:printf("d:%f ", v->u.d ); break; case kABufTFl: if( v->u.abuf == nullptr ) printf("abuf: "); @@ -224,6 +236,11 @@ namespace cw assert(0); break; + case kCfgTFl: + if( v->u.cfg != nullptr ) + v->u.cfg->print(); + break; + default: assert(0); break; @@ -235,23 +252,46 @@ namespace cw rc_t _val_get( const value_t* val, bool& valRef ) { rc_t rc = kOkRC; - switch( val->flags & kTypeMask ) + switch( val->tflag & kTypeMask ) { case kBoolTFl: valRef = val->u.b; break; case kUIntTFl: valRef = val->u.u!=0; break; case kIntTFl: valRef = val->u.i!=0; break; case kFloatTFl: valRef = val->u.f!=0; break; - case kDoubleTFl: valRef = val->u.d!=0; break; + case kDoubleTFl: valRef = val->u.d!=0; break; default: - rc = cwLogError(kTypeMismatchRC,"The type 0x%x could not be converted to a bool.",val->flags); + rc = cwLogError(kTypeMismatchRC,"The type %s (0x%x) could not be converted to a bool.",_typeFlagToLabel(val->tflag),val->tflag); } return rc; } + + rc_t _val_set( value_t* val, bool v ) + { + rc_t rc = kOkRC; + + switch( val->tflag & kTypeMask ) + { + case kBoolTFl: val->u.b=v; break; + case kUIntTFl: val->u.u=v; break; + case kIntTFl: val->u.i=v; break; + case kFloatTFl: val->u.f=v; break; + case kDoubleTFl: val->u.d=v; break; + case kInvalidTFl: + val->u.b = v; + val->tflag = kBoolTFl; + break; + + default: + rc = cwLogError(kTypeMismatchRC,"A bool could not be converted to a %s (0x%x).",_typeFlagToLabel(val->tflag),val->tflag); + } + + return rc; + } rc_t _val_get( const value_t* val, uint_t& valRef ) { rc_t rc = kOkRC; - switch( val->flags & kTypeMask ) + switch( val->tflag & kTypeMask ) { case kBoolTFl: valRef = val->u.b ? 1 : 0; break; case kUIntTFl: valRef = val->u.u; break; @@ -259,15 +299,38 @@ namespace cw case kFloatTFl: valRef = (uint_t)val->u.f; break; case kDoubleTFl: valRef = (uint_t)val->u.d; break; default: - rc = cwLogError(kTypeMismatchRC,"The type 0x%x could not be converted to a uint_t.",val->flags); + rc = cwLogError(kTypeMismatchRC,"The type %s (0x%x) could not be converted to a uint.",_typeFlagToLabel(val->tflag),val->tflag); } return rc; } + + rc_t _val_set( value_t* val, uint_t v ) + { + rc_t rc = kOkRC; + switch( val->tflag & kTypeMask ) + { + case kBoolTFl: val->u.b=v!=0; break; + case kUIntTFl: val->u.u=v; break; + case kIntTFl: val->u.i=v; break; + case kFloatTFl: val->u.f=v; break; + case kDoubleTFl: val->u.d=v; break; + case kInvalidTFl: + val->u.u = v; + val->tflag = kUIntTFl; + break; + + default: + rc = cwLogError(kTypeMismatchRC,"A uint could not be converted to a %s (0x%x).",_typeFlagToLabel(val->tflag),val->tflag); + } + + return rc; + } + rc_t _val_get( const value_t* val, int_t& valRef ) { rc_t rc = kOkRC; - switch( val->flags & kTypeMask ) + switch( val->tflag & kTypeMask ) { case kBoolTFl: valRef = val->u.b ? 1 : 0; break; case kUIntTFl: valRef = (int_t)val->u.u; break; @@ -275,15 +338,40 @@ namespace cw case kFloatTFl: valRef = (int_t)val->u.f; break; case kDoubleTFl: valRef = (int_t)val->u.d; break; default: - rc = cwLogError(kTypeMismatchRC,"The type 0x%x could not be converted to a int_t.",val->flags); + rc = cwLogError(kTypeMismatchRC,"The type %s (0x%x) could not be converted to an int.",_typeFlagToLabel(val->tflag),val->tflag); + } return rc; } + rc_t _val_set( value_t* val, int_t v ) + { + rc_t rc = kOkRC; + + switch( val->tflag & kTypeMask ) + { + case kBoolTFl: val->u.b=v!=0; break; + case kUIntTFl: val->u.u=v; break; + case kIntTFl: val->u.i=v; break; + case kFloatTFl: val->u.f=v; break; + case kDoubleTFl: val->u.d=v; break; + case kInvalidTFl: + val->u.i = v; + val->tflag = kIntTFl; + break; + + default: + rc = cwLogError(kTypeMismatchRC,"An int could not be converted to a %s (0x%x).",_typeFlagToLabel(val->tflag),val->tflag); + } + + return rc; + } + + rc_t _val_get( const value_t* val, float& valRef ) { rc_t rc = kOkRC; - switch( val->flags & kTypeMask ) + switch( val->tflag & kTypeMask ) { case kBoolTFl: valRef = val->u.b ? 1 : 0; break; case kUIntTFl: valRef = (float)val->u.u; break; @@ -291,15 +379,38 @@ namespace cw case kFloatTFl: valRef = (float)val->u.f; break; case kDoubleTFl: valRef = (float)val->u.d; break; default: - rc = cwLogError(kTypeMismatchRC,"The type 0x%x could not be converted to a float.", val->flags); + rc = cwLogError(kTypeMismatchRC,"The type %s (0x%x) could not be converted to a float.",_typeFlagToLabel(val->tflag),val->tflag); } return rc; } + rc_t _val_set( value_t* val, float v ) + { + rc_t rc = kOkRC; + + switch( val->tflag & kTypeMask ) + { + case kBoolTFl: val->u.b=v!=0; break; + case kUIntTFl: val->u.u=(unsigned)v; break; + case kIntTFl: val->u.i=(int)v; break; + case kFloatTFl: val->u.f=v; break; + case kDoubleTFl: val->u.d=v; break; + case kInvalidTFl: + val->u.f = v; + val->tflag = kFloatTFl; + break; + + default: + rc = cwLogError(kTypeMismatchRC,"A float could not be converted to a %s (0x%x).",_typeFlagToLabel(val->tflag),val->tflag); + } + + return rc; + } + rc_t _val_get( const value_t* val, double& valRef ) { rc_t rc = kOkRC; - switch( val->flags & kTypeMask ) + switch( val->tflag & kTypeMask ) { case kBoolTFl: valRef = val->u.b ? 1 : 0; break; case kUIntTFl: valRef = (double)val->u.u; break; @@ -307,33 +418,77 @@ namespace cw case kFloatTFl: valRef = (double)val->u.f; break; case kDoubleTFl: valRef = val->u.d; break; default: - rc = cwLogError(kTypeMismatchRC,"The type 0x%x could not be converted to a double.",val->flags); + rc = cwLogError(kTypeMismatchRC,"The type %s (0x%x) could not be converted to a double.",_typeFlagToLabel(val->tflag),val->tflag); } return rc; } + rc_t _val_set( value_t* val, double v ) + { + rc_t rc = kOkRC; + + switch( val->tflag & kTypeMask ) + { + case kBoolTFl: val->u.b=v!=0; break; + case kUIntTFl: val->u.u=(unsigned)v; break; + case kIntTFl: val->u.i=(int)v; break; + case kFloatTFl: val->u.f=(float)v; break; + case kDoubleTFl: val->u.d=v; break; + case kInvalidTFl: + val->u.d = v; + val->tflag = kDoubleTFl; + break; + + default: + rc = cwLogError(kTypeMismatchRC,"A double could not be converted to a %s (0x%x).",_typeFlagToLabel(val->tflag),val->tflag); + } + + return rc; + } + rc_t _val_get( const value_t* val, const char*& valRef ) { rc_t rc = kOkRC; - if( cwIsFlag(val->flags & kTypeMask, kStringTFl) ) + if( cwIsFlag(val->tflag & kTypeMask, kStringTFl) ) valRef = val->u.s; else { - rc = cwLogError(kTypeMismatchRC,"The type 0x%x could not be converted to a string.",val->flags); + rc = cwLogError(kTypeMismatchRC,"The type %s (0x%x) could not be converted to a string.",_typeFlagToLabel(val->tflag),val->tflag); valRef = nullptr; } return rc; } + rc_t _val_set( value_t* val, const char* v ) + { + rc_t rc = kOkRC; + + switch( val->tflag & kTypeMask ) + { + case kStringTFl: + val->u.s=mem::duplStr(v); break; + + case kInvalidTFl: + val->u.s = mem::duplStr(v); + val->tflag = kStringTFl; + break; + + default: + rc = cwLogError(kTypeMismatchRC,"A string could not be converted to a %s (0x%x).",_typeFlagToLabel(val->tflag),val->tflag); + } + + return rc; + } + rc_t _val_get( value_t* val, abuf_t*& valRef ) { rc_t rc = kOkRC; - if( cwIsFlag(val->flags & kTypeMask, kABufTFl) ) + if( cwIsFlag(val->tflag & kTypeMask, kABufTFl) ) valRef = val->u.abuf; else { - rc = cwLogError(kTypeMismatchRC,"The type 0x%x could not be converted to an abuf_t.",val->flags); + rc = cwLogError(kTypeMismatchRC,"The type %s (0x%x) could not be converted to an abuf.",_typeFlagToLabel(val->tflag),val->tflag); valRef = nullptr; } return rc; @@ -348,15 +503,37 @@ namespace cw return rc; } + rc_t _val_set( value_t* val, abuf_t* v ) + { + rc_t rc = kOkRC; + + switch( val->tflag & kTypeMask ) + { + case kABufTFl: + val->u.abuf=v; + break; + + case kInvalidTFl: + val->u.abuf=v; + val->tflag = kABufTFl; + break; + + default: + rc = cwLogError(kTypeMismatchRC,"A audio signal could not be converted to a %s (0x%x).",_typeFlagToLabel(val->tflag),val->tflag); + } + + return rc; + } + rc_t _val_get( value_t* val, fbuf_t*& valRef ) { rc_t rc = kOkRC; - if( cwIsFlag(val->flags & kTypeMask, kFBufTFl) ) + if( cwIsFlag(val->tflag & kTypeMask, kFBufTFl) ) valRef = val->u.fbuf; else { valRef = nullptr; - rc = cwLogError(kTypeMismatchRC,"The type 0x%x could not be converted to an fbuf_t.",val->flags); + rc = cwLogError(kTypeMismatchRC,"The type %s (0x%x) could not be converted to an fbuf.",_typeFlagToLabel(val->tflag),val->tflag); } return rc; } @@ -370,18 +547,63 @@ namespace cw return rc; } + rc_t _val_set( value_t* val, fbuf_t* v ) + { + rc_t rc = kOkRC; + + switch( val->tflag & kTypeMask ) + { + case kFBufTFl: + val->u.fbuf=v; + break; + + case kInvalidTFl: + val->u.fbuf=v; + val->tflag = kFBufTFl; + break; + + default: + rc = cwLogError(kTypeMismatchRC,"A spectrum signal could not be converted to a %s (0x%x).",_typeFlagToLabel(val->tflag),val->tflag); + } + + return rc; + } + rc_t _val_get( value_t* val, mbuf_t*& valRef ) { rc_t rc = kOkRC; - if( cwIsFlag(val->flags & kTypeMask, kMBufTFl) ) + if( cwIsFlag(val->tflag & kTypeMask, kMBufTFl) ) valRef = val->u.mbuf; else { valRef = nullptr; - rc = cwLogError(kTypeMismatchRC,"The type 0x%x could not be converted to an mbuf_t.",val->flags); + rc = cwLogError(kTypeMismatchRC,"The type %s (0x%x) could not be converted to an mbuf.",_typeFlagToLabel(val->tflag),val->tflag); } return rc; } + + rc_t _val_set( value_t* val, mbuf_t* v ) + { + rc_t rc = kOkRC; + + switch( val->tflag & kTypeMask ) + { + case kMBufTFl: + val->u.mbuf=v; + break; + + case kInvalidTFl: + val->u.mbuf=v; + val->tflag = kMBufTFl; + break; + + default: + rc = cwLogError(kTypeMismatchRC,"A MIDI signal could not be converted to a %s (0x%x).",_typeFlagToLabel(val->tflag),val->tflag); + } + + return rc; + } + rc_t _val_get( value_t* val, const mbuf_t*& valRef ) { @@ -391,7 +613,43 @@ namespace cw valRef = non_const_val; return rc; } + + rc_t _val_get( value_t* val, const object_t*& valRef ) + { + rc_t rc = kOkRC; + + if( cwIsFlag(val->tflag & kTypeMask, kCfgTFl) ) + valRef = val->u.cfg; + else + { + valRef = nullptr; + rc = cwLogError(kTypeMismatchRC,"The type %s (0x%x) could not be converted to a cfg.",_typeFlagToLabel(val->tflag),val->tflag); + + } + return rc; + } + rc_t _val_set( value_t* val, const object_t* v ) + { + rc_t rc = kOkRC; + + switch( val->tflag & kTypeMask ) + { + case kCfgTFl: + val->u.cfg=v; + break; + + case kInvalidTFl: + val->u.cfg=v; + val->tflag = kCfgTFl; + break; + + default: + rc = cwLogError(kTypeMismatchRC,"A cfg. could not be converted to a %s (0x%x).",_typeFlagToLabel(val->tflag),val->tflag); + } + + return rc; + } template< typename T > rc_t _val_get_driver( const variable_t* var, T& valRef ) @@ -403,8 +661,8 @@ namespace cw return cwLogError(kInvalidStateRC,"No value has been assigned to the variable: %s:%i.%s:%i ch:%i.",cwStringNullGuard(var->inst->label),var->inst->label_sfx_id,cwStringNullGuard(var->label),var->label_sfx_id,var->chIdx); return _val_get(var->value,valRef); - } - + +} rc_t _var_find_to_set( instance_t* inst, unsigned vid, unsigned chIdx, unsigned typeFl, variable_t*& varRef ) { rc_t rc = kOkRC; @@ -414,8 +672,14 @@ namespace cw if((rc = var_find(inst,vid,chIdx,varRef)) == kOkRC ) { // validate the type of the variable against the description + /* if( !cwIsFlag(varRef->varDesc->type,typeFl ) ) - rc = cwLogError(kTypeMismatchRC,"Type mismatch. Instance:%s:%i variable:%s:%i with type 0x%x does not match requested type:0x%x.",varRef->inst->label,varRef->inst->label_sfx_id,varRef->label,varRef->label_sfx_id,varRef->varDesc->type,typeFl); + rc = cwLogError(kTypeMismatchRC,"Type mismatch. Instance:%s:%i variable:%s:%i with type %s (0x%x) does not match requested type: %s (0x%x).", + varRef->inst->label,varRef->inst->label_sfx_id, + varRef->label, + varRef->label_sfx_id,_typeFlagToLabel(varRef->varDesc->type),varRef->varDesc->type, + _typeFlagToLabel(typeFl),typeFl); + */ } @@ -453,137 +717,119 @@ namespace cw return nullptr; } + rc_t _var_find_on_label_and_ch( instance_t* inst, const char* var_label, unsigned sfx_id, unsigned chIdx, variable_t*& var_ref ) + { + rc_t rc = kOkRC; + if((var_ref = _var_find_on_label_and_ch(inst,var_label,sfx_id,chIdx)) == nullptr ) + rc = cwLogError(kEleNotFoundRC,"The variable '%s:%i' cannot be found on the proc:%s.",cwStringNullGuard(var_label),sfx_id,cwStringNullGuard(inst->label)); + return rc; + } rc_t _validate_var_assignment( variable_t* var, unsigned typeFl ) { if( cwIsFlag(var->varDesc->flags, kSrcVarFl ) ) return cwLogError(kInvalidStateRC, "The variable '%s:%i' on instance '%s:%i' cannot be set because it is a 'src' variable.", var->label, var->label_sfx_id, var->inst->label,var->inst->label_sfx_id); - + /* if( !cwIsFlag(var->varDesc->type, typeFl ) ) return cwLogError(kTypeMismatchRC, "The variable '%s:%i' on instance '%s:%i' is not a '%s'.", var->label, var->label_sfx_id, var->inst->label, var->inst->label_sfx_id, _typeFlagToLabel( typeFl )); - + */ + return kOkRC; } + void _var_print( const variable_t* var ) + { + const char* conn_label = is_connected_to_source_proc(var) ? "extern" : " "; + + printf(" %20s:%5i id:%4i ch:%3i : %s : ", var->label, var->label_sfx_id, var->vid, var->chIdx, conn_label ); + + if( var->value == nullptr ) + _value_print( &var->local_value[0] ); + else + _value_print( var->value ); + + if( var->src_var != nullptr ) + printf(" src:%s:%i.%s:%i",var->src_var->inst->label,var->src_var->inst->label_sfx_id,var->src_var->label,var->src_var->label_sfx_id); + + printf("\n"); + } + + rc_t _var_broadcast_new_value( variable_t* var ) { rc_t rc = kOkRC; // notify each connected var that the value has changed - for(variable_t* con_var = var->connect_link; con_var!=nullptr; con_var=con_var->connect_link) + for(variable_t* con_var = var->dst_head; con_var!=nullptr; con_var=con_var->dst_link) { // the var->local_value[] slot used by the source variable may have changed - update the destination variable // so that it points to the correct value. con_var->value = var->value; - - //if((rc = con_var->inst->class_desc->members->value( con_var->inst, con_var )) != kOkRC ) - // break; - + + cwLogMod("%s:%i %s:%i -> %s:%i %s:%i\n", + var->inst->label,var->inst->label_sfx_id, + var->label,var->label_sfx_id, + con_var->inst->label,con_var->inst->label_sfx_id, + con_var->label,con_var->label_sfx_id ); + + // Call the value() function on the connected variable + if((rc = var_call_custom_value_func(con_var)) != kOkRC ) + break; + } return rc; } - template< typename T > - void _var_setter( variable_t* var, unsigned local_value_idx, T val ) - { - cwLogError(kAssertFailRC,"Unimplemented variable setter."); - assert(0); - } - template<> - void _var_setter( variable_t* var, unsigned local_value_idx, bool val ) - { - var->local_value[ local_value_idx ].u.b = val; - var->local_value[ local_value_idx ].flags = kBoolTFl; - cwLogMod("%s:%i.%s:%i ch:%i %i (bool).",var->inst->label,var->inst->label_sfx_id,var->label,var->label_sfx_id,var->chIdx,val); - } - - template<> - void _var_setter( variable_t* var, unsigned local_value_idx, unsigned val ) - { - var->local_value[ local_value_idx ].u.u = val; - var->local_value[ local_value_idx ].flags = kUIntTFl; - cwLogMod("%s:%i.%s:%i ch:%i %i (uint).",var->inst->label,var->inst->label_sfx_id,var->label,var->label_sfx_id,var->chIdx,val); - } - - template<> - void _var_setter( variable_t* var, unsigned local_value_idx, int val ) - { - var->local_value[ local_value_idx ].u.i = val; - var->local_value[ local_value_idx ].flags = kIntTFl; - cwLogMod("%s:%i.%s:%i ch:%i %i (int).",var->inst->label,var->inst->label_sfx_id,var->label,var->label_sfx_id,var->chIdx,val); - } - - template<> - void _var_setter( variable_t* var, unsigned local_value_idx, float val ) - { - var->local_value[ local_value_idx ].u.f = val; - var->local_value[ local_value_idx ].flags = kFloatTFl; - cwLogMod("%s:%i.%s:%i ch:%i %f (float).",var->inst->label,var->inst->label_sfx_id,var->label,var->label_sfx_id,var->chIdx,val); - } - - template<> - void _var_setter( variable_t* var, unsigned local_value_idx, double val ) - { - var->local_value[ local_value_idx ].u.d = val; - var->local_value[ local_value_idx ].flags = kDoubleTFl; - cwLogMod("%s:%i.%s:%i ch:%i %f (double).",var->inst->label,var->inst->label_sfx_id,var->label,var->label_sfx_id,var->chIdx,val); - } - - template<> - void _var_setter( variable_t* var, unsigned local_value_idx, const char* val ) - { - var->local_value[ local_value_idx ].u.s = mem::duplStr(val); - var->local_value[ local_value_idx ].flags = kStringTFl; - cwLogMod("%s:%i.%s:%i ch:%i %s (string).",var->inst->label,var->inst->label_sfx_id,var->label,var->label_sfx_id,var->chIdx,val); - } - - template<> - void _var_setter( variable_t* var, unsigned local_value_idx, abuf_t* val ) - { - var->local_value[ local_value_idx ].u.abuf = val; - var->local_value[ local_value_idx ].flags = kABufTFl; - cwLogMod("%s:%i.%s:%i ch:%i %s (abuf).",var->inst->label,var->inst->label_sfx_id,var->label,var->label_sfx_id,var->chIdx,abuf==nullptr ? "null" : "valid"); - } - - template<> - void _var_setter( variable_t* var, unsigned local_value_idx, mbuf_t* val ) - { - var->local_value[ local_value_idx ].u.mbuf = val; - var->local_value[ local_value_idx ].flags = kMBufTFl; - cwLogMod("%s:%i.%s:%i ch:%i %s (abuf).",var->inst->label,var->inst->label_sfx_id,var->label,var->label_sfx_id,var->chIdx,mbuf==nullptr ? "null" : "valid"); - } - template<> - void _var_setter( variable_t* var, unsigned local_value_idx, fbuf_t* val ) - { - var->local_value[ local_value_idx ].u.fbuf = val; - var->local_value[ local_value_idx ].flags = kFBufTFl; - cwLogMod("%s:%i.%s:%i ch:%i %s (fbuf).",var->inst->label,var->inst->label_sfx_id,var->label,var->label_sfx_id,var->chIdx,fbuf==nullptr ? "null" : "valid"); - } - - template< typename T > - rc_t _var_set_template( variable_t* var, unsigned typeFlag, T val ) + // 'typeFlag' is the type (tflag) of 'val'. + template< typename T > + rc_t _var_set_template( variable_t* var, unsigned argTypeFlag, T val ) { rc_t rc; - + // var->type is the allowable type for this var's value. + unsigned value_type_flag = var->type; + unsigned next_local_value_idx = (var->local_value_idx + 1) % kLocalValueN; // store the pointer to the current value of this variable value_t* original_value = var->value; unsigned original_value_idx = var->local_value_idx; - + // verify that this is a legal assignment - if((rc = _validate_var_assignment( var, typeFlag )) != kOkRC ) - { + if((rc = _validate_var_assignment( var, argTypeFlag )) != kOkRC ) goto errLabel; - } // release the previous value in the next slot _value_release(&var->local_value[next_local_value_idx]); + // if the value type of this variable has not been established + if( value_type_flag == kInvalidTFl ) + { + // if the var desc is a single type then use that .... + if( math::isPowerOfTwo(var->varDesc->type) ) + value_type_flag = var->varDesc->type; + else // ... Otherwise select a type from the one of the possible flags given by the var desc + value_type_flag = var->varDesc->type & argTypeFlag; + + // if the incoming type is not in the set of allowable types then it is an error + if( value_type_flag == 0 ) + { + rc = cwLogError(kTypeMismatchRC,"The type 0x%x is not valid for the variable: %s:%i %s:%i type:0x%x.", + argTypeFlag,var->inst->label,var->inst->label_sfx_id,var->label,var->label_sfx_id,var->varDesc->type); + goto errLabel; + } + } + + // set the type of the LHS to force the incoming value to be coerced to this type + var->local_value[ next_local_value_idx ].tflag = value_type_flag; + // set the new local value - _var_setter(var,next_local_value_idx,val); + if((rc = _val_set(var->local_value + next_local_value_idx, val )) != kOkRC ) + { + rc = cwLogError(rc,"Value set failed on '%s:%i %s:%i",var->inst->label,var->inst->label_sfx_id,var->label,var->label_sfx_id); + goto errLabel; + } // make the new local value current var->value = var->local_value + next_local_value_idx; @@ -597,9 +843,11 @@ namespace cw // call because calls' to 'proc.value()' will see the instance in a incomplete state) // Note 2: If this call returns an error then the value assignment is cancelled // and the value does not change. - rc = var->inst->class_desc->members->value( var->inst, var ); + var_call_custom_value_func( var ); } + //printf("%p set: %s:%s 0x%x\n",var->value, var->inst->label,var->label,var->value->tflag); + if( rc == kOkRC ) { // send the value to connected downstream proc's @@ -617,25 +865,11 @@ namespace cw } - bool is_connected_to_source_proc( const variable_t* var ) - { - // if this var does not have a 'src_ptr' then it can't be connected to an external proc - if( var->src_var == nullptr || var->value == nullptr ) - return false; - - // if this var is using a local value then it can't be connected to an external proc - for(unsigned i=0; ivalue == var->local_value + i ) - return false; - - return true; - } - - bool is_a_source_var( const variable_t* var ) - { return var->connect_link != nullptr; } + + // 'valueTypeFlag' is the type (tflag) of 'value' template< typename T > - rc_t _var_set_driver( variable_t* var, unsigned typeFlag, T value ) + rc_t _var_set_driver( variable_t* var, unsigned valueTypeFlag, T value ) { rc_t rc; @@ -647,15 +881,18 @@ namespace cw // if this assignment targets a specific channel ... if( var->chIdx != kAnyChIdx ) { - rc = _var_set_template( var, typeFlag, value ); // ... then set it alone + rc = _var_set_template( var, valueTypeFlag, value ); // ... then set it alone } else // ... otherwise set all channels. { for(; var!=nullptr; var=var->ch_link) - if((rc = _var_set_template( var, typeFlag, value )) != kOkRC) + if((rc = _var_set_template( var, valueTypeFlag, value )) != kOkRC) break; } + if(rc != kOkRC ) + rc = cwLogError(rc,"Variable value set failed on '%s:%i %s:%i",cwStringNullGuard(var->inst->label),var->inst->label_sfx_id,cwStringNullGuard(var->label),var->label_sfx_id); + return rc; } @@ -703,24 +940,77 @@ namespace cw { rc_t rc = kOkRC; - // get the variable type - note that the value type (value->flags) may be differnt - unsigned typeFlag = var->varDesc->type & kTypeMask; + // Determine the flow variable type of cfg. argument 'value'. + unsigned value_flag = 0; - switch( typeFlag ) + switch( value->type->id ) + { + case kCharTId: + case kUInt8TId: + case kUInt16TId: + case kUInt32TId: + value_flag = kUIntTFl; + break; + + case kInt8TId: + case kInt16TId: + case kInt32TId: + value_flag = kIntTFl; + break; + + case kInt64TId: + case kUInt64TId: + rc = cwLogError(kInvalidArgRC,"The flow system does not currently implement 64bit integers."); + goto errLabel; + break; + + case kFloatTId: + value_flag = kFloatTFl; + break; + + case kDoubleTId: + value_flag = kDoubleTFl; + break; + + case kBoolTId: + value_flag = kBoolTFl; + break; + + case kStringTId: + case kCStringTId: + value_flag = kStringTFl; + break; + + default: + value_flag = kCfgTFl; + + } + + /* + if( !cwIsFlag(var->varDesc->type & kTypeMask, value_flag) ) + { + rc = cwLogError(kTypeMismatchRC,"The var desc class type 0x%x does not permit the var to be instantiated with the type %s (0x%x).",var->varDesc->type,_typeFlagToLabel(value_flag),value_flag); + goto errLabel; + } + */ + + switch( value_flag ) { case kBoolTFl: { bool v; + // assign the value of 'value' to to 'v' (do type conversion if necessary) if((rc = value->value(v)) == kOkRC ) - rc = _var_set_driver( var, typeFlag, v ); + // set the value of 'var' where 'v' is the new value and 'value_flag' is the type of 'v'. + rc = _var_set_driver( var, value_flag, v ); } - break; + break; case kUIntTFl: { unsigned v; if((rc = value->value(v)) == kOkRC ) - rc = _var_set_driver( var, typeFlag, v ); + rc = _var_set_driver( var, value_flag, v ); } break; @@ -728,7 +1018,7 @@ namespace cw { int v; if((rc = value->value(v)) == kOkRC ) - rc = _var_set_driver( var, typeFlag, v ); + rc = _var_set_driver( var, value_flag, v ); } break; @@ -736,7 +1026,7 @@ namespace cw { float v; if((rc = value->value(v)) == kOkRC ) - rc = _var_set_driver( var, typeFlag, v ); + rc = _var_set_driver( var, value_flag, v ); } break; @@ -744,7 +1034,7 @@ namespace cw { double v; if((rc = value->value(v)) == kOkRC ) - rc = _var_set_driver( var, typeFlag, v ); + rc = _var_set_driver( var, value_flag, v ); } break; @@ -752,21 +1042,31 @@ namespace cw { const char* v; if((rc = value->value(v)) == kOkRC ) - rc = _var_set_driver( var, typeFlag, v ); + rc = _var_set_driver( var, value_flag, v ); } break; - default: - rc = cwLogError(kOpFailRC,"The variable type 0x%x cannot yet be set via a preset.", var->varDesc->type ); + case kCfgTFl: + { + //const object_t* v; + //if((rc = value->value(v)) == kOkRC ) + rc = _var_set_driver( var, value_flag, value ); + } + break; + + default: + rc = cwLogError(kOpFailRC,"The variable type 0x%x cannot yet be set via a cfg.", var->varDesc->type ); goto errLabel; } errLabel: if( rc != kOkRC ) rc = cwLogError(kSyntaxErrorRC,"The %s:%i.%s:%i could not extract a type:%s from a configuration value.",var->inst->label,var->inst->label_sfx_id,var->label,var->label_sfx_id,_typeFlagToLabel(var->varDesc->type & kTypeMask)); + return rc; } + rc_t _var_map_id_to_index( instance_t* inst, unsigned vid, unsigned chIdx, unsigned& idxRef ) { @@ -830,9 +1130,51 @@ namespace cw } + /* + void _var_set_value_to_typed_null( variable_t* var, unsigned type_flag ) + { + for(unsigned i=0; ilocal_value[i].tflag == kInvalidTFl ); + + // set the var-value to a typed null value - this will force later settings to be coerced to this type + set_null( var->local_value[i], type_flag ); + } + + var->value = var->local_value; + + } + */ + + rc_t _var_set_type( variable_t* var, unsigned type_flag ) + { + rc_t rc = kOkRC; + + if( cwIsNotFlag(var->classVarDesc->type,kRuntimeTFl) ) + { + rc = cwLogError(kOpFailRC,"It is invalid to change the type of a statically (non-runtime) type variable."); + goto errLabel; + } + + if( var->localVarDesc == nullptr ) + { + var->localVarDesc = mem::allocZ(); + *(var->localVarDesc) = *(var->classVarDesc); + var->localVarDesc->link = nullptr; + } + + var->localVarDesc->type = type_flag; + var->varDesc = var->localVarDesc; + + //_var_set_value_to_typed_null(var,type_flag); + + errLabel: + return rc; + } + // Create a variable and set it's value from 'value_cfg'. // If 'value_cfg' is null then use the value from var->varDesc->val_cfg. - rc_t _var_create( instance_t* inst, const char* var_label, unsigned sfx_id, unsigned id, unsigned chIdx, const object_t* value_cfg, variable_t*& varRef ) + rc_t _var_create( instance_t* inst, const char* var_label, unsigned sfx_id, unsigned id, unsigned chIdx, const object_t* value_cfg, unsigned altTypeFl, variable_t*& varRef ) { rc_t rc = kOkRC; variable_t* var = nullptr; @@ -858,19 +1200,24 @@ namespace cw var = mem::allocZ(); var->varDesc = vd; + var->classVarDesc = vd; var->inst = inst; var->label = mem::duplStr(var_label); var->label_sfx_id = sfx_id; var->vid = id; var->chIdx = chIdx; var->value = nullptr; + var->type = kInvalidTFl; + if( altTypeFl != kInvalidTFl ) + _var_set_type(var,altTypeFl); + // if no value was given then set the value to the value given in the class if( value_cfg == nullptr ) value_cfg = var->varDesc->val_cfg; // if value_cfg is valid set the variable value - if( value_cfg != nullptr ) + if( value_cfg != nullptr && cwIsNotFlag(vd->type,kRuntimeTFl)) if((rc = _set_var_value_from_cfg( var, value_cfg )) != kOkRC ) goto errLabel; @@ -897,23 +1244,6 @@ namespace cw return rc; } - void _var_print( const variable_t* var ) - { - const char* conn_label = is_connected_to_source_proc(var) ? "extern" : " "; - - printf(" %20s:%5i id:%4i ch:%3i : %s : ", var->label, var->label_sfx_id, var->vid, var->chIdx, conn_label ); - - if( var->value == nullptr ) - _value_print( &var->local_value[0] ); - else - _value_print( var->value ); - - if( var->src_var != nullptr ) - printf(" src:%s:%i.%s:%i",var->src_var->inst->label,var->src_var->inst->label_sfx_id,var->src_var->label,var->src_var->label_sfx_id); - - printf("\n"); - } - rc_t _preset_set_var_value( instance_t* inst, const char* var_label, unsigned sfx_id, unsigned chIdx, const object_t* value ) { @@ -932,9 +1262,6 @@ namespace cw return rc; } - - - } } @@ -1042,7 +1369,6 @@ cw::flow::fbuf_t* cw::flow::fbuf_create( srate_t srate, unsigned chN, const unsi return f; } - cw::flow::fbuf_t* cw::flow::fbuf_create( srate_t srate, unsigned chN, unsigned maxBinN, unsigned binN, unsigned hopSmpN, const fd_sample_t** magV, const fd_sample_t** phsV, const fd_sample_t** hzV ) { unsigned maxBinN_V[ chN ]; @@ -1053,11 +1379,8 @@ cw::flow::fbuf_t* cw::flow::fbuf_create( srate_t srate, unsigned chN, unsigned vop::fill( binN_V, chN, binN ); vop::fill( hopSmpN_V, chN, binN ); return fbuf_create( srate, chN, maxBinN_V, binN_V, hopSmpN_V, magV, phsV, hzV ); - } - - void cw::flow::fbuf_destroy( fbuf_t*& fbuf ) { if( fbuf == nullptr ) @@ -1120,6 +1443,9 @@ unsigned cw::flow::value_type_label_to_flag( const char* s ) return flags; } +const char* cw::flow::value_type_flag_to_label( unsigned flag ) +{ return _typeFlagToLabel(flag); } + cw::flow::class_desc_t* cw::flow::class_desc_find( flow_t* p, const char* label ) { @@ -1186,6 +1512,48 @@ void cw::flow::network_print( const network_t& net ) } } +cw::rc_t cw::flow::instance_validate( instance_t* inst ) +{ + rc_t rc = kOkRC; + + for(variable_t* var=inst->varL; var!=nullptr; var=var->var_link) + { + if( var->label == nullptr ) + { + rc = cwLogError(kInvalidStateRC,"A var with no label was encountered."); + continue; + } + + if( var->value == nullptr ) + { + rc = cwLogError(kInvalidStateRC,"The var '%s:%i' has no value.",var->label,var->label_sfx_id); + continue; + } + + if(!math::isPowerOfTwo( var->value->tflag ) ) + { + rc = cwLogError(kInvalidStateRC,"The var '%s:%i' has the invalid type flag:0x%x",var->label,var->label_sfx_id,var->value->tflag); + continue; + } + + if( !(var->varDesc->type & var->value->tflag) ) + { + rc = cwLogError(kInvalidStateRC, "The value type flag '%s' (0x%x) of the var '%s:%i' is not found in the variable class type flags: '%s' (0x%x)", + _typeFlagToLabel(var->value->tflag),var->value->tflag, + var->label,var->label_sfx_id, + _typeFlagToLabel(var->varDesc->type),var->varDesc->type); + continue; + } + + // By setting the var->type field all future assignments to this variable + // must be coercible to this type. See _var_set_template() + var->type = var->value->tflag; + } + + return rc; +} + + cw::flow::instance_t* cw::flow::instance_find( network_t& net, const char* inst_label, unsigned sfx_id ) { for(unsigned i=0; iclass_desc->members->report( inst ); } +unsigned cw::flow::instance_var_count( instance_t* inst ) +{ + unsigned n = 0; + for(variable_t* var=inst->varL; var!=nullptr; var=var->var_link) + ++n; + + return n; +} void cw::flow::_var_destroy( variable_t* var ) @@ -1239,17 +1615,21 @@ void cw::flow::_var_destroy( variable_t* var ) { for(unsigned i=0; ilocal_value+i); + + if( var->localVarDesc != nullptr ) + mem::release(var->localVarDesc); + mem::release(var->label); mem::release(var); } } -cw::rc_t cw::flow::var_create( instance_t* inst, const char* var_label, unsigned sfx_id, unsigned id, unsigned chIdx, const object_t* value_cfg, variable_t*& varRef ) +cw::rc_t cw::flow::var_create( instance_t* inst, const char* var_label, unsigned sfx_id, unsigned id, unsigned chIdx, const object_t* value_cfg, unsigned altTypeFl, variable_t*& varRef ) { rc_t rc = kOkRC; - - rc = _var_create( inst, var_label, sfx_id, id, chIdx, value_cfg, varRef ); + + rc = _var_create( inst, var_label, sfx_id, id, chIdx, value_cfg, altTypeFl, varRef ); return rc; } @@ -1281,7 +1661,7 @@ cw::rc_t cw::flow::var_channelize( instance_t* inst, const char* var_label, uns if( var == nullptr && chIdx != kAnyChIdx ) { // create the channelized var - if((rc = _var_create( inst, var_label, sfx_id, vid, chIdx, value_cfg, var )) != kOkRC ) + if((rc = _var_create( inst, var_label, sfx_id, vid, chIdx, value_cfg, kInvalidTFl, var )) != kOkRC ) goto errLabel; // if no value was set then set the value from the 'any' channel @@ -1321,6 +1701,85 @@ cw::rc_t cw::flow::var_channelize( instance_t* inst, const char* var_label, uns return rc; } +cw::rc_t cw::flow::var_call_custom_value_func( variable_t* var ) +{ + rc_t rc; + if((rc = var->inst->class_desc->members->value( var->inst, var )) != kOkRC ) + goto errLabel; + + if( var->flags & kLogVarFl ) + { + printf("%10s:%5i", var->inst->label,var->inst->label_sfx_id); + _var_print(var); + } + +errLabel: + return rc; + +} + +cw::rc_t cw::flow::var_flags( instance_t* inst, unsigned chIdx, const char* var_label, unsigned sfx_id, unsigned& flags_ref ) +{ + rc_t rc = kOkRC; + variable_t* var = nullptr; + + flags_ref = 0; + + if((rc = _var_find_on_label_and_ch(inst,var_label,sfx_id,chIdx,var)) != kOkRC ) + goto errLabel; + + flags_ref = var->flags; + +errLabel: + return rc; +} + +cw::rc_t cw::flow::var_set_flags( instance_t* inst, unsigned chIdx, const char* var_label, unsigned sfx_id, unsigned flag ) +{ + rc_t rc = kOkRC; + variable_t* var = nullptr; + + if((rc = _var_find_on_label_and_ch(inst,var_label,sfx_id,chIdx,var)) != kOkRC ) + goto errLabel; + + + var->flags |= flag; + +errLabel: + return rc; +} + +cw::rc_t cw::flow::var_clr_flags( instance_t* inst, unsigned chIdx, const char* var_label, unsigned sfx_id, unsigned flag ) +{ + rc_t rc = kOkRC; + variable_t* var = nullptr; + + if((rc = _var_find_on_label_and_ch(inst,var_label,sfx_id,chIdx,var)) != kOkRC ) + goto errLabel; + + var->flags = cwClrFlag(var->flags,flag); +errLabel: + return rc; +} +/* +cw::rc_t cw::flow::var_set_type( instance_t* inst, unsigned chIdx, const char* var_label, unsigned sfx_id, unsigned type_flag ) +{ + rc_t rc = kOkRC; + variable_t* var = nullptr; + + if((rc = _var_find_on_label_and_ch(inst,var_label,sfx_id,chIdx,var)) != kOkRC ) + goto errLabel; + + rc = _var_set_type(var,type_flag); + +errLabel: + if( rc != kOkRC ) + rc = cwLogError(rc,"Type set failed on the variable:'%s:%i",cwStringNullGuard(var_label),sfx_id); + + return rc; +} +*/ + bool cw::flow::var_exists( instance_t* inst, const char* label, unsigned sfx_id, unsigned chIdx ) { return _var_find_on_label_and_ch(inst,label,sfx_id,chIdx) != nullptr; } @@ -1492,6 +1951,60 @@ cw::rc_t cw::flow::var_register( instance_t* inst, const char* var_label, unsign return rc; } +bool cw::flow::is_connected_to_source_proc( const variable_t* var ) +{ + // if this var does not have a 'src_ptr' then it can't be connected to an external proc + if( var->src_var == nullptr || var->value == nullptr ) + return false; + + // if this var is using a local value then it can't be connected to an external proc + for(unsigned i=0; ivalue == var->local_value + i ) + return false; + + return true; +} + +bool cw::flow::is_a_source_var( const variable_t* var ) +{ return var->dst_head != nullptr; } + +cw::rc_t cw::flow::var_mult_sfx_id_array( instance_t* inst, const char* var_label, unsigned* idA, unsigned idAllocN, unsigned& idN_ref ) +{ + rc_t rc = kOkRC; + + idN_ref = 0; + + // for each variable whose 'label' is 'var_label' + for(variable_t* var=inst->varL; var!=nullptr; var=var->var_link) + if( textIsEqual(var->label,var_label) ) + { + // scan idA[] for a matching sfx_id + unsigned i=0; + for(; ilabel_sfx_id ) + break; + + // if the sfx_id of this var has not yet been included in idA[] + if( i == idN_ref ) + { + // ... and we still have space left in the output arrau + if( idN_ref >= idAllocN ) + { + rc = cwLogError(kBufTooSmallRC,"The mult-sfx-id result array is too small for the var:'%s'.",cwStringNullGuard(var_label)); + goto errLabel; + } + + // store this sfx_id in idA[] + idA[idN_ref++] = var->label_sfx_id; + } + } + +errLabel: + if( rc != kOkRC ) + idN_ref = 0; + + return rc; +} cw::rc_t cw::flow::var_register_and_set( instance_t* inst, const char* var_label, unsigned sfx_id, unsigned vid, unsigned chIdx, variable_t*& varRef ) { @@ -1588,6 +2101,54 @@ cw::rc_t cw::flow::var_get( const variable_t* var, const mbuf_t*& valRef ) cw::rc_t cw::flow::var_get( variable_t* var, mbuf_t*& valRef ) { return _val_get_driver(var,valRef); } +cw::rc_t cw::flow::var_get( const variable_t* var, const object_t*& valRef ) +{ return _val_get_driver(var,valRef); } + +cw::rc_t cw::flow::var_set( variable_t* var, const value_t* val ) +{ + rc_t rc = kOkRC; + + switch( val->tflag ) + { + case kBoolTFl: rc = _var_set_driver(var,val->tflag,val->u.b); break; + case kUIntTFl: rc = _var_set_driver(var,val->tflag,val->u.u); break; + case kIntTFl: rc = _var_set_driver(var,val->tflag,val->u.i); break; + case kFloatTFl: rc = _var_set_driver(var,val->tflag,val->u.f); break; + case kDoubleTFl: rc = _var_set_driver(var,val->tflag,val->u.d); break; + case kStringTFl: rc = _var_set_driver(var,val->tflag,val->u.s); break; + case kCfgTFl: rc = _var_set_driver(var,val->tflag,val->u.cfg); break; + case kABufTFl: rc = _var_set_driver(var,val->tflag,val->u.abuf); break; + case kFBufTFl: rc = _var_set_driver(var,val->tflag,val->u.fbuf); break; + case kMBufTFl: rc = _var_set_driver(var,val->tflag,val->u.mbuf); break; + default: + rc = cwLogError(kNotImplementedRC,"The var_set() from value_t has not been implemented for the type 0x%x.",val->tflag); + } + + return rc; +} + + +cw::rc_t cw::flow::var_set( variable_t* var, bool val ) { return _var_set_driver(var,kBoolTFl,val); } +cw::rc_t cw::flow::var_set( variable_t* var, uint_t val ) { return _var_set_driver(var,kUIntTFl,val); } +cw::rc_t cw::flow::var_set( variable_t* var, int_t val ) { return _var_set_driver(var,kIntTFl,val); } +cw::rc_t cw::flow::var_set( variable_t* var, float val ) { return _var_set_driver(var,kFloatTFl,val); } +cw::rc_t cw::flow::var_set( variable_t* var, double val ) { return _var_set_driver(var,kDoubleTFl,val); } +cw::rc_t cw::flow::var_set( variable_t* var, const char* val ) { return _var_set_driver(var,kStringTFl,val); } +cw::rc_t cw::flow::var_set( variable_t* var, abuf_t* val ) { return _var_set_driver(var,kABufTFl,val); } +cw::rc_t cw::flow::var_set( variable_t* var, fbuf_t* val ) { return _var_set_driver(var,kFBufTFl,val); } +cw::rc_t cw::flow::var_set( variable_t* var, mbuf_t* val ) { return _var_set_driver(var,kMBufTFl,val); } +cw::rc_t cw::flow::var_set( variable_t* var, const object_t* val ) { return _var_set_driver(var,kCfgTFl,val); } + +cw::rc_t cw::flow::var_set( instance_t* inst, unsigned vid, unsigned chIdx, const value_t* val ) +{ + rc_t rc = kOkRC; + variable_t* var = nullptr; + + if((rc = _var_find_to_set(inst, vid, chIdx, val->tflag, var )) == kOkRC ) + rc = var_set(var,val); + + return rc; +} cw::rc_t cw::flow::var_set( instance_t* inst, unsigned vid, unsigned chIdx, bool val ) { @@ -1595,7 +2156,7 @@ cw::rc_t cw::flow::var_set( instance_t* inst, unsigned vid, unsigned chIdx, bool variable_t* var = nullptr; if((rc = _var_find_to_set(inst, vid, chIdx, kBoolTFl, var )) == kOkRC ) - _var_set_driver( var, kBoolTFl, val ); + rc = _var_set_driver( var, kBoolTFl, val ); return rc; } @@ -1606,7 +2167,7 @@ cw::rc_t cw::flow::var_set( instance_t* inst, unsigned vid, unsigned chIdx, uint variable_t* var = nullptr; if((rc = _var_find_to_set(inst, vid, chIdx, kUIntTFl, var )) == kOkRC ) - _var_set_driver( var, kUIntTFl, val ); + rc = _var_set_driver( var, kUIntTFl, val ); return rc; } @@ -1617,7 +2178,7 @@ cw::rc_t cw::flow::var_set( instance_t* inst, unsigned vid, unsigned chIdx, int_ variable_t* var = nullptr; if((rc = _var_find_to_set(inst, vid, chIdx, kIntTFl, var )) == kOkRC ) - _var_set_driver( var, kIntTFl, val ); + rc = _var_set_driver( var, kIntTFl, val ); return rc; } @@ -1628,7 +2189,7 @@ cw::rc_t cw::flow::var_set( instance_t* inst, unsigned vid, unsigned chIdx, floa variable_t* var = nullptr; if((rc = _var_find_to_set(inst, vid, chIdx, kFloatTFl, var )) == kOkRC ) - _var_set_driver( var, kFloatTFl, val ); + rc = _var_set_driver( var, kFloatTFl, val ); return rc; } @@ -1639,7 +2200,7 @@ cw::rc_t cw::flow::var_set( instance_t* inst, unsigned vid, unsigned chIdx, doub variable_t* var = nullptr; if((rc = _var_find_to_set(inst, vid, chIdx, kDoubleTFl, var )) == kOkRC ) - _var_set_driver( var, kDoubleTFl, val ); + rc = _var_set_driver( var, kDoubleTFl, val ); return rc; } @@ -1650,11 +2211,23 @@ cw::rc_t cw::flow::var_set( instance_t* inst, unsigned vid, unsigned chIdx, cons variable_t* var = nullptr; if((rc = _var_find_to_set(inst, vid, chIdx, kStringTFl, var )) == kOkRC ) - _var_set_driver( var, kStringTFl, val ); + rc = _var_set_driver( var, kStringTFl, val ); return rc; } +cw::rc_t cw::flow::var_set( instance_t* inst, unsigned vid, unsigned chIdx, const object_t* val ) +{ + rc_t rc = kOkRC; + variable_t* var = nullptr; + + if((rc = _var_find_to_set(inst, vid, chIdx, kCfgTFl, var )) == kOkRC ) + rc = _var_set_driver( var, kCfgTFl, val ); + + return rc; +} + + const cw::flow::preset_t* cw::flow::class_preset_find( class_desc_t* cd, const char* preset_label ) { const preset_t* pr; diff --git a/cwFlowTypes.h b/cwFlowTypes.h index a0ae0c7..3116516 100644 --- a/cwFlowTypes.h +++ b/cwFlowTypes.h @@ -40,11 +40,11 @@ namespace cw unsigned* maxBinN_V; // 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_sample_t** magV; // magV[ chN ][ binN ] - fd_sample_t** phsV; // phsV[ chN ][ binN ] - fd_sample_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_sample_t* buf; // memory used by this buffer (or NULL if magV,phsV,hzV point are proxied to another buffer) + fd_sample_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 @@ -78,6 +78,8 @@ namespace cw kTypeMask = 0x0000ffff, + kRuntimeTFl = 0x80000000 + }; typedef struct mtx_str @@ -92,7 +94,8 @@ namespace cw typedef struct value_str { - unsigned flags; + unsigned tflag; + union { bool b; uint_t u; @@ -108,6 +111,7 @@ namespace cw char* s; const object_t* cfg; + void* p; } u; @@ -116,8 +120,9 @@ 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); } + 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,kBoolTFl|kUIntTFl|kIntTFl|kFloatTFl|kDoubleTFl); } + inline bool is_matrix( const value_t* v ) { return cwIsFlag(v->tflag,kBoolMtxTFl|kUIntMtxTFl|kIntMtxTFl|kFloatMtxTFl|kDoubleMtxTFl); } struct instance_str; struct variable_str; @@ -170,24 +175,40 @@ namespace cw unsigned polyLimitN; // max. poly copies of this class per network_t or 0 if no limit } class_desc_t; + enum { + kInvalidVarFl = 0x00, + kLogVarFl = 0x01 + }; // 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 + char* label; // this variables label - unsigned label_sfx_id; // the label suffix id of this variable or kInvalidIdx if this has no suffix - 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 - 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 label_sfx_id; // the label suffix id of this variable or kInvalidIdx if this has no suffix + unsigned vid; // this variables numeric id ( cat(vid,chIdx) forms a unique variable identifier on this 'inst' 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 + unsigned flags; // kLogVarFl + 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()) + struct variable_str* src_var; // pointer to this input variables source link (or null if it uses the local_value) + 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. + } variable_t; @@ -277,11 +298,13 @@ namespace cw 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->flags & kABufTFl; } - inline bool value_is_fbuf( const value_t* v ) { return v->flags & kFBufTFl; } + 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 ); + + //------------------------------------------------------------------------------------------------------------------------ // // Class and Variable Description @@ -305,6 +328,8 @@ namespace cw // // Instance // + + rc_t instance_validate( instance_t* inst ); instance_t* instance_find( network_t& net, const char* inst_label, unsigned sfx_id ); rc_t instance_find( network_t& net, const char* inst_label, unsigned sfx_id, instance_t*& instPtrRef ); @@ -313,6 +338,9 @@ namespace cw void instance_print( instance_t* inst ); + // Count of all var instances on this proc. This is a count of the length of inst->varL. + unsigned instance_var_count( instance_t* inst ); + //------------------------------------------------------------------------------------------------------------------------ @@ -321,13 +349,26 @@ 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 sfx_id, 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( instance_t* inst, const char* var_label, unsigned sfx_id, unsigned vid, unsigned chIdx, const object_t* value_cfg, unsigned altTypeFlag, variable_t*& varRef ); // 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 sfx_id, unsigned chIdx, const object_t* value_cfg, unsigned vid, variable_t*& varRef ); + // Set the var. type at runtime. + //rc_t var_set_type( instance_t* inst, unsigned chIdx, const char* var_label, unsigned sfx_id, unsigned type_flags ); + + // Wrapper around call to var->inst->members->value() + rc_t var_call_custom_value_func( variable_t* var ); + + // Sets and get the var->flags field + unsigned var_flags( instance_t* inst, unsigned chIdx, const char* var_label, unsigned sfx_id, unsigned& flags_ref ); + rc_t var_set_flags( instance_t* inst, unsigned chIdx, const char* var_label, unsigned sfx_id, unsigned flags ); + rc_t var_clr_flags( instance_t* inst, 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 sfx_id, unsigned vid, unsigned chIdx, const object_t* value_cfg, variable_t*& varRef ); @@ -336,8 +377,9 @@ namespace cw // Return true if this var is acting as a source for another var. bool is_a_source_var( const variable_t* var ); - - + + rc_t var_mult_sfx_id_array( instance_t* inst, const char* var_label, unsigned* idA, unsigned idAllocN, unsigned& idN_ref ); + //----------------- // // var_register @@ -461,18 +503,19 @@ namespace cw rc_t var_channel_count( const variable_t* var, unsigned& chCntRef ); - rc_t var_get( const variable_t* var, bool& valRef ); - rc_t var_get( const variable_t* var, uint_t& valRef ); - rc_t var_get( const variable_t* var, int_t& valRef ); - rc_t var_get( const variable_t* var, float& valRef ); - rc_t var_get( const variable_t* var, double& valRef ); - rc_t var_get( const variable_t* var, const char*& valRef ); - rc_t var_get( const variable_t* var, const abuf_t*& valRef ); - 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, bool& valRef ); + rc_t var_get( const variable_t* var, uint_t& valRef ); + rc_t var_get( const variable_t* var, int_t& valRef ); + rc_t var_get( const variable_t* var, float& valRef ); + rc_t var_get( const variable_t* var, double& valRef ); + rc_t var_get( const variable_t* var, const char*& valRef ); + rc_t var_get( const variable_t* var, const abuf_t*& valRef ); + 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) @@ -494,14 +537,28 @@ namespace cw 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 ); + 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( instance_t* inst, unsigned vid, unsigned chIdx, const value_t* val ); + 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 ); + rc_t var_set( instance_t* inst, unsigned vid, unsigned chIdx, const object_t* val ); const preset_t* class_preset_find( class_desc_t* cd, const char* preset_label ); diff --git a/flow/flow_doc.md b/flow/flow_doc.md index 45eac60..798f8bc 100644 --- a/flow/flow_doc.md +++ b/flow/flow_doc.md @@ -23,7 +23,32 @@ and audio processing networks and the application of network state data. ### Polyphonic Network. -3. Network with sub-nets. +### Network with sub-nets. + +## Proc Instance Syntax: + +``` +