Update NTK.
[nondaw.git] / mixer / src / Mixer_Strip.C
blob0b2c440dcdf9292b0e9f972b497f479e7b72c64e
2 /*******************************************************************************/
3 /* Copyright (C) 2009 Jonathan Moore Liles                                     */
4 /*                                                                             */
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.                                                  */
9 /*                                                                             */
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   */
13 /* more details.                                                               */
14 /*                                                                             */
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'
30  * and 'inserts'.
32  */
33 /* Each mixer strip comprises a fader and a panner */
35 #include "Mixer_Strip.H"
36 #include "Engine/Engine.H"
37 #include <dsp.h>
38 #include <string.h>
39 #include "debug.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>
46 #include <FL/Fl.H>
47 #include "Mixer.H"
49 #include "Chain.H"
50 #include "Gain_Module.H"
51 #include "Meter_Module.H"
52 #include "Controller_Module.H"
53 #include "Meter_Indicator_Module.H"
54 #include "debug.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>
61 extern Mixer *mixer;
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 );
71     init();
73     chain( new Chain() );
75     _chain->initialize_with_default();
77     _chain->configure_ports();
79     color( (Fl_Color)rand() );
81 //    name( strdup( strip_name ) );
83     log_create();
86 /* virgin strip created from journal */
87 Mixer_Strip::Mixer_Strip() : Fl_Group( 0, 0, 120, 600 )
89     init();
91     log_create();
94 Mixer_Strip::~Mixer_Strip ( )
96     DMESSAGE( "Destroying mixer strip" );
98     _chain->engine()->lock();
100     /* make sure this gets destroyed before the chain */
101     fader_tab->clear();
103     delete _chain;
104     _chain = NULL;
106     log_destroy();
108     mixer->remove( this );
113 void
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() );
126 void
127 Mixer_Strip::set ( Log_Entry &e )
129     for ( int i = 0; i < e.size(); ++i )
130     {
131         const char *s, *v;
133         e.get( i, &s, &v );
135         if ( ! strcmp( s, ":name" ) )
136             name( v );
137         else if ( ! strcmp( s, ":width" ) )
138         {
139             width_button->value( strcmp( v, "wide" ) == 0 );
140             width_button->do_callback();
141         }
142         else if ( ! strcmp( s, ":tab" ) )
143         {
144             tab_button->value( strcmp( v, "signal" ) == 0 );
145             tab_button->do_callback();
146         }
147         else if ( ! strcmp( s, ":color" ) )
148         {
149             color( (Fl_Color)atoll( v ) );
150             redraw();
151         }
152         else if ( ! strcmp( s, ":gain_mode" ) )
153         {
154             _gain_controller_mode = atoi( v );
155         }
156     }
158     if ( ! mixer->contains( this ) )
159         mixer->add( this );
162 void
163 Mixer_Strip::log_children ( void )
165     log_create();
167     _chain->log_children();
170 void
171 Mixer_Strip::color ( Fl_Color c )
173     _color = c;
174     name_field->color( _color );
175     name_field->redraw();
178 Fl_Color
179 Mixer_Strip::color ( void ) const
181     return _color;
184 void
185 Mixer_Strip::chain ( Chain *c )
187     if ( _chain )
188         delete _chain;
190     _chain = c;
192     c->strip( this );
194     Fl_Group *g = signal_tab;
196     c->resize( g->x(), g->y(), g->w(), g->h() );
197     g->add( c );
198     g->resizable( c );
200     c->labelsize( 10 );
201     c->align( FL_ALIGN_TOP );
202     c->color( FL_RED );
203     c->configure_outputs_callback( configure_outputs, this );
204     c->name( name() );
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 )
217     {
218         if ( tab_button->value() == 0 )
219         {
220             fader_tab->resize( tab_group->x(), tab_group->y(), tab_group->w(), tab_group->h() );
221             fader_tab->show();
222             signal_tab->hide();
223             tab_group->resizable( fader_tab );
224         }
225         else
226         {
227             signal_tab->resize( tab_group->x(), tab_group->y(), tab_group->w(), tab_group->h() );
228             signal_tab->show();
229             fader_tab->hide();
230             tab_group->resizable( signal_tab );
231         }
233     }
234     else if ( o == left_button )
235         command_move_left();
236     else if ( o == right_button )
237         command_move_right();
238     else if ( o == close_button )
239     {
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 ) )
241             command_close();
242     }
243     else if ( o == name_field )
244     {
245         name( name_field->value() );
246         Fl::focus( this );
247     }
248     else if ( o == width_button )
249     {
250         if ( width_button->value() )
251             size( 220, h() );
252         else
253             size( 96, h() );
255          if ( parent() )
256              parent()->parent()->redraw();
257     }
260 void Mixer_Strip::cb_handle(Fl_Widget* o, void* v) {
261     ((Mixer_Strip*)(v))->cb_handle(o);
264 void
265 Mixer_Strip::name ( const char *name )
267     if ( this->name() && !strcmp( name, this->name() ) )
268         return;
270     name = mixer->get_unique_track_name( name );
272     char *s = strdup( name );
274     if ( strlen( s ) > Chain::maximum_name_length() )
275     {
276         s[Chain::maximum_name_length() - 1] = '\0';
278         fl_alert( "Name \"%s\" is too long, truncating to \"%s\"", name, s );
279     }
281     name_field->value( s );
282     label( s );
283     if ( _chain )
284         _chain->name( s );
287 void
288 Mixer_Strip::configure_outputs ( Fl_Widget *, void *v )
290     ((Mixer_Strip*)v)->configure_outputs();
293 void
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 */
300 void
301 Mixer_Strip::handle_module_added ( Module *m )
303     if ( m->is_default() )
304     {
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 )
309         {
310             if ( !jack_input_controller->control_output[0].connected() )
311                 jack_input_controller->connect_to( &m->control_input[1] );
312         }
313         else if ( 0 == strcmp( m->name(), "Gain" ) )
314         {
315             gain_controller->connect_to( &m->control_input[0] );
316             gain_controller->mode( (Controller_Module::Mode)_gain_controller_mode );
317         }
318         else if ( 0 == strcmp( m->name(), "Meter" ) )
319         {
320             meter_indicator->connect_to( &m->control_output[0] );
321         }
322     }
323     else
324     {
325         if ( spatialization_controller->connect_spatializer_to( m ) )
326         {
327             spatialization_controller->show();
328             DMESSAGE( "Connected spatializer to module \"%s\"", m->name() );
329         }
330     }
334 /* called by the chain to let us know that a module has been removed */
335 void
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 )
341     {
342         spatialization_controller->hide();
343         DMESSAGE( "Module \"%s\" disconnected from spatialization controller", m->name() );
344     }
347 /* update GUI with values from RT thread */
348 void
349 Mixer_Strip::update ( void )
351     THREAD_ASSERT( UI );
354 void
355 Mixer_Strip::init ( )
357     selection_color( FL_RED );
359     _gain_controller_mode = 0;
360     _chain = 0;
362     box( FL_BORDER_BOX );
363     labeltype( FL_NO_LABEL );
365     Fl_Group::color( FL_BACKGROUND_COLOR );
367     set_visible_focus();
369      { Fl_Scalepack *o = new Fl_Scalepack( 2, 2, 116, 595 );
370          o->type( FL_VERTICAL );
371          o->spacing( 2 );
373         { Fl_Pack *o = new Fl_Pack( 2, 2, 114, 100 );
374             o->type( Fl_Pack::VERTICAL );
375             o->spacing( 2 );
376             {
377                 Fl_Sometimes_Input *o = new Fl_Sometimes_Input( 2, 2, 144, 24 );
378                 name_field = o;
380                 o->color( color() );
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 );
386                 o->value( name() );
387                 o->callback( cb_handle, (void*)this );
388             }
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" );
393                     o->type(0);
394                     o->labelsize(10);
395                     o->when( FL_WHEN_RELEASE );
396                     o->callback( ((Fl_Callback*)cb_handle), this );
397                 } // Fl_Button* o
399                 { Fl_Button* o = close_button = new Fl_Button(7, 143, 35, 25, "X");
400                     o->tooltip( "Remove strip" );
401                     o->type(0);
402                     o->labeltype( FL_EMBOSSED_LABEL );
403                     o->color( FL_LIGHT1 );
404                     o->selection_color( FL_RED );
405                     o->labelsize(10);
406                     o->when( FL_WHEN_RELEASE );
407                     o->callback( ((Fl_Callback*)cb_handle), this );
408                 } // Fl_Button* o
410                 { Fl_Button* o = right_button = new Fl_Button(7, 143, 35, 25, "@->");
411                     o->tooltip( "Move right" );
412                     o->type(0);
413                     o->labelsize(10);
414                     o->when( FL_WHEN_RELEASE );
415                     o->callback( ((Fl_Callback*)cb_handle), this );
416                 } // Fl_Button* o
418                 o->end();
419             } // Fl_Group* o
420             { Fl_Flip_Button* o = tab_button = new Fl_Flip_Button(61, 183, 45, 22, "fader/signal");
421                 o->type(1);
422                 o->labelsize( 14 );
423                 o->callback( ((Fl_Callback*)cb_handle), this );
424                 o->when(FL_WHEN_RELEASE);
425             }
426             { Fl_Flip_Button* o = width_button = new Fl_Flip_Button(61, 183, 45, 22, "narrow/wide");
427                 o->type(1);
428                 o->labelsize( 14 );
429                 o->callback( ((Fl_Callback*)cb_handle), this );
430                 o->when(FL_WHEN_RELEASE);
431             }
432             o->end();
433         }
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 );
440             o->box( FL_NO_BOX );
441             { Fl_Group *o = fader_tab = new Fl_Group( 2, 116, 105, 330, "Fader" );
442                 o->box( FL_NO_BOX );
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 );
447                     o->spacing( 20 );
448                     o->type( Fl_Scalepack::HORIZONTAL );
449                     { Controller_Module *o = gain_controller = new Controller_Module( true );
450                         o->pad( false );
451                         o->size( 33, 100 );
452                     }
453                     { Meter_Indicator_Module *o = meter_indicator = new Meter_Indicator_Module( true );
454                         o->pad( false );
455                         o->size( 38, 100 );
456                         Fl_Group::current()->resizable(o);
457                     }
458                     o->end();
459                     Fl_Group::current()->resizable(o);
460                 } // Fl_Group* o
461                 o->end();
462                 Fl_Group::current()->resizable(o);
463             }
464             { Fl_Group *o = signal_tab = new Fl_Group( 2, 116, 105, 330 );
465                 o->box( FL_NO_BOX );
466                 o->labeltype( FL_NO_LABEL );
467                 o->hide();
468                 o->end();
469             }
470             o->end();
471             Fl_Group::current()->resizable( o );
472         }
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) );
478                 o->labelsize( 10 );
479 //                o->label( "Spatialization" );
480             }
481             { Controller_Module *o = spatialization_controller = new Controller_Module( true );
482                 o->hide();
483                 o->pad( false );
484                 o->size( 100, 100 );
485             }
486             { Fl_Box *o = new Fl_Box( 0, 0, 100, 12 );
487                 o->align( (Fl_Align)(FL_ALIGN_BOTTOM | FL_ALIGN_INSIDE) );
488                 o->labelsize( 10 );
489                 o->label( "Inputs" );
490             }
491             {
492                 Controller_Module *m = jack_input_controller = new Controller_Module( true );
493                 m->labeltype( FL_NO_LABEL );
494                 m->chain( _chain );
495                 m->pad( false );
496                 m->size( 33, 24 );
497              }
498 /*             o->end(); */
499 /*         } */
500         o->end();
501     }
503     end();
505     color( FL_BLACK );
507     size( 96, h() );
509     redraw();
511     //  _chain->configure_ports();
514 void
515 Mixer_Strip::draw ( void )
517     if ( !fl_not_clipped( x(), y(), w(), h() ) )
518         return;
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 )
523         Fl_Group::draw();
525     Fl_Group::draw_box( FL_UP_FRAME, x(), y(), w(), h(), Fl::focus() == this ? Fl_Group::selection_color() : FL_BLACK );
528 /*****************/
529 /* Import/Export */
530 /*****************/
532 void
533 Mixer_Strip::snapshot ( void *v )
535     ((Mixer_Strip*)v)->snapshot();
538 void
539 Mixer_Strip::snapshot ( void )
541     log_children();
544 bool
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 );
550     return true;
553 bool
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();
560     return r;
567 void
568 Mixer_Strip::menu_cb ( const Fl_Menu_ *m )
570     char picked[256];
572     m->item_pathname( picked, sizeof( picked ) );
574     Logger log( this );
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" ) )
585         command_move_left();
586     else if ( ! strcmp( picked, "/Move Right" ) )
587         command_move_right();
588     else if ( ! strcmp( picked, "/Rename" ) )
589     {
590         ((Fl_Sometimes_Input*)name_field)->take_focus();
591     }
592     else if ( ! strcmp( picked, "/Color" ) )
593     {
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 ) );
601         redraw();
602     }
603     else if ( !strcmp( picked, "/Export Strip" ) )
604     {
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 );
612         if ( s )
613             export_strip( s );
615         fl_message( "Strip exported." );
616     }
617     else if ( ! strcmp( picked, "/Remove" ) )
618     {
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 ) )
620             command_close();
621     }
624 void
625 Mixer_Strip::menu_cb ( Fl_Widget *w, void *v )
627     ((Mixer_Strip*)v)->menu_cb( (Fl_Menu_*) w );
631 /** build the context menu */
632 Fl_Menu_Button &
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() );
639     m.label( label );
641 //    int c = output.size();
643     Fl_Menu_Item menu[] =
644         {
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 ) },
648             { 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 ) },
652             { 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 },
659             { 0 },
660         };
662     menu_set_callback( menu, &Mixer_Strip::menu_cb, (void*)this );
664     m.copy( menu, (void*)this );
666     return m;
670 Mixer_Strip::handle ( int m )
672     Logger log( this );
674     switch ( m )
675     {
676         case FL_KEYBOARD:
677         {
678             if ( Fl_Group::handle( m ) )
679                 return 1;
681             if ( Fl::event_key() == FL_Menu )
682             {
683                 menu_popup( &menu(), x(), y() );
684                 return 1;
685             }
686              else
687                 return menu().test_shortcut() != 0;
688             break;
689         }
690         case FL_PUSH:
691         {
692             int r = 0;
693             if ( Fl::event_button1() )
694             {
695                 take_focus();
696                 r = 1;
697             }
699             if ( Fl_Group::handle( m ) )
700                 return 1;
701             else if ( test_press( FL_BUTTON3 ) )
702             {
703                 menu_popup( &menu() );
704                 return 1;
705             }
706             else
707                 return r;
708             break;
709         }
710         case FL_FOCUS:
711             damage( FL_DAMAGE_USER1 );
712             return Fl_Group::handle( m ) || 1;
713         case FL_UNFOCUS:
714             damage( FL_DAMAGE_USER1 );
715             return Fl_Group::handle( m ) || 1;
716     }
718     return Fl_Group::handle( m );
722 /************/
723 /* Commands */
724 /************/
726 void
727 Mixer_Strip::command_move_left ( void )
729     mixer->move_left( this );
732 void
733 Mixer_Strip::command_move_right ( void )
735     mixer->move_right( this );
738 void
739 Mixer_Strip::command_close ( void )
741         mixer->remove( this );
742         Fl::delete_widget( this );
745 void
746 Mixer_Strip::command_rename ( const char * s )
748     name( s );
751 void
752 Mixer_Strip::command_width ( bool b )
754     width_button->value( b );
755     width_button->do_callback();
758 void
759 Mixer_Strip::command_view ( bool b )
761     tab_button->value( b );
762     tab_button->do_callback();