diff --git a/Makefile.am b/Makefile.am index aa2df76..e9fc954 100644 --- a/Makefile.am +++ b/Makefile.am @@ -56,7 +56,11 @@ include_HEADERS += $(cmHDR) lib_LTLIBRARIES += src/libcm/libcm.la -src_cmflapp_cmflapp_SOURCES = src/cmflapp/main.cpp src/cmflapp/app.h src/cmflapp/app.cpp -src_cmflapp_cmflapp_SOURCES += src/cmflapp/Fl_Splitter.h src/cmflapp/Fl_Splitter.cpp -src_cmflapp_cmflapp_LDADD = $(CMLIBS) $(MYLIBS) -bin_PROGRAMS += src/cmflapp/cmflapp +src_kc_kc_SOURCES = src/kc/kcMain.cpp +src_kc_kc_SOURCES += src/kc/kcApp.h src/kc/kcApp.cpp +src_kc_kc_SOURCES += src/kc/Fl_Splitter.h src/kc/Fl_Splitter.cpp +src_kc_kc_SOURCES += src/kc/Fl_File_Btn.h src/kc/Fl_Vert_Progress.h +src_kc_kc_SOURCES += src/kc/Fl_File_Btn.cpp src/kc/Fl_Vert_Progress.cpp + +src_kc_kc_LDADD = $(CMLIBS) $(MYLIBS) +bin_PROGRAMS += src/kc/kc diff --git a/configure.ac b/configure.ac index e941704..dc7e616 100644 --- a/configure.ac +++ b/configure.ac @@ -3,10 +3,10 @@ # this configure.ac or any of the Makefile.am files. # -AC_INIT([cmflapp],[1.0],[cmflapp@larke.org]) +AC_INIT([kc],[1.0],[kc@larke.org]) AC_CONFIG_AUX_DIR([build-aux]) # put aux files in build-aux AM_INIT_AUTOMAKE([1.9 -Wall foreign subdir-objects]) # subdir-objects needed for non-recursive make -AC_CONFIG_SRCDIR([src/cmflapp/main.cpp]) +AC_CONFIG_SRCDIR([src/kc/kcMain.cpp]) AC_CONFIG_HEADERS([config.h]) AC_CONFIG_MACRO_DIR([m4]) diff --git a/src/cmflapp/app.cpp b/src/cmflapp/app.cpp deleted file mode 100644 index 29dd6a6..0000000 --- a/src/cmflapp/app.cpp +++ /dev/null @@ -1,555 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "Fl_Splitter.h" - -#include "cmPrefix.h" -#include "cmGlobal.h" -#include "cmRpt.h" -#include "cmErr.h" -#include "cmCtx.h" -#include "cmMem.h" -#include "cmMallocDebug.h" -#include "cmFileSys.h" -#include "cmThread.h" - -#include "appErr.h" -#include "app.h" - -class drawWnd : public Fl_Widget -{ -public: - drawWnd(app* ap, int x, int y, int w, int h, const char* label = NULL ); - virtual ~drawWnd(); - - virtual int handle(int event); - virtual void resize(int x, int y, int w, int h ); - -protected: - virtual void draw(); - -private: - app* _app; -}; - -drawWnd::drawWnd(app* ap, int x, int y, int w, int h, const char* label ) - : Fl_Widget(x,y,w,h,label), - _app(ap) -{} - -drawWnd::~drawWnd() -{} - -int drawWnd::handle(int event) -{ - switch(event) - { - case FL_PUSH: - { - const char* label; - switch( Fl::event_button() ) - { - case FL_LEFT_MOUSE: label = "left"; break; - case FL_RIGHT_MOUSE: label = "right"; break; - case FL_MIDDLE_MOUSE: label = "middle"; break; - default: - label = "none"; - } - - _app->print("%i %i %s\n",Fl::event_x(),Fl::event_y(),label); - } - break; - - default: - return Fl_Widget::handle(event); - } - - return 1; -} - -void drawWnd::resize(int x, int y, int w, int h ) -{ - // must call base to make size change - Fl_Widget::resize(x,y,w,h); -} - -void drawWnd::draw() -{ - int offs = 10; - //fl_draw_box(FL_DOWN_BOX,x()+10,y()+10,w()-20,h()-20,FL_RED); - //fl_frame("XXXX",x()+offs,y()+offs,w()-2*offs,h()-2*offs); - fl_line_style(FL_SOLID,1,NULL); - fl_color(FL_RED); - fl_line(x()+offs,y()+offs,x()+w()-offs,y()+h()-offs); - - - int x = w()/2; - int y = h()/2; - - fl_color(FL_BLACK); - fl_draw(90,"string",x,y); - - // XXXXXXX - // X00000X - // X00000X - // X00X00X - // X00000X - // X00000X - // XXXXXXX - - fl_rect(x-3,y-3,7,7); - fl_point(x,y); -} - -//---------------------------------------------------------------------------------------------------- -//---------------------------------------------------------------------------------------------------- -//---------------------------------------------------------------------------------------------------- - -app::app(cmCtx_t* ctx, cmTsMp1cH_t printqH, int w, int h, const char* label, int argc, char* argv[]) - : Fl_Double_Window(w,h,label), _timerPeriodSecs(0), - _ctx(ctx), _splt(NULL),_buf(NULL),_con(NULL),_tabs(NULL),_menu(NULL), - _printqH(printqH),_printFl(0) -{ - - // install a callback to cleanup when the app window closes - // (the btn and menu callbacks also rely on a pointer to 'this' being found in app.user_data() - // see _getApp()) - 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); - _splt = new Fl_HSplitter(0, kMenuH, w, h-kStatusH, h-kStatusH-100); - end(); - - // Create a text display object for console output and - // add it to the lower splitter area - _buf = new Fl_Text_Buffer(); - _con = new Fl_Text_Display(_splt->container2->x(),_splt->container2->y(),_splt->container2->w(),_splt->container2->h()); - _con->buffer(_buf); - _splt->container2->add(_con); - - // create tabbed windows - const char* titles[] = { "Title 1", "Title 2" }; - _create_tabs(2,titles); - - // 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(_splt); - show(argc, argv); - - // The application is now visible. - // 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 - _timerPeriodSecs = 0.1; - Fl::add_timeout(_timerPeriodSecs,app::_s_timer_cb,this); - - // install an idle callback - //Fl::add_idle(_s_idle_cb,this); - - print("Started!\n"); -} - -app::~app() -{} - -void app::createControls(Fl_Group* grp) -{ - const int vBord = 3; - const int ctl_width = 100; - int xx = grp->x() + 5; - int yy = grp->y() + vBord; - - Fl_Check_Button* cbt = new Fl_Check_Button(xx,yy,ctl_width,kCtlH,"Check"); - cbt->callback( _s_btn_cb, kBtn1MId ); - yy += cbt->h() + vBord; - - Fl_Button* btn = new Fl_Button(xx,yy,ctl_width,kCtlH,"Button"); - btn->callback( _s_btn_cb, kBtn2MId ); - yy += btn->h() + vBord; - - // menu buttons callback through menuCallback() not btnCallback() - Fl_Menu_Button* mbt = new Fl_Menu_Button(xx,yy,ctl_width,kCtlH,"Menu"); - - mbt->add("Item 1",0,_s_menu_btn_cb, (void*)kMenuBtn1MId, 0); - mbt->add("Item 2",0,_s_menu_btn_cb, (void*)kMenuBtn2MId, 0); - yy += mbt->h() + vBord; - - Fl_Value_Input* vip = new Fl_Value_Input(xx,yy,ctl_width/2,kCtlH,"Value In"); - vip->callback(_s_value_cb, kValue1MId ); - vip->align(FL_ALIGN_RIGHT); // place label to the right of the input ctl - // Only make the callback when the enter key is struck or the ctl loses focus and the value has changed. - // Removing this line causes the callback whenever the input changes. - vip->when(FL_WHEN_ENTER_KEY | FL_WHEN_RELEASE ); - vip->bounds(-10.0,10.0); - vip->step(0.1); - yy += vip->h() + vBord; - - Fl_Value_Slider* sdp = new Fl_Value_Slider(xx,yy,ctl_width,kCtlH,"Slider"); - sdp->callback(_s_value_cb, kValue2MId ); - sdp->align(FL_ALIGN_RIGHT); - sdp->type(FL_HOR_NICE_SLIDER); - sdp->bounds(-10.0,10.0); - sdp->step(0.1); - yy += sdp->h() + vBord; - - Fl_Input* inp = new Fl_Input(xx,yy,ctl_width/2,kCtlH,"Text"); - inp->callback(_s_input_cb,kInput1MId); - inp->align(FL_ALIGN_RIGHT); - yy += inp->h() + vBord; - - -} - - -void app::initializeTab(int tabIndex, int x, int y, int w, int h, const char* label) -{ - switch(tabIndex) - { - case 0: - { - Fl_Group* grp = new Fl_Group(x,y,w,h,label); - grp->begin(); - createControls(grp); - grp->end(); - } - break; - - case 1: - { - Fl_Group* grp = new Fl_Group(x,y,w,h,label); - grp->begin(); - _draw = new drawWnd(this,x,y,w,h,NULL); - grp->end(); - } - break; - } - -} - -void app::createMenu(int w, int h) -{ - Fl_Menu_Item items[] = - { - { "&File", 0, 0, 0, FL_SUBMENU }, - { "&New File", FL_COMMAND + 'n', (Fl_Callback*)_s_menu_cb, (void*)kFileNewMId }, - { "&Open File", FL_COMMAND + 'o', (Fl_Callback*)_s_menu_cb, (void*)kFileOpenMId }, - { 0 }, - { "&Edit", 0, 0, 0, FL_SUBMENU }, - { "&Copy", FL_COMMAND + 'c', (Fl_Callback*)_s_menu_cb, (void*)kEditCopyMId }, - { "&Paste", FL_COMMAND + 'v', (Fl_Callback*)_s_menu_cb, (void*)kEditPasteMId }, - { 0 }, - { 0 } - }; - - _menu = new Fl_Menu_Bar(0,0,w,h); - _menu->copy(items); -} - -void app::menuCallback(const Fl_Menu_Item* mip, long int id) -{ - switch(id) - { - case kFileOpenMId: - { - // file chooser demo - - 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[] = "Select files"; - - Fl_File_Chooser fc = Fl_File_Chooser(pathStr,patStr,Fl_File_Chooser::MULTI,titleStr); - - fc.preview(0); // default the previous option to 'off'. - fc.show(); // show the chooser - - // make the chooser modal - while( fc.shown() ) - Fl::wait(); - - // print the selected files - int i; - for(i=1; i<=fc.count(); ++i) - print("%i %s\n",i,cmStringNullGuard(fc.value(i))); - - } - break; - } - - print("%i %s\n",id,mip->label()); -} - -void app::btnCallback(const Fl_Button* bp, int id ) -{ - print("%i %s %i\n",id,bp->label(),bp->value()); -} - -void app::valueCallback(const Fl_Valuator* vp, int id ) -{ - print("%i %s %f\n",id,vp->label(),vp->value()); -} - -void app::inputCallback(const Fl_Input* ip, int id ) -{ - print("%i %s %s\n",id,ip->label(),ip->value()); -} - -void app::appCallback(Fl_Widget* wp) -{ - switch( Fl::event() ) - { - case FL_CLOSE: - { - - /* - int cc = _closeCnt; - - // attempt to shut down the pgm - _closeCnt += finalizePgm() == false; - - // the first time the pgm fails to shut down - // do not allow the pgm to close the main window - // this will give a chance for the error messages - // to be diplayed in the console - all successive - // times the return value from finalizePgm() is - - // ignored and the program is terminated. - if( _closeCnt == 1 && cc==0) - { - // send a strong hint that a problem occurred on shutdown - // and prevent the app from receiving events that might cause it to - // crash - deactivate(); - return; - } - */ - - // 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(); - } - break; - } - -} -bool app::idleCallback() -{ return true; } - - -bool app::timerCallback() -{ - _checkPrintQueue(); - return true; -} - -void app::error( const char* fmt, ... ) -{ - va_list vl; - va_start(vl,fmt); - - int bufCharCnt = 511; - char buf[bufCharCnt+1]; - - int n = vsnprintf(buf,bufCharCnt,fmt,vl); - - if( n > 0 ) - print("%s Error: %s\n",label(),buf); - - va_end(vl); -} - -void app::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) ) - { - cmThRC_t thRC; - - // ... enqueue the text to print - if((thRC = cmTsMp1cEnqueueMsg(_printqH,buf,n+1)) != kOkThRC && _printFl==0 ) - { - // Print queue error msg's directly to stdout rather than throught - // the error mechanism - this prevents generating a stack overflow - // when the print queue runs out of space and attempts to print a - // 'queue out of memory' error msg. - // The queue itself does will not print errors because it does - // not have a valid cmRpt_t pointer. See cmMp1cCreate() above. - ++_printFl; - cmErrMsg(&_ctx->err,kQueueFailAppRC,"Print enqueue failed."); - --_printFl; - } - } - else - _print(buf); // ... otherwise just send the text directly to the output console - } - -} - -void app::print( const char* fmt, ... ) -{ - va_list vl; - va_start(vl,fmt); - vprint(fmt,vl); - va_end(vl); -} - -void app::_s_menu_cb(Fl_Widget* wp, void* data) -{ - const Fl_Menu_Item* mip; - app* ap; - - if((ap=_getApp(wp)) != NULL ) - if((mip = ap->_menu->mvalue()) != NULL ) - ap->menuCallback(mip,(long int)mip->user_data()); -} - -void app::_s_menu_btn_cb(Fl_Widget* wp, void* data) -{ - const Fl_Menu_Button* bp; - const Fl_Menu_Item* mip; - app* ap; - - if((ap=_getApp(wp)) != NULL ) - if((bp = static_cast(wp)) != NULL ) - if((mip = bp->mvalue()) != NULL) - ap->menuCallback(mip,(long int)data); -} - -void app::_s_btn_cb(Fl_Widget* wp, long data ) -{ - app* ap; - - if((ap = _getApp(wp)) != NULL ) - if( wp != NULL ) - ap->btnCallback( static_cast(wp),data); -} - -void app::_s_value_cb( Fl_Widget* wp, long data) -{ - app* ap; - Fl_Valuator* vp; - - if((ap = _getApp(wp)) != NULL ) - if((vp = static_cast(wp)) != NULL) - ap->valueCallback(vp,data); -} - -void app::_s_input_cb( Fl_Widget* wp, long data) -{ - app* ap; - Fl_Input* inp; - - if((ap = _getApp(wp)) != NULL) - if((inp = static_cast(wp)) != NULL ) - ap->inputCallback(inp,data); -} - -void app::_s_callback(Fl_Widget* wp, void* data) -{ ((app*)data)->appCallback(wp); } - -void app::_s_idle_cb(void *data) -{ - app* thisPtr = (app*)data; - - if( thisPtr->idleCallback() == false ) - Fl::remove_idle(_s_idle_cb,data); -} - -void app::_s_timer_cb(void* userPtr) -{ - app* thisPtr = (app*)userPtr; - - if( thisPtr->timerCallback() ) - Fl::repeat_timeout(thisPtr->_timerPeriodSecs,_s_timer_cb,userPtr); -} - -void app::_s_print( void* userPtr, const char* text ) -{ ((app*)userPtr)->print(text); } - - -void app::_create_tabs(int titleCnt, const char* titleArray[]) -{ - // Create a tab view and added it to the upper splitter area - _tabs = new Fl_Tabs(_splt->container1->x(),_splt->container1->y(),_splt->container1->w(),_splt->container1->h()); - _splt->container1->add(_tabs); - - int tx,ty,th,tw,i; - _tabs->client_area(tx,ty,tw,th); - - for(i=0; ibegin(); - initializeTab(i,tx,ty,tw,th,titleArray[i]); - _tabs->end(); - } - - // Create an empty tab group and make it resizable - // to prevent the other tab groups from being resizable. - _tabs->begin(); - _tabs->resizable(new Fl_Group(tx,ty+30,1,1)); - _tabs->end(); -} - -app* app::_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 app - as set in: callback(_s_callback,this); - return (app*)gp->user_data(); -} - -cmRC_t app::_s_print_queue_cb(void* userCbPtr, unsigned msgByteCnt, const void* msgDataPtr ) -{ - app* ap = (app*)userCbPtr; - ap->_print((const char*)msgDataPtr); - return cmOkRC; -} - -void app::_checkPrintQueue() -{ - while( cmTsMp1cMsgWaiting(_printqH) ) - if( cmTsMp1cDequeueMsg(_printqH, NULL, 0) != kOkThRC ) - error("Print dequeue failed."); -} - -void app::_print( const char* text ) -{ - if( _con != NULL ) - _con->insert(text); -} diff --git a/src/cmflapp/app.h b/src/cmflapp/app.h deleted file mode 100644 index e887159..0000000 --- a/src/cmflapp/app.h +++ /dev/null @@ -1,119 +0,0 @@ -#ifndef app_h -#define app_h - -class Fl_HSplitter; -class Fl_Text_Buffer; -class Fl_Text_Display; -class Fl_Box; -class Fl_Tabs; -struct Fl_Menu_Item; -class Fl_Menu_Bar; -class Fl_Group; -class Fl_Button; -class Fl_Valuator; -class Fl_Input; -class drawWnd; - -class app : public Fl_Double_Window -{ - -public: - - // widget id's for widgets created by createControls() - enum - { - kFileOpenMId, - kFileNewMId, - kEditCopyMId, - kEditPasteMId, - - kBtn1MId, - kBtn2MId, - kBtn3MId, - - kMenuBtn1MId, - kMenuBtn2MId, - - kValue1MId, - kValue2MId, - - kInput1MId - }; - - app(cmCtx_t* ctx, cmTsMp1cH_t printqH, int w, int h, const char *l, int argc, char *argv[]); - virtual ~app(); - - // Example tabbed window initialization example function - // which also demonstrates some widgets - void createControls(Fl_Group* grp); - - // called once from app() to initialize each tabbed window - virtual void initializeTab(int tabIndex, int x, int y, int w, int h, const char* label); - - // Create the application menu - virtual void createMenu( int menuWidth, int menuHeight ); - - // Widget type specific callbacks - virtual void menuCallback( const Fl_Menu_Item* ip, long int id ); // menu and menu btn callback - virtual void btnCallback( const Fl_Button* bp, int id ); // btn callback - virtual void valueCallback(const Fl_Valuator* vp, int id ); // valuator callback - virtual void inputCallback(const Fl_Input* ip, int id ); // text input callback - - virtual void appCallback(Fl_Widget* wp); // app event callback - virtual bool idleCallback(); // return false to stop callback - virtual bool timerCallback(); // return false to stop callback - - void error( const char* fmt, ... ); // report on error - - // print to the console - virtual void vprint( const char* fmt, va_list vl ); - virtual void print( const char* fmt, ... ); - -protected: - enum - { - kStatusH = 30, - kMenuH = 30, - kCtlH = 30 - }; - - // static callbacks - static void _s_menu_cb( Fl_Widget* wp, void* data); // menu item callback - static void _s_menu_btn_cb(Fl_Widget* wp, void* data); // menu btn callback - static void _s_btn_cb( Fl_Widget* wp, long data); // button callback - static void _s_value_cb( Fl_Widget* wp, long data); // value input or slider callback - static void _s_input_cb( Fl_Widget* wp, long data); // text input callback - static void _s_callback( Fl_Widget* wp, void* data); // main app callback - - static void _s_idle_cb( void* data); // idle callback - static void _s_timer_cb( void* userPtr); // timer callback - static void _s_print( void* userPtr, const char* text ); // print text to the console - - void _create_tabs(int titleCnt, const char* titleArray[]); - - // walk down the widget tree until the app (root) widget is located - static app* _getApp( Fl_Widget* w ); - - // called internally by cmTsQueueDequeue() to send text to _print() - static cmRC_t _s_print_queue_cb(void* userCbPtr, unsigned msgByteCnt, const void* msgDataPtr ); - - // called periodically to check the print queue for waiting text. - void _checkPrintQueue(); - - // send a string of text directly to the output console window - void _print( const char* text ); - - double _timerPeriodSecs; // repeat period for the timer callback - - cmCtx_t* _ctx; // - Fl_HSplitter* _splt; // main splitter window - Fl_Text_Buffer* _buf; // text buffer used by _con - Fl_Text_Display* _con; // text console output window - Fl_Tabs* _tabs; // tabs window - Fl_Menu_Bar* _menu; // app. menu - drawWnd* _draw; // custom widget - cmTsMp1cH_t _printqH; // thread-safe queue for controlling access to the output console from multiple threads - int _printFl; // used to prevent recursion due to throwing printing error -}; - -#endif diff --git a/src/cmflapp/appErr.h b/src/cmflapp/appErr.h deleted file mode 100644 index 99db748..0000000 --- a/src/cmflapp/appErr.h +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef appErr_h -#define appErr_h - -#ifdef __cplusplus -extern "C" { -#endif - -enum -{ - kOkAppRC, - kQueueFailAppRC, - kFileSysFailAppRC, - kMemFailAppRC, - kTextSysFailAppRC, - kPrefsFailAppRC -}; - - typedef unsigned cmAppRC_t; - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/src/cmflapp/main.cpp b/src/cmflapp/main.cpp deleted file mode 100644 index 34bb1d9..0000000 --- a/src/cmflapp/main.cpp +++ /dev/null @@ -1,110 +0,0 @@ -#include -#include -#include - -#include "cmPrefix.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 "cmThread.h" -#include "cmFileSys.h" -#include "cmText.h" - -#include "appErr.h" -#include "app.h" - - - - -void print( void*, const cmChar_t* text) -{ puts(text); } - -cmRC_t print_queue_cb(void* userCbPtr, unsigned msgByteCnt, const void* msgDataPtr ) -{ - print(NULL,(const char*)msgDataPtr); - return cmOkRC; -} - - -int main( int argc, char* argv[] ) -{ - cmCtx_t ctx; - cmTsMp1cH_t printqH = cmTsMp1cNullHandle; - int appWndW = 1000; - int appWndH = 750; - const char* appPrefDir = "gv"; - const char* appTitle = "GV Console"; - bool memDebugFl = cmDEBUG_FL; - unsigned memPadByteCnt = memDebugFl ? 8 : 0; - unsigned memAlignByteCnt = 16; - unsigned memFlags = memDebugFl ? (kTrackMmFl | kDeferFreeMmFl | kFillUninitMmFl) : 0; - - cmCtxSetup(&ctx,appTitle,print,print,NULL,memPadByteCnt,memAlignByteCnt,memFlags); - - // initialize the memory mgr - if(cmMdInitialize( memPadByteCnt, memAlignByteCnt, memFlags, &ctx.rpt ) != kOkMmRC ) - { - cmErrMsg(&ctx.err,kMemFailAppRC,"Heap initialization failed."); - goto errLabel; - } - - // initialize the file system - if( cmFsInitialize( &ctx, appPrefDir ) != kOkFsRC ) - cmErrMsg(&ctx.err,kFileSysFailAppRC,"File system initialization failed."); - - // initialize the text system - if( cmTsInitialize(&ctx) != kOkTxRC ) - cmErrMsg(&ctx.err,kTextSysFailAppRC,"Text system initialization failed."); - - // create the print queue - if( cmTsMp1cCreate( &printqH, 8192, print_queue_cb, NULL, NULL ) != kOkThRC ) - cmErrMsg(&ctx.err,kQueueFailAppRC,"Print queue creation failed."); - - - if( cmErrLastRC(&ctx.err) == kOkAppRC ) - { - app proj(&ctx, printqH, appWndW, appWndH, appTitle, argc, argv); - Fl::run(); - - // reset the pgm context and print queue console output to stdout - cmRptSetup(&ctx.rpt,print,print,NULL); - cmTsMp1cSetCbFunc(printqH, print_queue_cb, NULL ); - } - - // empty any pending text to stdout - while( cmTsMp1cMsgWaiting(printqH) ) - if( cmTsMp1cDequeueMsg(printqH, NULL, 0) != kOkThRC ) - cmErrMsg(&ctx.err,kQueueFailAppRC,"Print dequeue failed."); - - // destroy the print queue - if( cmTsMp1cDestroy(&printqH) != kOkThRC ) - cmErrMsg(&ctx.err,kQueueFailAppRC,"Print queue destroy failed."); - - // finalize the text system - if( cmTsFinalize() != kOkTxRC ) - cmErrMsg(&ctx.err,kTextSysFailAppRC,"Text system finalization failed."); - - // finalize the file system - if( cmFsFinalize() != kOkFsRC ) - cmErrMsg(&ctx.err,kFileSysFailAppRC,"File system finalize failed."); - - // report memory mgr errors - if( cmMdIsValid() ) - cmMdReport( kIgnoreNormalMmFl ); - - // finalize the memory manager - if( cmMdFinalize() != kOkMmRC ) - cmErrMsg(&ctx.err,kMemFailAppRC,"Heap finalization failed."); - - errLabel: - - - - - return 0; -} diff --git a/src/kc/Fl_File_Btn.cpp b/src/kc/Fl_File_Btn.cpp new file mode 100644 index 0000000..450e3d8 --- /dev/null +++ b/src/kc/Fl_File_Btn.cpp @@ -0,0 +1,103 @@ +#include +#include +#include +#include +#include +#include "Fl_File_Btn.h" + +Fl_File_Btn::Fl_File_Btn( int xx, int yy, int ww, int hh, const char* l ) + : Fl_Group(xx,yy,ww,hh,l), + _patStr(NULL),_typeId(kFile_Type_Id),_btn(NULL),_out(NULL) +{ + _init(); +} + +Fl_File_Btn::Fl_File_Btn( unsigned typeId, const char* patStr, int xx, int yy, int ww, int hh, const char* l ) + : Fl_Group(xx,yy,ww,hh,l), + _patStr(NULL),_typeId(typeId),_btn(NULL),_out(NULL) +{ + _init(); + type(typeId); + pattern_string(patStr); +} + +Fl_File_Btn::~Fl_File_Btn() +{ delete[] _patStr; } + +unsigned Fl_File_Btn::type() +{ return _typeId; } + +void Fl_File_Btn::type( unsigned typeId ) +{ + switch(typeId) + { + case kFile_Type_Id: + _btn->label("File"); + _typeId = typeId; + break; + + case kDir_Type_Id: + _btn->label("Dir"); + _typeId = typeId; + break; + } + + +} + +const char* Fl_File_Btn::pattern_string() +{ return _patStr; } + +void Fl_File_Btn::pattern_string( const char* pat) +{ + delete[] _patStr; + if( pat != NULL ) + { + _patStr = new char[ strlen(pat) + 1]; + strcpy(_patStr,pat); + } +} + +void Fl_File_Btn::btn_width( int ww ) +{ + _btn->resize(_btn->x(),_btn->y(),ww,_btn->h()); + _out->resize(_btn->x() + _btn->w() + 2, _out->y(), w() - _btn->w() - 2, _out->h()); +} + +int Fl_File_Btn::btn_width() +{ return _btn->w(); } + +void Fl_File_Btn::filename( const char* fn ) +{ _out->value(fn); _out->redraw(); } + +const char* Fl_File_Btn::filename() const +{ return _out->value(); } + + +void Fl_File_Btn::_s_btn_cb(Fl_Widget* w, void* data) +{ ((Fl_File_Btn*)data)->_btn_cb(); } + +void Fl_File_Btn::_btn_cb() +{ + char* fn; + if( _typeId == kFile_Type_Id ) + fn = fl_file_chooser("Select a file",_patStr,_out->value(),0); + else + fn = fl_dir_chooser("Select a directory",_out->value(),0); + + if( fn != NULL ) + { + _out->value(fn); + do_callback(this,user_data()); + } + +} + +void Fl_File_Btn::_init() +{ + _btn = new Fl_Button(x(),y(),40,h(),_typeId==kFile_Type_Id ? "File" : "Dir"); + _btn->callback(_s_btn_cb,this); + + _out = new Fl_Output(x() + _btn->w() + 2, y(), w() - _btn->w(), h()); + +} diff --git a/src/kc/Fl_File_Btn.h b/src/kc/Fl_File_Btn.h new file mode 100644 index 0000000..d9d937d --- /dev/null +++ b/src/kc/Fl_File_Btn.h @@ -0,0 +1,47 @@ +#ifndef Fl_File_Btn_h +#define Fl_File_Btn_h + +class Fl_Button; +class Fl_Output; + +class Fl_File_Btn : public Fl_Group +{ +public: + enum + { + kFile_Type_Id, + kDir_Type_Id + }; + + Fl_File_Btn( int x, int y, int w, int h, const char* l = 0 ); + + Fl_File_Btn( unsigned typeId, const char* patStr, int x, int y, int w, int h, const char* l = 0 ); + virtual ~Fl_File_Btn(); + + unsigned type(); + void type( unsigned typeId ); + + const char* pattern_string(); + void pattern_string( const char* pat); + + void btn_width( int w ); + int btn_width(); + + void filename( const char* fn ); + const char* filename() const; + +private: + char* _patStr; + unsigned _typeId; + Fl_Button* _btn; + Fl_Output* _out; + + static void _s_btn_cb(Fl_Widget* w, void* data); + void _btn_cb(); + + void _init(); + +}; + + +#endif diff --git a/src/cmflapp/Fl_Splitter.cpp b/src/kc/Fl_Splitter.cpp similarity index 99% rename from src/cmflapp/Fl_Splitter.cpp rename to src/kc/Fl_Splitter.cpp index b85ca05..c8887d2 100644 --- a/src/cmflapp/Fl_Splitter.cpp +++ b/src/kc/Fl_Splitter.cpp @@ -1,6 +1,6 @@ // Code based on: http://www.mail-archive.com/fltk@easysw.com/msg04573.html // Lucas Sanner/Ian MacArthur - +#include #include "Fl_Splitter.h" void Fl_HSplitter::draw() diff --git a/src/cmflapp/Fl_Splitter.h b/src/kc/Fl_Splitter.h similarity index 100% rename from src/cmflapp/Fl_Splitter.h rename to src/kc/Fl_Splitter.h diff --git a/src/kc/Fl_Vert_Progress.cpp b/src/kc/Fl_Vert_Progress.cpp new file mode 100644 index 0000000..8f09b28 --- /dev/null +++ b/src/kc/Fl_Vert_Progress.cpp @@ -0,0 +1,77 @@ +#include +#include +#include +#include "Fl_Vert_Progress.h" + +Fl_Vert_Progress::Fl_Vert_Progress(int x, int y, int w, int h, const char* l) + : Fl_Progress(x,y,w,h) +{ +} + +void Fl_Vert_Progress::draw() +{ + int progress; // Size of progress bar... + int bx, by, bw, bh; // Box areas... + int tx, ty, tw, th; // Temporary X + width + + float min_ = minimum(); + float max_ = maximum(); + float val_ = value(); + + // Get the box borders... + bx = Fl::box_dx(box()); + by = Fl::box_dy(box()); + bw = Fl::box_dw(box()); + bh = Fl::box_dh(box()); + + tx = x() + bx; + ty = y() + by; + tw = w() - bw; + th = h() - bh; + + + // Draw the progress bar... + if (max_ > min_) + progress = (int)( th * (val_ - min_) / (max_ - min_) ); + else + progress = 0; + + // Draw the box and label... + if (progress > 0) + { + Fl_Color c = labelcolor(); + labelcolor(fl_contrast(labelcolor(), selection_color())); + + + // draw the progress area + //fl_push_clip(x(), y() + h() - progress, w(), progress); + fl_push_clip(tx,ty + th - progress, tw, progress ); + draw_box(box(), x(), y(), w(), h(), active_r() ? selection_color() : fl_inactive(selection_color())); + draw_label(tx, y() + by, tw, h() - bh); + fl_pop_clip(); + + labelcolor(c); + + if (progress +#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 "cmProcTest.h" + +#include "cmDspValue.h" +#include "cmMsgProtocol.h" +#include "cmAudDspIF.h" +#include "cmAudDspLocal.h" +#include "kcApp.h" + +#include "kcApp.h" + +#define TIMER_PERIOD (1.0/20.0) // 50ms + + +kcApp::kcApp(int w, int h, const char *l, int argc, char *argv[]) + : Fl_Double_Window(w, h, l), + menu(NULL),splt(NULL), + _adlH(cmAdlNullHandle),_aiH(cmAiNullHandle), + _statIdx(0),_pageList(NULL),_ctlList(NULL),_prH(cmPrNullHandle),_prefsFn(NULL), + _stopTimerFl(false),_jsH(cmJsonNullHandle),_newPageFl(true),_incrColW(0),_colW(0),_horzBordFl(false),_horzBord(0), + _closeCnt(0),_ssCnt(0),_ssArray(0),_ssPhase(0),_ssUpdateFl(false),_printqH(cmTsQueueNullHandle) +{ + // 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(); + splt = new Fl_HSplitter(0, 0, w, h-kStatusH, h-kStatusH-100); + end(); + + // Create a text display object for console output and + // add it to the lower splitter area + buf = new Fl_Text_Buffer(); + con = new Fl_Text_Display(splt->container2->x(),splt->container2->y(),splt->container2->w(),splt->container2->h()); + con->buffer(buf); + splt->container2->add(con); + + // Create a tab view and added it to the upper splitter area + tabs = new Fl_Tabs(splt->container1->x(),splt->container1->y(),splt->container1->w(),splt->container1->h()); + tabs->callback(_s_tab_cb,this); + + splt->container1->add(tabs); + + int tx,ty,th,tw; + tabs->client_area(tx,ty,tw,th); + + // Create the 'Setup' tab group + tabs->begin(); + Fl_Group* setup_grp = new Fl_Group(tx,ty,tw,th,"Setup"); + _createSetupDlg(setup_grp); + tabs->end(); + + + tabs->begin(); + mstr_grp = new Fl_Group(tx,ty,tw,th,"Master"); + tabs->end(); + + // Create the 'Controls' tab group + /* + tabs->begin(); + ctl_grp = new Fl_Group(tx,ty,tw,th,"Controls"); + ctl_grp->begin(); + Fl_Box* bx = new Fl_Box(FL_NO_BOX,ctl_grp->x()+ctl_grp->w(),ctl_grp->y()+ctl_grp->h(),1,1,NULL); + ctl_grp->resizable(bx); + ctl_grp->end(); + tabs->end(); + */ + + // Create an empty tab group and make it resizable + // to prevent the other tab groups from being resizable. + tabs->begin(); + Fl_Group* wdgt = new Fl_Group(tx,ty+30,1,1); + tabs->resizable(wdgt); // make other tabs non-resizable + tabs->end(); + + + // 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(splt); + show(argc, argv); + + + // initialize the audio DSP system + audioDspInitialize(&_ctx,_s_print,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(); +} + + +// this function is only called from the constructor +kcApp::kcKmRC_t kcApp::audioDspInitialize(cmCtx_t* ctx, cmRptPrintFunc_t printFunc, void* printFuncArg) +{ + kcKmRC_t rc = kOkKmRC; + + // initialize the heap check library + bool memDebugFl = cmDEBUG_FL; + unsigned memPadByteCnt = memDebugFl ? 8 : 0; + unsigned memAlignByteCnt = 16; + unsigned memFlags = memDebugFl ? (kTrackMmFl | kDeferFreeMmFl | kFillUninitMmFl) : 0; + + cmCtxSetup(ctx,"KC Main",printFunc,printFunc,printFuncArg,memPadByteCnt,memAlignByteCnt,memFlags); + + cmMdInitialize( memPadByteCnt, memAlignByteCnt, memFlags, &ctx->rpt ); + + cmTsInitialize(ctx); + + cmFsInitialize( ctx, "kc" ); + + cmJsonInitialize(&_jsH,&_ctx); + + //_loadPrefs(ctx); + + // create the print queue + if( cmTsQueueCreate( &_printqH, 4*8192, kcApp::_s_print_queue_cb, this, &_ctx.rpt ) != kOkThRC ) + cmErrMsg(&ctx->err,kPrintQueFailKmRC,"Print queue creation failed."); + + cmAdIfDispatch_t r; + r.cbDataPtr = this; + r.ssInitFunc = _s_handleSsInitMsg; + r.statusFunc = _s_handleStatusMsg; + r.uiFunc = _s_handleUiMsg; + + if( cmAudDspLocalAllocate(ctx,&_adlH,&r) != kOkAdlRC ) + { + rc = cmErrMsg(&ctx->err,kEngFailKmRC,"The audio DSP local server allocation failed."); + goto errLabel; + } + + if(cmAdIfIsValid(_aiH = cmAudDspLocalIF_Handle(_adlH)) == false ) + { + rc = cmErrMsg(&ctx->err,kEngFailKmRC,"The audio DSP interface handle is not valid."); + goto errLabel; + } + + errLabel: + return rc; +} + +bool kcApp::audioDspIsValid() +{ return cmAudDspLocalIsValid(_adlH) && cmAdIfIsValid(_aiH); } + +kcApp::kcKmRC_t kcApp::audioDspFinalize() +{ + kcKmRC_t rc = kOkKmRC; + + _stopTimerFl = true; + + if( cmJsonFinalize(&_jsH) != kOkJsRC ) + rc = cmErrMsg(&_ctx.err,kJsonFailKmRC,"JSON object finalization failed."); + + // If the preferences are dirty or the prefs file does not exist then write them + if( cmPrefsIsValid(_prH) ) + { + + if( _prefsFn != NULL && (cmPrefsIsValid(_prH) && (cmPrefsIsDirty(_prH) || cmFsIsFile(_prefsFn)==false)) ) + if( cmPrefsWrite(_prH,_prefsFn) != kOkPrRC ) + rc = cmErrMsg(&_ctx.err,kPrefWriteFailKmRC,"Preference save failed on file '%s'.",_prefsFn); + + if( cmPrefsFinalize(&_prH) != kOkPrRC ) + rc = cmErrMsg(&_ctx.err,kPrefFailKmRC,"Prefrence finalization failed."); + + } + + // finalize the real-time system + //rc = stopEngine(_ctx,*hp); + + // release the engine + if( cmAudDspLocalFree(&_adlH) != kOkAdlRC ) + rc = cmErrMsg(&_ctx.err,kEngFailKmRC,"Audio DSP release failed."); + + // print any pending text in the print queue + _checkPrintQueue(); + + // the app threads are stopped so it is safe to stop using the print queue + if( cmTsQueueDestroy(&_printqH) == kOkThRC ) + _printqH = cmTsQueueNullHandle; + else + cmErrMsg(&_ctx.err,kPrintQueFailKmRC,"Print queue destroy failed."); + + // finalize the file system + cmFsFinalize(); + + cmTsFinalize(); + + // report memory mgr errors + if( cmMdReport( kIgnoreNormalMmFl ) != kOkMmRC ) + rc = kMemFailKmRC; + + // finalize the memory manager + if( cmMdFinalize() != kOkMmRC ) + rc = kMemFailKmRC; + + return rc; +} + + +// this function is called whenever a audio configuration parameter +// changes (e.g. device, sample rate ) +/* +kcApp::kcKmRC_t kcApp::audioDspStart() +{ + kcKmRC_t rc = kOkKmRC; + + // initializing the engine will result in a new control ui being generated so delete the current on + _clearCtlList(); + + // initialize the real-time system + if( kcEngInitialize(h,cfg) != kOkKeRC ) + { + rc = cmErrMsg(&ctx->err,kEngFailKmRC,"Engine initialization failed."); + goto errLabel; + } + + _stopTimerFl = false; + + // load the dsp program + if( kcEngLoad(h) != kOkKeRC ) + { + rc = cmErrMsg(&ctx->err,kEngFailKmRC,"Engine load failed."); + goto errLabel; + } + + // start the real-time system + if( kcEngEnable(h,true) != kOkKeRC ) + { + rc = cmErrMsg(&ctx->err,kEngFailKmRC,"Engine enable failed."); + goto errLabel; + } + + ena_chk->value(1); + + errLabel: + return rc; +} + +kcApp::kcKmRC_t kcApp::stopEngine( ) +{ + kcKmRC_t rc = kOkKmRC; + + + if( kcEngIsValid(h)) + { + // pause the real-time system + if( kcEngEnable(h,false) != kOkKeRC ) + rc = cmErrMsg(&ctx->err,kEngFailKmRC,"Engine disable failed."); + else + ena_chk->value(0); + + _stopTimerFl = true; + + // finalize the real-time system + if( kcEngFinalize(h) != kOkKeRC ) + rc = cmErrMsg(&ctx->err,kEngFailKmRC,"Engine finalize failed."); + } + return rc; +} +*/ + + +void kcApp::resize(int x, int y, int w, int h) +{ + Fl_Double_Window::resize(x, y, w, h); + splt->resize_splitter(0, 0, w, h-kStatusH); +} + + +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; + + 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[] = + { + { "&File", 0, 0, 0, FL_SUBMENU }, + { "&New File", 0, (Fl_Callback*)_s_file_new_cb }, + { "&Open File", FL_COMMAND + 'o', (Fl_Callback*)_s_file_open_cb }, + { 0 }, + { "&Edit", 0, 0, 0, FL_SUBMENU }, + { "&Copy", FL_COMMAND + 'c', (Fl_Callback*)_s_edit_copy_cb }, + { "&Paste", FL_COMMAND + 'v', (Fl_Callback*)_s_edit_paste_cb }, + { 0 }, + { 0 } + }; + + menu = new Fl_Menu_Bar(0,0,w,h); + menu->copy(items); +} +*/ + + void 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 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(); + +} + + +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 ) +{ + 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; + + _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); + + _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; 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 ) + --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::_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::_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::_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; + void* di = (void*)devIdx; + for(i=0; i"); + } + + +} + +void kcApp::_setSampleRateBtn( unsigned value ) +{ + unsigned i; + unsigned n = sr_btn->size(); + for(i=0; imenu()[i].argument() == 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; + 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; + + 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 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 kDeviceDuiId: + _insertDeviceLabel(m->instId,m->flags,cmDsvStrcz(&m->value)); + break; + + case kProgramDuiId: + _insertProgramLabel(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; + + } +} + +cmRC_t kcApp::_s_handleSsInitMsg( void* cbDataPtr, const cmAudioSysSsInitMsg_t* r, const char* iDevLabel, const char* oDevLabel ) +{ + kcApp* p = (kcApp*)cbDataPtr; + p->_handleSsInitMsg(r,iDevLabel,oDevLabel); + return cmOkRC; +} + +cmRC_t kcApp::_s_handleStatusMsg( void* cbDataPtr, const cmAudioSysStatus_t* r, const double* iMeterArray, const double* oMeterArray ) +{ + kcApp* p = (kcApp*)cbDataPtr; + p->_handleStatusMsg(r,iMeterArray,oMeterArray); + return cmOkRC; +} + +cmRC_t kcApp::_s_handleUiMsg( void* cbDataPtr, const cmDspUiHdr_t* r ) +{ + kcApp* p = (kcApp*)cbDataPtr; + p->_handleUiMsg(r); + return cmOkRC; +} + + + +// Check for and forward any messages sent to the UI +// that are waiting in the audio DSP msg queue. +void kcApp::_getEngMsg() +{ + + + if( audioDspIsValid() ) + { + 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(); +} + +/* +kcApp::kcKmRC_t kcApp::_restartEngine() +{ + kcKmRC_t rc; + if((rc = stopEngine(&_ctx,_keH)) != kOkKmRC ) + goto errLabel; + + if((rc = startEngine(&_ctx,_keH,&_cfg)) != kOkKmRC ) + goto errLabel; + + errLabel: + if( rc != kOkKmRC ) + cmErrMsg(&_ctx.err,kEngFailKmRC,"Engine restart failed."); + + return rc; +} + +void kcApp::_selAudDev( Fl_Menu_Button* b, bool inputFl, const cmChar_t* devLbl ) +{ + unsigned* curDevIdxPtr = inputFl ? &_cfg.asArgs.inDevIdx : &_cfg.asArgs.outDevIdx; + unsigned curDevIdx = *curDevIdxPtr; + const Fl_Menu_Item* mip = b->mvalue(); + bool updateBtnFl = true; + + if( mip == NULL ) + return; + + // get the select device index + unsigned newDevIdx = mip->argument(); + + // if the new dev idx is same as cur dev idx there is nothing to do + if( newDevIdx != curDevIdx ) + { + // assign the new dev idx to the audio setup cfg + *curDevIdxPtr = newDevIdx; + + // restart the engine with the new cfg + if( _restartEngine() == kOkKmRC ) + { + // if the audio engine restarted successfully then store the new dev idx to the preferences + if( cmPrefsSetScalarUInt(_prH,devLbl,newDevIdx) != kOkPrRC ) + cmErrMsg(&_ctx.err,kPrefSetFailKmRC,"An error occurred while attempting to set the audio device preference '%s'.",devLbl); + + } + else + { + // the new dev idx didn't work - restart the engine with the original dev idx + *curDevIdxPtr = curDevIdx; + _restartEngine(); + updateBtnFl = false; + } + } + + // set the menu btn to show the current device + if( updateBtnFl ) + b->copy_label( mip->label() ); + +} + +void kcApp::_selectAudioDevice(Fl_Menu_Button* b, unsigned id) +{ + switch(b->argument()) + { + case kInAudDevBtnId: + _selAudDev(b, true, "cfg/inAudDevIdx" ); + break; + + case kOutAudDevBtnId: + _selAudDev(b,false,"cfg/outAudDevIdx"); + break; + + default: + { assert(0); } + } +} + +void kcApp::_selectSampleRate(unsigned asSubIdx, unsigned id) +{ + const Fl_Menu_Item* i = sr_btn->mvalue(); + double curSrate = _cfg.asArgs.srate; + double newSrate = i->argument(); + bool updateBtnFl = true; + + //cmAdIfSetSampleRate(_aiH,asSubIdx,newSrate); + + if( curSrate != newSrate ) + { + _cfg.asArgs.srate = newSrate; + + if( _restartEngine() == kOkKmRC ) + { + if( cmPrefsSetScalarReal(_prH,"cfg/srate",newSrate) != kOkPrRC ) + cmErrMsg(&_ctx.err,kPrefSetFailKmRC,"An error occurred while settig the audio sample rate preference."); + } + else + { + _cfg.asArgs.srate = curSrate; + _restartEngine(); + updateBtnFl = false; + } + } + + if( updateBtnFl) + sr_btn->copy_label( sr_btn->mvalue()->label() ); +} +*/ + +/* +void kcApp::_s_prefCbFunc( cmPrH_t prH, void* cbDataPtr, unsigned prefId ) +{ ((kcApp*)cbDataPtr)->_prefCbFunc(prH,prefId); } + +void kcApp::_prefCbFunc( cmPrH_t prH, unsigned prefId ) +{ +} + +kcApp::kcKmRC_t kcApp::_loadPrefs( cmCtx_t* ctx ) +{ + kcKmRC_t rc = kOkKmRC; + const cmChar_t* prefDir = cmFsPrefsDir(); + const cmChar_t* rsrcDir = cmFsRsrcDir(); + const cmChar_t* fn = "org.larke.kc.txt"; + const cmChar_t* prefFn = cmFsMakeFn(prefDir,fn,NULL,NULL); + const cmChar_t* rsrcFn = cmFsMakeFn(rsrcDir,fn,NULL,NULL); + const cmChar_t* pfn = prefFn; + + if( cmFsIsFile(pfn) == false ) + { + pfn = rsrcFn; + if( cmFsIsFile(pfn) == false ) + { + rc = cmErrMsg(&ctx->err,kPrefsNotFoundKmRC,"The default preferences file '%s' was not found.",pfn); + pfn = NULL; + } + + } + + if( pfn != NULL ) + if( cmPrefsInitialize(&_prH,pfn,_s_prefCbFunc,this,ctx) != kOkPrRC ) + rc = cmErrMsg(&ctx->err,kPrefLoadFailKmRC,"Preference load from '%s' failed.",pfn); + + // Even if cmPrefsInitialize() fails we continue with _cfg setup using the hard coded default values. + + _cfg.asArgs.rpt = &ctx->rpt; + _cfg.asArgs.inDevIdx = cmPrefsUIntDef( _prH,"cfg/inAudDevIdx", 0); + _cfg.asArgs.outDevIdx = cmPrefsUIntDef( _prH,"cfg/outAudDevIdx", 2); + _cfg.asArgs.syncInputFl = cmPrefsBoolDef( _prH,"cfg/syncInputFl", true); + _cfg.asArgs.midiPortBufByteCnt = cmPrefsUIntDef( _prH,"cfg/midiPortBufByteCnt", 8192); + _cfg.asArgs.msgQueueByteCnt = cmPrefsUIntDef( _prH,"cfg/msgQueueByteCnt", 16384); + _cfg.asArgs.devFramesPerCycle = cmPrefsUIntDef( _prH,"cfg/devFramesPerCycle", 128); + _cfg.asArgs.dspFramesPerCycle = cmPrefsUIntDef( _prH,"cfg/dspFramesPerCycle", 64); + _cfg.asArgs.audioBufCnt = cmPrefsUIntDef( _prH,"cfg/audioBufCnt", 2); + _cfg.asArgs.srate = cmPrefsRealDef( _prH,"cfg/srate", 48000); + const cmChar_t* audioDir = cmPrefsStringDef( _prH,"cfg/audioDir", cmFsUserDir()); + + _cfg.meterMs = 50; + _cfg.prefH = _prH; + + + _prefsFn = prefFn; + + ad_btn->filename(audioDir); + + + + //print("%s\n",prefFn); + + return rc; +} +*/ + + +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); + +} + + +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 ) + { + unsigned cc = _closeCnt; + + // attempt to shut down the pgm + if( audioDspFinalize() != kOkKmRC ) + ++_closeCnt; + + // the first time the pgm fails to shut down + // do not allow the pgm to close the main window + // this will give a chance for the error messages + // to be diplayed in the console - all successive + // times the return value from finalizePgm() is + // ignored and the program is terminated. + if( _closeCnt == 1 && cc==0) + { + deactivate(); // send a strong hint that a problem occurred + return; + } + + + // 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::_s_on_idle(void *data) +{ ((kcApp*)data)->_on_idle(); } + +void kcApp::_on_idle() +{ + //_getEngMsg(); + //_getEngStatus(); +} + +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( cmTsQueueIsValid(_printqH) ) + _checkPrintQueue(); + + if( !_stopTimerFl ) + _getEngMsg(); + + if( _ssUpdateFl ) + { + ++_ssPhase; + if( _ssPhase >= kSsPhaseMax ) + { + _clearStatusIndicators(); + _ssPhase = 0; + } + } + + if( _stopTimerFl==false && cmAdIfIsValid(_aiH) ) + cmAdIfDispatchMsgToHost(_aiH); + + //if( !_stopTimerFl ) + // _getEngStatus(); + + return _stopTimerFl==false || cmTsQueueIsValid(_printqH); +} + + + + +void kcApp::_s_file_new_cb(Fl_Widget *w, void *data) +{ ((kcApp*)data)->_file_new_cb(w); } + +void kcApp::_file_new_cb(Fl_Widget *w) +{ } + + +void kcApp::_s_file_open_cb(Fl_Widget *w, void *data) +{ ((kcApp*)data)->_file_open_cb(w); } + +void kcApp::_file_open_cb(Fl_Widget *w) +{ } + + +void kcApp::_s_edit_copy_cb(Fl_Widget *w, void *data) +{ ((kcApp*)data)->_edit_copy_cb(w); } + +void kcApp::_edit_copy_cb(Fl_Widget *w) +{ } + + +void kcApp::_s_edit_paste_cb(Fl_Widget *w, void *data) +{ ((kcApp*)data)->_edit_paste_cb(w); } + +void kcApp::_edit_paste_cb(Fl_Widget *w) +{ } + + +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( audioDspIsValid() ) + { + 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; + + 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; + + default: + {assert(0);} + + } + + 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( cmTsQueueIsValid(_printqH) ) + { + // ... enqueue the text to print + if( cmTsQueueEnqueueMsg(_printqH,buf,n+1) != kOkThRC ) + { + printf("Print enqueue failed on msg:%s\n",buf); + // we can't call an error here because it would generate an error + // msg which would possibly fail at this same point resulting in + // a recursion which would eventually overflow the stack. + + } + } + 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( cmTsQueueMsgWaiting(_printqH) ) + if( cmTsQueueDequeueMsg(_printqH, NULL, 0) != kOkThRC ) + cmErrMsg(&_ctx.err,kPrintQueFailKmRC,"Print dequeue failed."); +} + +void kcApp::_print( const char* text ) +{ + if( con != NULL ) + con->insert(text); +#ifndef NDEBUG + fputs(text,stdout); +#endif +} diff --git a/src/kc/kcApp.h b/src/kc/kcApp.h new file mode 100644 index 0000000..8302834 --- /dev/null +++ b/src/kc/kcApp.h @@ -0,0 +1,406 @@ +#ifndef kcApp_h +#define kcApp_h + +extern "C" struct cmApBufState_str; + +class Fl_Menu_Bar; +class Fl_HSplitter; +class Fl_Text_Buffer; +class Fl_Text_Display; +class Fl_Box; +class Fl_Tabs; +class Fl_Check_Button; +class Fl_Value_Slider; +class Fl_Menu_Button; +class Fl_File_Btn; +class Fl_Select_Browser; +class Fl_Valuator; +class Fl_Value_Input; +class Fl_Progress; + +class kcApp : public Fl_Double_Window +{ +public: + typedef unsigned kcKmRC_t; + + + enum + { + kOkKmRC, + kEngFailKmRC, + kMemFailKmRC, + kPrefsNotFoundKmRC, + kPrefWriteFailKmRC, + kPrefLoadFailKmRC, + kPrefFailKmRC, + kPrefSetFailKmRC, + kMlistLoadFailKmRC, + kJsonFailKmRC, + kDeserialFailKmRC, + kPrintQueFailKmRC, + + }; + + + kcApp(int w, int h, const char *l, int argc, char *argv[]); + virtual ~kcApp(); + + kcKmRC_t audioDspInitialize( cmCtx_t* ctx, cmRptPrintFunc_t printFunc, void* printFuncArg ); + kcKmRC_t audioDspFinalize(); + bool audioDspIsValid(); + + void print( const char* fmt, ... ); + void vprint( const char* fmt, va_list vl ); + + +private: + enum + { + kMenuH = 30, + kStatusH = 30, + kIndH = 25, + kIndW = 25, + + kSsPhaseMax = 20 + }; + + Fl_Menu_Bar* menu; + Fl_HSplitter* splt; + Fl_Box* ind[4]; + Fl_Text_Buffer* buf; + Fl_Text_Display* con; + Fl_Tabs* tabs; + Fl_Check_Button* ena_chk; + //Fl_Group* ctl_grp; + + Fl_Menu_Button* as_btn; + Fl_Menu_Button* ai_btn; + Fl_Menu_Button* ao_btn; + Fl_Menu_Button* pgm_btn; + Fl_Menu_Button* ss_btn; + Fl_Menu_Button* sr_btn; + + Fl_Group* mstr_grp; + + + enum + { + kSldrTypeId, + kNumbTypeId, + kTextTypeId, + kButnTypeId, + kChckTypeId, + kLablTypeId, + kFnamTypeId, + kMlstTypeId, + kMetrTypeId, + + + kSldrMinArgIdx = 0, + kSldrMaxArgIdx = 1, + kSldrStpArgIdx = 2, + kSldrValArgIdx = 3, + kSldrLblArgIdx = 4, + kSldrVarCnt = 5, + + kTextValArgIdx = 0, + kTextLblArgIdx = 1, + kTextVarCnt = 2, + + kButnValArgIdx = 0, + kButnLblArgIdx = 1, + kButnVarCnt = 2, + + kLablValArgIdx = 0, + kLablAlignArgIdx= 1, + kLablVarCnt = 2, + + kChckValArgIdx = 0, + kChckLblArgIdx = 1, + kChckVarCnt = 2, + + kFnamValArgIdx = 0, + kFnamPatArgIdx = 1, + kFnamDirArgIdx = 2, + kFnamVarCnt = 3, + + kMlstHgtArgIdx = 0, + kMlstSelArgIdx = 1, + kMlstLstArgIdx = 2, + kMlstVarCnt = 3, + + kMetrMinArgIdx = 0, + kMetrMaxArgIdx = 1, + kMetrValArgIdx = 2, + kMetrLblArgIdx = 3, + kMetrVarCnt = 4 + + }; + + enum + { + kEnableBtnId = 1, + kAudDevRptBtnId, + kAudioSysCfgBtnId, + kInAudDevBtnId, + kOutAudDevBtnId, + kPgmBtnId, + kSubSystemIdxBtnId, + kSrateBtnId, + kTestBtnId + }; + + enum + { + k44100SrateIdx, + k48000SrateIdx, + k96000SrateIdx + }; + + typedef struct sldr_str + { + union + { + Fl_Value_Slider* sldr; + Fl_Value_Input* numb; + } u; + Fl_Valuator* val; + double defaultVal; + unsigned varIdArray[ kSldrVarCnt ]; + } sldr_t; + + typedef struct text_str + { + Fl_Input* text; + char* val; + unsigned varIdArray[ kTextVarCnt ]; + } text_t; + + typedef struct butn_str + { + Fl_Button* butn; + double val; + unsigned varIdArray[ kButnVarCnt ]; + } butn_t; + + typedef struct chck_str + { + Fl_Check_Button* chck; + double val; + unsigned varIdArray[ kChckVarCnt ]; + } chck_t; + + typedef struct labl_str + { + Fl_Box* box; + unsigned varIdArray[ kLablVarCnt ]; + } labl_t; + + typedef struct fname_str + { + Fl_File_Btn* fnam; + unsigned varIdArray[ kFnamVarCnt ]; + } fnam_t; + + typedef struct mlst_str + { + Fl_Select_Browser* mlst; + Fl_Menu_Button* mbtn; + int sel; + unsigned varIdArray[ kMlstVarCnt ]; + } mlst_t; + + typedef struct metr_str + { + Fl_Progress* prog; + unsigned varIdArray[ kMetrVarCnt ]; + } metr_t; + + typedef struct ctl_str + { + kcApp* thisPtr; + unsigned asSubIdx; + unsigned typeId; + unsigned instId; + Fl_Widget* wdgtPtr; + bool mstrFl; + union + { + sldr_t sldr; + text_t text; + butn_t butn; + chck_t chck; + labl_t labl; + fnam_t fnam; + mlst_t mlst; + metr_t metr; + } u; + struct ctl_str* linkPtr; + } ctl_t; + + typedef struct page_str + { + Fl_Group* grp; + struct page_str* link; + page_str() : grp(NULL), link(NULL) {} + } page_t; + + enum + { + kUpdateSsIdx, + kWakeupSsIdx, + kMsgSsIdx, + kAudioCbSsIdx, + kProgSsCnt + }; + + // each audio sub-system is represented by an ss_t record in _ssArray[_ssCnt] + typedef struct ss_str + { + Fl_Progress* prog[kProgSsCnt]; // status indicator controls + unsigned cnt[kProgSsCnt]; // previous status count value + + } ss_t; + + + cmCtx_t _ctx; + cmAdlH_t _adlH; + cmAiH_t _aiH; + unsigned _statIdx; + page_t* _pageList; + ctl_t* _ctlList; + const cmChar_t* _prefFn; + cmPrH_t _prH; + const cmChar_t* _prefsFn; + bool _stopTimerFl; + cmJsonH_t _jsH; + bool _newPageFl; + unsigned _incrColW; + unsigned _colW; + bool _horzBordFl; + unsigned _horzBord; + unsigned _closeCnt; + unsigned _ssCnt; // count of audio system sub-systems (set in _handleSsInitMsg()) + ss_t* _ssArray; + int _ssPhase; + bool _ssUpdateFl; + cmTsQueueH_t _printqH; // thread-safe queue for controlling access to the output console from multiple threads + + void resize(int x, int y, int w, int h); + + void _createSetupDlg(Fl_Group* grp); + + void _createPage( const char* title ); + + unsigned _getCtlCount() const; + void _clearCtlList(bool mstrFl); + void _clearSsArray(); + void _getNewCtlPosn( const cmDspUiHdr_t* m, int& x, int& y, int& w, int& h ); + ctl_t* _findCtl( unsigned instId, unsigned asSubIdx, unsigned mstrFl=false ); + ctl_t* _createCtl( const cmDspUiHdr_t* m, unsigned typeId, int& x, int& y, int& w, int& h ); + void _insertNewCtl( ctl_t* cp, const cmDspUiHdr_t* m, Fl_Widget* wdgt, unsigned* varIdArray, unsigned varIdCnt ); + + void _createSlider( const cmDspUiHdr_t* m ); + void _setSldrValue( ctl_t* cp, unsigned instVarId, const cmDspValue_t* vp ); + void _createText( const cmDspUiHdr_t* m ); + void _setTextValue( ctl_t* cp, unsigned instVarId, const cmDspValue_t* vp ); + void _createFnameCtl( const cmDspUiHdr_t* m ); + void _setFnamValue( ctl_t* cp, unsigned instVarId, const cmDspValue_t* vp ); + void _createMlistCtl( const cmDspUiHdr_t* m ); + kcKmRC_t _loadMlist( ctl_t* cp, const cmJsonNode_t* np ); + void _setMlistValue( ctl_t* cp, unsigned instVarId, const cmDspValue_t* vp ); + void _createMeter( const cmDspUiHdr_t* m ); + void _setMeterValue( ctl_t* cp, unsigned instVarId, const cmDspValue_t* vp ); + void _createButton( const cmDspUiHdr_t* m ); + void _setButtonValue( ctl_t* cp, unsigned instVarId, const cmDspValue_t* vp ); + void _createCheck( const cmDspUiHdr_t* m ); + void _setCheckValue( ctl_t* cp, unsigned instVarId, const cmDspValue_t* vp ); + void _createLabel( const cmDspUiHdr_t* m ); + void _setLabelValue( ctl_t* cp, unsigned instVarId, const cmDspValue_t* vp ); + void _newColumn( const cmDspUiHdr_t* m ); + + void _insertAudioSysCfgLabel(unsigned long idx, const cmChar_t* label ); + void _insertDeviceLabel( unsigned long idx, bool inputFl, const cmChar_t* label ); + void _insertProgramLabel( unsigned long idx, const cmChar_t* label ); + void _insertSubSysCnt( unsigned long subSysCnt ); + unsigned _getCurAudioSubSysIdx(); + void _setMenuButton( Fl_Menu_Button* b, unsigned value, const char* dfltLabel ); + void _setDeviceMenuButton( unsigned asSubIdx, bool inputFl, unsigned devIdx ); + void _setSampleRateBtn( unsigned value ); + + void _updateMeters( unsigned asSubIdx, unsigned devIdx, unsigned inFl, const double* meterArray, unsigned meterCnt ); + void _updateSsStatusIndicator( unsigned asSubIdx, unsigned indicatorIdx, unsigned cnt ); + void _printStatusCounts(); + void _handleStatusMsg( const cmAudioSysStatus_t* st, const double* iMeterArray, const double* oMeterArray ); + void _clearStatusIndicators(); + + void _sendMasterUiMsg( unsigned asSubIdx, unsigned selId, unsigned instId, unsigned instVarId, const cmDspValue_t* v ); + void _sendMasterUiValue(unsigned asSubIdx, unsigned instId, const double* v, unsigned vn, const cmChar_t* text ); + void _createMasterCtl( unsigned asSubIdx, unsigned selId, unsigned instId, unsigned varCnt, const double* dv, unsigned dn, const cmChar_t* label); + void _handleSsInitMsg( const cmAudioSysSsInitMsg_t* m, const cmChar_t* inDevLabel, const cmChar_t* outDevLabel ); + void _onRecvValue( const cmDspUiHdr_t* m ); + void _handleUiMsg( const cmDspUiHdr_t* m ); + void _getEngMsg(); + + static cmRC_t _s_handleSsInitMsg( void* cbDataPtr, const cmAudioSysSsInitMsg_t* r, const char* iDevLabel, const char* oDevLabel ); + static cmRC_t _s_handleStatusMsg( void* cbDataPtr, const cmAudioSysStatus_t* r, const double* iMeterArray, const double* oMeterArray ); + static cmRC_t _s_handleUiMsg( void* cbDataPtr, const cmDspUiHdr_t* r ); + + static kcApp* _getApp( Fl_Widget* w ); + + kcKmRC_t _restartEngine(); + + void _testStub(); + + // main app callback + static void _s_callback(Fl_Widget* wp, void* data); + void _callback(void* data); + + // idle callback + static void _s_on_idle(void* data); + void _on_idle(); + + + // timer callback + static void _s_status_timeout_cb(void* userPtr); + bool _status_timeout_cb(); + + static void _s_file_new_cb(Fl_Widget *w, void *data); + void _file_new_cb(Fl_Widget *w); + + static void _s_file_open_cb(Fl_Widget *w, void *data); + void _file_open_cb(Fl_Widget *w); + + static void _s_edit_copy_cb(Fl_Widget *w, void *data); + void _edit_copy_cb(Fl_Widget *w); + + static void _s_edit_paste_cb(Fl_Widget *w, void *data); + void _edit_paste_cb(Fl_Widget *w); + + static void _s_btn_cb(Fl_Widget* w, long data); + void _btn_cb(Fl_Widget* w, long arg); + + static void _s_ctl_cb(Fl_Widget* w, void* data); + void _ctl_cb(ctl_t* cp); + + static void _s_tab_cb(Fl_Widget* w, void* data); + void _tab_cb(Fl_Widget* w); + + // + static void _s_print( void* userPtr, const char* text ); + + // called internally by cmTsQueueDequeue() to send text to _print() + static cmRC_t _s_print_queue_cb(void* userCbPtr, unsigned msgByteCnt, const void* msgDataPtr ); + + // called periodically to check the print queue for waiting text. + void _checkPrintQueue(); + + // send a string of text directly to the output console window + void _print( const char* text ); + + +}; + + + +#endif diff --git a/src/kc/kcMain.cpp b/src/kc/kcMain.cpp new file mode 100644 index 0000000..567cb0e --- /dev/null +++ b/src/kc/kcMain.cpp @@ -0,0 +1,39 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Fl_Splitter.h" + +#include "cmGlobal.h" +#include "cmFloatTypes.h" +#include "cmRpt.h" +#include "cmErr.h" +#include "cmCtx.h" +#include "cmPrefs.h" +#include "cmJson.h" +#include "cmThread.h" +#include "cmDspValue.h" +#include "cmMsgProtocol.h" +#include "cmAudDspIF.h" +#include "cmAudDspLocal.h" +#include "kcApp.h" + + +void print(void* cmRptUserPtr, const char* text) +{ printf("%s",text); } + +int main(int argc, char* argv[] ) +{ + kcApp myApp(1600, 750, "Console", argc, argv); + Fl::run(); + + //test(); + + return 0; +}