Non reinvents the DAW. Powerful enough to form a complete studio, fast and light enough to run on low-end hardware like the eeePC or Raspberry Pi, and so reliable that it can be used live https://non.tuxfamily.org/
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.

600 lines
17KB

  1. /*******************************************************************************/
  2. /* Copyright (C) 2009 Jonathan Moore Liles */
  3. /* */
  4. /* This program is free software; you can redistribute it and/or modify it */
  5. /* under the terms of the GNU General Public License as published by the */
  6. /* Free Software Foundation; either version 2 of the License, or (at your */
  7. /* option) any later version. */
  8. /* */
  9. /* This program is distributed in the hope that it will be useful, but WITHOUT */
  10. /* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */
  11. /* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for */
  12. /* more details. */
  13. /* */
  14. /* You should have received a copy of the GNU General Public License along */
  15. /* with This program; see the file COPYING. If not,write to the Free Software */
  16. /* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
  17. /*******************************************************************************/
  18. #include <FL/Fl.H>
  19. #include <string.h>
  20. #include <stdlib.h>
  21. #include <stdio.h>
  22. #include <math.h>
  23. #include <FL/fl_draw.H>
  24. #include <FL/Fl_Pack.H>
  25. #include <FL/Fl_Box.H>
  26. #include <FL/Fl_Menu_Button.H>
  27. #include <FL/Fl_Counter.H>
  28. #include "FL/Fl_Flowpack.H"
  29. #include "FL/Fl_Labelpad_Group.H"
  30. #include "FL/Fl_Value_SliderX.H"
  31. #include "FL/Fl_DialX.H"
  32. #include <FL/Fl_Scroll.H>
  33. #include "Module.H"
  34. #include "Module_Parameter_Editor.H"
  35. #include "Controller_Module.H"
  36. #include "Chain.H"
  37. #include "Panner.H"
  38. #include <FL/fl_ask.H>
  39. #include "debug.h"
  40. #include <FL/Fl_Menu_Button.H>
  41. #include "FL/test_press.H"
  42. #include "FL/menu_popup.H"
  43. Module_Parameter_Editor::Module_Parameter_Editor ( Module *module ) : Fl_Double_Window( 900,240)
  44. {
  45. _module = module;
  46. _resized = false;
  47. _min_width = 100;
  48. char lab[256];
  49. if ( strcmp( module->name(), module->label() ) )
  50. {
  51. snprintf( lab, sizeof( lab ), "%s : %s", module->name(), module->label() );
  52. }
  53. else
  54. strcpy( lab, module->label() );
  55. char title[512];
  56. snprintf( title, sizeof( title ), "%s - %s - %s", "Mixer", module->chain()->name(), lab );
  57. copy_label( title );
  58. // fl_font( FL_HELVETICA, 14 );
  59. _min_width = 30 + fl_width( module->label() );
  60. { Fl_Group *o = new Fl_Group( 0, 0, w(), 25 );
  61. o->label( module->label() );
  62. o->labelfont( 2 );
  63. o->labeltype( FL_SHADOW_LABEL );
  64. o->labelsize( 14 );
  65. o->align( FL_ALIGN_TOP | FL_ALIGN_RIGHT | FL_ALIGN_INSIDE );
  66. { Fl_Menu_Button *o = mode_choice = new Fl_Menu_Button( 0, 0, 25, 25 );
  67. o->add( "Knobs" );
  68. o->add( "Horizontal Sliders" );
  69. o->add( "Vertical Sliders" );
  70. o->label( NULL );
  71. o->value( 1 );
  72. o->when( FL_WHEN_CHANGED );
  73. o->callback( cb_mode_handle, this );
  74. }
  75. o->resizable(0);
  76. o->end();
  77. }
  78. { Fl_Scroll *o = control_scroll = new Fl_Scroll( 0, 40, w(), h() - 40 );
  79. { Fl_Group *o = new Fl_Group( 0, 40, w(), h() - 40 );
  80. { Fl_Flowpack *o = control_pack = new Fl_Flowpack( 50, 40, w() - 100, h() - 40 );
  81. o->type( FL_HORIZONTAL );
  82. o->flow( true );
  83. o->vspacing( 5 );
  84. o->hspacing( 5 );
  85. o->end();
  86. }
  87. o->resizable( 0 );
  88. o->end();
  89. }
  90. o->end();
  91. }
  92. resizable(control_scroll);
  93. end();
  94. make_controls();
  95. }
  96. Module_Parameter_Editor::~Module_Parameter_Editor ( )
  97. {
  98. }
  99. void
  100. Module_Parameter_Editor::make_controls ( void )
  101. {
  102. Module *module = _module;
  103. control_pack->clear();
  104. controls_by_port.clear();
  105. /* these are for detecting related parameter groups which can be
  106. better represented by a single control */
  107. azimuth_port_number = -1;
  108. float azimuth_value = 0.0f;
  109. elevation_port_number = -1;
  110. float elevation_value = 0.0f;
  111. radius_port_number = -1;
  112. float radius_value = 0.0f;
  113. Fl_Color fc = fl_color_add_alpha( FL_CYAN, 200 );
  114. Fl_Color bc = FL_BACKGROUND2_COLOR;
  115. controls_by_port.resize( module->control_input.size() );
  116. if ( mode_choice->value() == 1 )
  117. {
  118. control_pack->vspacing( 1 );
  119. control_pack->hspacing( 10 );
  120. control_pack->flow(true);
  121. control_pack->flowdown(true);
  122. control_pack->type( FL_HORIZONTAL );
  123. control_pack->size( 900, 240 );
  124. }
  125. else if ( mode_choice->value() == 2 )
  126. {
  127. control_pack->vspacing( 10 );
  128. control_pack->hspacing( 10 );
  129. control_pack->flow(true);
  130. control_pack->flowdown(false);
  131. control_pack->type( FL_HORIZONTAL );
  132. control_pack->size( 900, 350 );
  133. }
  134. else if ( mode_choice->value() == 0 )
  135. {
  136. control_pack->vspacing( 10 );
  137. control_pack->hspacing( 10 );
  138. control_pack->flow(true);
  139. control_pack->flowdown(false);
  140. control_pack->type( FL_HORIZONTAL );
  141. control_pack->size( 700, 50 );
  142. }
  143. for ( unsigned int i = 0; i < module->control_input.size(); ++i )
  144. {
  145. Fl_Widget *w;
  146. Module::Port *p = &module->control_input[i];
  147. /* if ( !p->hints.visible ) */
  148. /* continue; */
  149. if ( !strcasecmp( "Azimuth", p->name() ) &&
  150. 180.0f == p->hints.maximum &&
  151. -180.0f == p->hints.minimum )
  152. {
  153. azimuth_port_number = i;
  154. azimuth_value = p->control_value();
  155. continue;
  156. }
  157. else if ( !strcasecmp( "Elevation", p->name() ) &&
  158. 90.0f == p->hints.maximum &&
  159. -90.0f == p->hints.minimum )
  160. {
  161. elevation_port_number = i;
  162. elevation_value = p->control_value();
  163. continue;
  164. }
  165. else if ( !strcasecmp( "Radius", p->name() ) )
  166. {
  167. radius_port_number = i;
  168. radius_value = p->control_value();
  169. continue;
  170. }
  171. if ( p->hints.type == Module::Port::Hints::BOOLEAN )
  172. {
  173. Fl_Button *o = new Fl_Button( 0, 0, 24, 24, p->name() );
  174. w = o;
  175. o->selection_color( fc );
  176. o->type( FL_TOGGLE_BUTTON );
  177. o->value( p->control_value() );
  178. o->align(FL_ALIGN_TOP);
  179. }
  180. else if ( p->hints.type == Module::Port::Hints::INTEGER )
  181. {
  182. Fl_Counter *o = new Fl_Counter(0, 0, 58, 24, p->name() );
  183. w = o;
  184. o->type(1);
  185. o->step(1);
  186. o->align(FL_ALIGN_TOP);
  187. if ( p->hints.ranged )
  188. {
  189. o->minimum( p->hints.minimum );
  190. o->maximum( p->hints.maximum );
  191. }
  192. o->value( p->control_value() );
  193. }
  194. else
  195. {
  196. if ( mode_choice->value() == 0 )
  197. {
  198. Fl_DialX *o = new Fl_DialX( 0, 0, 60, 60, p->name() );
  199. w = o;
  200. if ( p->hints.ranged )
  201. {
  202. DMESSAGE( "Min: %f, max: %f", p->hints.minimum, p->hints.maximum );
  203. o->minimum( p->hints.minimum );
  204. o->maximum( p->hints.maximum );
  205. }
  206. o->color( bc );
  207. o->selection_color( fc );
  208. o->value( p->control_value() );
  209. o->align(FL_ALIGN_TOP);
  210. o->box( FL_DOWN_BOX );
  211. /* a couple of plugins have ridiculously small units */
  212. float r = fabs( p->hints.maximum - p->hints.minimum );
  213. if ( r <= 0.01f )
  214. o->precision( 4 );
  215. else if ( r <= 0.1f )
  216. o->precision( 3 );
  217. else if ( r <= 100.0f )
  218. o->precision( 2 );
  219. else if ( r <= 5000.0f )
  220. o->precision( 1 );
  221. /* else if ( r <= 10000.0f ) */
  222. /* o->precision( 1 ); */
  223. else
  224. o->precision( 0 );
  225. }
  226. else
  227. {
  228. Fl_Value_SliderX *o = new Fl_Value_SliderX( 0, 0, 120, 24, p->name() );
  229. w = o;
  230. if ( mode_choice->value() == 1 )
  231. {
  232. o->type( FL_HORIZONTAL );
  233. o->align( FL_ALIGN_RIGHT );
  234. o->size( 200, 24 );
  235. if ( p->hints.ranged )
  236. {
  237. o->minimum( p->hints.minimum );
  238. o->maximum( p->hints.maximum );
  239. }
  240. }
  241. else
  242. {
  243. o->type( FL_VERTICAL );
  244. o->align(FL_ALIGN_TOP);
  245. o->size( 24, 200 );
  246. /* have to reverse the meaning of these to get the
  247. * orientation of the slider right */
  248. o->maximum( p->hints.minimum );
  249. o->minimum( p->hints.maximum );
  250. }
  251. /* a couple of plugins have ridiculously small units */
  252. float r = fabs( p->hints.maximum - p->hints.minimum );
  253. if ( r <= 0.01f )
  254. o->precision( 4 );
  255. else if ( r <= 0.1f )
  256. o->precision( 3 );
  257. else if ( r <= 100.0f )
  258. o->precision( 2 );
  259. else if ( r <= 5000.0f )
  260. o->precision( 1 );
  261. /* else if ( r <= 10000.0f ) */
  262. /* o->precision( 1 ); */
  263. else
  264. o->precision( 0 );
  265. o->textsize( 8 );
  266. // o->box( FL_NO_BOX );
  267. o->slider( FL_UP_BOX );
  268. o->color( bc );
  269. o->selection_color( fc );
  270. o->value( p->control_value() );
  271. }
  272. }
  273. // w->align(FL_ALIGN_TOP);
  274. w->labelsize( 10 );
  275. controls_by_port[i] = w;
  276. w->tooltip( p->osc_path() );
  277. _callback_data.push_back( callback_data( this, i ) );
  278. if ( p->hints.type == Module::Port::Hints::BOOLEAN )
  279. w->callback( cb_button_handle, &_callback_data.back() );
  280. else
  281. w->callback( cb_value_handle, &_callback_data.back() );
  282. {
  283. Fl_Labelpad_Group *flg = new Fl_Labelpad_Group( w );
  284. flg->set_visible_focus();
  285. control_pack->add( flg );
  286. }
  287. }
  288. if ( azimuth_port_number >= 0 && elevation_port_number >= 0 )
  289. {
  290. Panner *o = new Panner( 0,0, 502,502 );
  291. o->box(FL_FLAT_BOX);
  292. o->color(FL_GRAY0);
  293. o->selection_color(FL_BACKGROUND_COLOR);
  294. o->labeltype(FL_NORMAL_LABEL);
  295. o->labelfont(0);
  296. o->labelcolor(FL_FOREGROUND_COLOR);
  297. o->align(FL_ALIGN_TOP);
  298. o->when(FL_WHEN_CHANGED);
  299. o->label( "Spatialization" );
  300. o->labelsize( 10 );
  301. _callback_data.push_back( callback_data( this, azimuth_port_number, elevation_port_number, radius_port_number ) );
  302. o->callback( cb_panner_value_handle, &_callback_data.back() );
  303. o->point( 0 )->azimuth( azimuth_value );
  304. o->point( 0 )->elevation( elevation_value );
  305. if ( radius_port_number >= 0 )
  306. {
  307. o->point( 0 )->radius_enabled = true;
  308. o->point( 0 )->radius( radius_value );
  309. }
  310. Fl_Labelpad_Group *flg = new Fl_Labelpad_Group( o );
  311. flg->resizable(o);
  312. control_pack->add( flg );
  313. controls_by_port[azimuth_port_number] = o;
  314. controls_by_port[elevation_port_number] = o;
  315. if ( radius_port_number >= 0 )
  316. controls_by_port[radius_port_number] = o;
  317. }
  318. update_control_visibility();
  319. control_pack->dolayout();
  320. int width = control_pack->w() + 100;
  321. int height = control_pack->h() + 60;
  322. if ( width < _min_width )
  323. width = _min_width;
  324. control_pack->parent()->size( control_pack->w() + 100, control_pack->h() );
  325. control_scroll->scroll_to(0, 0 );
  326. size( width, height );
  327. size_range( width, height, width, height );
  328. }
  329. void
  330. Module_Parameter_Editor::update_control_visibility ( void )
  331. {
  332. for ( unsigned int i = 0; i < _module->control_input.size(); ++i )
  333. {
  334. const Module::Port *p = &_module->control_input[i];
  335. if ( p->hints.visible )
  336. controls_by_port[i]->parent()->show();
  337. else
  338. controls_by_port[i]->parent()->hide();
  339. }
  340. }
  341. void
  342. Module_Parameter_Editor::cb_value_handle ( Fl_Widget *w, void *v )
  343. {
  344. callback_data *cd = (callback_data*)v;
  345. cd->base_widget->set_value( cd->port_number[0], ((Fl_Valuator*)w)->value() );
  346. }
  347. void
  348. Module_Parameter_Editor::cb_button_handle ( Fl_Widget *w, void *v )
  349. {
  350. callback_data *cd = (callback_data*)v;
  351. cd->base_widget->set_value( cd->port_number[0], ((Fl_Button*)w)->value() );
  352. }
  353. void
  354. Module_Parameter_Editor::cb_panner_value_handle ( Fl_Widget *w, void *v )
  355. {
  356. callback_data *cd = (callback_data*)v;
  357. cd->base_widget->set_value( cd->port_number[0], ((Panner*)w)->point( 0 )->azimuth() );
  358. cd->base_widget->set_value( cd->port_number[1], ((Panner*)w)->point( 0 )->elevation() );
  359. cd->base_widget->set_value( cd->port_number[2], ((Panner*)w)->point( 0 )->radius() );
  360. }
  361. void
  362. Module_Parameter_Editor::cb_mode_handle ( Fl_Widget *, void *v )
  363. {
  364. ((Module_Parameter_Editor*)v)->make_controls();
  365. }
  366. void
  367. Module_Parameter_Editor::bind_control ( int i )
  368. {
  369. Module::Port *p = &_module->control_input[i];
  370. if ( p->connected() )
  371. /* can only bind once */
  372. return;
  373. Controller_Module *o = new Controller_Module();
  374. o->label( p->name() );
  375. o->chain( _module->chain() );
  376. o->horizontal( true );
  377. o->connect_to( p );
  378. _module->chain()->add_control( o );
  379. _module->redraw();
  380. }
  381. /* Display changes initiated via automation or from other parts of the GUI */
  382. void
  383. Module_Parameter_Editor::handle_control_changed ( Module::Port *p )
  384. {
  385. int i = _module->control_input_port_index( p );
  386. Fl_Widget *w = controls_by_port[i];
  387. if ( i == azimuth_port_number ||
  388. i == elevation_port_number ||
  389. i == radius_port_number )
  390. {
  391. Panner *_panner = (Panner*)w;
  392. if ( i == azimuth_port_number )
  393. _panner->point(0)->azimuth( p->control_value() );
  394. else if ( i == elevation_port_number )
  395. _panner->point(0)->elevation( p->control_value() );
  396. else if ( i == radius_port_number )
  397. _panner->point(0)->radius( p->control_value() );
  398. _panner->redraw();
  399. return;
  400. }
  401. if ( p->hints.type == Module::Port::Hints::BOOLEAN )
  402. {
  403. Fl_Button *v = (Fl_Button*)w;
  404. v->value( p->control_value() );
  405. }
  406. else
  407. {
  408. Fl_Valuator *v = (Fl_Valuator*)w;
  409. v->value( p->control_value() );
  410. }
  411. }
  412. void
  413. Module_Parameter_Editor::reload ( void )
  414. {
  415. // make_controls();
  416. update_control_visibility();
  417. redraw();
  418. }
  419. void
  420. Module_Parameter_Editor::set_value (int i, float value )
  421. {
  422. if ( i >= 0 )
  423. {
  424. _module->control_input[i].control_value( value );
  425. if ( _module->control_input[i].connected() )
  426. _module->control_input[i].connected_port()->module()->handle_control_changed( _module->control_input[i].connected_port() );
  427. }
  428. // _module->handle_control_changed( &_module->control_input[i] );
  429. }
  430. void
  431. Module_Parameter_Editor::menu_cb ( Fl_Widget *w, void *v )
  432. {
  433. ((Module_Parameter_Editor*)v)->menu_cb((Fl_Menu_*)w);
  434. }
  435. void
  436. Module_Parameter_Editor::menu_cb ( Fl_Menu_* m )
  437. {
  438. char picked[256];
  439. if ( ! m->mvalue() || m->mvalue()->flags & FL_SUBMENU_POINTER || m->mvalue()->flags & FL_SUBMENU )
  440. return;
  441. strncpy( picked, m->mvalue()->label(), sizeof( picked ) );
  442. // m->item_pathname( picked, sizeof( picked ) );
  443. DMESSAGE( "%s", picked );
  444. if ( ! strcmp( picked, "Bind" ) )
  445. {
  446. bind_control( _selected_control );
  447. }
  448. }
  449. Fl_Menu_Button &
  450. Module_Parameter_Editor::menu ( void ) const
  451. {
  452. static Fl_Menu_Button m( 0, 0, 0, 0, "Control" );
  453. m.clear();
  454. m.add( "Bind", 0, 0, 0, FL_MENU_RADIO | (_module->control_input[_selected_control].connected() ? FL_MENU_VALUE : 0 ));
  455. // m.add( "Unbind", 0, &Module::menu_cb, this, 0, FL_MENU_RADIO );
  456. m.callback( menu_cb, (void*)this );
  457. return m;
  458. }
  459. int
  460. Module_Parameter_Editor::handle ( int m )
  461. {
  462. switch ( m )
  463. {
  464. case FL_PUSH:
  465. if ( test_press( FL_BUTTON3 ) )
  466. {
  467. for ( unsigned int i = 0; i < controls_by_port.size(); i++ )
  468. {
  469. if ( Fl::event_inside( controls_by_port[i] ) )
  470. {
  471. _selected_control = i;
  472. Fl_Menu_Button &m = menu();
  473. menu_popup(&m,Fl::event_x(), Fl::event_y());
  474. return 1;
  475. }
  476. }
  477. return 0;
  478. }
  479. }
  480. return Fl_Group::handle(m);
  481. }