/*******************************************************************************/ /* Copyright (C) 2009 Jonathan Moore Liles */ /* */ /* This program is free software; you can redistribute it and/or modify it */ /* under the terms of the GNU General Public License as published by the */ /* Free Software Foundation; either version 2 of the License, or (at your */ /* option) any later version. */ /* */ /* This program is distributed in the hope that it will be useful, but WITHOUT */ /* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */ /* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for */ /* more details. */ /* */ /* You should have received a copy of the GNU General Public License along */ /* with This program; see the file COPYING. If not,write to the Free Software */ /* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /*******************************************************************************/ /* Mixer strip control. Handles GUI and control I/O for this strip. */ /* A mixer strip is home to some (JACK) input ports, a fader, some * meters, and a filter chain which can terminate either at the input * to the spacializer or some (JACK) output ports. Since mixer strips * are not necessarily in a 1:1 association with Non-DAW tracks, there * is no need for busses per se. If you want to route the output of * several strips into a single fader or filter chain, then you just * gives those strips JACK outputs and connect them to the common * inputs. This mechanism can also do away with the need for 'sends' * and 'inserts'. */ /* Each mixer strip comprises a fader and a panner */ #include "Mixer_Strip.H" #include "Engine/Engine.H" #include #include #include "debug.h" #include "FL/Fl_Flowpack.H" #include #include #include "Mixer.H" #include "Chain.H" #include "Gain_Module.H" #include "Meter_Module.H" #include "Controller_Module.H" #include "Meter_Indicator_Module.H" #include "util/debug.h" #include #include "FL/test_press.H" #include "FL/menu_popup.H" extern Mixer *mixer; /* add a new mixer strip (with default configuration) */ Mixer_Strip::Mixer_Strip( const char *strip_name ) : Fl_Group( 0, 0, 120, 600 ) { label( strdup( strip_name ) ); labeltype( FL_NO_LABEL ); init(); chain( new Chain() ); _chain->initialize_with_default(); _chain->configure_ports(); color( (Fl_Color)rand() ); // name( strdup( strip_name ) ); log_create(); } /* virgin strip created from journal */ Mixer_Strip::Mixer_Strip() : Fl_Group( 0, 0, 120, 600 ) { init(); log_create(); } Mixer_Strip::~Mixer_Strip ( ) { DMESSAGE( "Destroying mixer strip" ); delete _chain; _chain = NULL; log_destroy(); mixer->remove( this ); } void Mixer_Strip::get ( Log_Entry &e ) const { e.add( ":name", name() ); e.add( ":width", width_button->value() ? "wide" : "narrow" ); e.add( ":tab", tab_button->value() ? "signal" : "fader" ); e.add( ":color", (unsigned long)color()); } void Mixer_Strip::set ( Log_Entry &e ) { for ( int i = 0; i < e.size(); ++i ) { const char *s, *v; e.get( i, &s, &v ); if ( ! strcmp( s, ":name" ) ) name( v ); else if ( ! strcmp( s, ":width" ) ) { width_button->value( strcmp( v, "wide" ) == 0 ); width_button->do_callback(); } else if ( ! strcmp( s, ":tab" ) ) { tab_button->value( strcmp( v, "signal" ) == 0 ); tab_button->do_callback(); } else if ( ! strcmp( s, ":color" ) ) { color( (Fl_Color)atoll( v ) ); redraw(); } } if ( ! mixer->contains( this ) ) mixer->add( this ); } void Mixer_Strip::log_children ( void ) { log_create(); _chain->log_children(); } void Mixer_Strip::color ( Fl_Color c ) { _color = c; name_field->color( _color ); name_field->redraw(); } Fl_Color Mixer_Strip::color ( void ) const { return _color; } void Mixer_Strip::chain ( Chain *c ) { if ( _chain ) delete _chain; _chain = c; c->strip( this ); Fl_Group *g = signal_tab; c->resize( g->x(), g->y(), g->w(), g->h() ); g->add( c ); g->resizable( c ); c->labelsize( 10 ); c->align( FL_ALIGN_TOP ); c->color( FL_RED ); c->configure_outputs_callback( configure_outputs, this ); c->name( name() ); gain_controller->chain( c ); jack_input_controller->chain( c ); meter_indicator->chain( c ); } void Mixer_Strip::cb_handle(Fl_Widget* o) { // parent()->parent()->damage( FL_DAMAGE_ALL, x(), y(), w(), h() ); if ( o == tab_button ) { if ( tab_button->value() == 0 ) { fader_tab->show(); signal_tab->hide(); } else { signal_tab->show(); fader_tab->hide(); } } else if ( o == left_button ) command_move_left(); else if ( o == right_button ) command_move_right(); else if ( o == close_button ) { 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 ) ) command_close(); } else if ( o == name_field ) name( name_field->value() ); else if ( o == width_button ) { if ( ((Fl_Button*)o)->value() ) size( 300, h() ); else size( 120, h() ); if ( parent() ) parent()->parent()->redraw(); } } void Mixer_Strip::cb_handle(Fl_Widget* o, void* v) { ((Mixer_Strip*)(v))->cb_handle(o); } void Mixer_Strip::name ( const char *name ) { char *s = strdup( name ); name_field->value( s ); label( s ); if ( _chain ) _chain->name( s ); } void Mixer_Strip::configure_outputs ( Fl_Widget *o, void *v ) { ((Mixer_Strip*)v)->configure_outputs(); } void Mixer_Strip::configure_outputs ( void ) { DMESSAGE( "Got signal to configure outputs" ); } /* called by the chain to let us know that a module has been added */ void Mixer_Strip::handle_module_added ( Module *m ) { if ( m->is_default() ) { DMESSAGE( "Connecting controls to default module \"%s\"", m->name() ); /* connect default modules to their default controllers/indicators */ if ( 0 == strcmp( m->name(), "JACK" ) && m->ninputs() == 0 ) { if ( !jack_input_controller->control_output[0].connected() ) jack_input_controller->connect_to( &m->control_input[1] ); } else if ( 0 == strcmp( m->name(), "Gain" ) ) { gain_controller->connect_to( &m->control_input[0] ); } else if ( 0 == strcmp( m->name(), "Meter" ) ) { meter_indicator->connect_to( &m->control_output[0] ); } } } /* update GUI with values from RT thread */ void Mixer_Strip::update ( void ) { THREAD_ASSERT( UI ); } void Mixer_Strip::init ( ) { selection_color( FL_FOREGROUND_COLOR ); _chain = 0; // box(FL_THIN_UP_BOX); box( FL_RFLAT_BOX ); Fl_Group::color( fl_darker( FL_BACKGROUND_COLOR ) ); clip_children( 1 ); Fl_Pack *gain_pack; { Fl_Pack *o = new Fl_Pack( 2, 2, 114, 100 ); o->type( Fl_Pack::VERTICAL ); o->spacing( 2 ); { Fl_Sometimes_Input *o = new Fl_Sometimes_Input( 2, 2, 144, 24 ); name_field = o; o->color( color() ); o->up_box( FL_ROUNDED_BOX ); o->box( FL_ROUNDED_BOX ); o->labeltype( FL_NO_LABEL ); o->labelcolor( FL_GRAY0 ); o->textcolor( FL_FOREGROUND_COLOR ); o->value( name() ); o->callback( cb_handle, (void*)this ); } { Fl_Scalepack *o = new Fl_Scalepack( 7, 143, 110, 25 ); o->type( Fl_Pack::HORIZONTAL ); { Fl_Button* o = left_button = new Fl_Button(7, 143, 35, 25, "@<-"); o->tooltip( "Move left" ); o->type(0); o->labelsize(10); o->when( FL_WHEN_RELEASE ); o->callback( ((Fl_Callback*)cb_handle), this ); } // Fl_Button* o { Fl_Button* o = close_button = new Fl_Button(7, 143, 35, 25, "X"); o->tooltip( "Remove strip" ); o->type(0); o->labeltype( FL_EMBOSSED_LABEL ); o->color( FL_LIGHT1 ); o->selection_color( FL_RED ); o->labelsize(10); o->when( FL_WHEN_RELEASE ); o->callback( ((Fl_Callback*)cb_handle), this ); } // Fl_Button* o { Fl_Button* o = right_button = new Fl_Button(7, 143, 35, 25, "@->"); o->tooltip( "Move right" ); o->type(0); o->labelsize(10); o->when( FL_WHEN_RELEASE ); o->callback( ((Fl_Callback*)cb_handle), this ); } // Fl_Button* o o->end(); } // Fl_Group* o { Fl_Flip_Button* o = width_button = new Fl_Flip_Button(61, 183, 45, 22, "narrow/wide"); o->type(1); o->labelsize(14); o->callback( ((Fl_Callback*)cb_handle), this ); o->when(FL_WHEN_RELEASE); } // Fl_Flip_Button* o { Fl_Flip_Button* o = tab_button = new Fl_Flip_Button(61, 183, 45, 22, "fader/signal"); o->type(1); o->labelsize( 14 ); o->callback( cb_handle, this ); o->when(FL_WHEN_RELEASE); } // { Fl_Pack* o = new Fl_Pack(8, 208, 103, 471); // { Fl_Pack* o = gain_pack = new Fl_Pack(8, 208, 103, 516 ); o->end(); } Fl_Pack *fader_pack; { Fl_Group *o = fader_tab = new Fl_Group( 7, 115, 105, 330, "Fader" ); o->box( FL_NO_BOX ); o->labeltype( FL_NO_LABEL ); { Fl_Pack* o = fader_pack = new Fl_Pack(7, 116, 103, 330 ); o->spacing( 20 ); o->type( Fl_Pack::HORIZONTAL ); { Controller_Module *o = gain_controller = new Controller_Module( true ); o->pad( false ); o->size( 33, 0 ); } { Meter_Indicator_Module *o = meter_indicator = new Meter_Indicator_Module( true ); o->pad( false ); o->size( 58, 0 ); o->clip_children( 0 ); Fl_Group::current()->resizable(o); } o->end(); Fl_Group::current()->resizable(o); } // Fl_Group* o o->end(); Fl_Group::current()->resizable(o); } { Fl_Group *o = signal_tab = new Fl_Group( 7, 115, 105, 330 ); o->box( FL_NO_BOX ); o->labeltype( FL_NO_LABEL ); o->hide(); o->end(); } { Fl_Pack *o = panner_pack = new Fl_Pack( 2, 443, 114, 40 ); o->spacing( 2 ); o->type( Fl_Pack::VERTICAL ); o->box( FL_NO_BOX ); { Fl_Box *o = new Fl_Box( 0, 0, 100, 24 ); o->align( (Fl_Align)(FL_ALIGN_BOTTOM | FL_ALIGN_INSIDE) ); o->labelsize( 10 ); o->label( "Spatialization" ); } { Panner* o = new Panner(0, 0, 110, 90); o->deactivate(); o->box(FL_THIN_UP_BOX); o->color(FL_GRAY0); o->selection_color(FL_BACKGROUND_COLOR); o->labeltype(FL_NORMAL_LABEL); o->labelfont(0); o->labelsize(11); o->labelcolor(FL_FOREGROUND_COLOR); o->align(FL_ALIGN_TOP); o->when(FL_WHEN_RELEASE); } // Panner* o { Fl_Box *o = new Fl_Box( 0, 0, 100, 12 ); o->align( (Fl_Align)(FL_ALIGN_BOTTOM | FL_ALIGN_INSIDE) ); o->labelsize( 10 ); o->label( "Inputs" ); } { Controller_Module *m = jack_input_controller = new Controller_Module( true ); m->labeltype( FL_NO_LABEL ); m->chain( _chain ); m->pad( false ); // m->connect_to( &_chain->module( 0 )->control_input[1] ); m->size( 33, 24 ); } o->end(); } end(); color( FL_BLACK ); // _chain->configure_ports(); } void Mixer_Strip::draw ( void ) { if ( !fl_not_clipped( x(), y(), w(), h() ) ) return; /* don't bother drawing anything else, all we're doing is drawing the focus. */ if ( damage() != FL_DAMAGE_USER1 ) Fl_Group::draw(); Fl_Group::draw_box( FL_ROUNDED_FRAME, x(), y(), w(), h(), _focused ? Fl_Group::selection_color() : Fl_Group::color() ); } void Mixer_Strip::menu_cb ( const Fl_Menu_ *m ) { char picked[256]; m->item_pathname( picked, sizeof( picked ) ); Logger log( this ); if ( ! strcmp( picked, "Width/Narrow" ) ) command_width( false ); else if ( ! strcmp( picked, "Width/Wide" ) ) command_width( true ); else if ( ! strcmp( picked, "View/Fader" ) ) command_view( false ); else if ( ! strcmp( picked, "View/Signal" ) ) command_view( true ); else if ( ! strcmp( picked, "/Move Left" ) ) command_move_left(); else if ( ! strcmp( picked, "/Move Right" ) ) command_move_right(); else if ( ! strcmp( picked, "/Rename" ) ) name_field->take_focus(); else if ( ! strcmp( picked, "/Remove" ) ) { 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 ) ) command_close(); } } void Mixer_Strip::menu_cb ( Fl_Widget *w, void *v ) { ((Mixer_Strip*)v)->menu_cb( (Fl_Menu_*) w ); } /** build the context menu */ Fl_Menu_Button & Mixer_Strip::menu ( void ) const { static Fl_Menu_Button m( 0, 0, 0, 0, "Strip" ); static char label[256]; snprintf( label, sizeof(label), "Strip/%s", name() ); m.label( label ); // int c = output.size(); Fl_Menu_Item menu[] = { { "Width", 0, 0, 0, FL_SUBMENU }, { "Narrow", 'n', 0, 0, FL_MENU_RADIO | ( ! width_button->value() ? FL_MENU_VALUE : 0 ) }, { "Wide", 'w', 0, 0, FL_MENU_RADIO | ( width_button->value() ? FL_MENU_VALUE : 0 ) }, { 0 }, { "View", 0, 0, 0, FL_SUBMENU }, { "Fader", 'f', 0, 0, FL_MENU_RADIO | ( 0 == tab_button->value() ? FL_MENU_VALUE : 0 ) }, { "Signal", 's', 0, 0, FL_MENU_RADIO | ( 1 == tab_button->value() ? FL_MENU_VALUE : 0 ) }, { 0 }, { "Move Left", '[', 0, 0 }, { "Move Right", ']', 0, 0 }, { "Color", 0, 0, 0 }, { "Rename", FL_CTRL + 'n', 0, 0 }, { "Remove", FL_Delete, 0, 0 }, { 0 }, }; menu_set_callback( menu, &Mixer_Strip::menu_cb, (void*)this ); m.copy( menu, (void*)this ); return m; } int Mixer_Strip::handle ( int m ) { Logger log( this ); switch ( m ) { case FL_KEYBOARD: if ( Fl_Group::handle( m ) ) return 1; if ( test_press( FL_Menu ) ) { menu_popup( &menu(), x(), y() ); return 1; } else return menu().test_shortcut() != 0; case FL_PUSH: { if ( Fl_Group::handle( m ) ) return 1; else if ( test_press( FL_BUTTON3 ) ) { menu_popup( &menu() ); return 1; } return 0; } case FL_FOCUS: _focused = true; damage( FL_DAMAGE_USER1 ); return 1; case FL_UNFOCUS: _focused = false; damage( FL_DAMAGE_USER1 ); return 1; /* case FL_ENTER: */ /* name_field->color( FL_BLACK ); */ /* name_field->redraw(); */ /* return 1; */ /* break; */ /* case FL_LEAVE: */ /* name_field->color( _color ); */ /* name_field->redraw(); */ /* return 1; */ /* break; */ default: return Fl_Group::handle( m ); } return Fl_Group::handle( m ); } /************/ /* Commands */ /************/ void Mixer_Strip::command_move_left ( void ) { mixer->move_left( this ); } void Mixer_Strip::command_move_right ( void ) { mixer->move_right( this ); } void Mixer_Strip::command_close ( void ) { mixer->remove( this ); Fl::delete_widget( this ); } void Mixer_Strip::command_rename ( const char * s ) { name( s ); } void Mixer_Strip::command_width ( bool b ) { width_button->value( b ); width_button->do_callback(); } void Mixer_Strip::command_view ( bool b ) { tab_button->value( b ); tab_button->do_callback(); }