2 /*******************************************************************************/
3 /* Copyright (C) 2009 Jonathan Moore Liles */
5 /* This program is free software; you can redistribute it and/or modify it */
6 /* under the terms of the GNU General Public License as published by the */
7 /* Free Software Foundation; either version 2 of the License, or (at your */
8 /* option) any later version. */
10 /* This program is distributed in the hope that it will be useful, but WITHOUT */
11 /* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */
12 /* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for */
15 /* You should have received a copy of the GNU General Public License along */
16 /* with This program; see the file COPYING. If not,write to the Free Software */
17 /* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
18 /*******************************************************************************/
20 /* Mixer strip control. Handles GUI and control I/O for this strip. */
22 /* A mixer strip is home to some (JACK) input ports, a fader, some
23 * meters, and a filter chain which can terminate either at the input
24 * to the spacializer or some (JACK) output ports. Since mixer strips
25 * are not necessarily in a 1:1 association with Non-DAW tracks, there
26 * is no need for busses per se. If you want to route the output of
27 * several strips into a single fader or filter chain, then you just
28 * gives those strips JACK outputs and connect them to the common
29 * inputs. This mechanism can also do away with the need for 'sends'
33 /* Each mixer strip comprises a fader and a panner */
35 #include "Mixer_Strip.H"
36 #include "Engine/Engine.H"
42 #include "FL/Fl_Flowpack.H"
43 #include <FL/Fl_Input.H>
44 #include <FL/fl_ask.H>
45 #include <FL/Fl_Color_Chooser.H>
50 #include "Gain_Module.H"
51 #include "Meter_Module.H"
52 #include "Controller_Module.H"
53 #include "Meter_Indicator_Module.H"
56 #include <FL/Fl_Menu_Button.H>
57 #include "FL/test_press.H"
58 #include "FL/menu_popup.H"
59 #include <FL/Fl_File_Chooser.H>
65 /* add a new mixer strip (with default configuration) */
66 Mixer_Strip::Mixer_Strip( const char *strip_name ) : Fl_Group( 0, 0, 120, 600 )
68 label( strdup( strip_name ) );
69 labeltype( FL_NO_LABEL );
75 _chain->initialize_with_default();
77 _chain->configure_ports();
79 color( (Fl_Color)rand() );
81 // name( strdup( strip_name ) );
86 /* virgin strip created from journal */
87 Mixer_Strip::Mixer_Strip() : Fl_Group( 0, 0, 120, 600 )
94 Mixer_Strip::~Mixer_Strip ( )
96 DMESSAGE( "Destroying mixer strip" );
98 _chain->engine()->lock();
100 /* make sure this gets destroyed before the chain */
108 mixer->remove( this );
114 Mixer_Strip::get ( Log_Entry &e ) const
116 e.add( ":name", name() );
117 e.add( ":width", width_button->value() ? "wide" : "narrow" );
118 e.add( ":tab", tab_button->value() ? "signal" : "fader" );
119 e.add( ":color", (unsigned long)color());
120 /* since the default controllers aren't logged, we have to store
121 * this setting as part of the mixer strip */
122 e.add( ":gain_mode", gain_controller->mode() );
127 Mixer_Strip::set ( Log_Entry &e )
129 for ( int i = 0; i < e.size(); ++i )
135 if ( ! strcmp( s, ":name" ) )
137 else if ( ! strcmp( s, ":width" ) )
139 width_button->value( strcmp( v, "wide" ) == 0 );
140 width_button->do_callback();
142 else if ( ! strcmp( s, ":tab" ) )
144 tab_button->value( strcmp( v, "signal" ) == 0 );
145 tab_button->do_callback();
147 else if ( ! strcmp( s, ":color" ) )
149 color( (Fl_Color)atoll( v ) );
152 else if ( ! strcmp( s, ":gain_mode" ) )
154 _gain_controller_mode = atoi( v );
158 if ( ! mixer->contains( this ) )
163 Mixer_Strip::log_children ( void )
167 _chain->log_children();
171 Mixer_Strip::color ( Fl_Color c )
174 name_field->color( _color );
175 name_field->redraw();
179 Mixer_Strip::color ( void ) const
185 Mixer_Strip::chain ( Chain *c )
194 Fl_Group *g = signal_tab;
196 c->resize( g->x(), g->y(), g->w(), g->h() );
201 c->align( FL_ALIGN_TOP );
203 c->configure_outputs_callback( configure_outputs, this );
206 gain_controller->chain( c );
207 jack_input_controller->chain( c );
208 meter_indicator->chain( c );
212 void Mixer_Strip::cb_handle(Fl_Widget* o) {
213 // parent()->parent()->damage( FL_DAMAGE_ALL, x(), y(), w(), h() );
214 DMESSAGE( "Callback for %s", o->label() );
216 if ( o == tab_button )
218 if ( tab_button->value() == 0 )
220 fader_tab->resize( tab_group->x(), tab_group->y(), tab_group->w(), tab_group->h() );
223 tab_group->resizable( fader_tab );
227 signal_tab->resize( tab_group->x(), tab_group->y(), tab_group->w(), tab_group->h() );
230 tab_group->resizable( signal_tab );
234 else if ( o == left_button )
236 else if ( o == right_button )
237 command_move_right();
238 else if ( o == close_button )
240 if ( Fl::event_shift() || 1 == fl_choice( "Are you sure you want to remove this strip?\n\n(this action cannot be undone)", "Cancel", "Remove", NULL ) )
243 else if ( o == name_field )
245 name( name_field->value() );
248 else if ( o == width_button )
250 if ( width_button->value() )
256 parent()->parent()->redraw();
260 void Mixer_Strip::cb_handle(Fl_Widget* o, void* v) {
261 ((Mixer_Strip*)(v))->cb_handle(o);
265 Mixer_Strip::name ( const char *name )
267 if ( this->name() && !strcmp( name, this->name() ) )
270 name = mixer->get_unique_track_name( name );
272 char *s = strdup( name );
274 if ( strlen( s ) > Chain::maximum_name_length() )
276 s[Chain::maximum_name_length() - 1] = '\0';
278 fl_alert( "Name \"%s\" is too long, truncating to \"%s\"", name, s );
281 name_field->value( s );
288 Mixer_Strip::configure_outputs ( Fl_Widget *, void *v )
290 ((Mixer_Strip*)v)->configure_outputs();
294 Mixer_Strip::configure_outputs ( void )
296 DMESSAGE( "Got signal to configure outputs" );
299 /* called by the chain to let us know that a module has been added */
301 Mixer_Strip::handle_module_added ( Module *m )
303 if ( m->is_default() )
305 DMESSAGE( "Connecting controls to default module \"%s\"", m->name() );
307 /* connect default modules to their default controllers/indicators */
308 if ( 0 == strcmp( m->name(), "JACK" ) && m->ninputs() == 0 )
310 if ( !jack_input_controller->control_output[0].connected() )
311 jack_input_controller->connect_to( &m->control_input[1] );
313 else if ( 0 == strcmp( m->name(), "Gain" ) )
315 gain_controller->connect_to( &m->control_input[0] );
316 gain_controller->mode( (Controller_Module::Mode)_gain_controller_mode );
318 else if ( 0 == strcmp( m->name(), "Meter" ) )
320 meter_indicator->connect_to( &m->control_output[0] );
325 if ( spatialization_controller->connect_spatializer_to( m ) )
327 spatialization_controller->show();
328 DMESSAGE( "Connected spatializer to module \"%s\"", m->name() );
334 /* called by the chain to let us know that a module has been removed */
336 Mixer_Strip::handle_module_removed ( Module *m )
339 if ( spatialization_controller->control_output[0].connected() &&
340 spatialization_controller->control_output[0].connected_port()->module() == m )
342 spatialization_controller->hide();
343 DMESSAGE( "Module \"%s\" disconnected from spatialization controller", m->name() );
347 /* update GUI with values from RT thread */
349 Mixer_Strip::update ( void )
355 Mixer_Strip::init ( )
357 selection_color( FL_RED );
359 _gain_controller_mode = 0;
362 box( FL_BORDER_BOX );
363 labeltype( FL_NO_LABEL );
365 Fl_Group::color( FL_BACKGROUND_COLOR );
369 { Fl_Scalepack *o = new Fl_Scalepack( 2, 2, 116, 595 );
370 o->type( FL_VERTICAL );
373 { Fl_Pack *o = new Fl_Pack( 2, 2, 114, 100 );
374 o->type( Fl_Pack::VERTICAL );
377 Fl_Sometimes_Input *o = new Fl_Sometimes_Input( 2, 2, 144, 24 );
381 o->up_box( FL_ROUNDED_BOX );
382 o->box( FL_ROUNDED_BOX );
383 o->labeltype( FL_NO_LABEL );
384 o->labelcolor( FL_GRAY0 );
385 o->textcolor( FL_FOREGROUND_COLOR );
387 o->callback( cb_handle, (void*)this );
389 { Fl_Scalepack *o = new Fl_Scalepack( 7, 143, 110, 25 );
390 o->type( Fl_Pack::HORIZONTAL );
391 { Fl_Button* o = left_button = new Fl_Button(7, 143, 35, 25, "@<-");
392 o->tooltip( "Move left" );
395 o->when( FL_WHEN_RELEASE );
396 o->callback( ((Fl_Callback*)cb_handle), this );
399 { Fl_Button* o = close_button = new Fl_Button(7, 143, 35, 25, "X");
400 o->tooltip( "Remove strip" );
402 o->labeltype( FL_EMBOSSED_LABEL );
403 o->color( FL_LIGHT1 );
404 o->selection_color( FL_RED );
406 o->when( FL_WHEN_RELEASE );
407 o->callback( ((Fl_Callback*)cb_handle), this );
410 { Fl_Button* o = right_button = new Fl_Button(7, 143, 35, 25, "@->");
411 o->tooltip( "Move right" );
414 o->when( FL_WHEN_RELEASE );
415 o->callback( ((Fl_Callback*)cb_handle), this );
420 { Fl_Flip_Button* o = tab_button = new Fl_Flip_Button(61, 183, 45, 22, "fader/signal");
423 o->callback( ((Fl_Callback*)cb_handle), this );
424 o->when(FL_WHEN_RELEASE);
426 { Fl_Flip_Button* o = width_button = new Fl_Flip_Button(61, 183, 45, 22, "narrow/wide");
429 o->callback( ((Fl_Callback*)cb_handle), this );
430 o->when(FL_WHEN_RELEASE);
435 /* { Fl_Scalepack *o = new Fl_Scalepack( 2, 103, 114, 490 ); */
436 /* o->type( FL_VERTICAL ); */
437 // o->box( FL_FLAT_BOX );
438 // o->color( FL_BACKGROUND_COLOR );
439 { Fl_Group *o = tab_group = new Fl_Group( 2, 116, 105, 330 );
441 { Fl_Group *o = fader_tab = new Fl_Group( 2, 116, 105, 330, "Fader" );
443 o->labeltype( FL_NO_LABEL );
444 { Fl_Scalepack* o = new Fl_Scalepack(2, 116, 105, 330 );
445 // o->box( FL_BORDER_BOX );
446 // o->color( FL_RED );
448 o->type( Fl_Scalepack::HORIZONTAL );
449 { Controller_Module *o = gain_controller = new Controller_Module( true );
453 { Meter_Indicator_Module *o = meter_indicator = new Meter_Indicator_Module( true );
456 Fl_Group::current()->resizable(o);
459 Fl_Group::current()->resizable(o);
462 Fl_Group::current()->resizable(o);
464 { Fl_Group *o = signal_tab = new Fl_Group( 2, 116, 105, 330 );
466 o->labeltype( FL_NO_LABEL );
471 Fl_Group::current()->resizable( o );
473 /* { Fl_Pack *o = panner_pack = new Fl_Pack( 2, 465, 114, 40 ); */
474 /* o->spacing( 2 ); */
475 /* o->type( Fl_Pack::VERTICAL ); */
476 { Fl_Box *o = new Fl_Box( 0, 0, 100, 12 );
477 o->align( (Fl_Align)(FL_ALIGN_BOTTOM | FL_ALIGN_INSIDE) );
479 // o->label( "Spatialization" );
481 { Controller_Module *o = spatialization_controller = new Controller_Module( true );
486 { Fl_Box *o = new Fl_Box( 0, 0, 100, 12 );
487 o->align( (Fl_Align)(FL_ALIGN_BOTTOM | FL_ALIGN_INSIDE) );
489 o->label( "Inputs" );
492 Controller_Module *m = jack_input_controller = new Controller_Module( true );
493 m->labeltype( FL_NO_LABEL );
511 // _chain->configure_ports();
515 Mixer_Strip::draw ( void )
517 if ( !fl_not_clipped( x(), y(), w(), h() ) )
520 /* don't bother drawing anything else, all we're doing is drawing the focus. */
521 if ( damage() & FL_DAMAGE_ALL ||
522 damage() & FL_DAMAGE_CHILD )
525 Fl_Group::draw_box( FL_UP_FRAME, x(), y(), w(), h(), Fl::focus() == this ? Fl_Group::selection_color() : FL_BLACK );
533 Mixer_Strip::snapshot ( void *v )
535 ((Mixer_Strip*)v)->snapshot();
539 Mixer_Strip::snapshot ( void )
545 Mixer_Strip::export_strip ( const char *filename )
547 MESSAGE( "Exporting chain state" );
548 Loggable::snapshot_callback( &Mixer_Strip::snapshot, this );
549 Loggable::snapshot( filename );
554 Mixer_Strip::import_strip ( const char *filename )
556 MESSAGE( "Importing new chain state" );
557 Loggable::begin_relative_id_mode();
558 int r = Loggable::replay( filename );
559 Loggable::end_relative_id_mode();
568 Mixer_Strip::menu_cb ( const Fl_Menu_ *m )
572 m->item_pathname( picked, sizeof( picked ) );
576 if ( ! strcmp( picked, "Width/Narrow" ) )
577 command_width( false );
578 else if ( ! strcmp( picked, "Width/Wide" ) )
579 command_width( true );
580 else if ( ! strcmp( picked, "View/Fader" ) )
581 command_view( false );
582 else if ( ! strcmp( picked, "View/Signal" ) )
583 command_view( true );
584 else if ( ! strcmp( picked, "/Move Left" ) )
586 else if ( ! strcmp( picked, "/Move Right" ) )
587 command_move_right();
588 else if ( ! strcmp( picked, "/Rename" ) )
590 ((Fl_Sometimes_Input*)name_field)->take_focus();
592 else if ( ! strcmp( picked, "/Color" ) )
594 unsigned char r, g, b;
596 Fl::get_color( color(), r, g, b );
598 if ( fl_color_chooser( "Strip Color", r, g, b ) )
599 color( fl_rgb_color( r, g, b ) );
603 else if ( !strcmp( picked, "/Export Strip" ) )
605 char *suggested_name;
606 asprintf( &suggested_name, "%s.strip", name() );
608 const char *s = fl_file_chooser( "Export strip to filename:", "*.strip", suggested_name, 0 );
610 free( suggested_name );
615 fl_message( "Strip exported." );
617 else if ( ! strcmp( picked, "/Remove" ) )
619 if ( Fl::event_shift() || 1 == fl_choice( "Are you sure you want to remove this strip?\n\n(this action cannot be undone)", "Cancel", "Remove", NULL ) )
625 Mixer_Strip::menu_cb ( Fl_Widget *w, void *v )
627 ((Mixer_Strip*)v)->menu_cb( (Fl_Menu_*) w );
631 /** build the context menu */
633 Mixer_Strip::menu ( void ) const
635 static Fl_Menu_Button m( 0, 0, 0, 0, "Strip" );
636 static char label[256];
638 snprintf( label, sizeof(label), "Strip/%s", name() );
641 // int c = output.size();
643 Fl_Menu_Item menu[] =
645 { "Width", 0, 0, 0, FL_SUBMENU },
646 { "Narrow", 'n', 0, 0, FL_MENU_RADIO | ( ! width_button->value() ? FL_MENU_VALUE : 0 ) },
647 { "Wide", 'w', 0, 0, FL_MENU_RADIO | ( width_button->value() ? FL_MENU_VALUE : 0 ) },
649 { "View", 0, 0, 0, FL_SUBMENU },
650 { "Fader", 'f', 0, 0, FL_MENU_RADIO | ( 0 == tab_button->value() ? FL_MENU_VALUE : 0 ) },
651 { "Signal", 's', 0, 0, FL_MENU_RADIO | ( 1 == tab_button->value() ? FL_MENU_VALUE : 0 ) },
653 { "Move Left", '[', 0, 0 },
654 { "Move Right", ']', 0, 0 },
655 { "Color", 0, 0, 0 },
656 { "Export Strip", 0, 0, 0 },
657 { "Rename", FL_CTRL + 'n', 0, 0 },
658 { "Remove", FL_Delete, 0, 0 },
662 menu_set_callback( menu, &Mixer_Strip::menu_cb, (void*)this );
664 m.copy( menu, (void*)this );
670 Mixer_Strip::handle ( int m )
678 if ( Fl_Group::handle( m ) )
681 if ( Fl::event_key() == FL_Menu )
683 menu_popup( &menu(), x(), y() );
687 return menu().test_shortcut() != 0;
693 if ( Fl::event_button1() )
699 if ( Fl_Group::handle( m ) )
701 else if ( test_press( FL_BUTTON3 ) )
703 menu_popup( &menu() );
711 damage( FL_DAMAGE_USER1 );
712 return Fl_Group::handle( m ) || 1;
714 damage( FL_DAMAGE_USER1 );
715 return Fl_Group::handle( m ) || 1;
718 return Fl_Group::handle( m );
727 Mixer_Strip::command_move_left ( void )
729 mixer->move_left( this );
733 Mixer_Strip::command_move_right ( void )
735 mixer->move_right( this );
739 Mixer_Strip::command_close ( void )
741 mixer->remove( this );
742 Fl::delete_widget( this );
746 Mixer_Strip::command_rename ( const char * s )
752 Mixer_Strip::command_width ( bool b )
754 width_button->value( b );
755 width_button->do_callback();
759 Mixer_Strip::command_view ( bool b )
761 tab_button->value( b );
762 tab_button->do_callback();