Programmable real-time audio signal processing application
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

app.cpp 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555
  1. #include <FL/Fl.H>
  2. #include <FL/Fl_Widget.H>
  3. #include <FL/Fl_Double_Window.H>
  4. #include <FL/Fl_Text_Buffer.H>
  5. #include <FL/Fl_Text_Display.H>
  6. #include <FL/Fl_Tabs.H>
  7. #include <FL/Fl_Menu_Item.H>
  8. #include <FL/Fl_Menu_Bar.H>
  9. #include <FL/Fl_Button.H>
  10. #include <FL/Fl_Check_Button.H>
  11. #include <FL/Fl_Menu_Button.H>
  12. #include <FL/Fl_Value_Input.H>
  13. #include <FL/Fl_Value_Slider.H>
  14. #include <FL/Fl_Input.H>
  15. #include <FL/Fl_Box.H>
  16. #include <FL/Fl_File_Chooser.H>
  17. #include <FL/fl_draw.H>
  18. #include "Fl_Splitter.h"
  19. #include "cmPrefix.h"
  20. #include "cmGlobal.h"
  21. #include "cmRpt.h"
  22. #include "cmErr.h"
  23. #include "cmCtx.h"
  24. #include "cmMem.h"
  25. #include "cmMallocDebug.h"
  26. #include "cmFileSys.h"
  27. #include "cmThread.h"
  28. #include "appErr.h"
  29. #include "app.h"
  30. class drawWnd : public Fl_Widget
  31. {
  32. public:
  33. drawWnd(app* ap, int x, int y, int w, int h, const char* label = NULL );
  34. virtual ~drawWnd();
  35. virtual int handle(int event);
  36. virtual void resize(int x, int y, int w, int h );
  37. protected:
  38. virtual void draw();
  39. private:
  40. app* _app;
  41. };
  42. drawWnd::drawWnd(app* ap, int x, int y, int w, int h, const char* label )
  43. : Fl_Widget(x,y,w,h,label),
  44. _app(ap)
  45. {}
  46. drawWnd::~drawWnd()
  47. {}
  48. int drawWnd::handle(int event)
  49. {
  50. switch(event)
  51. {
  52. case FL_PUSH:
  53. {
  54. const char* label;
  55. switch( Fl::event_button() )
  56. {
  57. case FL_LEFT_MOUSE: label = "left"; break;
  58. case FL_RIGHT_MOUSE: label = "right"; break;
  59. case FL_MIDDLE_MOUSE: label = "middle"; break;
  60. default:
  61. label = "none";
  62. }
  63. _app->print("%i %i %s\n",Fl::event_x(),Fl::event_y(),label);
  64. }
  65. break;
  66. default:
  67. return Fl_Widget::handle(event);
  68. }
  69. return 1;
  70. }
  71. void drawWnd::resize(int x, int y, int w, int h )
  72. {
  73. // must call base to make size change
  74. Fl_Widget::resize(x,y,w,h);
  75. }
  76. void drawWnd::draw()
  77. {
  78. int offs = 10;
  79. //fl_draw_box(FL_DOWN_BOX,x()+10,y()+10,w()-20,h()-20,FL_RED);
  80. //fl_frame("XXXX",x()+offs,y()+offs,w()-2*offs,h()-2*offs);
  81. fl_line_style(FL_SOLID,1,NULL);
  82. fl_color(FL_RED);
  83. fl_line(x()+offs,y()+offs,x()+w()-offs,y()+h()-offs);
  84. int x = w()/2;
  85. int y = h()/2;
  86. fl_color(FL_BLACK);
  87. fl_draw(90,"string",x,y);
  88. // XXXXXXX
  89. // X00000X
  90. // X00000X
  91. // X00X00X
  92. // X00000X
  93. // X00000X
  94. // XXXXXXX
  95. fl_rect(x-3,y-3,7,7);
  96. fl_point(x,y);
  97. }
  98. //----------------------------------------------------------------------------------------------------
  99. //----------------------------------------------------------------------------------------------------
  100. //----------------------------------------------------------------------------------------------------
  101. app::app(cmCtx_t* ctx, cmTsMp1cH_t printqH, int w, int h, const char* label, int argc, char* argv[])
  102. : Fl_Double_Window(w,h,label), _timerPeriodSecs(0),
  103. _ctx(ctx), _splt(NULL),_buf(NULL),_con(NULL),_tabs(NULL),_menu(NULL),
  104. _printqH(printqH),_printFl(0)
  105. {
  106. // install a callback to cleanup when the app window closes
  107. // (the btn and menu callbacks also rely on a pointer to 'this' being found in app.user_data()
  108. // see _getApp())
  109. callback(_s_callback,this);
  110. // the main window is divided between the menu bar on top
  111. // and a horizontal splitter on the bottom
  112. begin();
  113. createMenu(w,kMenuH);
  114. _splt = new Fl_HSplitter(0, kMenuH, w, h-kStatusH, h-kStatusH-100);
  115. end();
  116. // Create a text display object for console output and
  117. // add it to the lower splitter area
  118. _buf = new Fl_Text_Buffer();
  119. _con = new Fl_Text_Display(_splt->container2->x(),_splt->container2->y(),_splt->container2->w(),_splt->container2->h());
  120. _con->buffer(_buf);
  121. _splt->container2->add(_con);
  122. // create tabbed windows
  123. const char* titles[] = { "Title 1", "Title 2" };
  124. _create_tabs(2,titles);
  125. // make the splitter the resizable group element (thereby making the menu non-resizable).
  126. // see:http://fltk.org/articles.php?L415+I0+T+M1000+P1
  127. resizable(_splt);
  128. show(argc, argv);
  129. // The application is now visible.
  130. // direct all output to the console window
  131. cmRptSetup(&_ctx->rpt,_s_print,_s_print, this);
  132. cmTsMp1cSetCbFunc(_printqH, _s_print_queue_cb, this );
  133. // install a timer
  134. _timerPeriodSecs = 0.1;
  135. Fl::add_timeout(_timerPeriodSecs,app::_s_timer_cb,this);
  136. // install an idle callback
  137. //Fl::add_idle(_s_idle_cb,this);
  138. print("Started!\n");
  139. }
  140. app::~app()
  141. {}
  142. void app::createControls(Fl_Group* grp)
  143. {
  144. const int vBord = 3;
  145. const int ctl_width = 100;
  146. int xx = grp->x() + 5;
  147. int yy = grp->y() + vBord;
  148. Fl_Check_Button* cbt = new Fl_Check_Button(xx,yy,ctl_width,kCtlH,"Check");
  149. cbt->callback( _s_btn_cb, kBtn1MId );
  150. yy += cbt->h() + vBord;
  151. Fl_Button* btn = new Fl_Button(xx,yy,ctl_width,kCtlH,"Button");
  152. btn->callback( _s_btn_cb, kBtn2MId );
  153. yy += btn->h() + vBord;
  154. // menu buttons callback through menuCallback() not btnCallback()
  155. Fl_Menu_Button* mbt = new Fl_Menu_Button(xx,yy,ctl_width,kCtlH,"Menu");
  156. mbt->add("Item 1",0,_s_menu_btn_cb, (void*)kMenuBtn1MId, 0);
  157. mbt->add("Item 2",0,_s_menu_btn_cb, (void*)kMenuBtn2MId, 0);
  158. yy += mbt->h() + vBord;
  159. Fl_Value_Input* vip = new Fl_Value_Input(xx,yy,ctl_width/2,kCtlH,"Value In");
  160. vip->callback(_s_value_cb, kValue1MId );
  161. vip->align(FL_ALIGN_RIGHT); // place label to the right of the input ctl
  162. // Only make the callback when the enter key is struck or the ctl loses focus and the value has changed.
  163. // Removing this line causes the callback whenever the input changes.
  164. vip->when(FL_WHEN_ENTER_KEY | FL_WHEN_RELEASE );
  165. vip->bounds(-10.0,10.0);
  166. vip->step(0.1);
  167. yy += vip->h() + vBord;
  168. Fl_Value_Slider* sdp = new Fl_Value_Slider(xx,yy,ctl_width,kCtlH,"Slider");
  169. sdp->callback(_s_value_cb, kValue2MId );
  170. sdp->align(FL_ALIGN_RIGHT);
  171. sdp->type(FL_HOR_NICE_SLIDER);
  172. sdp->bounds(-10.0,10.0);
  173. sdp->step(0.1);
  174. yy += sdp->h() + vBord;
  175. Fl_Input* inp = new Fl_Input(xx,yy,ctl_width/2,kCtlH,"Text");
  176. inp->callback(_s_input_cb,kInput1MId);
  177. inp->align(FL_ALIGN_RIGHT);
  178. yy += inp->h() + vBord;
  179. }
  180. void app::initializeTab(int tabIndex, int x, int y, int w, int h, const char* label)
  181. {
  182. switch(tabIndex)
  183. {
  184. case 0:
  185. {
  186. Fl_Group* grp = new Fl_Group(x,y,w,h,label);
  187. grp->begin();
  188. createControls(grp);
  189. grp->end();
  190. }
  191. break;
  192. case 1:
  193. {
  194. Fl_Group* grp = new Fl_Group(x,y,w,h,label);
  195. grp->begin();
  196. _draw = new drawWnd(this,x,y,w,h,NULL);
  197. grp->end();
  198. }
  199. break;
  200. }
  201. }
  202. void app::createMenu(int w, int h)
  203. {
  204. Fl_Menu_Item items[] =
  205. {
  206. { "&File", 0, 0, 0, FL_SUBMENU },
  207. { "&New File", FL_COMMAND + 'n', (Fl_Callback*)_s_menu_cb, (void*)kFileNewMId },
  208. { "&Open File", FL_COMMAND + 'o', (Fl_Callback*)_s_menu_cb, (void*)kFileOpenMId },
  209. { 0 },
  210. { "&Edit", 0, 0, 0, FL_SUBMENU },
  211. { "&Copy", FL_COMMAND + 'c', (Fl_Callback*)_s_menu_cb, (void*)kEditCopyMId },
  212. { "&Paste", FL_COMMAND + 'v', (Fl_Callback*)_s_menu_cb, (void*)kEditPasteMId },
  213. { 0 },
  214. { 0 }
  215. };
  216. _menu = new Fl_Menu_Bar(0,0,w,h);
  217. _menu->copy(items);
  218. }
  219. void app::menuCallback(const Fl_Menu_Item* mip, long int id)
  220. {
  221. switch(id)
  222. {
  223. case kFileOpenMId:
  224. {
  225. // file chooser demo
  226. const char* pathStr = cmFsUserDir(); // make the default dir the user's home dir
  227. const char patStr[] = "Text Files (*.txt)\tAudio Files (*.{wav,aif,aiff})"; // All Files (*.*) is append to this automatically
  228. const char titleStr[] = "Select files";
  229. Fl_File_Chooser fc = Fl_File_Chooser(pathStr,patStr,Fl_File_Chooser::MULTI,titleStr);
  230. fc.preview(0); // default the previous option to 'off'.
  231. fc.show(); // show the chooser
  232. // make the chooser modal
  233. while( fc.shown() )
  234. Fl::wait();
  235. // print the selected files
  236. int i;
  237. for(i=1; i<=fc.count(); ++i)
  238. print("%i %s\n",i,cmStringNullGuard(fc.value(i)));
  239. }
  240. break;
  241. }
  242. print("%i %s\n",id,mip->label());
  243. }
  244. void app::btnCallback(const Fl_Button* bp, int id )
  245. {
  246. print("%i %s %i\n",id,bp->label(),bp->value());
  247. }
  248. void app::valueCallback(const Fl_Valuator* vp, int id )
  249. {
  250. print("%i %s %f\n",id,vp->label(),vp->value());
  251. }
  252. void app::inputCallback(const Fl_Input* ip, int id )
  253. {
  254. print("%i %s %s\n",id,ip->label(),ip->value());
  255. }
  256. void app::appCallback(Fl_Widget* wp)
  257. {
  258. switch( Fl::event() )
  259. {
  260. case FL_CLOSE:
  261. {
  262. /*
  263. int cc = _closeCnt;
  264. // attempt to shut down the pgm
  265. _closeCnt += finalizePgm() == false;
  266. // the first time the pgm fails to shut down
  267. // do not allow the pgm to close the main window
  268. // this will give a chance for the error messages
  269. // to be diplayed in the console - all successive
  270. // times the return value from finalizePgm() is
  271. // ignored and the program is terminated.
  272. if( _closeCnt == 1 && cc==0)
  273. {
  274. // send a strong hint that a problem occurred on shutdown
  275. // and prevent the app from receiving events that might cause it to
  276. // crash
  277. deactivate();
  278. return;
  279. }
  280. */
  281. // When all windows are windows are closed then the app.
  282. // will close - so hiding the application window
  283. // causes the program to close.
  284. //
  285. // Note that simply returning from this callback will
  286. // prevent the application from closing. Because the existence
  287. // of the callback alone is enough to disable default
  288. // event handling.
  289. hide();
  290. }
  291. break;
  292. }
  293. }
  294. bool app::idleCallback()
  295. { return true; }
  296. bool app::timerCallback()
  297. {
  298. _checkPrintQueue();
  299. return true;
  300. }
  301. void app::error( const char* fmt, ... )
  302. {
  303. va_list vl;
  304. va_start(vl,fmt);
  305. int bufCharCnt = 511;
  306. char buf[bufCharCnt+1];
  307. int n = vsnprintf(buf,bufCharCnt,fmt,vl);
  308. if( n > 0 )
  309. print("%s Error: %s\n",label(),buf);
  310. va_end(vl);
  311. }
  312. void app::vprint(const char* fmt, va_list vl )
  313. {
  314. int bufCharCnt = 511;
  315. char buf[bufCharCnt+1];
  316. int n = vsnprintf(buf,bufCharCnt,fmt,vl);
  317. if( n > 0 )
  318. {
  319. // if the print queue exists (it might not during startup or shutdown) ...
  320. if( cmTsMp1cIsValid(_printqH) )
  321. {
  322. cmThRC_t thRC;
  323. // ... enqueue the text to print
  324. if((thRC = cmTsMp1cEnqueueMsg(_printqH,buf,n+1)) != kOkThRC && _printFl==0 )
  325. {
  326. // Print queue error msg's directly to stdout rather than throught
  327. // the error mechanism - this prevents generating a stack overflow
  328. // when the print queue runs out of space and attempts to print a
  329. // 'queue out of memory' error msg.
  330. // The queue itself does will not print errors because it does
  331. // not have a valid cmRpt_t pointer. See cmMp1cCreate() above.
  332. ++_printFl;
  333. cmErrMsg(&_ctx->err,kQueueFailAppRC,"Print enqueue failed.");
  334. --_printFl;
  335. }
  336. }
  337. else
  338. _print(buf); // ... otherwise just send the text directly to the output console
  339. }
  340. }
  341. void app::print( const char* fmt, ... )
  342. {
  343. va_list vl;
  344. va_start(vl,fmt);
  345. vprint(fmt,vl);
  346. va_end(vl);
  347. }
  348. void app::_s_menu_cb(Fl_Widget* wp, void* data)
  349. {
  350. const Fl_Menu_Item* mip;
  351. app* ap;
  352. if((ap=_getApp(wp)) != NULL )
  353. if((mip = ap->_menu->mvalue()) != NULL )
  354. ap->menuCallback(mip,(long int)mip->user_data());
  355. }
  356. void app::_s_menu_btn_cb(Fl_Widget* wp, void* data)
  357. {
  358. const Fl_Menu_Button* bp;
  359. const Fl_Menu_Item* mip;
  360. app* ap;
  361. if((ap=_getApp(wp)) != NULL )
  362. if((bp = static_cast<Fl_Menu_Button*>(wp)) != NULL )
  363. if((mip = bp->mvalue()) != NULL)
  364. ap->menuCallback(mip,(long int)data);
  365. }
  366. void app::_s_btn_cb(Fl_Widget* wp, long data )
  367. {
  368. app* ap;
  369. if((ap = _getApp(wp)) != NULL )
  370. if( wp != NULL )
  371. ap->btnCallback( static_cast<const Fl_Button*>(wp),data);
  372. }
  373. void app::_s_value_cb( Fl_Widget* wp, long data)
  374. {
  375. app* ap;
  376. Fl_Valuator* vp;
  377. if((ap = _getApp(wp)) != NULL )
  378. if((vp = static_cast<Fl_Valuator*>(wp)) != NULL)
  379. ap->valueCallback(vp,data);
  380. }
  381. void app::_s_input_cb( Fl_Widget* wp, long data)
  382. {
  383. app* ap;
  384. Fl_Input* inp;
  385. if((ap = _getApp(wp)) != NULL)
  386. if((inp = static_cast<Fl_Input*>(wp)) != NULL )
  387. ap->inputCallback(inp,data);
  388. }
  389. void app::_s_callback(Fl_Widget* wp, void* data)
  390. { ((app*)data)->appCallback(wp); }
  391. void app::_s_idle_cb(void *data)
  392. {
  393. app* thisPtr = (app*)data;
  394. if( thisPtr->idleCallback() == false )
  395. Fl::remove_idle(_s_idle_cb,data);
  396. }
  397. void app::_s_timer_cb(void* userPtr)
  398. {
  399. app* thisPtr = (app*)userPtr;
  400. if( thisPtr->timerCallback() )
  401. Fl::repeat_timeout(thisPtr->_timerPeriodSecs,_s_timer_cb,userPtr);
  402. }
  403. void app::_s_print( void* userPtr, const char* text )
  404. { ((app*)userPtr)->print(text); }
  405. void app::_create_tabs(int titleCnt, const char* titleArray[])
  406. {
  407. // Create a tab view and added it to the upper splitter area
  408. _tabs = new Fl_Tabs(_splt->container1->x(),_splt->container1->y(),_splt->container1->w(),_splt->container1->h());
  409. _splt->container1->add(_tabs);
  410. int tx,ty,th,tw,i;
  411. _tabs->client_area(tx,ty,tw,th);
  412. for(i=0; i<titleCnt; ++i)
  413. {
  414. _tabs->begin();
  415. initializeTab(i,tx,ty,tw,th,titleArray[i]);
  416. _tabs->end();
  417. }
  418. // Create an empty tab group and make it resizable
  419. // to prevent the other tab groups from being resizable.
  420. _tabs->begin();
  421. _tabs->resizable(new Fl_Group(tx,ty+30,1,1));
  422. _tabs->end();
  423. }
  424. app* app::_getApp( Fl_Widget* w )
  425. {
  426. // walk up the widget tree until the top widget is found
  427. Fl_Group* gp = w->parent();
  428. while( gp->parent() != NULL )
  429. gp=gp->parent();
  430. // the user data for the top widget is a pointer app - as set in: callback(_s_callback,this);
  431. return (app*)gp->user_data();
  432. }
  433. cmRC_t app::_s_print_queue_cb(void* userCbPtr, unsigned msgByteCnt, const void* msgDataPtr )
  434. {
  435. app* ap = (app*)userCbPtr;
  436. ap->_print((const char*)msgDataPtr);
  437. return cmOkRC;
  438. }
  439. void app::_checkPrintQueue()
  440. {
  441. while( cmTsMp1cMsgWaiting(_printqH) )
  442. if( cmTsMp1cDequeueMsg(_printqH, NULL, 0) != kOkThRC )
  443. error("Print dequeue failed.");
  444. }
  445. void app::_print( const char* text )
  446. {
  447. if( _con != NULL )
  448. _con->insert(text);
  449. }