//| Copyright: (C) 2019-2020 Kevin Larke //| License: GNU GPL version 3.0 or above. See the accompanying LICENSE file. #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "Fl_Vert_Progress.h" #include "Fl_File_Btn.h" #include "Fl_Splitter.h" #include "cmGlobal.h" #include "cmFloatTypes.h" #include "cmRpt.h" #include "cmErr.h" #include "cmCtx.h" #include "cmMem.h" #include "cmMallocDebug.h" #include "cmLinkedHeap.h" #include "cmText.h" #include "cmSymTbl.h" #include "cmJson.h" #include "cmFileSys.h" #include "cmPrefs.h" #include "cmAudioFile.h" #include "cmThread.h" #include "cmSymTbl.h" #include "cmProcTest.h" #include "cmDspValue.h" #include "cmMsgProtocol.h" #include "cmAudDspIF.h" #include "cmAudDspLocal.h" #include "cmAudioFile.h" #include "cmAudioFileMgr.h" #include "cmTime.h" #include "cmMidi.h" #include "cmMidiFile.h" #include "cmTimeLine.h" #include "cmScore.h" #include "cmTakeSeqBldr.h" #include "cmdIf.h" #include "tlCtl.h" #include "kcApp.h" #include "cmGr.h" #include "cmGrDevCtx.h" #include "cmGrPlot.h" #include "cmGrPage.h" #include "cmGrFltk.h" #include "cmGrFltk.h" #include "gvHashFunc.h" #include "cmGrTksbFltk.h" #include "cmGr2dFltk.h" #define TIMER_PERIOD (1.0/20.0) // 50ms kcApp::kcApp(cmCtx_t* ctx, cmPrH_t prH, cmTsMp1cH_t printqH, int w, int h, const cmChar_t* title, cmAiH_t aiH, int argc, char* argv[] ) : Fl_Double_Window(w, h,title), _ctx(ctx),_prH(prH),_aiH(aiH), _menu(NULL),_con(NULL),_tabs(NULL),_mstr_grp(NULL), _as_btn(NULL),_ai_btn(NULL),_ao_btn(NULL),_pgm_btn(NULL), _ss_btn(NULL),_sr_btn(NULL),_ena_chk(NULL), _pageList(NULL),_ctlList(NULL), _stopTimerFl(false),_newPageFl(true), _incrColW(0),_colW(0),_horzBordFl(false),_horzBord(0), _ssCnt(0),_ssArray(0),_ssPhase(0),_ssUpdateFl(false), _printqH(printqH),_printFl(0), _tlCtl(NULL) { // install a callback to cleanup when the app window closes // (the btn callbacks and _getApp() rely on a pointer to 'this' being found in kcApp.user_data()) callback(_s_callback,this); // the main window is divided between the menu bar on top // and a horizontal splitter on the bottom begin(); _createMenu(w,kMenuH); end(); // Create a tab view add(_tabs = new Fl_Tabs(this->x(),this->y()+kMenuH,this->w(),this->h()-kMenuH)); _tabs->callback(_s_tab_cb,this); _tabs->end(); int tx=x(),ty=y(),th=this->h(),tw=this->w(); _tabs->client_area(tx,ty,tw,th); // Create the 'Setup' tab group Fl_Group* setup_grp = new Fl_Group(tx,ty,tw,th,"Setup"); _tabs->add(setup_grp); _createSetupDlg(setup_grp); // Create the console window Fl_Group* con_grp = new Fl_Group(tx,ty,tw,th,"Console"); _tabs->add(con_grp); Fl_Text_Buffer* buf = new Fl_Text_Buffer(); _con = new Fl_Text_Display(tx,ty,tw,th); _con->buffer(buf); con_grp->add(_con); // Create the master group _tabs->add(_mstr_grp = new Fl_Group(tx,ty,tw,th,"Master")); // Create an empty tab group and make it resizable // to prevent the other tab groups from being resizable. Fl_Group* wdgt = new Fl_Group(tx,ty+30,1,1); _tabs->add(wdgt); _tabs->resizable(wdgt); // make other tabs non-resizable // make the splitter the resizable group element (thereby making the menu non-resizable). // see:http://fltk.org/articles.php?L415+I0+T+M1000+P1 resizable(_tabs); show(argc, argv); // direct all output to the console window cmRptSetup(&_ctx->rpt,_s_print,_s_print, this); cmTsMp1cSetCbFunc(_printqH, _s_print_queue_cb, this ); // install a timer to check for messages from the engine Fl::add_timeout(TIMER_PERIOD,_s_status_timeout_cb,this); } kcApp::~kcApp() { _clearCtlList(true); _clearSsArray(); } void kcApp::resize(int x, int y, int w, int h) { Fl_Double_Window::resize(x, y, w, h); cmPrefsPathSetInt(_prH,"appWndW",w); cmPrefsPathSetInt(_prH,"appWndH",h); cmPrefsPathSetInt(_prH,"appWndX",x); cmPrefsPathSetInt(_prH,"appWndY",y); } void kcApp::tlCtlNewTimeLineFile( tlCtl* tlCtl, const cmChar_t* fn ) {} void kcApp::tlCtlNewScoreFile( tlCtl* tlCtl, const cmChar_t* fn ) {} void kcApp::_createSetupDlg( Fl_Group* grp ) { int ctl_width = 200; int vBord = 5; int xx = grp->x() + 5; int yy = grp->y() + vBord; grp->begin(); Fl_Button* btn = new Fl_Button(xx,yy,200,kMenuH,"Audio Device Report"); btn->callback( _s_btn_cb, kAudDevRptBtnId ); yy += btn->h() + vBord; _as_btn = new Fl_Menu_Button(xx,yy,ctl_width,kMenuH,"Audio System Cfg"); _as_btn->callback( _s_btn_cb, kAudioSysCfgBtnId); yy += _as_btn->h() + vBord; _ai_btn = new Fl_Menu_Button(xx,yy,ctl_width,kMenuH,"Audio Input Devices"); _ai_btn->callback( _s_btn_cb, kInAudDevBtnId); yy += _ai_btn->h() + vBord; _ao_btn = new Fl_Menu_Button(xx,yy,ctl_width,kMenuH,"Audio Output Devices"); _ao_btn->callback( _s_btn_cb, kOutAudDevBtnId); yy += _ao_btn->h() + vBord; _pgm_btn = new Fl_Menu_Button(xx,yy,ctl_width,kMenuH,"Programs"); _pgm_btn->callback( _s_btn_cb, kPgmBtnId); yy += _pgm_btn->h() + vBord; _ss_btn = new Fl_Menu_Button(xx,yy,ctl_width,kMenuH,"Sub-System"); _ss_btn->callback( _s_btn_cb, kSubSystemIdxBtnId); yy += _ss_btn->h() + vBord; _sr_btn = new Fl_Menu_Button(xx,yy,ctl_width,kMenuH,"Sample Rate"); _sr_btn->callback( _s_btn_cb, kSrateBtnId); _sr_btn->add("44100",0,NULL,(void*)44100,0); _sr_btn->add("48000",0,NULL,(void*)48000,0); _sr_btn->add("96000",0,NULL,(void*)96000,0); yy += _sr_btn->h() + vBord; btn = new Fl_Button(xx,yy,ctl_width,kMenuH,"Test"); btn->callback( _s_btn_cb, kTestBtnId ); yy += btn->h() + vBord; btn = new Fl_Button(xx,yy,200,kMenuH,"Print program"); btn->callback( _s_btn_cb, kPrintPgmBtnId ); yy += btn->h() + vBord; _ena_chk = new Fl_Check_Button(xx,yy,ctl_width,kMenuH,"Enable Audio"); _ena_chk->callback( _s_btn_cb, kEnableBtnId ); yy += _ena_chk->h() + vBord; // place this ctrl in the lower left corner to prevent the other controls from resizing Fl_Box* bx = new Fl_Box(FL_NO_BOX,xx+ctl_width,yy,10,10,NULL); grp->resizable(bx); grp->end(); } void kcApp::_createMenu(int w, int h) { Fl_Menu_Item items[] = { { "kc", 0, 0, 0, FL_SUBMENU }, { "&About", FL_COMMAND + 'a', (Fl_Callback*)_s_menu_cb, (void*)kAboutMenuId }, { "&Quit", FL_COMMAND + 'q', (Fl_Callback*)_s_menu_cb, (void*)kQuitMenuId }, { 0 }, { 0 } }; _menu = new Fl_Menu_Bar(0,0,w,h); _menu->copy(items); this->add(_menu); } kcApp::page_t* kcApp::_createPage( const char* title ) { int tx,ty,th,tw; _tabs->client_area(tx,ty,tw,th); page_t* page = new page_t; page->link = _pageList; _pageList = page; _newPageFl = true; // Create the 'Controls' tab group _tabs->begin(); page->grp = new Fl_Group(tx,ty,tw,th,title); page->grp->begin(); Fl_Box* bx = new Fl_Box(FL_NO_BOX,page->grp->x()+page->grp->w(),page->grp->y()+page->grp->h(),1,1,NULL); page->grp->resizable(bx); page->grp->end(); _tabs->end(); return page; } // return the count of engine ctls unsigned kcApp::_getCtlCount() const { unsigned cnt = 0; ctl_t* cp = _ctlList; for(; cp!=NULL; cp=cp->linkPtr) ++cnt; return cnt; } // delete all engine ctls - (except maybe the master ctrl's) void kcApp::_clearCtlList(bool mstrFl) { ctl_t* ncl = NULL; ctl_t* cp = _ctlList; while( cp != NULL ) { ctl_t* np = cp->linkPtr; // delete the ctrl unless it is a master ctl and the mstrFl is set if( cp->mstrFl==false || mstrFl ) { delete cp; } else { // append the master ctl to the new ctl list cp->linkPtr = ncl; ncl = cp; } cp = np; } // clear the page groups and pages page_t* pg = _pageList; while( pg != NULL ) { page_t* np = pg->link; pg->grp->clear(); delete pg->grp; delete pg; pg = np; } _pageList = NULL; //ctl_grp->clear(); _ctlList = ncl; // make the new ctl list the current ctl list if( mstrFl ) _mstr_grp->clear(); _tlCtl = NULL; } void kcApp::_clearSsArray() { if( _ssArray != NULL ) { delete[] _ssArray; _ssArray = NULL; } _ssCnt = 0; } // find a engine ui ctl given a dsp instance id kcApp::ctl_t* kcApp::_findCtl( unsigned instId, unsigned asSubIdx, unsigned mstrFl ) { ctl_t* cp = _ctlList; for(; cp!=NULL; cp=cp->linkPtr) if( cp->instId == instId && cp->asSubIdx==asSubIdx && cp->mstrFl==mstrFl ) return cp; //printf("not found:0x%x ssi:%i mstr:%i\n",instId,asSubIdx,mstrFl); return NULL; } // calc. the position and size of a new ctl being created // at the request of the engine void kcApp::_getNewCtlPosn( const cmDspUiHdr_t* m, int& x, int& y, int& w, int& h ) { enum { kPageH=700, kCtlH=30, kCtlW=200, kHBord=2, kVBord=2 }; // if this is control is being placed in the 'master' group if( m->uiId == cmInvalidId ) { //unsigned devIdx = cmAudioSysUiInstIdToDevIndex(m->instId); unsigned chIdx = cmAudioSysUiInstIdToChIndex(m->instId); unsigned outFl = !cmAudioSysUiInstIdToInFlag(m->instId); unsigned ctlId = cmAudioSysUiInstIdToCtlId(m->instId); int xOffs = 20; int yOffs = 100; int chH = 205; int chW = 44; int sldrH = 120; int sldrW = 20; int chkH = 20; int chkW = 35; x = xOffs + (chIdx * chW); y = yOffs + (m->asSubIdx * 2 * chH) + (outFl * chH); switch( ctlId ) { // slider case kSliderUiAsId: w=sldrW; h=sldrH; break; // meter case kMeterUiAsId: w=sldrW; h=sldrH; x+=sldrW; break; // mute case kMuteUiAsId: w=chkW; h=chkH; y+=sldrH + 2; break; // tone case kToneUiAsId: w=chkW; h=chkH; y+=sldrH + chkH + 4; break; // pass case kPassUiAsId: w=chkW; h=chkH; y+=sldrH + 2*chkH + 6; break; default: { assert(0); } } return; } // if this control is being placed in the 'controls' group ctl_t* cp = _ctlList; if( w == 0 ) { if( _colW == 0 ) _colW = kCtlW; w = _colW; } if( h == 0 ) h = kCtlH; // skip over controls that are assigned to the 'master' group while(cp != NULL && cp->mstrFl ) cp = cp->linkPtr; // if the page list is empty - create a default blank page if( _pageList == NULL ) _createPage("Controls"); Fl_Group* ctl_grp = _pageList->grp; // if the ctl list is empty if( cp == NULL || _newPageFl ) { _newPageFl = false; x = kHBord + ctl_grp->x(); y = kVBord + ctl_grp->y(); _horzBord = y; } else { // if a new horizontal - upper border was requested if( _horzBordFl ) { _horzBordFl = false; // locate the current control with the max bottom coordinate ctl_t* tp = cp; if( tp != NULL ) { int maxY = tp->wdgtPtr->y() + tp->wdgtPtr->h(); while(tp!=NULL) { if( tp->mstrFl == false && (tp->wdgtPtr->y() + tp->wdgtPtr->h()) > maxY ) maxY = tp->wdgtPtr->y() + tp->wdgtPtr->h(); tp = tp->linkPtr; } // set the new upper boundary to just below the bottom of // the lowest control _horzBord = maxY + kVBord*2; } x = kHBord + ctl_grp->x(); y = _horzBord; } else { // use the last ctl inserted in the list // (the first ctl in the list) // to position the next ctl x = cp->wdgtPtr->x(); y = cp->wdgtPtr->y() + cp->wdgtPtr->h() + kVBord; // if this control goes off the page or a new col was requested if( y+h > kPageH || _incrColW ) { x += _colW + kHBord; _colW = _incrColW; _incrColW = 0; y = _horzBord; } } } } kcApp::ctl_t* kcApp::_createCtl( const cmDspUiHdr_t* m, unsigned typeId, int& x, int& y, int& w, int& h, bool posnFl ) { ctl_t* cp = new ctl_t; cp->thisPtr = this; cp->asSubIdx = m->asSubIdx; cp->instId = m->instId; cp->typeId = typeId; cp->mstrFl = m->uiId == cmInvalidId; if( posnFl ) _getNewCtlPosn(m,x,y,w,h); return cp; } void kcApp::_insertNewCtl( ctl_t* cp, const cmDspUiHdr_t* m, Fl_Widget* wdgt, unsigned* varIdArray, unsigned varIdCnt ) { unsigned i; // _genNewCtlPosn() should have been called before this - where a new page list would have been created. assert( cp->mstrFl || (cp->mstrFl==false && _pageList != NULL) ); Fl_Group* grp = cp->mstrFl ? _mstr_grp : _pageList->grp; cp->wdgtPtr = wdgt; cp->wdgtPtr->callback(_s_ctl_cb, cp ); grp->add(cp->wdgtPtr); const unsigned* srcVarIdArray = cmDsvUIntCMtx(&m->value); assert( varIdArray != NULL && cmDsvEleCount(&m->value)==varIdCnt); for(i=0; ilinkPtr = _ctlList; _ctlList = cp; } // create a slider ctl at the request of the engine void kcApp::_createSlider( const cmDspUiHdr_t* m ) { int x,y,w=0,h=0; ctl_t* cp = _createCtl(m, m->selId==kSliderDuiId? kSldrTypeId : kNumbTypeId, x,y,w,h ); switch(m->selId) { case kSliderDuiId: cp->u.sldr.u.sldr = new Fl_Value_Slider(x,y,w,h); cp->u.sldr.val = static_cast(cp->u.sldr.u.sldr); cp->u.sldr.u.sldr->type(cp->mstrFl ? FL_VERT_NICE_SLIDER : FL_HOR_NICE_SLIDER); break; case kNumberDuiId: cp->u.sldr.u.numb = new Fl_Value_Input(x,y,w/2,h); cp->u.sldr.u.numb->when(FL_WHEN_ENTER_KEY | FL_WHEN_RELEASE ); cp->u.sldr.val = static_cast(cp->u.sldr.u.numb); break; } _insertNewCtl(cp, m, cp->u.sldr.val, cp->u.sldr.varIdArray, kSldrVarCnt ); } // handle slider value msg's arriving from the engine void kcApp::_setSldrValue( ctl_t* cp, unsigned instVarId, const cmDspValue_t* vp ) { unsigned i=0; Fl_Valuator* sp = cp->u.sldr.val; for(i=0; iu.sldr.varIdArray[i] == instVarId ) { switch(i) { case kSldrMinArgIdx: sp->minimum(cmDsvGetDouble(vp)); //sp->step((sp->maximum()-sp->minimum())/sp->w()); break; case kSldrMaxArgIdx: sp->maximum(cmDsvGetDouble(vp)); //sp->step((sp->maximum()-sp->minimum())/sp->w()); break; case kSldrStpArgIdx: sp->step(cmDsvGetDouble(vp)); break; case kSldrValArgIdx: sp->value(cmDsvGetDouble(vp)); break; case kSldrLblArgIdx: { int ww=0,hh=0; if( cmDsvStrcz(vp) == NULL ) { sp->label(NULL); sp->align(FL_ALIGN_INSIDE | FL_ALIGN_CENTER ); } else { sp->copy_label( cmDsvStrcz(vp) ); sp->align(FL_ALIGN_RIGHT); } if( cp->typeId == kSldrTypeId ) { sp->measure_label(ww,hh); sp->resize( sp->x(), sp->y(), sp->w() - ww, sp->h()); sp->redraw(); } } break; default: { assert(0); } } break; } } void kcApp::_createText( const cmDspUiHdr_t* m ) { int x,y,w=0,h=0; ctl_t* cp = _createCtl(m, kTextTypeId, x,y,w,h ); cp->u.text.text = new Fl_Input(x,y,w,h); cp->u.text.text->when(FL_WHEN_RELEASE | FL_WHEN_ENTER_KEY | FL_WHEN_NOT_CHANGED); _insertNewCtl(cp, m, cp->u.text.text, cp->u.text.varIdArray, kTextVarCnt ); } void kcApp::_setTextValue( ctl_t* cp, unsigned instVarId, const cmDspValue_t* vp ) { unsigned i=0; Fl_Input* tp = cp->u.text.text; for(i=0; iu.text.varIdArray[i] == instVarId ) { switch(i) { case kTextValArgIdx: tp->value( cmDsvStrcz(vp)); break; case kTextLblArgIdx: { const char* lbl = cmDsvStrcz(vp); if( lbl == NULL ) tp->label(NULL); else tp->copy_label( lbl ); tp->align(FL_ALIGN_RIGHT); tp->resize( tp->x(), tp->y(), tp->w()/2, tp->h()); tp->redraw(); } break; default: { assert(0); } } } } void kcApp::_createFnameCtl( const cmDspUiHdr_t* m ) { int x,y,w=0,h=0; ctl_t* cp = _createCtl(m, kFnamTypeId, x,y,w,h ); cp->u.fnam.fnam = new Fl_File_Btn(x,y,w,h); _insertNewCtl(cp, m, cp->u.fnam.fnam, cp->u.fnam.varIdArray, kFnamVarCnt ); } void kcApp::_setFnamValue( ctl_t* cp, unsigned instVarId, const cmDspValue_t* vp ) { unsigned i=0; Fl_File_Btn* bp = cp->u.fnam.fnam; for(i=0; iu.fnam.varIdArray[i] == instVarId ) { switch(i) { case kFnamValArgIdx: assert( cmDsvIsType( vp, kStrzDsvFl ) ); bp->filename( cmDsvStrcz(vp) ); bp->redraw(); break; case kFnamPatArgIdx: assert( cmDsvIsType( vp, kStrzDsvFl ) ); bp->pattern_string( cmDsvStrcz(vp) ); break; case kFnamDirArgIdx: bp->type( cmDsvBool(vp) ? Fl_File_Btn::kDir_Type_Id : Fl_File_Btn::kFile_Type_Id ); break; default: { assert(0); } } } } void kcApp::_createMlistCtl( const cmDspUiHdr_t* m ) { const unsigned* varIdArray = cmDsvUIntCMtx(&m->value); assert( varIdArray != NULL && cmDsvEleCount(&m->value)==kMlstVarCnt); int x,y,w=0; int h = varIdArray[kMlstHgtArgIdx]; bool menuBtnFl = h==0; h = menuBtnFl ? kMenuH : kMenuH * h; ctl_t* cp = _createCtl(m, kMlstTypeId, x,y,w,h ); Fl_Widget* wdgt; if(menuBtnFl) { cp->u.mlst.mlst = NULL; wdgt = cp->u.mlst.mbtn = new Fl_Menu_Button(x,y,w,h); } else { cp->u.mlst.mbtn = NULL; wdgt = cp->u.mlst.mlst = new Fl_Select_Browser(x,y,w,h); } _insertNewCtl(cp, m, wdgt, cp->u.mlst.varIdArray, kMlstVarCnt ); } kcApp::kcKmRC_t kcApp::_loadMlist( ctl_t* cp, const cmJsonNode_t* np ) { assert( cmJsonIsArray(np) ); kcKmRC_t rc = kOkKmRC; unsigned n = cmJsonChildCount(np); unsigned ri = 0; // empty the ctl data list if( cp->u.mlst.mbtn == NULL ) cp->u.mlst.mlst->clear(); else cp->u.mlst.mbtn->clear(); // for each element (row) in the JSON array for(ri=0; rierr,kMlistLoadFailKmRC,"The msg list line character buffer is too small at JSON array element index %i.",ri); break; case kInvalidNodeTypeJsRC: rc = cmErrMsg(&_ctx->err,kMlistLoadFailKmRC,"The msg list array element at index %i is not a JSON leaf node.",ri); break; default: rc = cmErrMsg(&_ctx->err,kMlistLoadFailKmRC,"JSON to msg list text conversion failed on array element %i.",ri); break; } break; } bbp = buf + strlen(buf); assert(bbp <= bep); // add the tab column marker if( j+1 < m ) { if( bep - bbp < 1 ) rc = cmErrMsg(&_ctx->err,kMlistLoadFailKmRC,"The msg list line buffer is too small."); else { *bbp++ = '\t'; *bbp = 0; } } } // insert the text for row i and skip the title row // See the _setMListValue() and _ctl_cb() for the associated index incr/decr. // so that the engine list indexes and UI list indexes match if( rc == kOkKmRC && ri > 0) { if( cp->u.mlst.mbtn==NULL) cp->u.mlst.mlst->add(buf); else cp->u.mlst.mbtn->add(buf); } } if( rc == kOkKmRC ) if( cp->u.mlst.mbtn != NULL ) { if( cp->u.mlst.sel < cp->u.mlst.mbtn->size() ) cp->u.mlst.mbtn->label(cp->u.mlst.mbtn->text(cp->u.mlst.sel));; } return rc; } void kcApp::_setMlistValue( ctl_t* cp, unsigned instVarId, const cmDspValue_t* vp ) { unsigned i=0; for(i=0; iu.mlst.varIdArray[i] == instVarId ) { switch(i) { case kMlstHgtArgIdx: break; case kMlstSelArgIdx: { unsigned idx = cmDsvUInt(vp); // decr. to account for skipping title row // (see the complementary incrementn in _ctl_cb()) if( idx > 0 && cp->u.mlst.mbtn!=NULL) --idx; if( cp->u.mlst.mbtn==NULL) cp->u.mlst.mlst->value(idx); else { cp->u.mlst.sel = idx; if( cp->u.mlst.sel < cp->u.mlst.mbtn->size() ) { cp->u.mlst.mbtn->value(cp->u.mlst.sel); cp->u.mlst.mbtn->label( cp->u.mlst.mbtn->text(cp->u.mlst.sel) ); } } cp->wdgtPtr->redraw(); } break; case kMlstLstArgIdx: _loadMlist( cp, cmDsvJson(vp) ); break; default: { assert(0); } } } } // create a meter ctl at the request of the engine void kcApp::_createMeter( const cmDspUiHdr_t* m ) { int x,y,w=0,h=15; ctl_t* cp = _createCtl(m,kMetrTypeId, x,y,w,h); if( cp->mstrFl ) cp->u.metr.prog = new Fl_Vert_Progress(x,y,w,h); else cp->u.metr.prog = new Fl_Progress(x,y,w,h); cp->u.metr.prog->color( fl_rgb_color((unsigned char)256),fl_rgb_color((unsigned char)128)); _insertNewCtl(cp,m,cp->u.metr.prog,cp->u.metr.varIdArray,kMetrVarCnt); } // handle meter value msg's arriving from the engine void kcApp::_setMeterValue( ctl_t* cp, unsigned instVarId, const cmDspValue_t* vp ) { unsigned i = 0; Fl_Progress* sp = cp->u.metr.prog; bool redrawFl = false; for(i=0; iu.metr.varIdArray[i] == instVarId ) { switch(i) { case kMetrMinArgIdx: sp->minimum(cmDsvGetDouble(vp)); redrawFl = true; break; case kMetrMaxArgIdx: sp->maximum(cmDsvGetDouble(vp)); redrawFl = true; break; case kMetrValArgIdx: { double v = cmDsvGetDouble(vp); if( sp->value() != v ) { sp->value(v); redrawFl = true; } break; } case kMetrLblArgIdx: { int ww=0,hh=0; sp->copy_label( cmDsvStrcz(vp) ); sp->align(FL_ALIGN_RIGHT); sp->measure_label(ww,hh); sp->resize( sp->x(), sp->y(), sp->w() - ww, sp->h()); redrawFl = true; } break; default: { assert(0); } } break; } if( redrawFl ) sp->redraw(); } // create a button ctl at the request of the engine void kcApp::_createButton( const cmDspUiHdr_t* m ) { int x,y,w=0,h=0; ctl_t* cp = _createCtl(m,kButnTypeId, x,y,w,h); cp->u.butn.butn = new Fl_Button(x,y,w,h); _insertNewCtl(cp,m,cp->u.butn.butn,cp->u.butn.varIdArray,kButnVarCnt); } // handle button value msg's arriving from the engine void kcApp::_setButtonValue( ctl_t* cp, unsigned instVarId, const cmDspValue_t* vp ) { unsigned i = 0; Fl_Button* sp = cp->u.butn.butn; bool redrawFl = false; for(i=0; iu.butn.varIdArray[i] == instVarId ) { switch(i) { case kButnValArgIdx: cp->u.butn.val = cmDsvGetDouble(vp); break; case kButnLblArgIdx: { sp->copy_label( cmDsvStrcz(vp) ); redrawFl = true; } break; default: { assert(0); } } break; } if( redrawFl ) sp->redraw(); } // create a button ctl at the request of the engine void kcApp::_createCheck( const cmDspUiHdr_t* m ) { int x,y,w=0,h=0; ctl_t* cp = _createCtl(m,kChckTypeId, x,y,w,h); cp->u.chck.chck = new Fl_Check_Button(x,y,w,h); _insertNewCtl(cp,m,cp->u.chck.chck,cp->u.chck.varIdArray,kChckVarCnt); } // handle check button value msg's arriving from the engine void kcApp::_setCheckValue( ctl_t* cp, unsigned instVarId, const cmDspValue_t* vp ) { unsigned i = 0; Fl_Button* sp = cp->u.chck.chck; bool redrawFl = false; for(i=0; iu.chck.varIdArray[i] == instVarId ) { switch(i) { case kChckValArgIdx: cp->u.chck.val = cmDsvGetDouble(vp); cp->u.chck.chck->value(cmDsvGetDouble(vp)>0); break; case kChckLblArgIdx: { int ww=0,hh=0; sp->copy_label( cmDsvStrcz(vp) ); sp->align(FL_ALIGN_RIGHT); sp->measure_label(ww,hh); sp->resize( sp->x(), sp->y(), sp->w() - ww, sp->h()); redrawFl = true; } break; default: { assert(0); } } break; } if( redrawFl ) sp->redraw(); } void kcApp::_createLabel( const cmDspUiHdr_t* m ) { int x,y,w=0,h=0; ctl_t* cp = _createCtl(m,kLablTypeId, x,y,w,h); cp->u.labl.box = new Fl_Box(x,y,w,h); _insertNewCtl(cp,m,cp->u.labl.box,cp->u.labl.varIdArray,kLablVarCnt); } void kcApp::_setLabelValue( ctl_t* cp, unsigned instVarId, const cmDspValue_t* vp ) { unsigned i = 0; Fl_Box* sp = cp->u.labl.box; bool redrawFl = false; for(i=0; iu.labl.varIdArray[i] == instVarId ) { switch(i) { case kLablValArgIdx: sp->copy_label(cmDsvStrcz(vp)); redrawFl = true; break; case kLablAlignArgIdx: switch( cmDsvUInt(vp) ) { case kRightAlignDuiId: sp->align(FL_ALIGN_RIGHT | FL_ALIGN_INSIDE); break; case kLeftAlignDuiId: sp->align(FL_ALIGN_LEFT | FL_ALIGN_INSIDE); break; case kCenterAlignDuiId: sp->align(FL_ALIGN_CENTER | FL_ALIGN_INSIDE); break; default: { assert(0); } } redrawFl = true; break; default: {assert(0);} } } if( redrawFl ) sp->redraw(); } void kcApp::_createTmln( const cmDspUiHdr_t* m ) { int x,y,w=0,h=0; page_t* pg = _createPage("TimeLine"); ctl_t* cp = _createCtl(m, kTmlnTypeId, x,y,w,h, true ); w = pg->grp->w(); h = pg->grp->h(); // currently we only support one score control because // we have not yet implmenented a method of providing // timer callbacks to a list of UI controls if( _tlCtl != NULL ) cp->u.tmln.tlctl = _tlCtl; else { _tlCtl = new tlCtl(_ctx,this,_menu,this); cp->u.tmln.tlctl = _tlCtl; } Fl_Widget* wdgt = cp->u.tmln.tlctl->initTimeLineCtlr(x,y,w,h); _insertNewCtl(cp,m,wdgt,cp->u.tmln.varIdArray,kTmlnVarCnt); } void kcApp::_setTmlnValue( ctl_t* cp, unsigned instVarId, const cmDspValue_t* vp ) { unsigned i=0; for(i=0; iu.tmln.varIdArray[i] == instVarId ) { switch(i) { case kTmlnFileArgIdx: { const char* fn; if((fn = cmDsvStrcz(vp)) != NULL ) { cp->u.tmln.tlctl->openTlFile(fn); //cp->u.tmln.tlctl->redraw(); } } break; case kTmlnPathArgIdx: { const char* path; if((path = cmDsvStrcz(vp)) != NULL ) { cp->u.tmln.tlctl->setAudioFilePath(path); } } break; case kTmlnSelArgIdx: //tp->value( cmDsvStrcz(vp)); break; case kTmlnMeasArgIdx: cp->u.tmln.tlctl->setTimeLineSelectBar( cmDsvUInt(vp)); break; case kTmlnCursArgIdx: cp->u.tmln.tlctl->setAudioFileCursor(cmDsvUInt(vp)); break; default: { assert(0); } } } } void kcApp::_createScor( const cmDspUiHdr_t* m ) { int x,y,w=0,h=0; page_t* pg = _createPage("Score"); ctl_t* cp = _createCtl(m, kScorTypeId, x,y,w,h, true ); w = pg->grp->w(); h = pg->grp->h(); // currently we only support one score control because // we have not yet implmenented a method of providing // timer callbacks to a list of UI controls if( _tlCtl != NULL ) cp->u.scor.tlctl = _tlCtl; else { _tlCtl = new tlCtl(_ctx,this,_menu,this); cp->u.scor.tlctl = _tlCtl; cp->u.scor.smpIdx = cmInvalidIdx; } Fl_Widget* wdgt = cp->u.scor.tlctl->initScoreCtlr(x,y,w,h); _insertNewCtl(cp,m,wdgt,cp->u.scor.varIdArray,kScorVarCnt); } void kcApp::_setScorValue( ctl_t* cp, unsigned instVarId, const cmDspValue_t* vp ) { unsigned i=0; //tlCtl* tp = cp->u.scor.tlctl; for(i=0; iu.scor.varIdArray[i] == instVarId ) { switch(i) { case kScorSelArgIdx: //tp->value( cmDsvStrcz(vp)); break; case kScorFileArgIdx: { const char* fn; if((fn = cmDsvStrcz(vp)) != NULL ) { cp->u.scor.tlctl->openScoreFile(fn); //cp->u.scor.tlctl->redraw(); } } break; case kScorSmpIdxArgIdx: cp->u.scor.smpIdx = cmDsvUInt(vp); break; case kScorPitchArgIdx: cp->u.scor.pitch = cmDsvUInt(vp); break; case kScorVelArgIdx: cp->u.scor.vel = cmDsvUInt(vp); break; case kScorEvtIdxArgIdx: cp->u.scor.evtIdx = cmDsvUInt(vp); break; case kScorDynArgIdx: { assert(cp->u.scor.evtIdx!=cmInvalidIdx); _tlCtl->setScoreDynLevel(cp->u.scor.evtIdx,cmDsvUInt(vp)); cp->u.scor.evtIdx = cmInvalidIdx; } break; case kScorLocIdxArgIdx: { assert( cp->u.scor.smpIdx != cmInvalidIdx ); cp->u.scor.locIdx = cmDsvUInt(vp); _tlCtl->setScoreLocation(cp->u.scor.locIdx,cp->u.scor.smpIdx,cp->u.scor.pitch,cp->u.scor.vel); cp->u.scor.smpIdx = cmInvalidIdx; } break; case kScorValTypeArgIdx: cp->u.scor.varId = cmDsvUInt(vp); break; case kScorValueArgIdx: _tlCtl->setScoreVarValue(cp->u.scor.locIdx,cp->u.scor.varId, cmDsvDouble(vp)); break; case kScorMeasArgIdx: _tlCtl->setScoreSelectBar( cmDsvUInt(vp)); break; default: { assert(0); } } } } void kcApp::_createTksb( const cmDspUiHdr_t* m ) { int x,y,w=0,h=0; page_t* pg = _createPage("Bldr"); ctl_t* cp = _createCtl(m, kTksbTypeId, x,y,w,h, true ); w = pg->grp->w(); h = pg->grp->h(); // currently we only support one Take sequence builder control because // we have not yet implmenented a method of providing // timer callbacks to a list of UI controls if( _tlCtl != NULL ) cp->u.tksb.tlctl = _tlCtl; else { _tlCtl = new tlCtl(_ctx,this,_menu,this); cp->u.tksb.tlctl = _tlCtl; } Fl_Widget* wdgt = cp->u.tksb.tlctl->initTakeSeqBldrCtlr(x,y,w,h); _insertNewCtl(cp,m,wdgt,cp->u.tksb.varIdArray,kTksbVarCnt); } void kcApp::_setTksbValue( ctl_t* cp, unsigned instVarId, const cmDspValue_t* vp ) { unsigned i; for(i=0; iu.tksb.varIdArray[i] == instVarId ) { switch(i) { case kTksbFileArgIdx: break; case kTksbPtrArgIdx: if( _tlCtl != NULL ) _tlCtl->openTakeSeqBldr(cmDsvPtr(vp)); break; case kTksbSelArgIdx: // TODO: call cmGrTksbFltk from here to to animate the time cursor // with a new location break; case kTksbRefreshArgIdx: break; default: { assert(0); } } } } void kcApp::_createTksr( const cmDspUiHdr_t* m ) { int x,y,w=0,h=0; page_t* pg = _createPage("Rndr"); ctl_t* cp = _createCtl(m, kTksrTypeId, x,y,w,h, true ); w = pg->grp->w(); h = pg->grp->h(); // currently we only support one Take sequence builder control because // we have not yet implmenented a method of providing // timer callbacks to a list of UI controls if( _tlCtl != NULL ) cp->u.tksr.tlctl = _tlCtl; else { _tlCtl = new tlCtl(_ctx,this,_menu,this); cp->u.tksr.tlctl = _tlCtl; } Fl_Widget* wdgt = cp->u.tksr.tlctl->initTakeSeqRendCtlr(x,y,w,h); _insertNewCtl(cp,m,wdgt,cp->u.tksr.varIdArray,kTksrVarCnt); } void kcApp::_setTksrValue( ctl_t* cp, unsigned instVarId, const cmDspValue_t* vp ) { unsigned i; for(i=0; iu.tksr.varIdArray[i] == instVarId ) { switch(i) { case kTksrPtrArgIdx: if( _tlCtl != NULL ) _tlCtl->openTakeSeqRend(cmDsvPtr(vp)); break; case kTksrRefreshArgIdx: if( _tlCtl != NULL ) _tlCtl->refreshTakeSeqRend(); break; case kTksrSelArgIdx: break; default: { assert(0); } } } } void kcApp::_createTwod( const cmDspUiHdr_t* m ) { int x,y,w=0,h=0; page_t* pg = _createPage("2-D"); ctl_t* cp = _createCtl(m, kTwodTypeId, x,y,w,h, true ); w = pg->grp->w(); h = pg->grp->h(); // currently we only support one Take sequence builder control because // we have not yet implmenented a method of providing // timer callbacks to a list of UI controls if( _tlCtl != NULL ) cp->u.twod.tlctl = _tlCtl; else { _tlCtl = new tlCtl(_ctx,this,_menu,this); cp->u.twod.tlctl = _tlCtl; } Fl_Widget* wdgt = cp->u.twod.tlctl->init2dCtlr(x,y,w,h); _insertNewCtl(cp,m,wdgt,cp->u.twod.varIdArray,kTwodVarCnt); } void kcApp::_setTwodValue( ctl_t* cp, unsigned instVarId, const cmDspValue_t* vp ) { unsigned i; for(i=0; iu.twod.varIdArray[i] == instVarId ) { switch(i) { case kTwodXArgIdx: case kTwodYArgIdx: case kTwodAngleArgIdx: case kTwodRadiusArgIdx: break; default: { assert(0); } } } } void kcApp::_newColumn( const cmDspUiHdr_t* m ) { _incrColW = cmDsvGetUInt(&m->value); if( _incrColW == 0 ) _incrColW = 200; } void kcApp::_insertAudioSysCfgLabel(unsigned long idx, const char* label) { if( idx == 0 ) _as_btn->clear(); _as_btn->add(label,0,NULL,(void*)idx,0); } void kcApp::_setDfltAudioSysCfg(unsigned long idx, const char* label ) { int i; if((i = _as_btn->find_index(label)) != -1 ) { _setMenuButton(_as_btn,i,"Audio System Cfg"); cmAdIfSetAudioSysCfg(_aiH,_as_btn->mvalue()->argument()); } } void kcApp::_insertDeviceLabel( unsigned long devIdx, bool inputFl, const cmChar_t* label ) { Fl_Menu_Button* bp = inputFl ? _ai_btn : _ao_btn; // ??? if( idx == 0 ) // bp->clear(); bp->add(label,0,NULL,(void*)devIdx,0); } void kcApp::_insertProgramLabel( unsigned long idx, const cmChar_t* label ) { if( idx == 0 ) _pgm_btn->clear(); _pgm_btn->add(label,0,NULL,(void*)idx,0); } void kcApp::_setDfltProgram(unsigned long idx, const char* label ) { int i; if((i = _pgm_btn->find_index(label)) != -1 ) { _setMenuButton(_pgm_btn,i,"Programs"); cmAdIfLoadProgram(_aiH,_getCurAudioSubSysIdx(),_pgm_btn->mvalue()->argument()); } } void kcApp::_insertSubSysCnt( unsigned long subSysCnt ) { long unsigned i; int bufByteCnt = 15; char buf[bufByteCnt+1]; for(i=0; iadd(label,0,NULL,(void*)id); } _ss_btn->value(0); _ss_btn->copy_label( _ss_btn->mvalue()->label() ); } unsigned kcApp::_getCurAudioSubSysIdx() { const Fl_Menu_Item* mip; unsigned retVal = 0; if( (mip = _ss_btn->mvalue()) != NULL) retVal = mip->argument(); return retVal; } void kcApp::_setMenuButton( Fl_Menu_Button* b, unsigned value, const char* dfltLabel ) { const Fl_Menu_Item* mip; bool fl = value != cmInvalidIdx; // if value is a valid menu index then make it the current menu selection if( fl ) b->value(value); // get the new current menu item and set its label if( (mip = b->mvalue()) != NULL ) b->copy_label( fl ? mip->label() : dfltLabel ); } void kcApp::_setDeviceMenuButton( unsigned asSubIdx, bool inputFl, unsigned devIdx ) { // don't set the device name if the currenly selected sub-system is not the // same as the one the device is assigned to if( _getCurAudioSubSysIdx() != asSubIdx ) return; Fl_Menu_Button* mbp = inputFl ? _ai_btn : _ao_btn; const Fl_Menu_Item* map = mbp->menu(); unsigned n = mbp->size(); unsigned i; char* di = 0; di += devIdx; // BEWARE: devious int to ptr trick for(i=0; i"); } } void kcApp::_setSampleRateBtn( unsigned value ) { unsigned i; unsigned n = _sr_btn->size(); for(i=0; imenu()[i].argument() == (int)value ) { _sr_btn->value(i); _sr_btn->copy_label( _sr_btn->mvalue()->label() ); return; } } _sr_btn->copy_label("Sample Rate?"); } void kcApp::_updateMeters( unsigned asSubIdx, unsigned devIdx, unsigned inFl, const double* meterArray, unsigned meterCnt ) { unsigned i; for(i=0; icnt[kUpdateSsIdx],ssp->cnt[kWakeupSsIdx],ssp->cnt[kMsgSsIdx],ssp->cnt[kAudioCbSsIdx]); } void kcApp::_updateSsStatusIndicator( unsigned asSubIdx, unsigned indicatorIdx, unsigned cnt ) { ss_t* ss = _ssArray + asSubIdx; float val = ss->cnt[indicatorIdx] != cnt; ss->cnt[indicatorIdx] = cnt; if( ss->prog[indicatorIdx]->value() != val ) { ss->prog[indicatorIdx]->value(val); ss->prog[indicatorIdx]->redraw(); } } void kcApp::_handleStatusMsg(const cmAudioSysStatus_t* st, const double* iMeterArray, const double* oMeterArray ) { //unsigned asSubIdx = ((const unsigned*)msgBuf)[0]; //const cmAudioSysStatus_t* st = (const cmAudioSysStatus_t*)(msgBuf + (2 * sizeof(unsigned))); //const double* iMeterArray = (const double*)(st + 1); //const double* oMeterArray = iMeterArray + st->iMeterCnt; _updateMeters(st->asSubIdx, st->iDevIdx,1,iMeterArray,st->iMeterCnt); _updateMeters(st->asSubIdx, st->oDevIdx,0,oMeterArray,st->oMeterCnt); assert( st->asSubIdx < _ssCnt ); _updateSsStatusIndicator(st->asSubIdx, kUpdateSsIdx, st->updateCnt ); _updateSsStatusIndicator(st->asSubIdx, kWakeupSsIdx, st->wakeupCnt ); _updateSsStatusIndicator(st->asSubIdx, kMsgSsIdx, st->msgCbCnt ); _updateSsStatusIndicator(st->asSubIdx, kAudioCbSsIdx,st->audioCbCnt ); } void kcApp::_clearStatusIndicators() { for(unsigned i=0; i<_ssCnt; ++i) { _updateSsStatusIndicator(i, kUpdateSsIdx, _ssArray[i].cnt[kUpdateSsIdx] ); _updateSsStatusIndicator(i, kWakeupSsIdx, _ssArray[i].cnt[kWakeupSsIdx] ); _updateSsStatusIndicator(i, kMsgSsIdx, _ssArray[i].cnt[kMsgSsIdx] ); _updateSsStatusIndicator(i, kAudioCbSsIdx,_ssArray[i].cnt[kAudioCbSsIdx] ); } } // Create a master control UI msg and send it to _handleUiMsg(). This function is intended to // mimic the reception of a cmDspUiHdr_t msg from the audio system. It is used to create // and send values to the controls on the master page. void kcApp::_sendMasterUiMsg( unsigned asSubIdx, unsigned selId, unsigned instId, unsigned instVarId, const cmDspValue_t* vp ) { // Determine the size of the message buffer unsigned valByteCnt = cmDsvSerialDataByteCount(vp); unsigned bufByteCnt = sizeof(cmDspUiHdr_t) + valByteCnt; char buf[ bufByteCnt ]; cmDspUiHdr_t* h = (cmDspUiHdr_t*)buf; h->asSubIdx = asSubIdx; h->uiId = cmInvalidId; // the uiId field of master controls is always set to cmInvalidId h->selId = selId; h->flags = 0; h->instId = instId; h->instVarId = instVarId; // Serialize 'v' into the buffer beginning at the address of h->value. // (this function relies on the 'hdr.value' field being the last field in 'h') cmDsvSerialize( vp, &h->value, sizeof(cmDspValue_t) + valByteCnt); _handleUiMsg(h); } // Send a value to a control on the master page. void kcApp::_sendMasterUiValue( unsigned asSubIdx, unsigned instId, const double* v, unsigned vn, const cmChar_t* text ) { cmDspValue_t val; unsigned i; for(i=0; iasSubCnt; _ssArray = new ss_t[ _ssCnt ]; } assert( m->asSubIdx < _ssCnt ); ss_t* ss = _ssArray + m->asSubIdx; int x = 20; int y = 30 + kMenuH; int w = 80; int h = 20; char lblArray[][10] = { "Update","Wakeup","Mesg","Audio" }; // create the sub-system status indicators for(i=0; icolor( FL_RED, FL_GREEN ); prog->minimum(0); prog->maximum(1); prog->copy_label(lblArray[i]); _mstr_grp->add(prog); ss->cnt[i] = 0; ss->prog[i] = prog; x += ss->prog[i]->w() + 4; } for(i=0; i<2; ++i) { unsigned inFl = i==0; unsigned chCnt = inFl ? m->inChCnt : m->outChCnt; unsigned devIdx = inFl ? m->inDevIdx : m->outDevIdx; double zero = 0; for(j=0; jasSubIdx,instId+kMeterUiAsId); // create the volume slider double sv[] = { 3, 0, 0.01, 1 }; unsigned sn = sizeof(sv)/sizeof(sv[0]); _createMasterCtl( m->asSubIdx, kSliderDuiId, instId + kSliderUiAsId, kSldrVarCnt, sv, sn, NULL); // create the meter double mv[] = { 0, 1.0, 0 }; unsigned mn = sizeof(mv)/sizeof(mv[0]); _createMasterCtl( m->asSubIdx, kMeterDuiId, instId + kMeterUiAsId, kMetrVarCnt, mv, mn, NULL); // create the mute button _createMasterCtl( m->asSubIdx, kCheckDuiId, instId + kMuteUiAsId, kChckVarCnt, &zero, 1, "M"); // create the tone button _createMasterCtl( m->asSubIdx, kCheckDuiId, instId + kToneUiAsId, kChckVarCnt, &zero, 1, "T"); // create the pass button _createMasterCtl( m->asSubIdx, kCheckDuiId, instId + kPassUiAsId, kChckVarCnt, &zero, 1, "P"); } } } // handle kValueDuiId messages coming from the engine void kcApp::_onRecvValue( const cmDspUiHdr_t* m ) { ctl_t* cp; if((cp = _findCtl(m->instId,m->asSubIdx,m->uiId==cmInvalidId)) == NULL ) return; switch( cp->typeId ) { case kNumbTypeId: case kSldrTypeId: _setSldrValue(cp, m->instVarId, &m->value ); break; case kTextTypeId: _setTextValue(cp, m->instVarId, &m->value ); break; case kButnTypeId: _setButtonValue(cp, m->instVarId, &m->value); break; case kChckTypeId: _setCheckValue(cp, m->instVarId, &m->value); break; case kLablTypeId: _setLabelValue(cp, m->instVarId, &m->value); break; case kFnamTypeId: _setFnamValue(cp, m->instVarId, &m->value); break; case kMlstTypeId: _setMlistValue(cp, m->instVarId, &m->value); break; case kMetrTypeId: _setMeterValue(cp, m->instVarId, &m->value); break; case kTmlnTypeId: _setTmlnValue(cp, m->instVarId, &m->value); break; case kScorTypeId: _setScorValue(cp, m->instVarId, &m->value); break; case kTksbTypeId: _setTksbValue(cp, m->instVarId, &m->value); break; case kTksrTypeId: _setTksrValue(cp, m->instVarId, &m->value); break; case kTwodTypeId: _setTwodValue(cp, m->instVarId, &m->value); break; default: assert(0); } } // This is the main UI<-Engine msg handler/dispatch function void kcApp::_handleUiMsg( const cmDspUiHdr_t* m ) { switch( m->selId ) { case kPrintDuiId: cmDsvPrint(&m->value,NULL,&_ctx->rpt); break; case kNumberDuiId: case kSliderDuiId: _createSlider(m); break; case kTextDuiId: _createText(m); break; case kButtonDuiId: _createButton(m); break; case kLabelDuiId: _createLabel(m); break; case kCheckDuiId: _createCheck(m); break; case kFnameDuiId: _createFnameCtl(m); break; case kMsgListDuiId: _createMlistCtl(m); break; case kMeterDuiId: _createMeter(m); break; case kTimeLineDuiId: _createTmln(m); break; case kScoreDuiId: _createScor(m); break; case kTakeSeqBldrDuiId: _createTksb(m); break; case kTakeSeqRendDuiId: _createTksr(m); break; case kTwodDuiId: _createTwod(m); break; case kValueDuiId: _onRecvValue(m); break; case kColumnDuiId: _newColumn(m); break; case kHBorderDuiId: _horzBordFl = true; break; case kPageDuiId: _createPage(cmDsvStrcz(&m->value)); break; case kAudioSysCfgDuiId: _insertAudioSysCfgLabel(m->instId,cmDsvStrcz(&m->value)); break; case kAudioSysCfgDfltDuiId: _setDfltAudioSysCfg(m->instId,cmDsvStrcz(&m->value)); break; case kDeviceDuiId: _insertDeviceLabel(m->instId,m->flags,cmDsvStrcz(&m->value)); break; case kProgramDuiId: _insertProgramLabel(m->instId,cmDsvStrcz(&m->value)); break; case kProgramDfltDuiId: _setDfltProgram(m->instId,cmDsvStrcz(&m->value)); break; // the below codes are used to notify the application // of changes in state of the audio DSP system case kSubSysCntDuiId: _insertSubSysCnt(cmDsvUInt(&m->value)); break; case kSetAudioCfgDuiId: _setMenuButton(_as_btn,cmDsvUInt(&m->value),"Audio System Cfg"); break; case kSetAudioDevDuiId: _setDeviceMenuButton( m->asSubIdx, m->flags, cmDsvUInt(&m->value) ); break; case kSetSampleRateDuiId: _setSampleRateBtn(cmDsvUInt(&m->value)); break; case kSetPgmDuiId: _setMenuButton(_pgm_btn,cmDsvUInt(&m->value),"Program?"); _clearCtlList(false); break; case kEnableDuiId: _ena_chk->value(m->flags); break; } } // Check for and forward any messages sent to the UI // that are waiting in the audio DSP msg queue. void kcApp::_getEngMsg() { if( cmAdIfIsValid(_aiH) ) { unsigned i; for(i=0; i<10; ++i) { cmAiRC_t aiRC; // cmAdIfDispatchMsgToHost() results in calls to // the _s_handleXXX() message handlers if((aiRC = cmAdIfDispatchMsgToHost(_aiH)) != kOkAiRC) { if( aiRC == kNoMsgAiRC ) break; cmErrMsg(&_ctx->err,kEngFailKmRC,"Audio DSP dispatch message request failed."); break; } } } } kcApp* kcApp::_getApp( Fl_Widget* w ) { // walk up the widget tree until the top widget is found Fl_Group* gp = w->parent(); while( gp->parent() != NULL ) gp=gp->parent(); // the user data for the top widget is a pointer kcApp() return (kcApp*)gp->user_data(); } void kcApp::_onCloseApp() { if( _tlCtl != NULL ) { delete _tlCtl; _tlCtl = NULL; } _stopTimerFl = true; // When all windows are windows are closed then the app. // will close - so hiding the application window // causes the program to close. // // Note that simply returning from this callback will // prevent the application from closing. Because the existence // of the callback alone is enough to disable default // event handling. hide(); } void kcApp::_testStub() { /* cmAudioFileTest( // "/Users/kevin/media/audio/20100819-Kreisberg/fragments/Bass End_21a.wav", "/Users/kevin/media/audio/McGill-1/1 Audio Track.aiff", "/Users/kevin/src/octave/cm_audio_file_test.m",_ctx->err.rpt ); */ //cmSymTblTest(&_ctx); //_kcTranslateFile("/Users/kevin/src/kc/src/data/Sec1_14.txt","/Users/kevin/src/kc/src/data/Sec1_14_out.txt",&_ctx); //_kcTranslateFile("/Users/kevin/src/kc/src/data/Mix_17-20.txt","/Users/kevin/src/kc/src/data/Mix_17-20_out.txt",&_ctx); /* ctl_t* cp = _ctlList; for(; cp!=NULL; cp=cp->linkPtr) printf("%i %i %i\n", cp->instId,cp->asSubIdx,cp->mstrFl ); */ //_printStatusCounts(); //cmProcTestNoInit(&_ctx); _tlCtl->testStub(); } void kcApp::_printPgm() { const char* pathStr = cmFsUserDir(); // make the default dir the user's home dir const char patStr[] = ""; //"Text Files (*.txt)\tAudio Files (*.{wav,aif,aiff})"; // All Files (*.*) is append to this automatically const char titleStr[] = "JSON output file name"; Fl_File_Chooser fc = Fl_File_Chooser(pathStr,patStr,Fl_File_Chooser::CREATE,titleStr); fc.preview(0); // default the previous option to 'off'. fc.show(); // show the chooser // make the chooser modal while( fc.shown() ) Fl::wait(); if( fc.count() > 0) cmAdIfPrintPgm(_aiH, cmInvalidIdx, fc.value(0) ); } void kcApp::_s_callback(Fl_Widget* wp, void* data) { ((kcApp*)data)->_callback(NULL); } // this callback is called when the window is closing void kcApp::_callback(void* data) { if( Fl::event() == FL_CLOSE ) { _onCloseApp(); } } void kcApp::_s_status_timeout_cb(void* userPtr) { if( ((kcApp*)userPtr)->_status_timeout_cb() ) Fl::repeat_timeout(TIMER_PERIOD,_s_status_timeout_cb,userPtr); } bool kcApp::_status_timeout_cb() { if( cmTsMp1cIsValid(_printqH) ) _checkPrintQueue(); if( !_stopTimerFl ) { _getEngMsg(); if( _tlCtl != NULL ) _tlCtl->onIdle(); } if( _ssUpdateFl ) { ++_ssPhase; if( _ssPhase >= kSsPhaseMax ) { _clearStatusIndicators(); _ssPhase = 0; } } if( _stopTimerFl==false && cmAdIfIsValid(_aiH) ) cmAdIfDispatchMsgToHost(_aiH); //if( !_stopTimerFl ) // _getEngStatus(); return _stopTimerFl==false || cmTsMp1cIsValid(_printqH); } void kcApp::_s_menu_cb(Fl_Widget *w, void *data) { const Fl_Menu_Item* mip; kcApp* p; if((p=_getApp(w)) == NULL ) return; if((mip = p->_menu->mvalue()) == NULL ) return; unsigned id = (long int)mip->user_data(); switch(id) { case kAboutMenuId: fl_message("%s Compiled:%s %s",PACKAGE_STRING,__DATE__,__TIME__); break; case kQuitMenuId: p->_onCloseApp(); break; } } void kcApp::_s_tab_cb(Fl_Widget* w, void* data) { ((kcApp*)data)->_tab_cb(w); } void kcApp::_tab_cb(Fl_Widget*) { Fl_Widget* w = _tabs->value(); _ssUpdateFl = w == (Fl_Widget*)_mstr_grp; _ssPhase = kSsPhaseMax; if( cmAdIfIsValid(_aiH) ) { if( cmAdIfEnableStatusNotify(_aiH, _ssUpdateFl ) != kOkAiRC ) cmErrMsg(&_ctx->err,kEngFailKmRC,"A request to enable/disable status notification failed."); } } void kcApp::_s_btn_cb(Fl_Widget* w, long data) { _getApp(w)->_btn_cb(w,data); } void kcApp::_btn_cb(Fl_Widget* w, long data) { unsigned arg = w->argument(); switch( arg ) { case kAudDevRptBtnId: cmAdIfDeviceReport(_aiH); break; case kEnableBtnId: cmAdIfEnableAudio(_aiH,static_cast(w)->value()!=0); break; case kAudioSysCfgBtnId: cmAdIfSetAudioSysCfg(_aiH,static_cast(w)->mvalue()->argument()); break; case kInAudDevBtnId: cmAdIfSetAudioDevice(_aiH,_getCurAudioSubSysIdx(),true,static_cast(w)->mvalue()->argument()); break; case kOutAudDevBtnId: cmAdIfSetAudioDevice(_aiH,_getCurAudioSubSysIdx(),false,static_cast(w)->mvalue()->argument()); break; case kPgmBtnId: cmAdIfLoadProgram(_aiH,_getCurAudioSubSysIdx(),static_cast(w)->mvalue()->argument()); break; case kSubSystemIdxBtnId: // TODO: change device and sample rate menu's to reflect the device and srate assigned to this asSubIdx. break; case kSrateBtnId: cmAdIfSetSampleRate(_aiH,_getCurAudioSubSysIdx(),static_cast(w)->mvalue()->argument()); break; case kTestBtnId: _testStub(); break; case kPrintPgmBtnId: _printPgm(); break; default: { assert(0); } } } void kcApp::_s_ctl_cb(Fl_Widget* w, void* data) { ctl_t* cp = ((ctl_t*)data); cp->thisPtr->_ctl_cb(cp); } void kcApp::_ctl_cb(ctl_t* cp) { cmDspValue_t value = cmDspNullValue; unsigned instVarId = cmInvalidId; switch( cp->typeId ) { case kNumbTypeId: case kSldrTypeId: instVarId = cp->u.sldr.varIdArray[ kSldrValArgIdx ]; cmDsvSetDouble(&value,cp->u.sldr.val->value()); break; case kTextTypeId: instVarId = cp->u.text.varIdArray[ kTextValArgIdx ]; cmDsvSetStrz(&value,(cmChar_t*)cp->u.text.text->value()); break; case kButnTypeId: instVarId = cp->u.butn.varIdArray[ kButnValArgIdx ]; cmDsvSetDouble(&value,cp->u.butn.val); break; case kChckTypeId: { bool fl = cp->u.chck.chck->value(); instVarId = cp->u.chck.varIdArray[ kChckValArgIdx ]; cmDsvSetDouble(&value, fl ? 1.0 : 0.0 ); } break; case kFnamTypeId: instVarId = cp->u.fnam.varIdArray[ kFnamValArgIdx ]; cmDsvSetStrz( &value, (cmChar_t*)cp->u.fnam.fnam->filename()); break; case kMlstTypeId: instVarId = cp->u.mlst.varIdArray[ kMlstSelArgIdx ]; // add one to the selected index to account for skipping title row if( cp->u.mlst.mbtn==NULL) { unsigned idx = cp->u.mlst.mlst->value(); //printf("list:%i\n",idx); cmDsvSetUInt( &value, idx); } else { unsigned idx = cp->u.mlst.mbtn->value(); //printf("mbtn:%i\n",idx); cmDsvSetUInt( &value, idx + 1); cp->u.mlst.mbtn->label( cp->u.mlst.mbtn->text(idx) ); } break; case kTmlnTypeId: { instVarId = cp->u.tmln.varIdArray[ kTmlnSelArgIdx ]; unsigned selMarkerId = cp->u.tmln.tlctl->timeLineSelectedMarkerId(); cmDsvSetUInt(&value, selMarkerId ); } break; case kScorTypeId: { instVarId = cp->u.scor.varIdArray[ kScorSelArgIdx ]; unsigned selEleIdx = cp->u.scor.tlctl->scoreSelectedEleIndex(); cmDsvSetUInt(&value, selEleIdx ); } break; case kTksbTypeId: { cmGrTksbFltk* tksbCtl = dynamic_cast(cp->wdgtPtr); switch( tksbCtl->cbTypeId() ) { case cmGrTksbFltk::kSelectTId: { instVarId = cp->u.tksb.varIdArray[ kTksbSelArgIdx ]; unsigned selEleIdx = cp->u.tksb.tlctl->tksbSelectedEleIndex(); cmDsvSetUInt(&value, selEleIdx ); } break; case cmGrTksbFltk::kRefreshTId: instVarId = cp->u.tksb.varIdArray[ kTksbRefreshArgIdx ]; cmDsvSetInt(&value,0); break; default: { assert(0); } } } break; case kTksrTypeId: { } break; case kTwodTypeId: { cmGr2dFltk* twodCtl = dynamic_cast(cp->wdgtPtr); unsigned i; for(i=0; ix()); break; case kTwodYArgIdx: cmDsvSetDouble(&value,twodCtl->y()); break; case kTwodAngleArgIdx: cmDsvSetDouble(&value,twodCtl->angle()); break; case kTwodRadiusArgIdx: cmDsvSetDouble(&value,twodCtl->radius()); break; } cmAdIfSendMsgToAudioDSP(_aiH,cp->asSubIdx,kUiSelAsId,kValueDuiId,0,cp->instId,cp->u.twod.varIdArray[i],&value); } instVarId = cmInvalidId; } break; default: {assert(0);} } if( instVarId != cmInvalidId ) if( cmAdIfSendMsgToAudioDSP( _aiH, cp->asSubIdx, cp->mstrFl ? kUiMstrSelAsId : kUiSelAsId, kValueDuiId, 0, cp->instId, instVarId, &value) != kOkAiRC) { cmErrMsg(&_ctx->err,kEngFailKmRC,"An attempt to send a UI message to the audio DSP interface failed."); } } void kcApp::vprint(const char* fmt, va_list vl ) { int bufCharCnt = 511; char buf[bufCharCnt+1]; int n = vsnprintf(buf,bufCharCnt,fmt,vl); if( n > 0 ) { // if the print queue exists (it might not during startup or shutdown) ... if( cmTsMp1cIsValid(_printqH) ) { // ... enqueue the text to print if( cmTsMp1cEnqueueMsg(_printqH,buf,n+1) != kOkThRC && _printFl==0 ) { // use _printFl to guard against recursion which would eventually overflow the stack. ++_printFl; cmErrMsg(&_ctx->err,kQueueFailKmRC,"Print enqueue failed on msg:%s.",buf); --_printFl; } } else _print(buf); // ... otherwise just send the text directly to the output console } } void kcApp::print( const char* fmt, ... ) { va_list vl; va_start(vl,fmt); vprint(fmt,vl); va_end(vl); } void kcApp::_s_print( void* userPtr, const char* text ) { ((kcApp*)userPtr)->print(text); } cmRC_t kcApp::_s_print_queue_cb(void* userCbPtr, unsigned msgByteCnt, const void* msgDataPtr ) { kcApp* ap = (kcApp*)userCbPtr; ap->_print((const char*)msgDataPtr); return cmOkRC; } void kcApp::_checkPrintQueue() { while( cmTsMp1cMsgWaiting(_printqH) ) if( cmTsMp1cDequeueMsg(_printqH, NULL, 0) != kOkThRC && _printFl==0 ) { ++_printFl; cmErrMsg(&_ctx->err,kPrintQueFailKmRC,"Print dequeue failed."); --_printFl; } } void kcApp::_print( const char* text ) { if( _con != NULL ) _con->insert(text); #ifndef NDEBUG fputs(text,stdout); #endif }