| @@ -96,9 +96,10 @@ DONE := $(BOLD)$(GREEN)done$(SGR0) | |||
| include FL/makefile.inc | |||
| include nonlib/makefile.inc | |||
| include Timeline/makefile.inc | |||
| include Mixer/makefile.inc | |||
| SRCS:=$(FL_SRCS) $(nonlib_SRCS) $(Timeline_SRCS) | |||
| OBJS:=$(FL_OBJS) $(nonlib_OBJS) $(Timeline_OBJS) | |||
| SRCS:=$(FL_SRCS) $(nonlib_SRCS) $(Timeline_SRCS) $(Mixer_SRCS) | |||
| OBJS:=$(FL_OBJS) $(nonlib_OBJS) $(Timeline_OBJS) $(Mixer_OBJS) | |||
| # FIXME: isn't there a better way? | |||
| $(OBJS): .config Makefile | |||
| @@ -110,13 +111,14 @@ TAGS: $(SRCS) | |||
| ifneq ($(CALCULATING),yes) | |||
| @ echo -n Calculating dependencies... | |||
| @ makedepend -f- -- $(CXXFLAGS) $(INCLUDES) -- $(SRCS) 2>/dev/null > .deps && echo $(DONE) | |||
| # @ gcc -M $(CXXFLAGS) $(INCLUDES) $(SRCS) > .deps && echo $(DONE) | |||
| endif | |||
| install: all | |||
| @ echo -n "Installing..." | |||
| @ install Timeline/timeline $(prefix)/bin/non-daw | |||
| # @ install Mixer/mixer $(prefix)/bin/non-mixer | |||
| @ install Mixer/mixer $(prefix)/bin/non-mixer | |||
| @ mkdir -p $(SYSTEM_PATH) | |||
| @ mkdir -p $(PIXMAP_PATH) | |||
| @ cp pixmaps/*.png $(PIXMAP_PATH) | |||
| @@ -125,7 +127,7 @@ install: all | |||
| ifneq ($(USE_DEBUG),yes) | |||
| @ echo -n "Stripping..." | |||
| @ strip $(prefix)/bin/non-daw | |||
| # @ strip $(prefix)/bin/non-mixer | |||
| @ strip $(prefix)/bin/non-mixer | |||
| @ echo "$(DONE)" | |||
| endif | |||
| @@ -134,7 +136,7 @@ clean_deps: | |||
| .PHONEY: clean config depend clean_deps | |||
| clean: FL_clean nonlib_clean Timeline_clean | |||
| clean: FL_clean nonlib_clean Timeline_clean Mixer_clean | |||
| dist: | |||
| git archive --prefix=non-daw-$(VERSION)/ v$(VERSION) | bzip2 > non-daw-$(VERSION).tar.bz2 | |||
| @@ -0,0 +1,583 @@ | |||
| /*******************************************************************************/ | |||
| /* 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. */ | |||
| /*******************************************************************************/ | |||
| /* Filter chain. This would all be much simpler if we chose not to | |||
| * allow non 1:1 plugins to be mixed in a single chain... | |||
| * | |||
| * Supporting the mixture requires duplicating some inputs (to satisfy | |||
| * stereo input plugins reading mono outputs) and duplicating some | |||
| * plugins (to satisfy mono input plugins reading stereo outputs). | |||
| * | |||
| * Basically, what this means is that the intermediate number of | |||
| * buffers need not have any relation to the starting and ending | |||
| * buffer count. (Picture an ambisonic panner going into an ambisonic | |||
| * decoder (1:6:2). | |||
| * | |||
| * The chain will allocate enough buffers to hold data from the | |||
| * maximum number of channels used by a contained module. | |||
| * | |||
| * The process thread goes as follows: | |||
| * | |||
| * 1. Copy inputs to chain buffers. | |||
| * | |||
| * 2. process() each module in turn (reusing buffers in-place) (inputs | |||
| * will be copied or plugins duplicated as necessary) | |||
| * | |||
| * 3. Copy chain buffers to outputs. | |||
| * | |||
| * For chains where the number of channels never exceeds the maximum | |||
| * of the number of inputs and outputs, the first copy can be | |||
| * optimized out. | |||
| */ | |||
| #include "Chain.H" | |||
| #include "Module.H" | |||
| #include "Meter_Module.H" | |||
| #include "JACK_Module.H" | |||
| #include "Gain_Module.H" | |||
| #include "Plugin_Module.H" | |||
| #include <Fl/Fl_Box.H> | |||
| #include <FL/Fl_Menu.H> | |||
| #include <FL/fl_ask.H> | |||
| #include <stdlib.h> | |||
| #include "util/debug.h" | |||
| #include <stdio.h> | |||
| #include <FL/fl_draw.H> | |||
| #include "Engine/Engine.H" | |||
| #include <FL/Fl_Tabs.H> | |||
| #include "FL/Fl_Flowpack.H" | |||
| #include "FL/Fl_Scroll.H" | |||
| #include <string.h> | |||
| Chain::Chain ( int X, int Y, int W, int H, const char *L ) : | |||
| Fl_Group( X, Y, W, H, L) | |||
| { | |||
| _outs = 1; | |||
| _ins = 1; | |||
| _configure_outputs_callback = NULL; | |||
| _name = NULL; | |||
| { Fl_Tabs *o = tabs = new Fl_Tabs( X, Y, W, H ); | |||
| { Fl_Group *o = new Fl_Group( X, Y + 24, W, H - 24, "Chain" ); | |||
| o->box( FL_FLAT_BOX ); | |||
| o->labelsize( 9 ); | |||
| { Fl_Pack *o = modules_pack = new Fl_Pack( X, Y + 24, W, H - 24 ); | |||
| o->type( Fl_Pack::VERTICAL ); | |||
| o->spacing( 10 ); | |||
| o->end(); | |||
| } | |||
| o->end(); | |||
| } | |||
| { Fl_Group *o = new Fl_Group( X, Y + 24, W, H - 24, "Controls" ); | |||
| o->labelsize( 9 ); | |||
| o->hide(); | |||
| { Fl_Scroll *o = new Fl_Scroll( X, Y + 24, W, H - 24 ); | |||
| o->type( Fl_Scroll::VERTICAL ); | |||
| { Fl_Flowpack *o = controls_pack = new Fl_Flowpack( X, Y + 24, W, H - 24 ); | |||
| o->hspacing( 10 ); | |||
| o->vspacing( 10 ); | |||
| // o->box( FL_FLAT_BOX ); | |||
| // o->color( FL_RED ); | |||
| o->end(); | |||
| Fl_Group::current()->resizable( o ); | |||
| } | |||
| o->end(); | |||
| Fl_Group::current()->resizable( o ); | |||
| } | |||
| o->end(); | |||
| Fl_Group::current()->resizable( o ); | |||
| } | |||
| o->end(); | |||
| Fl_Group::current()->resizable( o ); | |||
| } | |||
| end(); | |||
| } | |||
| /* Fill this chain with JACK I/O, Gain, and Meter modules. */ | |||
| void | |||
| Chain::initialize_with_default ( void ) | |||
| { | |||
| { | |||
| JACK_Module *jm = new JACK_Module( 50, 50, "JACK" ); | |||
| jm->chain( this ); | |||
| jm->configure_outputs( 1 ); | |||
| jm->initialize(); | |||
| jm->color( FL_BLACK ); | |||
| insert( NULL, jm ); | |||
| } | |||
| { | |||
| JACK_Module *m = new JACK_Module( 50, 50, "JACK" ); | |||
| m->chain( this ); | |||
| m->initialize(); | |||
| m->color( FL_BLACK ); | |||
| insert( NULL, m ); | |||
| } | |||
| } | |||
| void Chain::cb_handle(Fl_Widget* o) { | |||
| /* if ( o == head_button ) */ | |||
| /* { */ | |||
| /* Module *m = Module::pick_plugin(); */ | |||
| /* insert_before( (Module*)modules_pack->child( 0 ), m ); */ | |||
| /* } */ | |||
| /* else if ( o == tail_button ) */ | |||
| /* { */ | |||
| /* Module *m = Module::pick_plugin(); */ | |||
| /* insert_before( 0, m ); */ | |||
| /* } */ | |||
| } | |||
| void Chain::cb_handle(Fl_Widget* o, void* v) { | |||
| ((Chain*)(v))->cb_handle(o); | |||
| } | |||
| /* remove a module from the chain. this isn't guaranteed to succeed, | |||
| * because removing the module might result in an invalid routing */ | |||
| void | |||
| Chain::remove ( Module *m ) | |||
| { | |||
| int i = modules_pack->find( m ); | |||
| int ins = 0; | |||
| if ( i != 0 ) | |||
| ins = module( i - 1 )->noutputs(); | |||
| if ( ! can_configure_outputs( m, ins ) ) | |||
| { | |||
| fl_alert( "Can't remove module at this point because the resultant chain is invalid" ); | |||
| } | |||
| modules_pack->remove( m ); | |||
| configure_ports(); | |||
| } | |||
| /* determine number of output ports, signal if changed. */ | |||
| void | |||
| Chain::configure_ports ( void ) | |||
| { | |||
| int old_outs = outs(); | |||
| int nouts = 0; | |||
| engine->lock(); | |||
| for ( int i = 0; i < modules(); ++i ) | |||
| { | |||
| module( i )->configure_inputs( nouts ); | |||
| nouts = module( i )->noutputs(); | |||
| } | |||
| outs( nouts ); | |||
| int req_buffers = required_buffers(); | |||
| if ( outs() != old_outs ) | |||
| { | |||
| if ( configure_outputs_callback() ) | |||
| configure_outputs_callback()( this, _configure_outputs_userdata ); | |||
| } | |||
| DMESSAGE( "required_buffers = %i", req_buffers ); | |||
| if ( port.size() != req_buffers ) | |||
| { | |||
| for ( unsigned int i = port.size(); i--; ) | |||
| delete[] port[i].buffer(); | |||
| port.clear(); | |||
| for ( unsigned int i = 0; i < req_buffers; ++i ) | |||
| { | |||
| Module::Port p( NULL, Module::Port::OUTPUT, Module::Port::AUDIO ); | |||
| p.connect_to( new sample_t[engine->nframes()] ); | |||
| port.push_back( p ); | |||
| } | |||
| } | |||
| build_process_queue(); | |||
| engine->unlock(); | |||
| parent()->redraw(); | |||
| } | |||
| /* calculate the minimum number of buffers required to satisfy this chain */ | |||
| int | |||
| Chain::required_buffers ( void ) | |||
| { | |||
| int buffers = 0; | |||
| int outs = 0; | |||
| for ( int i = 0; i < modules(); ++i ) | |||
| { | |||
| outs = module( i )->can_support_inputs( outs ); | |||
| if ( outs > buffers ) | |||
| buffers = outs; | |||
| } | |||
| return buffers; | |||
| } | |||
| /* called by a module when it wants to alter the number of its | |||
| * outputs. Also used to test for chain validity when inserting / | |||
| * removing modules */ | |||
| bool | |||
| Chain::can_configure_outputs ( Module *m, int n ) const | |||
| { | |||
| /* start at the requesting module */ | |||
| int outs = n; | |||
| int i = modules_pack->find( m ); | |||
| if ( modules() - 1 == i ) | |||
| /* last module */ | |||
| return true; | |||
| for ( i++ ; i < modules(); ++i ) | |||
| { | |||
| outs = module( i )->can_support_inputs( outs ); | |||
| if ( outs < 0 ) | |||
| return false; | |||
| } | |||
| return true; | |||
| } | |||
| /* return true if this chain can be converted to support /n/ input channels */ | |||
| bool | |||
| Chain::can_support_input_channels ( int n ) | |||
| { | |||
| /* FIXME: implement */ | |||
| return true; | |||
| } | |||
| /* rename chain... we have to let our modules know our name has | |||
| * changed so they can take the appropriate action (in particular the | |||
| * JACK module). */ | |||
| void | |||
| Chain::name ( const char *name ) | |||
| { | |||
| _name = name; | |||
| for ( int i = 0; i < modules(); ++i ) | |||
| module( i )->handle_chain_name_changed(); | |||
| } | |||
| #include "FL/menu_popup.H" | |||
| bool | |||
| Chain::insert ( Module *m, Module *n ) | |||
| { | |||
| engine->lock(); | |||
| if ( !m ) | |||
| { | |||
| if ( modules() == 0 && n->can_support_inputs( 0 ) >= 0 ) | |||
| { | |||
| n->configure_inputs( 0 ); | |||
| modules_pack->add( n ); | |||
| n->chain( this ); | |||
| } | |||
| else if ( n->can_support_inputs( module( modules() - 1 )->noutputs() ) >= 0 ) | |||
| { | |||
| n->configure_inputs( module( modules() - 1 )->noutputs() ); | |||
| modules_pack->add( n ); | |||
| n->chain( this ); | |||
| } | |||
| else | |||
| goto err; | |||
| } | |||
| else | |||
| { | |||
| int i = modules_pack->find( m ); | |||
| if ( 0 == i ) | |||
| { | |||
| /* inserting to head of chain*/ | |||
| if ( n->can_support_inputs( 0 ) >= 0 ) | |||
| n->configure_inputs( 0 ); | |||
| else | |||
| goto err; | |||
| } | |||
| else | |||
| { | |||
| if ( n->can_support_inputs( module( i - 1 )->noutputs() ) >= 0 ) | |||
| { | |||
| n->configure_inputs( module( i - 1 )->noutputs() ); | |||
| m->configure_inputs( n->noutputs() ); | |||
| for ( int j = i + 1; j < modules(); ++j ) | |||
| module( j )->configure_inputs( module( j - 1 )->noutputs() ); | |||
| } | |||
| else | |||
| goto err; | |||
| } | |||
| modules_pack->insert( *n, i ); | |||
| n->chain( this ); | |||
| } | |||
| DMESSAGE( "Module has %i:%i audio and %i:%i control ports", | |||
| n->ninputs(), | |||
| n->noutputs(), | |||
| n->ncontrol_inputs(), | |||
| n->ncontrol_outputs() ); | |||
| configure_ports(); | |||
| engine->unlock(); | |||
| return true; | |||
| err: | |||
| engine->unlock(); | |||
| return false; | |||
| } | |||
| /* add a control to the control strip. Assumed to already be connected! */ | |||
| void | |||
| Chain::add_control ( Module *m ) | |||
| { | |||
| controls_pack->add( m ); | |||
| } | |||
| void | |||
| Chain::draw_connections ( Module *m ) | |||
| { | |||
| int spacing; | |||
| int offset; | |||
| Fl_Color c =fl_color_average( FL_WHITE, FL_YELLOW, 0.50 ); | |||
| fl_color( c ); | |||
| if ( m->ninputs() ) | |||
| { | |||
| spacing = w() / m->ninputs(); | |||
| offset = spacing / 2; | |||
| for ( int i = m->ninputs(); i--; ) | |||
| fl_rectf( m->x() + offset + ( spacing * i ), m->y() - 5, 2, 5 ); | |||
| } | |||
| fl_color( fl_darker( c ) ); | |||
| if ( m->noutputs() ) | |||
| { | |||
| spacing = w() / m->noutputs(); | |||
| offset = spacing / 2; | |||
| for ( int i = m->noutputs(); i--; ) | |||
| fl_rectf( m->x() + offset + ( spacing * i ), m->y() + m->h(), 2, 5 ); | |||
| } | |||
| } | |||
| void | |||
| Chain::add_to_process_queue ( Module *m ) | |||
| { | |||
| for ( std::list<Module*>::const_iterator i = process_queue.begin(); i != process_queue.end(); ++i ) | |||
| if ( m == *i ) | |||
| return; | |||
| process_queue.push_back( m ); | |||
| } | |||
| /* run any time the internal connection graph might have | |||
| * changed... Tells the process thread what order modules need to be | |||
| * run in. */ | |||
| void | |||
| Chain::build_process_queue ( void ) | |||
| { | |||
| process_queue.clear(); | |||
| for ( int i = 0; i < modules(); ++i ) | |||
| { | |||
| Module *m = (Module*)module( i ); | |||
| /* controllers */ | |||
| for ( unsigned int j = 0; j < m->control_input.size(); ++j ) | |||
| { | |||
| if ( m->control_input[j].connected() ) | |||
| { | |||
| add_to_process_queue( m->control_input[j].connected_port()->module() ); | |||
| } | |||
| } | |||
| /* audio modules */ | |||
| add_to_process_queue( m ); | |||
| /* indicators */ | |||
| for ( unsigned int j = 0; j < m->control_output.size(); ++j ) | |||
| { | |||
| if ( m->control_output[j].connected() ) | |||
| { | |||
| add_to_process_queue( m->control_output[j].connected_port()->module() ); | |||
| } | |||
| } | |||
| } | |||
| /* connect all the ports to the buffers */ | |||
| for ( int i = 0; i < modules(); ++i ) | |||
| { | |||
| Module *m = module( i ); | |||
| for ( unsigned int j = 0; j < m->audio_input.size(); ++j ) | |||
| { | |||
| m->audio_input[j].connect_to( &port[j] ); | |||
| } | |||
| for ( unsigned int j = 0; j < m->audio_output.size(); ++j ) | |||
| { | |||
| m->audio_output[j].connect_to( &port[j] ); | |||
| } | |||
| } | |||
| DMESSAGE( "Process queue looks like:" ); | |||
| for ( std::list<Module*>::const_iterator i = process_queue.begin(); i != process_queue.end(); ++i ) | |||
| { | |||
| const Module* m = *i; | |||
| if ( m->audio_input.size() || m->audio_output.size() ) | |||
| DMESSAGE( "\t%s", (*i)->name() ); | |||
| else if ( m->control_output.size() ) | |||
| DMESSAGE( "\t%s -->", (*i)->name() ); | |||
| else if ( m->control_input.size() ) | |||
| DMESSAGE( "\t%s <--", (*i)->name() ); | |||
| } | |||
| } | |||
| void | |||
| Chain::draw ( void ) | |||
| { | |||
| Fl_Group::draw(); | |||
| if ( 0 == strcmp( "Chain", tabs->value()->label() ) ) | |||
| for ( int i = 0; i < modules(); ++i ) | |||
| draw_connections( module( i ) ); | |||
| } | |||
| void | |||
| Chain::resize ( int X, int Y, int W, int H ) | |||
| { | |||
| Fl_Group::resize( X, Y, W, H ); | |||
| /* this won't naturally resize because it's inside of an Fl_Scroll... */ | |||
| controls_pack->size( W, controls_pack->h() ); | |||
| } | |||
| #include "FL/test_press.H" | |||
| int | |||
| Chain::handle ( int m ) | |||
| { | |||
| switch ( m ) | |||
| { | |||
| case FL_PUSH: | |||
| { | |||
| if ( Fl::belowmouse() != this ) | |||
| { | |||
| Module *m = NULL; | |||
| for ( int i = 0; i < modules(); ++i ) | |||
| if ( Fl::event_inside( module( i ) ) ) | |||
| { | |||
| m = module( i ); | |||
| break; | |||
| } | |||
| if ( m ) | |||
| { | |||
| if ( test_press( FL_BUTTON3 | FL_CTRL ) ) | |||
| { | |||
| if ( FL_BLACK == m->color() ) | |||
| { | |||
| /* FIXME: hack */ | |||
| fl_alert( "Cannot delete this module." ); | |||
| } | |||
| else | |||
| { | |||
| remove( m ); | |||
| delete m; | |||
| redraw(); | |||
| } | |||
| return 1; | |||
| } | |||
| else if ( test_press( FL_BUTTON1 | FL_SHIFT ) ) | |||
| { | |||
| Module *mod = (Module*)Plugin_Module::pick_plugin(); | |||
| if ( mod ) | |||
| { | |||
| if ( ! insert( m, mod ) ) | |||
| fl_alert( "Cannot insert this module at this point in the chain" ); | |||
| redraw(); | |||
| } | |||
| return 1; | |||
| } | |||
| else if ( test_press( FL_BUTTON1 | FL_CTRL ) ) | |||
| { | |||
| if ( m->active() ) | |||
| m->deactivate(); | |||
| else | |||
| m->activate(); | |||
| return 1; | |||
| } | |||
| } | |||
| } | |||
| break; | |||
| } | |||
| } | |||
| return Fl_Group::handle( m ); | |||
| } | |||
| void | |||
| Chain::process ( nframes_t nframes ) | |||
| { | |||
| for ( std::list<Module*>::const_iterator i = process_queue.begin(); i != process_queue.end(); ++i ) | |||
| { | |||
| Module *m = *i; | |||
| m->nframes( nframes ); | |||
| if ( m->active() ) | |||
| m->process(); | |||
| } | |||
| } | |||
| @@ -0,0 +1,104 @@ | |||
| /*******************************************************************************/ | |||
| /* 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. */ | |||
| /*******************************************************************************/ | |||
| #include <FL/Fl.H> | |||
| #include <FL/Fl_Pack.H> | |||
| #include <FL/Fl_Button.H> | |||
| #include "Module.H" | |||
| #include "JACK/Port.H" | |||
| #include <vector> | |||
| #include <list> | |||
| class Fl_Flowpack; | |||
| class Fl_Tabs; | |||
| class Chain : public Fl_Group { | |||
| Fl_Pack *modules_pack; | |||
| Fl_Flowpack *controls_pack; | |||
| Fl_Tabs *tabs; | |||
| void cb_handle(Fl_Widget*); | |||
| static void cb_handle(Fl_Widget*, void*); | |||
| int _ins; | |||
| int _outs; | |||
| // sample_t **_buffer; | |||
| // int _nbuffers; | |||
| Fl_Callback *_configure_outputs_callback; | |||
| void *_configure_outputs_userdata; | |||
| const char *_name; | |||
| void draw_connections ( Module *m ); | |||
| std::list<Module*> process_queue; | |||
| void build_process_queue ( void ); | |||
| void add_to_process_queue ( Module *m ); | |||
| public: | |||
| std::vector <Module::Port> port; | |||
| const char *name ( void ) const { return _name; } | |||
| void name ( const char *name ); | |||
| void configure_ports ( void ); | |||
| int required_buffers ( void ); | |||
| Chain ( int X, int Y, int W, int H, const char *L = 0 ); | |||
| bool can_support_input_channels ( int n ); | |||
| void ins ( int i ) { _ins = i; } | |||
| void outs ( int i ) { _outs = i; } | |||
| int ins ( void ) const { return _ins; } | |||
| int outs ( void ) const { return _outs; } | |||
| int modules ( void ) const { return modules_pack->children(); } | |||
| Module *module ( int n ) const { return (Module*)modules_pack->child( n ); } | |||
| void remove ( Module *m ); | |||
| bool insert ( Module *m, Module *n ); | |||
| void add_control ( Module *m ); | |||
| void initialize_with_default ( void ); | |||
| bool can_configure_outputs ( Module *m, int n ) const; | |||
| void configure_outputs_callback ( Fl_Callback *cb, void *v ) | |||
| { | |||
| _configure_outputs_callback = cb; | |||
| _configure_outputs_userdata = v; | |||
| } | |||
| Fl_Callback * configure_outputs_callback ( void ) const { return _configure_outputs_callback; } | |||
| void process ( nframes_t ); | |||
| protected: | |||
| int handle ( int m ); | |||
| void draw ( void ); | |||
| void resize ( int X, int Y, int W, int H ); | |||
| }; | |||
| @@ -0,0 +1,259 @@ | |||
| /*******************************************************************************/ | |||
| /* 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. */ | |||
| /*******************************************************************************/ | |||
| #include "Controller_Module.H" | |||
| #include <FL/Fl.H> | |||
| #include <FL/Fl_Value_Slider.H> | |||
| #include <FL/Fl_Box.H> | |||
| #include <FL/Fl_Counter.H> | |||
| #include "FL/Fl_Arc_Dial.H" | |||
| #include "FL/Fl_Light_Button.H" | |||
| #include "FL/Boxtypes.H" | |||
| #include <FL/fl_draw.H> | |||
| #include "FL/Fl_Labelpad_Group.H" | |||
| #include <stdio.h> | |||
| #include "Engine/Engine.H" | |||
| #include "Chain.H" | |||
| const float CONTROL_UPDATE_FREQ = 0.1f; | |||
| Controller_Module::Controller_Module ( int W, int H, const char *L ) | |||
| : Module ( W, 100, L ) | |||
| { | |||
| // label( "" ); | |||
| box( FL_NO_BOX ); | |||
| _pad = true; | |||
| control = 0; | |||
| control_value =0.0f; | |||
| add_port( Port( this, Port::OUTPUT, Port::CONTROL ) ); | |||
| mode( GUI ); | |||
| // mode( CV ); | |||
| // configure_inputs( 1 ); | |||
| end(); | |||
| Fl::add_timeout( CONTROL_UPDATE_FREQ, update_cb, this ); | |||
| } | |||
| Controller_Module::~Controller_Module ( ) | |||
| { | |||
| } | |||
| void | |||
| Controller_Module::update_cb ( void *v ) | |||
| { | |||
| ((Controller_Module*)v)->update_cb(); | |||
| } | |||
| void | |||
| Controller_Module::update_cb ( void ) | |||
| { | |||
| Fl::repeat_timeout( CONTROL_UPDATE_FREQ, update_cb, this ); | |||
| if ( control && control_output[0].connected() ) | |||
| control->value(control_value); | |||
| } | |||
| void | |||
| Controller_Module::cb_handle ( Fl_Widget *w, void *v ) | |||
| { | |||
| ((Controller_Module*)v)->cb_handle( w ); | |||
| } | |||
| void | |||
| Controller_Module::cb_handle ( Fl_Widget *w ) | |||
| { | |||
| control_value = ((Fl_Valuator*)w)->value(); | |||
| if ( control_output[0].connected() ) | |||
| { | |||
| control_output[0].control_value( control_value ); | |||
| Port *p = control_output[0].connected_port(); | |||
| Module *m = p->module(); | |||
| m->handle_control_changed( p ); | |||
| } | |||
| } | |||
| void | |||
| Controller_Module::connect_to ( Port *p ) | |||
| { | |||
| control_output[0].connect_to( p ); | |||
| if( mode() == CV ) | |||
| { | |||
| engine->lock(); | |||
| { | |||
| char name[256]; | |||
| snprintf( name, sizeof( name ), "%s-CV", p->name() ); | |||
| JACK::Port po( engine->client(), JACK::Port::Input, chain()->name(), 0, name ); | |||
| if ( po.valid() ) | |||
| { | |||
| jack_input.push_back( po ); | |||
| } | |||
| } | |||
| engine->unlock(); | |||
| } | |||
| Fl_Widget *w; | |||
| if ( p->hints.type == Module::Port::Hints::BOOLEAN ) | |||
| { | |||
| Fl_Light_Button *o = new Fl_Light_Button( 0, 0, 40, 40, p->name() ); | |||
| w = o; | |||
| o->value( p->control_value() ); | |||
| } | |||
| else if ( p->hints.type == Module::Port::Hints::INTEGER ) | |||
| { | |||
| Fl_Counter *o = new Fl_Counter(0, 0, 58, 24, p->name() ); | |||
| control = o; | |||
| w = o; | |||
| o->type(1); | |||
| o->step(1); | |||
| if ( p->hints.ranged ) | |||
| { | |||
| o->minimum( p->hints.minimum ); | |||
| o->maximum( p->hints.maximum ); | |||
| } | |||
| o->value( p->control_value() ); | |||
| } | |||
| else if ( p->hints.type == Module::Port::Hints::LOGARITHMIC ) | |||
| { | |||
| Fl_Value_Slider *o = new Fl_Value_Slider(0, 0, 30, 250, p->name() ); | |||
| control = o; | |||
| w = o; | |||
| o->type(4); | |||
| o->color(FL_GRAY0); | |||
| o->selection_color((Fl_Color)1); | |||
| o->minimum(1.5); | |||
| o->maximum(0); | |||
| o->step(0.01); | |||
| o->value(1); | |||
| o->textsize(14); | |||
| // o->type( FL_VERTICAL ); | |||
| // o->type(1); | |||
| if ( p->hints.ranged ) | |||
| { | |||
| o->minimum( p->hints.maximum ); | |||
| o->maximum( p->hints.minimum ); | |||
| } | |||
| o->value( p->control_value() ); | |||
| } | |||
| else | |||
| { | |||
| { Fl_Arc_Dial *o = new Fl_Arc_Dial( 0, 0, 40, 40, p->name() ); | |||
| w = o; | |||
| control = o; | |||
| if ( p->hints.ranged ) | |||
| { | |||
| o->minimum( p->hints.minimum ); | |||
| o->maximum( p->hints.maximum ); | |||
| } | |||
| o->box( FL_BURNISHED_OVAL_BOX ); | |||
| // o->box( FL_OVAL_BOX ); | |||
| // o->type( FL_FILL_DIAL ); | |||
| o->color( fl_darker( fl_darker( FL_GRAY ) ) ); | |||
| o->selection_color( FL_WHITE ); | |||
| o->value( p->control_value() ); | |||
| } | |||
| } | |||
| control_value = p->control_value(); | |||
| w->align(FL_ALIGN_TOP); | |||
| w->labelsize( 10 ); | |||
| w->callback( cb_handle, this ); | |||
| if ( _pad ) | |||
| { | |||
| Fl_Labelpad_Group *flg = new Fl_Labelpad_Group( w ); | |||
| size( flg->w(), flg->h() ); | |||
| add( flg ); | |||
| } | |||
| else | |||
| { | |||
| w->resize( x(), y(), this->w(), h() ); | |||
| add( w ); | |||
| resizable( w ); | |||
| } | |||
| } | |||
| int | |||
| Controller_Module::handle ( int m ) | |||
| { | |||
| return Fl_Group::handle( m ); | |||
| } | |||
| void | |||
| Controller_Module::process ( void ) | |||
| { | |||
| if ( control_output[0].connected() ) | |||
| { | |||
| float f = control_value; | |||
| if ( mode() == CV ) | |||
| { | |||
| f = *((float*)jack_input[0].buffer( engine->nframes() )); | |||
| const Port *p = control_output[0].connected_port(); | |||
| if (p->hints.ranged ) | |||
| { | |||
| // scale value to range. | |||
| // we assume that CV values are between 0 and 1 | |||
| float scale = p->hints.maximum - p->hints.minimum; | |||
| float offset = p->hints.minimum; | |||
| f = ( f * scale ) + offset; | |||
| } | |||
| } | |||
| // else | |||
| // f = *((float*)control_output[0].buffer()); | |||
| *((float*)control_output[0].buffer()) = f; | |||
| control_value = f; | |||
| } | |||
| } | |||
| @@ -0,0 +1,81 @@ | |||
| /*******************************************************************************/ | |||
| /* 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. */ | |||
| /*******************************************************************************/ | |||
| #pragma once | |||
| #include "Module.H" | |||
| #include <vector> | |||
| #include "JACK/Port.H" | |||
| class Fl_Valuator; | |||
| class Controller_Module : public Module | |||
| { | |||
| static void update_cb ( void *v ); | |||
| void update_cb ( void ); | |||
| bool _pad; | |||
| volatile float control_value; | |||
| public: | |||
| enum Mode { GUI, CV, OSC, MIDI }; | |||
| Mode mode ( void ) const { return _mode; } | |||
| void mode ( Mode v ) { _mode = v; } | |||
| Controller_Module ( int W, int H, const char *L=0 ); | |||
| virtual ~Controller_Module ( ); | |||
| const char *name ( void ) const { return "Controller"; } | |||
| int can_support_inputs ( int n ) { return 0; } | |||
| bool configure_inputs ( int n ) { return false; } | |||
| void pad ( bool v ) { _pad = v; } | |||
| static void cb_handle ( Fl_Widget *w, void *v ); | |||
| void cb_handle ( Fl_Widget *w ); | |||
| void connect_to ( Port *p ); | |||
| protected: | |||
| // virtual void draw ( void ); | |||
| virtual void process ( void ); | |||
| virtual void draw ( void ) | |||
| { | |||
| draw_box(); | |||
| Fl_Group::draw(); | |||
| } | |||
| virtual int handle ( int m ); | |||
| private: | |||
| std::vector<JACK::Port> jack_input; | |||
| Mode _mode; | |||
| Fl_Valuator *control; | |||
| }; | |||
| @@ -0,0 +1,179 @@ | |||
| /*******************************************************************************/ | |||
| /* Copyright (C) 2008 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. */ | |||
| /*******************************************************************************/ | |||
| /* a Digital Peak Meter, either horizontal or vertical. Color is a | |||
| gradient from min_color() to max_color(). box() is used to draw the | |||
| individual 'lights'. division() controls how many 'lights' there | |||
| are. value() is volume in dBFS */ | |||
| #include "DPM.H" | |||
| /* we cache the gradient for (probably excessive) speed */ | |||
| float DPM::_dim; | |||
| Fl_Color DPM::_gradient[128] = { (Fl_Color)-1 }; | |||
| Fl_Color DPM::_dim_gradient[128]; | |||
| #include <FL/Fl.H> | |||
| #include <FL/fl_draw.H> | |||
| #include <FL/Fl_Group.H> | |||
| #include <math.h> | |||
| #include <stdio.h> | |||
| DPM::DPM ( int X, int Y, int W, int H, const char *L ) : | |||
| Meter( X, Y, W, H, L ) | |||
| { | |||
| _last_drawn_hi_segment = 0; | |||
| pixels_per_segment( 4 ); | |||
| type( FL_VERTICAL ); | |||
| resize( X, Y, W, H ); | |||
| dim( 0.70f ); | |||
| /* initialize gradients */ | |||
| if ( DPM::_gradient[ 0 ] == -1 ) | |||
| DPM::blend( FL_GREEN, FL_RED ); | |||
| box( FL_ROUNDED_BOX ); | |||
| } | |||
| /* which marks to draw beside meter */ | |||
| const int marks [] = { -70, -50, -40, -30, -20, -10, -3, 0, 4 }; | |||
| void | |||
| DPM::draw_label ( void ) | |||
| { | |||
| /* dirty hack */ | |||
| if ( parent()->child( 0 ) == this ) | |||
| { | |||
| fl_font( FL_TIMES, 8 ); | |||
| fl_color( FL_WHITE ); | |||
| /* draw marks */ | |||
| char pat[5]; | |||
| if ( type() == FL_HORIZONTAL ) | |||
| { | |||
| for ( int i = sizeof( marks ) / sizeof( marks[0] ); i-- ; ) | |||
| { | |||
| sprintf( pat, "%d", marks[ i ] ); | |||
| int v = w() * deflection( (float)marks[ i ] ); | |||
| fl_draw( pat, x() + v, (y() + h() + 8), 19, 8, (Fl_Align) (FL_ALIGN_RIGHT | FL_ALIGN_TOP) ); | |||
| } | |||
| } | |||
| else | |||
| { | |||
| for ( int i = sizeof( marks ) / sizeof( marks[0] ); i-- ; ) | |||
| { | |||
| sprintf( pat, "%d", marks[ i ] ); | |||
| int v = h() * deflection( (float)marks[ i ] ); | |||
| fl_draw( pat, x() - 20, (y() + h() - 8) - v, 19, 8, (Fl_Align) (FL_ALIGN_RIGHT | FL_ALIGN_TOP) ); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| void | |||
| DPM::resize ( int X, int Y, int W, int H ) | |||
| { | |||
| if ( type() == FL_HORIZONTAL ) | |||
| _segments = W / _pixels_per_segment; | |||
| else | |||
| _segments = H / _pixels_per_segment; | |||
| Fl_Widget::resize( X, Y, W, H ); | |||
| } | |||
| void | |||
| DPM::draw ( void ) | |||
| { | |||
| int v = pos( value() ); | |||
| int pv = pos( peak() ); | |||
| int bh = h() / _segments; | |||
| int bw = w() / _segments; | |||
| if ( damage() == FL_DAMAGE_ALL ) | |||
| draw_label(); | |||
| const int active = active_r(); | |||
| int hi, lo; | |||
| /* only draw as many segments as necessary */ | |||
| if ( damage() == FL_DAMAGE_USER1 ) | |||
| { | |||
| if ( _last_drawn_hi_segment > pos( value() ) ) | |||
| { | |||
| hi = _last_drawn_hi_segment; | |||
| lo = v; | |||
| } | |||
| else | |||
| { | |||
| hi = v; | |||
| lo = _last_drawn_hi_segment; | |||
| } | |||
| } | |||
| else | |||
| { | |||
| lo = 0; | |||
| hi = _segments; | |||
| } | |||
| _last_drawn_hi_segment = hi; | |||
| for ( int p = hi; p > lo; p-- ) | |||
| { | |||
| Fl_Color c = DPM::div_color( p ); | |||
| if ( p > v && p != pv ) | |||
| c = dim_div_color( p ); | |||
| if ( ! active ) | |||
| c = fl_inactive( c ); | |||
| if ( _pixels_per_segment < 4 ) | |||
| { | |||
| if ( type() == FL_HORIZONTAL ) | |||
| fl_rectf( x() + (p * bw), y(), bw, h(), c ); | |||
| else | |||
| fl_rectf( x(), y() + h() - (p * bh), w(), bh, c ); | |||
| } | |||
| else | |||
| { | |||
| if ( type() == FL_HORIZONTAL ) | |||
| fl_draw_box( box(), x() + (p * bw), y(), bw, h(), c ); | |||
| else | |||
| fl_draw_box( box(), x(), y() + h() - (p * bh), w(), bh, c ); | |||
| } | |||
| /* fl_color( c ); */ | |||
| /* fl_rectf( x(), y() + h() - (p * bh), w(), bh ); */ | |||
| /* fl_color( FL_BLACK ); */ | |||
| /* fl_rect( x(), y() + h() - (p * bh), w(), bh ); */ | |||
| } | |||
| } | |||
| @@ -0,0 +1,81 @@ | |||
| /*******************************************************************************/ | |||
| /* Copyright (C) 2008 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. */ | |||
| /*******************************************************************************/ | |||
| #pragma once | |||
| #include <FL/Fl_Valuator.H> // for FL_HORIZONTAL and FL_VERTICAL | |||
| #include "Meter.H" | |||
| class DPM : public Meter | |||
| { | |||
| int _segments; | |||
| int _pixels_per_segment; | |||
| int _last_drawn_hi_segment; | |||
| int pos ( float v ) | |||
| { | |||
| return deflection( v ) * _segments; | |||
| } | |||
| static float _dim; | |||
| static Fl_Color _gradient[]; | |||
| static Fl_Color _dim_gradient[]; | |||
| Fl_Color | |||
| div_color ( int i ) | |||
| { | |||
| return _gradient[ i * 127 / _segments ]; | |||
| } | |||
| Fl_Color | |||
| dim_div_color ( int i ) | |||
| { | |||
| return _dim_gradient[ i * 127 / _segments ]; | |||
| } | |||
| protected: | |||
| virtual void draw_label ( void ); | |||
| virtual void draw ( void ); | |||
| virtual void resize ( int, int, int, int ); | |||
| public: | |||
| DPM ( int X, int Y, int W, int H, const char *L = 0 ); | |||
| // void value ( float v ) { if ( pos( v ) != pos( value() ) ) redraw(); Meter::value( v ) } | |||
| void pixels_per_segment ( int v ) { _pixels_per_segment = v; } | |||
| float dim ( void ) const { return _dim; } | |||
| void dim ( float v ) { _dim = v; redraw(); } | |||
| static | |||
| void | |||
| blend ( Fl_Color min, Fl_Color max ) | |||
| { | |||
| for ( int i = 128; i-- ; ) | |||
| _gradient[ i ] = fl_color_average( max, min, i / (float)128 ); | |||
| for ( int i = 128; i-- ; ) | |||
| _dim_gradient[ i ] = fl_color_average( FL_BLACK, _gradient[ i ], _dim ); | |||
| } | |||
| }; | |||
| @@ -0,0 +1,147 @@ | |||
| /*******************************************************************************/ | |||
| /* Copyright (C) 2008 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. */ | |||
| /*******************************************************************************/ | |||
| #include "Engine.H" | |||
| #include "../Mixer.H" // for process() | |||
| /* This is the home of the JACK process callback */ | |||
| // #include "const.h" | |||
| #include "util/debug.h" | |||
| #include "util/Thread.H" | |||
| Engine::Engine ( ) : _thread( "RT" ) | |||
| { | |||
| _buffers_dropped = 0; | |||
| } | |||
| Engine::~Engine ( ) | |||
| { | |||
| } | |||
| /*************/ | |||
| /* Callbacks */ | |||
| /*************/ | |||
| /* THREAD: RT */ | |||
| /** This is the jack xrun callback */ | |||
| int | |||
| Engine::xrun ( void ) | |||
| { | |||
| return 0; | |||
| } | |||
| /* THREAD: RT */ | |||
| void | |||
| Engine::freewheel ( bool starting ) | |||
| { | |||
| if ( starting ) | |||
| DMESSAGE( "entering freewheeling mode" ); | |||
| else | |||
| DMESSAGE( "leaving freewheeling mode" ); | |||
| } | |||
| /* THREAD: RT (non-RT) */ | |||
| int | |||
| Engine::buffer_size ( nframes_t nframes ) | |||
| { | |||
| // timeline->resize_buffers( nframes ); | |||
| return 0; | |||
| } | |||
| int Engine::sync ( jack_transport_state_t state, jack_position_t *pos ) | |||
| { | |||
| } | |||
| void | |||
| Engine::timebase ( jack_transport_state_t state, jack_nframes_t nframes, jack_position_t *pos, int new_pos ) | |||
| { | |||
| } | |||
| void | |||
| Engine::timebase ( jack_transport_state_t state, jack_nframes_t nframes, jack_position_t *pos ) | |||
| { | |||
| } | |||
| /* THREAD: RT */ | |||
| int | |||
| Engine::process ( nframes_t nframes ) | |||
| { | |||
| /* FIXME: wrong place for this */ | |||
| _thread.set( "RT" ); | |||
| if ( freewheeling() ) | |||
| { | |||
| /* /\* freewheeling mode/export. We're actually running */ | |||
| /* non-RT. Assume that everything is quiescent, locking is */ | |||
| /* unecessary and do I/O synchronously *\/ */ | |||
| /* if ( timeline ) */ | |||
| /* timeline->process( nframes ); */ | |||
| /* /\* because we're going faster than realtime. *\/ */ | |||
| /* timeline->wait_for_buffers(); */ | |||
| } | |||
| else | |||
| { | |||
| if ( ! trylock() ) | |||
| { | |||
| /* the data structures we need to access here (tracks and | |||
| * their ports, but not track contents) may be in an | |||
| * inconsistent state at the moment. Just punt and drop this | |||
| * buffer. */ | |||
| ++_buffers_dropped; | |||
| return 0; | |||
| } | |||
| /* handle chicken/egg problem */ | |||
| if ( mixer ) | |||
| /* this will initiate the process() call graph for the various | |||
| * number and types of tracks, which will in turn send data out | |||
| * the appropriate ports. */ | |||
| mixer->process( nframes ); | |||
| unlock(); | |||
| } | |||
| return 0; | |||
| } | |||
| /* TRHEAD: RT */ | |||
| void | |||
| Engine::thread_init ( void ) | |||
| { | |||
| _thread.set( "RT" ); | |||
| } | |||
| /* THREAD: RT */ | |||
| void | |||
| Engine::shutdown ( void ) | |||
| { | |||
| } | |||
| @@ -0,0 +1,66 @@ | |||
| /*******************************************************************************/ | |||
| /* Copyright (C) 2008 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. */ | |||
| /*******************************************************************************/ | |||
| #pragma once | |||
| #include "util/Mutex.H" | |||
| class Port; | |||
| #include "JACK/Client.H" | |||
| #include "Thread.H" | |||
| class Engine : public JACK::Client, public Mutex | |||
| { | |||
| Thread _thread; /* only used for thread checking */ | |||
| int _buffers_dropped; /* buffers dropped because of locking */ | |||
| /* int _buffers_dropped; /\* buffers dropped because of locking *\/ */ | |||
| void shutdown ( void ); | |||
| int process ( nframes_t nframes ); | |||
| int sync ( jack_transport_state_t state, jack_position_t *pos ); | |||
| int xrun ( void ); | |||
| void timebase ( jack_transport_state_t state, jack_nframes_t nframes, jack_position_t *pos ); | |||
| void timebase ( jack_transport_state_t state, jack_nframes_t nframes, jack_position_t *pos, int new_pos ); | |||
| void freewheel ( bool yes ); | |||
| int buffer_size ( nframes_t nframes ); | |||
| void thread_init ( void ); | |||
| Engine ( const Engine &rhs ); | |||
| Engine & operator = ( const Engine &rhs ); | |||
| void request_locate ( nframes_t frame ); | |||
| private: | |||
| friend class Port; | |||
| friend class Transport; | |||
| public: | |||
| Engine ( ); | |||
| ~Engine ( ); | |||
| int dropped ( void ) const { return _buffers_dropped; } | |||
| }; | |||
| extern Engine * engine; | |||
| @@ -0,0 +1,98 @@ | |||
| /*******************************************************************************/ | |||
| /* 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. */ | |||
| /*******************************************************************************/ | |||
| #include "Gain_Module.H" | |||
| #include <FL/Fl_Single_Window.H> | |||
| #include <math.h> | |||
| #include <dsp.h> | |||
| Gain_Module::Gain_Module ( int W, int H, const char *L ) | |||
| : Module ( W, 24, L ) | |||
| { | |||
| add_port( Port( this, Port::INPUT, Port::AUDIO ) ); | |||
| add_port( Port( this, Port::OUTPUT, Port::AUDIO ) ); | |||
| Port p( this, Port::INPUT, Port::CONTROL, "gain" ); | |||
| p.hints.type = Port::Hints::LOGARITHMIC; | |||
| p.hints.ranged = true; | |||
| p.hints.minimum = 0.0f; | |||
| // p.hints.maximum = HUGE; | |||
| p.hints.maximum = 10.0f; | |||
| p.hints.default_value = 1.0f; | |||
| p.connect_to( new float ); | |||
| p.control_value( 1.0f ); | |||
| add_port( p ); | |||
| color( FL_BLACK ); | |||
| end(); | |||
| } | |||
| Gain_Module::~Gain_Module ( ) | |||
| { | |||
| } | |||
| bool | |||
| Gain_Module::configure_inputs ( int n ) | |||
| { | |||
| audio_input.clear(); | |||
| audio_output.clear(); | |||
| // control_input.clear(); | |||
| for ( int i = 0; i < n; ++i ) | |||
| { | |||
| add_port( Port( this, Port::INPUT, Port::AUDIO ) ); | |||
| add_port( Port( this, Port::OUTPUT, Port::AUDIO ) ); | |||
| // add_port( Port( this, Port::INPUT, Port::CONTROL ) ); | |||
| /* Port p( Port::OUTPUT, Port::CONTROL, "dB level" ); */ | |||
| /* p.hints.type = Port::Hints::LOGARITHMIC; */ | |||
| /* add_port( p ); */ | |||
| } | |||
| return true; | |||
| } | |||
| void | |||
| Gain_Module::process ( void ) | |||
| { | |||
| if ( control_input[0].connected() ) | |||
| { | |||
| float g = control_input[0].control_value(); | |||
| for ( int i = audio_input.size(); i--; ) | |||
| { | |||
| if ( audio_input[i].connected() && audio_output[i].connected() ) | |||
| { | |||
| buffer_apply_gain( (sample_t*)audio_input[i].buffer(), nframes(), g ); | |||
| /* buffer_copy_and_apply_gain( (sample_t*)audio_output[0].buffer(), */ | |||
| /* (sample_t*)audio_input[0].buffer(), */ | |||
| /* nframes(), */ | |||
| /* g ); */ | |||
| } | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,40 @@ | |||
| /*******************************************************************************/ | |||
| /* 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. */ | |||
| /*******************************************************************************/ | |||
| #pragma once | |||
| #include "Module.H" | |||
| class Gain_Module : public Module | |||
| { | |||
| public: | |||
| Gain_Module ( int W, int H, const char *L=0 ); | |||
| virtual ~Gain_Module ( ); | |||
| const char *name ( void ) const { return "Gain"; } | |||
| int can_support_inputs ( int n ) { return n; } | |||
| bool configure_inputs ( int n ); | |||
| protected: | |||
| virtual void process ( void ); | |||
| }; | |||
| @@ -0,0 +1,187 @@ | |||
| /*******************************************************************************/ | |||
| /* 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. */ | |||
| /*******************************************************************************/ | |||
| #include "JACK_Module.H" | |||
| #include <FL/Fl_Single_Window.H> | |||
| #include "Engine/Engine.H" | |||
| #include "dsp.h" | |||
| #include <string.h> | |||
| #include "Chain.H" | |||
| JACK_Module::JACK_Module ( int W, int H, const char *L ) | |||
| : Module ( W, 24, L ) | |||
| { | |||
| /* FIXME: how do Controls find out that a connected value has changed? How does this work in ladspa? */ | |||
| { | |||
| Port p( this, Port::INPUT, Port::CONTROL, "Inputs" ); | |||
| p.hints.type = Port::Hints::INTEGER; | |||
| p.hints.minimum = 0; | |||
| p.hints.maximum = 6; | |||
| p.connect_to( new float ); | |||
| p.control_value_no_callback( 0 ); | |||
| add_port( p ); | |||
| } | |||
| { | |||
| Port p( this, Port::INPUT, Port::CONTROL, "Outputs" ); | |||
| p.hints.type = Port::Hints::INTEGER; | |||
| p.hints.minimum = 0; | |||
| p.hints.maximum = 6; | |||
| p.connect_to( new float ); | |||
| p.control_value_no_callback( 0 ); | |||
| add_port( p ); | |||
| } | |||
| end(); | |||
| } | |||
| int | |||
| JACK_Module::can_support_inputs ( int n ) | |||
| { | |||
| return audio_output.size(); | |||
| } | |||
| bool | |||
| JACK_Module::configure_inputs ( int n ) | |||
| { | |||
| int on = audio_input.size(); | |||
| if ( n > on ) | |||
| { | |||
| for ( int i = on; i < n; ++i ) | |||
| { | |||
| JACK::Port po( engine->client(), JACK::Port::Output, chain()->name(), i ); | |||
| if ( po.valid() ) | |||
| { | |||
| add_port( Port( this, Port::INPUT, Port::AUDIO ) ); | |||
| jack_output.push_back( po ); | |||
| } | |||
| } | |||
| } | |||
| else | |||
| { | |||
| for ( int i = on; i > n; --i ) | |||
| { | |||
| audio_input.back().disconnect(); | |||
| audio_input.pop_back(); | |||
| jack_output.back().shutdown(); | |||
| jack_output.pop_back(); | |||
| } | |||
| } | |||
| control_input[0].control_value_no_callback( n ); | |||
| return true; | |||
| } | |||
| bool | |||
| JACK_Module::configure_outputs ( int n ) | |||
| { | |||
| int on = audio_output.size(); | |||
| if ( n > on ) | |||
| { | |||
| for ( int i = on; i < n; ++i ) | |||
| { | |||
| JACK::Port po( engine->client(), JACK::Port::Input, chain()->name(), i ); | |||
| if ( po.valid() ) | |||
| { | |||
| add_port( Port( this, Port::OUTPUT, Port::AUDIO ) ); | |||
| jack_input.push_back( po ); | |||
| } | |||
| } | |||
| } | |||
| else | |||
| { | |||
| for ( int i = on; i > n; --i ) | |||
| { | |||
| audio_output.back().disconnect(); | |||
| audio_output.pop_back(); | |||
| jack_input.back().shutdown(); | |||
| jack_input.pop_back(); | |||
| } | |||
| } | |||
| control_input[1].control_value_no_callback( n ); | |||
| return true; | |||
| } | |||
| bool | |||
| JACK_Module::initialize ( void ) | |||
| { | |||
| // configure_inputs( 1 ); | |||
| return true; | |||
| } | |||
| JACK_Module::~JACK_Module ( ) | |||
| { | |||
| configure_inputs( 0 ); | |||
| } | |||
| void | |||
| JACK_Module::handle_control_changed ( Port *p ) | |||
| { | |||
| if ( 0 == strcmp( p->name(), "Inputs" ) ) | |||
| { | |||
| DMESSAGE( "Adjusting number of inputs (JACK outputs)" ); | |||
| configure_inputs( p->control_value() ); | |||
| chain()->configure_ports(); | |||
| } | |||
| else if ( 0 == strcmp( p->name(), "Outputs" ) ) | |||
| { | |||
| DMESSAGE( "Adjusting number of outputs (JACK inputs)" ); | |||
| if ( chain()->can_configure_outputs( this, p->control_value() ) ) | |||
| { | |||
| configure_outputs( p->control_value() ); | |||
| chain()->configure_ports(); | |||
| } | |||
| } | |||
| } | |||
| void | |||
| JACK_Module::handle_chain_name_changed ( void ) | |||
| { | |||
| for ( unsigned int i = 0; i < jack_output.size(); ++i ) | |||
| jack_output[ i ].name( chain()->name(), i ); | |||
| for ( unsigned int i = 0; i < jack_input.size(); ++i ) | |||
| jack_input[ i ].name( chain()->name(), i ); | |||
| } | |||
| void | |||
| JACK_Module::process ( void ) | |||
| { | |||
| for ( int i = 0; i < audio_input.size(); ++i ) | |||
| if ( audio_input[i].connected() ) | |||
| buffer_copy( (sample_t*)jack_output[i].buffer( nframes() ), (sample_t*)audio_input[i].buffer(), nframes() ); | |||
| for ( int i = 0; i < audio_output.size(); ++i ) | |||
| if ( audio_output[i].connected() ) | |||
| buffer_copy( (sample_t*)audio_output[i].buffer(), (sample_t*)jack_input[i].buffer( nframes() ), nframes() ); | |||
| } | |||
| @@ -0,0 +1,58 @@ | |||
| /*******************************************************************************/ | |||
| /* 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. */ | |||
| /*******************************************************************************/ | |||
| #pragma once | |||
| #include "Module.H" | |||
| #include "JACK/Port.H" | |||
| #include <vector> | |||
| class JACK_Module : public Module | |||
| { | |||
| const char *_strip_name; | |||
| std::vector<JACK::Port> jack_input; | |||
| std::vector<JACK::Port> jack_output; | |||
| public: | |||
| JACK_Module ( int W, int H, const char *L=0 ); | |||
| virtual ~JACK_Module ( ); | |||
| const char *name ( void ) const { return "JACK"; } | |||
| void strip_name ( const char *name ) { _strip_name = name; } | |||
| bool initialize ( void ); | |||
| int can_support_inputs ( int ); | |||
| bool configure_inputs ( int n ); | |||
| bool configure_outputs ( int n ); | |||
| void add_output ( void ); | |||
| void handle_control_changed ( Port *p ); | |||
| void handle_chain_name_changed (); | |||
| protected: | |||
| virtual void process ( void ); | |||
| }; | |||
| @@ -0,0 +1,791 @@ | |||
| // | |||
| // LADSPAInfo.C - Class for indexing information on LADSPA Plugins | |||
| // | |||
| // Copyleft (C) 2002 Mike Rawes <myk@waxfrenzy.org> | |||
| // | |||
| // 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; if not, write to the Free Software | |||
| // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |||
| // | |||
| // #include <config.h> | |||
| #include <vector> | |||
| #include <string> | |||
| #include <list> | |||
| #include <map> | |||
| #include <iostream> | |||
| #include <sstream> | |||
| #include <algorithm> | |||
| #include <stdio.h> | |||
| #include <cstring> | |||
| #include <cstdlib> | |||
| #include <sys/types.h> | |||
| #include <sys/stat.h> | |||
| #include <dirent.h> | |||
| #include <dlfcn.h> | |||
| #include <ladspa.h> | |||
| #define HAVE_LIBLRDF 1 | |||
| #ifdef HAVE_LIBLRDF | |||
| #include <lrdf.h> | |||
| #endif | |||
| #include "LADSPAInfo.h" | |||
| using namespace std; | |||
| LADSPAInfo::LADSPAInfo(bool override, | |||
| const char *path_list) | |||
| { | |||
| if (strlen(path_list) > 0) { | |||
| m_ExtraPaths = strdup(path_list); | |||
| } else { | |||
| m_ExtraPaths = NULL; | |||
| } | |||
| m_LADSPAPathOverride = override; | |||
| RescanPlugins(); | |||
| } | |||
| LADSPAInfo::~LADSPAInfo() | |||
| { | |||
| CleanUp(); | |||
| } | |||
| void | |||
| LADSPAInfo::RescanPlugins(void) | |||
| { | |||
| // Clear out what we've got | |||
| CleanUp(); | |||
| if (!m_LADSPAPathOverride) { | |||
| // Get $LADPSA_PATH, if available | |||
| char *ladspa_path = getenv("LADSPA_PATH"); | |||
| if (ladspa_path) { | |||
| ScanPathList(ladspa_path, &LADSPAInfo::ExaminePluginLibrary); | |||
| } else { | |||
| cerr << "WARNING: LADSPA_PATH environment variable not set" << endl; | |||
| cerr << " Assuming /usr/lib/ladspa:/usr/local/lib/ladspa" << endl; | |||
| ScanPathList("/usr/lib/ladspa:/usr/local/lib/ladspa", &LADSPAInfo::ExaminePluginLibrary); | |||
| } | |||
| } | |||
| // Check any supplied extra paths | |||
| if (m_ExtraPaths) { | |||
| ScanPathList(m_ExtraPaths, &LADSPAInfo::ExaminePluginLibrary); | |||
| } | |||
| // Do we have any plugins now? | |||
| if (m_Plugins.size() == 0) { | |||
| cerr << "WARNING: No plugins found" << endl; | |||
| } else { | |||
| cerr << m_Plugins.size() << " plugins found in " << m_Libraries.size() << " libraries" << endl; | |||
| #ifdef HAVE_LIBLRDF | |||
| // Got some plugins. Now search for RDF data | |||
| lrdf_init(); | |||
| char *rdf_path = getenv("LADSPA_RDF_PATH"); | |||
| if (rdf_path) { | |||
| // Examine rdf info | |||
| ScanPathList(rdf_path, &LADSPAInfo::ExamineRDFFile); | |||
| } else { | |||
| cerr << "WARNING: LADSPA_RDF_PATH environment variable not set" << endl; | |||
| cerr << " Assuming /usr/share/ladspa/rdf:/usr/local/share/ladspa/rdf" << endl; | |||
| // Examine rdf info | |||
| ScanPathList("/usr/share/ladspa/rdf:/usr/local/share/ladspa/rdf", &LADSPAInfo::ExamineRDFFile); | |||
| } | |||
| MetadataRDFDescend(LADSPA_BASE "Plugin", 0); | |||
| // See which plugins were not added to an rdf group, and add them | |||
| // all into the top level 'LADSPA' one | |||
| list<unsigned long> rdf_p; | |||
| // Get indices of plugins added to groups | |||
| for (vector<RDFURIInfo>::iterator ri = m_RDFURIs.begin(); ri != m_RDFURIs.end(); ri++) { | |||
| rdf_p.insert(rdf_p.begin(), ri->Plugins.begin(), ri->Plugins.end()); | |||
| } | |||
| // Add all uncategorized plugins to top level group, subclassed by their | |||
| // library's basename. | |||
| rdf_p.unique(); | |||
| rdf_p.sort(); | |||
| unsigned long last_p = 0; | |||
| for (list<unsigned long>::iterator p = rdf_p.begin(); p != rdf_p.end(); p++) { | |||
| if ((*p - last_p) > 1) { | |||
| for (unsigned long i = last_p + 1; i < *p; i++) { | |||
| // URI 0 is top-level "LADSPA" group | |||
| m_RDFURIs[0].Plugins.push_back(i); | |||
| } | |||
| } | |||
| last_p = *p; | |||
| } | |||
| while (++last_p < m_Plugins.size()) { | |||
| // URI 0 is top-level "LADSPA" group | |||
| m_RDFURIs[0].Plugins.push_back(last_p); | |||
| } | |||
| lrdf_cleanup(); | |||
| #else | |||
| // No RDF. Add all plugins to top-level group | |||
| RDFURIInfo ri; | |||
| ri.URI = ""; | |||
| ri.Label = "LADSPA"; | |||
| m_RDFURIs.push_back(ri); | |||
| m_RDFLabelLookup["LADSPA"] = 0; | |||
| for (unsigned long i = 0; i < m_Plugins.size(); i++) { | |||
| // Add plugin index | |||
| m_RDFURIs[0].Plugins.push_back(i); | |||
| } | |||
| #endif | |||
| } | |||
| } | |||
| void | |||
| LADSPAInfo::UnloadAllLibraries(void) | |||
| { | |||
| // Blank descriptors | |||
| for (vector<PluginInfo>::iterator i = m_Plugins.begin(); | |||
| i != m_Plugins.end(); i++) { | |||
| if (i->Descriptor) i->Descriptor = NULL; | |||
| } | |||
| // Unload DLLs, | |||
| for (vector<LibraryInfo>::iterator i = m_Libraries.begin(); | |||
| i != m_Libraries.end(); i++) { | |||
| if (i->Handle) { | |||
| dlclose(i->Handle); | |||
| i->Handle = NULL; | |||
| } | |||
| i->RefCount = 0; | |||
| } | |||
| } | |||
| const LADSPA_Descriptor * | |||
| LADSPAInfo::GetDescriptorByID(unsigned long unique_id) | |||
| { | |||
| if (m_IDLookup.find(unique_id) == m_IDLookup.end()) { | |||
| cerr << "LADSPA Plugin ID " << unique_id << " not found!" << endl; | |||
| return NULL; | |||
| } | |||
| // Got plugin index | |||
| unsigned long plugin_index = m_IDLookup[unique_id]; | |||
| PluginInfo *pi = &(m_Plugins[plugin_index]); | |||
| LibraryInfo *li = &(m_Libraries[pi->LibraryIndex]); | |||
| if (!(pi->Descriptor)) { | |||
| LADSPA_Descriptor_Function desc_func = GetDescriptorFunctionForLibrary(pi->LibraryIndex); | |||
| if (desc_func) pi->Descriptor = desc_func(pi->Index); | |||
| } | |||
| if (pi->Descriptor) { | |||
| // Success, so increment ref counter for library | |||
| li->RefCount++; | |||
| } | |||
| return pi->Descriptor; | |||
| } | |||
| void | |||
| LADSPAInfo::DiscardDescriptorByID(unsigned long unique_id) | |||
| { | |||
| if (m_IDLookup.find(unique_id) == m_IDLookup.end()) { | |||
| cerr << "LADSPA Plugin ID " << unique_id << " not found!" << endl; | |||
| } else { | |||
| // Get plugin index | |||
| unsigned long plugin_index = m_IDLookup[unique_id]; | |||
| PluginInfo *pi = &(m_Plugins[plugin_index]); | |||
| LibraryInfo *li = &(m_Libraries[pi->LibraryIndex]); | |||
| pi->Descriptor = NULL; | |||
| // Decrement reference counter for library, and unload if last | |||
| if (li->RefCount > 0) { | |||
| li->RefCount--; | |||
| if (li->RefCount == 0) { | |||
| // Unload library | |||
| dlclose(li->Handle); | |||
| li->Handle = NULL; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| // **************************************************************************** | |||
| // ** SSM Specific Functions ** | |||
| // **************************************************************************** | |||
| unsigned long | |||
| LADSPAInfo::GetIDFromFilenameAndLabel(std::string filename, | |||
| std::string label) | |||
| { | |||
| bool library_loaded = false; | |||
| if (m_FilenameLookup.find(filename) == m_FilenameLookup.end()) { | |||
| cerr << "LADSPA Library " << filename << " not found!" << endl; | |||
| return 0; | |||
| } | |||
| unsigned long library_index = m_FilenameLookup[filename]; | |||
| if (!(m_Libraries[library_index].Handle)) library_loaded = true; | |||
| LADSPA_Descriptor_Function desc_func = GetDescriptorFunctionForLibrary(library_index); | |||
| if (!desc_func) { | |||
| return 0; | |||
| } | |||
| // Search for label in library | |||
| const LADSPA_Descriptor *desc; | |||
| for (unsigned long i = 0; (desc = desc_func(i)) != NULL; i++) { | |||
| string l = desc->Label; | |||
| if (l == label) { | |||
| // If we had to load the library, unload it | |||
| unsigned long id = desc->UniqueID; | |||
| if (library_loaded) { | |||
| dlclose(m_Libraries[library_index].Handle); | |||
| m_Libraries[library_index].Handle = NULL; | |||
| } | |||
| return id; | |||
| } | |||
| } | |||
| cerr << "Plugin " << label << " not found in library " << filename << endl; | |||
| return 0; | |||
| } | |||
| const vector<LADSPAInfo::PluginEntry> | |||
| LADSPAInfo::GetMenuList(void) | |||
| { | |||
| m_SSMMenuList.clear(); | |||
| DescendGroup("", "LADSPA", 1); | |||
| return m_SSMMenuList; | |||
| } | |||
| unsigned long | |||
| LADSPAInfo::GetPluginListEntryByID(unsigned long unique_id) | |||
| { | |||
| unsigned long j = 0; | |||
| for (vector<PluginEntry>::iterator i = m_SSMMenuList.begin(); | |||
| i != m_SSMMenuList.end(); i++, j++) { | |||
| if (i->UniqueID == unique_id) return j; | |||
| } | |||
| return m_SSMMenuList.size(); | |||
| } | |||
| // **************************************************************************** | |||
| // ** Private Member Functions ** | |||
| // **************************************************************************** | |||
| // Build a list of plugins by group, suitable for SSM LADSPA Plugin drop-down | |||
| // The top-level "LADSPA" group is not included | |||
| void | |||
| LADSPAInfo::DescendGroup(string prefix, | |||
| const string group, | |||
| unsigned int depth) | |||
| { | |||
| list<string> groups = GetSubGroups(group); | |||
| if (prefix.length() > 0) { | |||
| // Add an explicit '/' as we're creating sub-menus from groups | |||
| prefix += "/"; | |||
| } | |||
| for (list<string>::iterator g = groups.begin(); g != groups.end(); g++) { | |||
| string name; | |||
| // Escape '/' and '|' characters | |||
| unsigned int x = g->find_first_of("/|"); | |||
| if (x == string::npos) { | |||
| name = *g; | |||
| } else { | |||
| unsigned int last_x = 0; | |||
| while (x < string::npos) { | |||
| name += g->substr(last_x, x - last_x) + '\\' + (*g)[x]; | |||
| last_x = x + 1; | |||
| x = g->find_first_of("/|", x + 1); | |||
| } | |||
| name += g->substr(last_x, x - last_x); | |||
| } | |||
| DescendGroup(prefix + name, *g, depth + 1); | |||
| } | |||
| if (m_RDFLabelLookup.find(group) != m_RDFLabelLookup.end()) { | |||
| unsigned long uri_index = m_RDFLabelLookup[group]; | |||
| // Create group for unclassified plugins | |||
| if (prefix.length() == 0) { | |||
| prefix = "Unclassified/"; | |||
| depth = depth + 1; | |||
| } | |||
| // Temporary list (for sorting the plugins by name) | |||
| list<PluginEntry> plugins; | |||
| for (vector<unsigned long>::iterator p = m_RDFURIs[uri_index].Plugins.begin(); | |||
| p != m_RDFURIs[uri_index].Plugins.end(); p++) { | |||
| PluginInfo *pi = &(m_Plugins[*p]); | |||
| string name; | |||
| // Escape '/' and '|' characters | |||
| unsigned int x = pi->Name.find_first_of("/|"); | |||
| if (x == string::npos) { | |||
| name = pi->Name; | |||
| } else { | |||
| unsigned int last_x = 0; | |||
| while (x < string::npos) { | |||
| name += pi->Name.substr(last_x, x - last_x) + '\\' + pi->Name[x]; | |||
| last_x = x + 1; | |||
| x = pi->Name.find_first_of("/|", x + 1); | |||
| } | |||
| name += pi->Name.substr(last_x, x - last_x); | |||
| } | |||
| PluginEntry pe; | |||
| pe.Depth = depth; | |||
| pe.UniqueID = pi->UniqueID; | |||
| pe.Name = prefix + name; | |||
| plugins.push_back(pe); | |||
| } | |||
| plugins.sort(); | |||
| // Deal with duplicates by numbering them | |||
| for (list<PluginEntry>::iterator i = plugins.begin(); | |||
| i != plugins.end(); ) { | |||
| string name = i->Name; | |||
| i++; | |||
| unsigned long n = 2; | |||
| while ((i != plugins.end()) && (i->Name == name)) { | |||
| stringstream s; | |||
| s << n; | |||
| i->Name = name + " (" + s.str() + ")"; | |||
| n++; | |||
| i++; | |||
| } | |||
| } | |||
| // Add all ordered entries to the Menu List | |||
| // This ensures that plugins appear after groups | |||
| for (list<PluginEntry>::iterator p = plugins.begin(); p != plugins.end(); p++) { | |||
| m_SSMMenuList.push_back(*p); | |||
| } | |||
| } | |||
| } | |||
| // Get list of groups that are within given group. The root group is | |||
| // always "LADSPA" | |||
| list<string> | |||
| LADSPAInfo::GetSubGroups(const string group) | |||
| { | |||
| list<string> groups; | |||
| unsigned long uri_index; | |||
| if (m_RDFLabelLookup.find(group) == m_RDFLabelLookup.end()) { | |||
| return groups; | |||
| } else { | |||
| uri_index = m_RDFLabelLookup[group]; | |||
| } | |||
| for (vector<unsigned long>::iterator sg = m_RDFURIs[uri_index].Children.begin(); | |||
| sg != m_RDFURIs[uri_index].Children.end(); sg++) { | |||
| groups.push_back(m_RDFURIs[*sg].Label); | |||
| } | |||
| groups.sort(); | |||
| return groups; | |||
| } | |||
| // Unload any loaded DLLs and clear vectors etc | |||
| void | |||
| LADSPAInfo::CleanUp(void) | |||
| { | |||
| m_MaxInputPortCount = 0; | |||
| m_IDLookup.clear(); | |||
| m_Plugins.clear(); | |||
| // Unload loaded dlls | |||
| for (vector<LibraryInfo>::iterator i = m_Libraries.begin(); | |||
| i != m_Libraries.end(); i++) { | |||
| if (i->Handle) dlclose(i->Handle); | |||
| } | |||
| m_Libraries.clear(); | |||
| m_Paths.clear(); | |||
| m_RDFURILookup.clear(); | |||
| m_RDFURIs.clear(); | |||
| if (m_ExtraPaths) { | |||
| free(m_ExtraPaths); | |||
| m_ExtraPaths = NULL; | |||
| } | |||
| } | |||
| // Given a colon-separated list of paths, examine the contents of each | |||
| // path, examining any regular files using the given member function, | |||
| // which currently can be: | |||
| // | |||
| // ExaminePluginLibrary - add plugin library info from plugins | |||
| // ExamineRDFFile - add plugin information from .rdf/.rdfs files | |||
| void | |||
| LADSPAInfo::ScanPathList(const char *path_list, | |||
| void (LADSPAInfo::*ExamineFunc)(const string, | |||
| const string)) | |||
| { | |||
| const char *start; | |||
| const char *end; | |||
| int extra; | |||
| char *path; | |||
| string basename; | |||
| DIR *dp; | |||
| struct dirent *ep; | |||
| struct stat sb; | |||
| // This does the same kind of thing as strtok, but strtok won't | |||
| // like the const | |||
| start = path_list; | |||
| while (*start != '\0') { | |||
| while (*start == ':') start++; | |||
| end = start; | |||
| while (*end != ':' && *end != '\0') end++; | |||
| if (end - start > 0) { | |||
| extra = (*(end - 1) == '/') ? 0 : 1; | |||
| path = (char *)malloc(end - start + 1 + extra); | |||
| if (path) { | |||
| strncpy(path, start, end - start); | |||
| if (extra == 1) path[end - start] = '/'; | |||
| path[end - start + extra] = '\0'; | |||
| dp = opendir(path); | |||
| if (!dp) { | |||
| cerr << "WARNING: Could not open path " << path << endl; | |||
| } else { | |||
| while ((ep = readdir(dp))) { | |||
| // Stat file to get type | |||
| basename = ep->d_name; | |||
| if (!stat((path + basename).c_str(), &sb)) { | |||
| // We only want regular files | |||
| if (S_ISREG(sb.st_mode)) (*this.*ExamineFunc)(path, basename); | |||
| } | |||
| } | |||
| closedir(dp); | |||
| } | |||
| free(path); | |||
| } | |||
| } | |||
| start = end; | |||
| } | |||
| } | |||
| // Check given file is a valid LADSPA Plugin library | |||
| // | |||
| // If so, add path, library and plugin info | |||
| // to the m_Paths, m_Libraries and m_Plugins vectors. | |||
| // | |||
| void | |||
| LADSPAInfo::ExaminePluginLibrary(const string path, | |||
| const string basename) | |||
| { | |||
| void *handle; | |||
| LADSPA_Descriptor_Function desc_func; | |||
| const LADSPA_Descriptor *desc; | |||
| string fullpath = path + basename; | |||
| // We're not executing any code, so be lazy about resolving symbols | |||
| handle = dlopen(fullpath.c_str(), RTLD_LAZY); | |||
| if (!handle) { | |||
| cerr << "WARNING: File " << fullpath | |||
| << " could not be examined" << endl; | |||
| cerr << "dlerror() output:" << endl; | |||
| cerr << dlerror() << endl; | |||
| } else { | |||
| // It's a DLL, so now see if it's a LADSPA plugin library | |||
| desc_func = (LADSPA_Descriptor_Function)dlsym(handle, | |||
| "ladspa_descriptor"); | |||
| if (!desc_func) { | |||
| // Is DLL, but not a LADSPA one | |||
| cerr << "WARNING: DLL " << fullpath | |||
| << " has no ladspa_descriptor function" << endl; | |||
| cerr << "dlerror() output:" << endl; | |||
| cerr << dlerror() << endl; | |||
| } else { | |||
| // Got ladspa_descriptor, so we can now get plugin info | |||
| bool library_added = false; | |||
| unsigned long i = 0; | |||
| desc = desc_func(i); | |||
| while (desc) { | |||
| // First, check that it's not a dupe | |||
| if (m_IDLookup.find(desc->UniqueID) != m_IDLookup.end()) { | |||
| unsigned long plugin_index = m_IDLookup[desc->UniqueID]; | |||
| unsigned long library_index = m_Plugins[plugin_index].LibraryIndex; | |||
| unsigned long path_index = m_Libraries[library_index].PathIndex; | |||
| cerr << "WARNING: Duplicated Plugin ID (" | |||
| << desc->UniqueID << ") found:" << endl; | |||
| cerr << " Plugin " << m_Plugins[plugin_index].Index | |||
| << " in library: " << m_Paths[path_index] | |||
| << m_Libraries[library_index].Basename | |||
| << " [First instance found]" << endl; | |||
| cerr << " Plugin " << i << " in library: " << fullpath | |||
| << " [Duplicate not added]" << endl; | |||
| } else { | |||
| if (CheckPlugin(desc)) { | |||
| // Add path if not already added | |||
| unsigned long path_index; | |||
| vector<string>::iterator p = find(m_Paths.begin(), m_Paths.end(), path); | |||
| if (p == m_Paths.end()) { | |||
| path_index = m_Paths.size(); | |||
| m_Paths.push_back(path); | |||
| } else { | |||
| path_index = p - m_Paths.begin(); | |||
| } | |||
| // Add library info if not already added | |||
| if (!library_added) { | |||
| LibraryInfo li; | |||
| li.PathIndex = path_index; | |||
| li.Basename = basename; | |||
| li.RefCount = 0; | |||
| li.Handle = NULL; | |||
| m_Libraries.push_back(li); | |||
| library_added = true; | |||
| } | |||
| // Add plugin info | |||
| PluginInfo pi; | |||
| pi.LibraryIndex = m_Libraries.size() - 1; | |||
| pi.Index = i; | |||
| pi.UniqueID = desc->UniqueID; | |||
| pi.Label = desc->Label; | |||
| pi.Name = desc->Name; | |||
| pi.Descriptor = NULL; | |||
| m_Plugins.push_back(pi); | |||
| // Find number of input ports | |||
| unsigned long in_port_count = 0; | |||
| for (unsigned long p = 0; p < desc->PortCount; p++) { | |||
| if (LADSPA_IS_PORT_INPUT(desc->PortDescriptors[p])) { | |||
| in_port_count++; | |||
| } | |||
| } | |||
| if (in_port_count > m_MaxInputPortCount) { | |||
| m_MaxInputPortCount = in_port_count; | |||
| } | |||
| // Add to index | |||
| m_IDLookup[desc->UniqueID] = m_Plugins.size() - 1; | |||
| } else { | |||
| cerr << "WARNING: Plugin " << desc->UniqueID << " not added" << endl; | |||
| } | |||
| } | |||
| desc = desc_func(++i); | |||
| } | |||
| } | |||
| dlclose(handle); | |||
| } | |||
| } | |||
| #ifdef HAVE_LIBLRDF | |||
| // Examine given RDF plugin meta-data file | |||
| void | |||
| LADSPAInfo::ExamineRDFFile(const std::string path, | |||
| const std::string basename) | |||
| { | |||
| string fileuri = "file://" + path + basename; | |||
| if (lrdf_read_file(fileuri.c_str())) { | |||
| cerr << "WARNING: File " << path + basename << " could not be parsed [Ignored]" << endl; | |||
| } | |||
| } | |||
| // Recursively add rdf information for plugins that have been | |||
| // found from scanning LADSPA_PATH | |||
| void | |||
| LADSPAInfo::MetadataRDFDescend(const char * uri, | |||
| unsigned long parent) | |||
| { | |||
| unsigned long this_uri_index; | |||
| // Check URI not already added | |||
| if (m_RDFURILookup.find(uri) == m_RDFURILookup.end()) { | |||
| // Not found | |||
| RDFURIInfo ri; | |||
| ri.URI = uri; | |||
| if (ri.URI == LADSPA_BASE "Plugin") { | |||
| // Add top level group as "LADSPA" | |||
| // This will always happen, even if there are no .rdf files read by liblrdf | |||
| // or if there is no liblrdf support | |||
| ri.Label = "LADSPA"; | |||
| } else { | |||
| char * label = lrdf_get_label(uri); | |||
| if (label) { | |||
| ri.Label = label; | |||
| } else { | |||
| ri.Label = "(No label)"; | |||
| } | |||
| } | |||
| // Add any instances found | |||
| lrdf_uris * instances = lrdf_get_instances(uri); | |||
| if (instances) { | |||
| for (long j = 0; j < instances->count; j++) { | |||
| unsigned long uid = lrdf_get_uid(instances->items[j]); | |||
| if (m_IDLookup.find(uid) != m_IDLookup.end()) { | |||
| ri.Plugins.push_back(m_IDLookup[uid]); | |||
| } | |||
| } | |||
| } | |||
| lrdf_free_uris(instances); | |||
| m_RDFURIs.push_back(ri); | |||
| this_uri_index = m_RDFURIs.size() - 1; | |||
| m_RDFURILookup[ri.URI] = this_uri_index; | |||
| m_RDFLabelLookup[ri.Label] = this_uri_index; | |||
| } else { | |||
| // Already added | |||
| this_uri_index = m_RDFURILookup[uri]; | |||
| } | |||
| // Only add parent - child info if this uri is NOT the first (root) uri | |||
| if (this_uri_index > 0) { | |||
| m_RDFURIs[this_uri_index].Parents.push_back(parent); | |||
| m_RDFURIs[parent].Children.push_back(this_uri_index); | |||
| } | |||
| lrdf_uris * uris = lrdf_get_subclasses(uri); | |||
| if (uris) { | |||
| for (long i = 0; i < uris->count; i++) { | |||
| MetadataRDFDescend(uris->items[i], this_uri_index); | |||
| } | |||
| } | |||
| lrdf_free_uris(uris); | |||
| } | |||
| #endif | |||
| bool | |||
| LADSPAInfo::CheckPlugin(const LADSPA_Descriptor *desc) | |||
| { | |||
| #define test(t, m) { \ | |||
| if (!(t)) { \ | |||
| cerr << m << endl; \ | |||
| return false; \ | |||
| } \ | |||
| } | |||
| test(desc->instantiate, "WARNING: Plugin has no instatiate function"); | |||
| test(desc->connect_port, "WARNING: Warning: Plugin has no connect_port funciton"); | |||
| test(desc->run, "WARNING: Plugin has no run function"); | |||
| test(!(desc->run_adding != 0 && desc->set_run_adding_gain == 0), | |||
| "WARNING: Plugin has run_adding but no set_run_adding_gain"); | |||
| test(!(desc->run_adding == 0 && desc->set_run_adding_gain != 0), | |||
| "WARNING: Plugin has set_run_adding_gain but no run_adding"); | |||
| test(desc->cleanup, "WARNING: Plugin has no cleanup function"); | |||
| test(!LADSPA_IS_INPLACE_BROKEN(desc->Properties), | |||
| "WARNING: Plugin cannot use in place processing"); | |||
| test(desc->PortCount, "WARNING: Plugin has no ports"); | |||
| return true; | |||
| } | |||
| LADSPA_Descriptor_Function | |||
| LADSPAInfo::GetDescriptorFunctionForLibrary(unsigned long library_index) | |||
| { | |||
| LibraryInfo *li = &(m_Libraries[library_index]); | |||
| if (!(li->Handle)) { | |||
| // Need full path | |||
| string fullpath = m_Paths[li->PathIndex]; | |||
| fullpath.append(li->Basename); | |||
| // Immediate symbol resolution, as plugin code is likely to be executed | |||
| li->Handle = dlopen(fullpath.c_str(), RTLD_NOW); | |||
| if (!(li->Handle)) { | |||
| // Plugin library changed since last path scan | |||
| cerr << "WARNING: Plugin library " << fullpath << " cannot be loaded" << endl; | |||
| cerr << "Rescan of plugins recommended" << endl; | |||
| cerr << "dlerror() output:" << endl; | |||
| cerr << dlerror() << endl; | |||
| return NULL; | |||
| } | |||
| } | |||
| // Got handle so now verify that it's a LADSPA plugin library | |||
| const LADSPA_Descriptor_Function desc_func = (LADSPA_Descriptor_Function)dlsym(li->Handle, | |||
| "ladspa_descriptor"); | |||
| if (!desc_func) { | |||
| // Is DLL, but not a LADSPA one (changed since last path scan?) | |||
| cerr << "WARNING: DLL " << m_Paths[li->PathIndex] << li->Basename | |||
| << " has no ladspa_descriptor function" << endl; | |||
| cerr << "Rescan of plugins recommended" << endl; | |||
| cerr << "dlerror() output:" << endl; | |||
| cerr << dlerror() << endl; | |||
| // Unload library | |||
| dlclose(li->Handle); | |||
| return NULL; | |||
| } | |||
| return desc_func; | |||
| } | |||
| @@ -0,0 +1,199 @@ | |||
| // | |||
| // LADSPAInfo.h - Header file for LADSPA Plugin info class | |||
| // | |||
| // Copyleft (C) 2002 Mike Rawes <myk@waxfrenzy.org> | |||
| // | |||
| // 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; if not, write to the Free Software | |||
| // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |||
| // | |||
| #ifndef __ladspa_info_h__ | |||
| #define __ladspa_info_h__ | |||
| // #include <config.h> | |||
| #include <string> | |||
| #include <vector> | |||
| #include <list> | |||
| #include <map> | |||
| #include <ladspa.h> | |||
| class LADSPAInfo | |||
| { | |||
| public: | |||
| // If override is false, examine $LADSPA_PATH | |||
| // Also examine supplied path list | |||
| // For all paths, add basic plugin information for later lookup, | |||
| // instantiation and so on. | |||
| LADSPAInfo(bool override = false, const char *path_list = ""); | |||
| // Unload all loaded plugins and clean up | |||
| ~LADSPAInfo(); | |||
| // ************************************************************************ | |||
| // Loading/Unloading plugin libraries | |||
| // | |||
| // At first, no library dlls are loaded. | |||
| // | |||
| // A plugin library may have more than one plugin descriptor. The | |||
| // descriptor is used to instantiate, activate, execute plugin instances. | |||
| // Administration of plugin instances are outwith the scope of this class, | |||
| // instead, descriptors are requested using GetDecriptorByID, and disposed | |||
| // of using DiscardDescriptorByID. | |||
| // | |||
| // Each library keeps a reference count of descriptors requested. A library | |||
| // is loaded when a descriptor is requested for the first time, and remains | |||
| // loaded until the number of discards matches the number of requests. | |||
| // Rescan all paths in $LADSPA_PATH, as per constructor. | |||
| // This will also unload all libraries, and make any descriptors that | |||
| // have not been discarded with DiscardDescriptorByID invalid. | |||
| void RescanPlugins(void); | |||
| // Unload all dlopened libraries. This will make any descriptors that | |||
| // have not been discarded with DiscardDescriptorByID invalid. | |||
| void UnloadAllLibraries(void); | |||
| // Get descriptor of plugin with given ID. This increments the descriptor | |||
| // count for the corresponding library. | |||
| const LADSPA_Descriptor *GetDescriptorByID(unsigned long unique_id); | |||
| // Notify that a descriptor corresponding to the given ID has been | |||
| // discarded. This decrements the descriptor count for the corresponding | |||
| // library. | |||
| void DiscardDescriptorByID(unsigned long unique_id); | |||
| // ************************************************************************ | |||
| // SSM Specific options | |||
| // Get unique ID of plugin identified by given library filename and label. | |||
| // This is for backwards compatibility with older versions of SSM where the | |||
| // path and label of the plugin was stored in the configuration - current | |||
| // versions store the Unique ID | |||
| unsigned long GetIDFromFilenameAndLabel(std::string filename, | |||
| std::string label); | |||
| // Struct for plugin information returned by queries | |||
| struct PluginEntry | |||
| { | |||
| unsigned int Depth; | |||
| unsigned long UniqueID; | |||
| std::string Name; | |||
| bool operator<(const PluginEntry& pe) | |||
| { | |||
| return (Name<pe.Name); | |||
| } | |||
| }; | |||
| // Get ordered list of plugin names and IDs for plugin menu | |||
| const std::vector<PluginEntry> GetMenuList(void); | |||
| // Get the index in the above list for given Unique ID | |||
| // If not found, this returns the size of the above list | |||
| unsigned long GetPluginListEntryByID(unsigned long unique_id); | |||
| // Get the number of input ports for the plugin with the most | |||
| // input ports | |||
| unsigned long GetMaxInputPortCount(void) { return m_MaxInputPortCount; } | |||
| private: | |||
| // See LADSPAInfo.C for comments on these functions | |||
| void DescendGroup(std::string prefix, | |||
| const std::string group, | |||
| unsigned int depth); | |||
| std::list<std::string> GetSubGroups(const std::string group); | |||
| void CleanUp(void); | |||
| void ScanPathList(const char *path_list, | |||
| void (LADSPAInfo::*ExamineFunc)(const std::string, | |||
| const std::string)); | |||
| void ExaminePluginLibrary(const std::string path, | |||
| const std::string basename); | |||
| bool CheckPlugin(const LADSPA_Descriptor *desc); | |||
| LADSPA_Descriptor_Function GetDescriptorFunctionForLibrary(unsigned long library_index); | |||
| #ifdef HAVE_LIBLRDF | |||
| void ExamineRDFFile(const std::string path, | |||
| const std::string basename); | |||
| void MetadataRDFDescend(const char *uri, | |||
| unsigned long parent); | |||
| #endif | |||
| // For cached library information | |||
| struct LibraryInfo | |||
| { | |||
| unsigned long PathIndex; // Index of path in m_Paths | |||
| std::string Basename; // Filename | |||
| unsigned long RefCount; // Count of descriptors requested | |||
| void *Handle; // DLL Handle, NULL | |||
| }; | |||
| // For cached plugin information | |||
| struct PluginInfo | |||
| { | |||
| unsigned long LibraryIndex; // Index of library in m_Libraries | |||
| unsigned long Index; // Plugin index in library | |||
| unsigned long UniqueID; // Unique ID | |||
| std::string Label; // Plugin label | |||
| std::string Name; // Plugin Name | |||
| const LADSPA_Descriptor *Descriptor; // Descriptor, NULL | |||
| }; | |||
| // For cached RDF uri information | |||
| struct RDFURIInfo | |||
| { | |||
| std::string URI; // Full URI for use with lrdf | |||
| std::string Label; // Label | |||
| std::vector<unsigned long> Parents; // Index of parents in m_RDFURIs | |||
| std::vector<unsigned long> Children; // Indices of children in m_RDFURIs | |||
| std::vector<unsigned long> Plugins; // Indices of plugins in m_Plugins | |||
| }; | |||
| // Lookup maps | |||
| typedef std::map<unsigned long, | |||
| unsigned long, | |||
| std::less<unsigned long> > IDMap; | |||
| typedef std::map<std::string, | |||
| unsigned long, | |||
| std::less<std::string> > StringMap; | |||
| bool m_LADSPAPathOverride; | |||
| char *m_ExtraPaths; | |||
| // LADSPA Plugin information database | |||
| std::vector<std::string> m_Paths; | |||
| std::vector<LibraryInfo> m_Libraries; | |||
| std::vector<PluginInfo> m_Plugins; | |||
| // Plugin lookup maps | |||
| IDMap m_IDLookup; | |||
| // RDF URI database | |||
| std::vector<RDFURIInfo> m_RDFURIs; | |||
| // RDF URI lookup map | |||
| StringMap m_RDFURILookup; | |||
| // RDF Label lookup map | |||
| StringMap m_RDFLabelLookup; | |||
| // SSM specific data | |||
| std::vector<PluginEntry> m_SSMMenuList; | |||
| StringMap m_FilenameLookup; | |||
| unsigned long m_MaxInputPortCount; | |||
| }; | |||
| #endif // __ladspa_info_h__ | |||
| @@ -0,0 +1,138 @@ | |||
| /*******************************************************************************/ | |||
| /* Copyright (C) 2008 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. */ | |||
| /*******************************************************************************/ | |||
| /* Base class for all meters */ | |||
| #include <FL/Fl.H> | |||
| #include <FL/Fl_Widget.H> | |||
| #include <FL/Fl_Valuator.H> | |||
| class Meter : public Fl_Valuator | |||
| { | |||
| float _peak; | |||
| float _old_value; | |||
| float _value; | |||
| protected: | |||
| virtual void draw ( void ) = 0; | |||
| virtual int handle ( int m ) | |||
| { | |||
| if ( m == FL_PUSH ) | |||
| { | |||
| // if ( Fl::event_button3() ) | |||
| // hide(); | |||
| // else | |||
| reset(); | |||
| return 1; | |||
| } | |||
| return Fl_Widget::handle( m ); | |||
| } | |||
| float | |||
| deflection ( float db ) | |||
| { | |||
| float def = 0.0f; | |||
| if ( db < -70.0f ) | |||
| def = 0.0f; | |||
| else if ( db < -60.0f ) | |||
| def = ( db + 70.0f ) * 0.25f; | |||
| else if ( db < -50.0f ) | |||
| def = ( db + 60.0f ) * 0.5f + 2.5f; | |||
| else if ( db < -40.0f ) | |||
| def = ( db + 50.0f ) * 0.75f + 7.5f; | |||
| else if ( db < -30.0f ) | |||
| def = ( db + 40.0f ) * 1.5f + 15.0f; | |||
| else if ( db < -20.0f ) | |||
| def = ( db + 30.0f ) * 2.0f + 30.0f; | |||
| else if ( db < 6.0f ) | |||
| def = ( db + 20.0f ) * 2.5f + 50.0f; | |||
| else | |||
| def = 115.0f; | |||
| return def / 115.0f; | |||
| } | |||
| float old_value ( void ) const { return _old_value; } | |||
| public: | |||
| Meter ( int X, int Y, int W, int H, const char *L = 0 ) : | |||
| Fl_Valuator( X, Y, W, H, L ) | |||
| { | |||
| _peak = _value = -80.0f; | |||
| _old_value = 4.0f; | |||
| } | |||
| virtual ~Meter ( ) { } | |||
| void value ( float v ) | |||
| { | |||
| if ( _value != v ) | |||
| { | |||
| damage( FL_DAMAGE_USER1 ); | |||
| _old_value = _value; | |||
| _value = v; | |||
| if ( _value > _peak ) | |||
| _peak = _value; | |||
| } | |||
| } | |||
| float value ( void ) const { return _value; } | |||
| float peak ( void ) const { return _peak; } | |||
| void reset ( void ) { _peak = -80.0f; redraw(); } | |||
| }; | |||
| #include <FL/Fl_Group.H> | |||
| #include <stdio.h> | |||
| /* ... Extension methods for any group containing only meters. Access | |||
| * via a cast to (Meter_Pack *) */ | |||
| class Meter_Pack : public Fl_Group | |||
| { | |||
| public: | |||
| /** return a pointer to the meter for channel /c/ in group of meters /g/ */ | |||
| Meter * | |||
| channel ( int c ) | |||
| { | |||
| if ( c > children() ) | |||
| { | |||
| fprintf( stderr, "no such channel\n" ); | |||
| return NULL; | |||
| } | |||
| return (Meter *)child( c ); | |||
| } | |||
| int | |||
| channels ( void ) const { return children(); } | |||
| }; | |||
| @@ -0,0 +1,180 @@ | |||
| /*******************************************************************************/ | |||
| /* 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. */ | |||
| /*******************************************************************************/ | |||
| #include "Meter_Indicator_Module.H" | |||
| #include <FL/Fl.H> | |||
| #include <FL/Fl_Value_Slider.H> | |||
| #include <FL/Fl_Box.H> | |||
| #include <FL/Fl_Counter.H> | |||
| #include "FL/Fl_Arc_Dial.H" | |||
| #include "FL/Fl_Light_Button.H" | |||
| #include "FL/Boxtypes.H" | |||
| #include <FL/fl_draw.H> | |||
| #include "FL/Fl_Labelpad_Group.H" | |||
| #include <stdio.h> | |||
| #include "Engine/Engine.H" | |||
| #include "Chain.H" | |||
| #include "DPM.H" | |||
| #include "FL/Fl_Scalepack.H" | |||
| const float CONTROL_UPDATE_FREQ = 0.1f; | |||
| Meter_Indicator_Module::Meter_Indicator_Module ( int W, int H, const char *L ) | |||
| : Module ( W, 100, L ) | |||
| { | |||
| box( FL_NO_BOX ); | |||
| _pad = true; | |||
| control = 0; | |||
| control_value = 0; | |||
| add_port( Port( this, Port::INPUT, Port::CONTROL ) ); | |||
| dpm_pack = new Fl_Scalepack( x(), y(), w(), h() ); | |||
| dpm_pack->type( FL_HORIZONTAL ); | |||
| control_value = new float[1]; | |||
| end(); | |||
| Fl::add_timeout( CONTROL_UPDATE_FREQ, update_cb, this ); | |||
| } | |||
| Meter_Indicator_Module::~Meter_Indicator_Module ( ) | |||
| { | |||
| } | |||
| void | |||
| Meter_Indicator_Module::update_cb ( void *v ) | |||
| { | |||
| ((Meter_Indicator_Module*)v)->update_cb(); | |||
| } | |||
| void | |||
| Meter_Indicator_Module::update_cb ( void ) | |||
| { | |||
| Fl::repeat_timeout( CONTROL_UPDATE_FREQ, update_cb, this ); | |||
| if ( control_input[0].connected() ) | |||
| { | |||
| // A little hack to detect that the connected module's number | |||
| // of control outs has changed. | |||
| Port *p = control_input[0].connected_port(); | |||
| if ( dpm_pack->children() != p->hints.dimensions ) | |||
| { | |||
| engine->lock(); | |||
| dpm_pack->clear(); | |||
| control_value = new float[p->hints.dimensions]; | |||
| for ( int i = p->hints.dimensions; i--; ) | |||
| { | |||
| DPM *dpm = new DPM( x(), y(), w(), h() ); | |||
| dpm->type( FL_VERTICAL ); | |||
| align( (Fl_Align)(FL_ALIGN_CENTER | FL_ALIGN_INSIDE ) ); | |||
| dpm_pack->add( dpm ); | |||
| control_value[i] = -70.0f; | |||
| dpm->value( -70.0f ); | |||
| } | |||
| engine->unlock(); | |||
| } | |||
| else | |||
| { | |||
| for ( int i = 0; i < dpm_pack->children(); ++i ) | |||
| { | |||
| ((DPM*)dpm_pack->child( i ))->value( control_value[i] ); | |||
| } | |||
| } | |||
| } | |||
| redraw(); | |||
| } | |||
| void | |||
| Meter_Indicator_Module::connect_to ( Port *p ) | |||
| { | |||
| control_input[0].connect_to( p ); | |||
| /* else if ( p->hints.type == Module::Port::Hints::LOGARITHMIC ) */ | |||
| /* { */ | |||
| { | |||
| DPM *o = new DPM( x(), y(), this->w(), h() ); | |||
| o->type( FL_VERTICAL ); | |||
| align( (Fl_Align)(FL_ALIGN_CENTER | FL_ALIGN_INSIDE ) ); | |||
| dpm_pack->add( o ); | |||
| } | |||
| // control = o; | |||
| // w = o; | |||
| // o->value( p->control_value() ); | |||
| /* } */ | |||
| /* w->align(FL_ALIGN_TOP); */ | |||
| /* w->labelsize( 10 ); */ | |||
| /* if ( _pad ) */ | |||
| /* { */ | |||
| /* Fl_Labelpad_Group *flg = new Fl_Labelpad_Group( w ); */ | |||
| /* size( flg->w(), flg->h() ); */ | |||
| /* add( flg ); */ | |||
| /* } */ | |||
| /* else */ | |||
| /* { */ | |||
| /* w->resize( x(), y(), this->w(), h() ); */ | |||
| /* add( w ); */ | |||
| /* resizable( w ); */ | |||
| /* } */ | |||
| } | |||
| int | |||
| Meter_Indicator_Module::handle ( int m ) | |||
| { | |||
| return Fl_Group::handle( m ); | |||
| } | |||
| void | |||
| Meter_Indicator_Module::process ( void ) | |||
| { | |||
| if ( control_input[0].connected() ) | |||
| { | |||
| Port *p = control_input[0].connected_port(); | |||
| for ( int i = 0; i < p->hints.dimensions; ++i ) | |||
| control_value[i] = ((float*)control_input[0].buffer())[i]; | |||
| } | |||
| } | |||
| @@ -0,0 +1,75 @@ | |||
| /*******************************************************************************/ | |||
| /* 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. */ | |||
| /*******************************************************************************/ | |||
| #pragma once | |||
| #include "Module.H" | |||
| #include <vector> | |||
| #include "JACK/Port.H" | |||
| class Fl_Valuator; | |||
| class Fl_Scalepack; | |||
| class Meter_Indicator_Module : public Module | |||
| { | |||
| Fl_Scalepack *dpm_pack; | |||
| static void update_cb ( void *v ); | |||
| void update_cb ( void ); | |||
| bool _pad; | |||
| volatile float *control_value; | |||
| public: | |||
| Meter_Indicator_Module ( int W, int H, const char *L=0 ); | |||
| virtual ~Meter_Indicator_Module ( ); | |||
| const char *name ( void ) const { return "Meter Indicator"; } | |||
| int can_support_inputs ( int n ) { return 0; } | |||
| bool configure_inputs ( int n ) { return false; } | |||
| void pad ( bool v ) { _pad = v; } | |||
| static void cb_handle ( Fl_Widget *w, void *v ); | |||
| void cb_handle ( Fl_Widget *w ); | |||
| void connect_to ( Port *p ); | |||
| protected: | |||
| // virtual void draw ( void ); | |||
| virtual void process ( void ); | |||
| virtual void draw ( void ) | |||
| { | |||
| draw_box(); | |||
| Fl_Group::draw(); | |||
| } | |||
| virtual int handle ( int m ); | |||
| private: | |||
| Fl_Valuator *control; | |||
| }; | |||
| @@ -0,0 +1,201 @@ | |||
| /*******************************************************************************/ | |||
| /* 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. */ | |||
| /*******************************************************************************/ | |||
| #include "Meter_Module.H" | |||
| #include "DPM.H" | |||
| #include <FL/Fl.H> | |||
| #include <FL/Fl_Single_Window.H> | |||
| #include <FL/Fl_Scalepack.H> | |||
| #include "JACK/Port.H" | |||
| #include <math.h> | |||
| const float METER_UPDATE_FREQ = 0.1f; | |||
| Meter_Module::Meter_Module ( int W, int H, const char *L ) | |||
| : Module ( W, 100, L ) | |||
| { | |||
| box( FL_THIN_UP_FRAME ); | |||
| dpm_pack = new Fl_Scalepack( x(), y(), w(), h() ); | |||
| dpm_pack->type( FL_HORIZONTAL ); | |||
| color( FL_BLACK ); | |||
| end(); | |||
| Port p( this, Port::OUTPUT, Port::CONTROL, "dB level" ); | |||
| p.hints.type = Port::Hints::LOGARITHMIC; | |||
| p.hints.ranged = true; | |||
| p.hints.maximum = 6.0f; | |||
| p.hints.minimum = -70.0f; | |||
| p.hints.dimensions = 1; | |||
| p.connect_to( new float[1] ); | |||
| p.control_value_no_callback( -70.0f ); | |||
| add_port( p ); | |||
| Fl::add_timeout( METER_UPDATE_FREQ, update_cb, this ); | |||
| } | |||
| Meter_Module::~Meter_Module ( ) | |||
| { | |||
| } | |||
| void | |||
| Meter_Module::update_cb ( void *v ) | |||
| { | |||
| ((Meter_Module*)v)->update_cb(); | |||
| } | |||
| void | |||
| Meter_Module::update_cb ( void ) | |||
| { | |||
| Fl::repeat_timeout( METER_UPDATE_FREQ, update_cb, this ); | |||
| for ( int i = dpm_pack->children(); i--; ) | |||
| dpm_pack->child( i )->redraw(); | |||
| } | |||
| bool | |||
| Meter_Module::configure_inputs ( int n ) | |||
| { | |||
| int tx, ty, tw, th; | |||
| bbox( tx,ty,tw,th ); | |||
| int on = audio_input.size(); | |||
| if ( n > on ) | |||
| { | |||
| for ( int i = on; i < n; ++i ) | |||
| { | |||
| DPM *dpm = new DPM( tx, ty, tw, th ); | |||
| dpm->type( FL_VERTICAL ); | |||
| align( (Fl_Align)(FL_ALIGN_CENTER | FL_ALIGN_INSIDE ) ); | |||
| dpm_pack->add( dpm ); | |||
| add_port( Port( this, Port::INPUT, Port::AUDIO ) ); | |||
| add_port( Port( this, Port::OUTPUT, Port::AUDIO ) ); | |||
| } | |||
| } | |||
| else | |||
| { | |||
| for ( int i = on; i > n; --i ) | |||
| { | |||
| DPM *dpm = (DPM*)dpm_pack->child( dpm_pack->children() - 1 ); | |||
| dpm_pack->remove( dpm ); | |||
| delete dpm; | |||
| audio_input.back().disconnect(); | |||
| audio_input.pop_back(); | |||
| audio_output.back().disconnect(); | |||
| audio_output.pop_back(); | |||
| control_output.back().disconnect(); | |||
| control_output.pop_back(); | |||
| } | |||
| } | |||
| control_output[0].hints.dimensions = n; | |||
| delete[] (float*)control_output[0].buffer(); | |||
| { | |||
| float *f = new float[n]; | |||
| for ( int i = n; i--; ) | |||
| f[i] = -70.0f; | |||
| control_output[0].connect_to( f); | |||
| } | |||
| return true; | |||
| } | |||
| int | |||
| Meter_Module::handle ( int m ) | |||
| { | |||
| switch ( m ) | |||
| { | |||
| case FL_PUSH: | |||
| { | |||
| /* Fl_Single_Window *win = new Fl_Single_Window( 0, 0, 400, 400 ); */ | |||
| /* win->add( dpm ); */ | |||
| /* win->resizable( dpm ); */ | |||
| /* win->end(); */ | |||
| /* win->show(); */ | |||
| /* break; */ | |||
| } | |||
| } | |||
| return Module::handle( m ); | |||
| } | |||
| static float | |||
| get_peak_sample ( const sample_t* buf, nframes_t nframes ) | |||
| { | |||
| float p = 0.0f; | |||
| const sample_t *f = buf; | |||
| for ( int j = nframes; j--; ++f ) | |||
| { | |||
| float s = *f; | |||
| /* rectify */ | |||
| if ( s < 0.0f ) | |||
| s = 0 - s; | |||
| if ( s > p ) | |||
| p = s; | |||
| } | |||
| return p; | |||
| } | |||
| void | |||
| Meter_Module::process ( void ) | |||
| { | |||
| for ( int i = 0; i < audio_input.size(); ++i ) | |||
| { | |||
| DPM *dpm = (DPM*)dpm_pack->child( i ); | |||
| if ( audio_input[i].connected() ) | |||
| { | |||
| dpm->activate(); | |||
| float dB = 20 * log10( get_peak_sample( (float*)audio_input[i].buffer(), nframes() ) / 2.0f ); | |||
| dpm->value( dB ); | |||
| /* if ( control_output[i].connected() ) */ | |||
| /* { */ | |||
| ((float*)control_output[0].buffer())[i] = dB; | |||
| /* } */ | |||
| } | |||
| else | |||
| dpm->deactivate(); | |||
| } | |||
| } | |||
| @@ -0,0 +1,51 @@ | |||
| /*******************************************************************************/ | |||
| /* 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. */ | |||
| /*******************************************************************************/ | |||
| #pragma once | |||
| #include "Module.H" | |||
| class Fl_Scalepack; | |||
| class Meter_Module : public Module | |||
| { | |||
| Fl_Scalepack *dpm_pack; | |||
| public: | |||
| Meter_Module ( int W, int H, const char *L=0 ); | |||
| virtual ~Meter_Module ( ); | |||
| const char *name ( void ) const { return "Meter"; } | |||
| int can_support_inputs ( int n ) { return n > 0 ? n : -1; } | |||
| bool configure_inputs ( int n ); | |||
| static void update_cb ( void *v ); | |||
| void update_cb ( void ); | |||
| protected: | |||
| // virtual void draw ( void ); | |||
| virtual int handle ( int m ); | |||
| virtual void process ( void ); | |||
| }; | |||
| @@ -0,0 +1,230 @@ | |||
| /*******************************************************************************/ | |||
| /* 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. */ | |||
| /*******************************************************************************/ | |||
| /* This is the main mixer group. It contains and manages Mixer_Strips. */ | |||
| #include "Mixer.H" | |||
| #include "Mixer_Strip.H" | |||
| #include <FL/Fl_Pack.H> | |||
| #include <FL/Fl_Scroll.H> | |||
| #include "Engine/Engine.H" | |||
| #include <string.h> | |||
| #include "debug.h" | |||
| const double STATUS_UPDATE_FREQ = 0.2f; | |||
| static Fl_Pack *mixer_strips; | |||
| #include "util/debug.h" | |||
| static void update_cb( void *v ) { | |||
| Fl::repeat_timeout( STATUS_UPDATE_FREQ, update_cb, v ); | |||
| ((Mixer*)v)->update(); | |||
| } | |||
| Mixer::Mixer ( int X, int Y, int W, int H, const char *L ) : | |||
| Fl_Group( X, Y, W, H, L ) | |||
| { | |||
| label( "Non-Mixer" ); | |||
| align( (Fl_Align)(FL_ALIGN_CENTER | FL_ALIGN_INSIDE) ); | |||
| labelsize( 96 ); | |||
| { | |||
| Fl_Scroll *o = scroll = new Fl_Scroll( 0, 0, W, H ); | |||
| o->type( Fl_Scroll::HORIZONTAL_ALWAYS ); | |||
| { | |||
| Fl_Pack *o = mixer_strips = new Fl_Pack( 0, 4, W, H - 24 ); | |||
| o->type( Fl_Pack::HORIZONTAL ); | |||
| o->spacing( 2 ); | |||
| o->end(); | |||
| Fl_Group::current()->resizable( o ); | |||
| } | |||
| o->end(); | |||
| Fl_Group::current()->resizable( o ); | |||
| } | |||
| end(); | |||
| // Fl::add_timeout( STATUS_UPDATE_FREQ, update_cb, this ); | |||
| MESSAGE( "Scanning for plugins..." ); | |||
| } | |||
| Mixer::~Mixer ( ) | |||
| { | |||
| /* FIXME: teardown */ | |||
| } | |||
| void Mixer::resize ( int X, int Y, int W, int H ) | |||
| { | |||
| mixer_strips->size( W, H - 24 ); | |||
| scroll->resize( X, Y, W, H ); | |||
| Fl_Group::resize( X, Y, W, H ); | |||
| } | |||
| void Mixer::add ( Mixer_Strip *ms ) | |||
| { | |||
| MESSAGE( "Add mixer strip \"%s\"", ms->name() ); | |||
| engine->lock(); | |||
| mixer_strips->add( ms ); | |||
| // mixer_strips->insert( *ms, 0 ); | |||
| engine->unlock(); | |||
| redraw(); | |||
| } | |||
| void Mixer::remove ( Mixer_Strip *ms ) | |||
| { | |||
| MESSAGE( "Remove mixer strip \"%s\"", ms->name() ); | |||
| engine->lock(); | |||
| mixer_strips->remove( ms ); | |||
| engine->unlock(); | |||
| delete ms; | |||
| parent()->redraw(); | |||
| } | |||
| bool | |||
| Mixer::contains ( Mixer_Strip *ms ) | |||
| { | |||
| return ms->parent() == mixer_strips; | |||
| } | |||
| void Mixer::update ( void ) | |||
| { | |||
| THREAD_ASSERT( UI ); | |||
| for ( int i = mixer_strips->children(); i--; ) | |||
| { | |||
| ((Mixer_Strip*)mixer_strips->child( i ))->update(); | |||
| } | |||
| // redraw(); | |||
| } | |||
| void | |||
| Mixer::process ( unsigned int nframes ) | |||
| { | |||
| THREAD_ASSERT( RT ); | |||
| for ( int i = mixer_strips->children(); i--; ) | |||
| { | |||
| ((Mixer_Strip*)mixer_strips->child( i ))->process( nframes ); | |||
| } | |||
| } | |||
| /** retrun a pointer to the track named /name/, or NULL if no track is named /name/ */ | |||
| Mixer_Strip * | |||
| Mixer::track_by_name ( const char *name ) | |||
| { | |||
| for ( int i = mixer_strips->children(); i-- ; ) | |||
| { | |||
| Mixer_Strip *t = (Mixer_Strip*)mixer_strips->child( i ); | |||
| if ( ! strcmp( name, t->name() ) ) | |||
| return t; | |||
| } | |||
| return NULL; | |||
| } | |||
| /** return a malloc'd string representing a unique name for a new track */ | |||
| char * | |||
| Mixer::get_unique_track_name ( const char *name ) | |||
| { | |||
| char pat[256]; | |||
| strcpy( pat, name ); | |||
| for ( int i = 1; track_by_name( pat ); ++i ) | |||
| snprintf( pat, sizeof( pat ), "%s.%d", name, i ); | |||
| return strdup( pat ); | |||
| } | |||
| void | |||
| Mixer::snapshot ( void ) | |||
| { | |||
| for ( int i = 0; i < mixer_strips->children(); ++i ) | |||
| ((Mixer_Strip*)mixer_strips->child( i ))->log_create(); | |||
| } | |||
| void | |||
| Mixer::new_strip ( void ) | |||
| { | |||
| engine->lock(); | |||
| add( new Mixer_Strip( get_unique_track_name( "Unnamed" ), 1 ) ); | |||
| engine->unlock(); | |||
| // scroll->size( mixer_strips->w(), scroll->h() ); | |||
| } | |||
| int | |||
| Mixer::handle ( int m ) | |||
| { | |||
| int r = Fl_Group::handle( m ); | |||
| switch ( m ) | |||
| { | |||
| case FL_ENTER: | |||
| case FL_LEAVE: | |||
| return 1; | |||
| case FL_SHORTCUT: | |||
| { | |||
| if ( Fl::event_key() == 'a' ) | |||
| { | |||
| new_strip(); | |||
| return 1; | |||
| } | |||
| else if ( Fl::event_ctrl() && Fl::event_key() == 's' ) | |||
| { | |||
| MESSAGE( "Saving state" ); | |||
| Loggable::snapshot_callback( &Mixer::snapshot, this ); | |||
| Loggable::snapshot( "save.mix" ); | |||
| return 1; | |||
| } | |||
| else | |||
| return r; | |||
| break; | |||
| } | |||
| default: | |||
| return r; | |||
| break; | |||
| } | |||
| // return 0; | |||
| return r; | |||
| } | |||
| @@ -0,0 +1,63 @@ | |||
| /*******************************************************************************/ | |||
| /* 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. */ | |||
| /*******************************************************************************/ | |||
| #pragma once | |||
| #include <FL/Fl.H> | |||
| #include <FL/Fl_Widget.H> | |||
| #include <FL/Fl_Group.H> | |||
| #include <FL/Fl_Scroll.H> | |||
| #include <FL/Fl_Pack.H> | |||
| #include "Mixer_Strip.H" | |||
| class Mixer : public Fl_Group | |||
| { | |||
| private: | |||
| Mixer_Strip* track_by_name ( const char *name ); | |||
| char * get_unique_track_name ( const char *name ); | |||
| void snapshot ( void ); | |||
| static void snapshot ( void *v ) { ((Mixer*)v)->snapshot(); } | |||
| Fl_Scroll *scroll; | |||
| Fl_Pack *pack; | |||
| protected: | |||
| virtual int handle ( int m ); | |||
| public: | |||
| virtual void resize ( int X, int Y, int W, int H ); | |||
| void update ( void ); | |||
| void new_strip ( void ); | |||
| void process ( unsigned int nframes ); | |||
| void add ( Mixer_Strip *ms ); | |||
| void remove ( Mixer_Strip *ms ); | |||
| bool contains ( Mixer_Strip *ms ); | |||
| Mixer ( int X, int Y, int W, int H, const char *L ); | |||
| virtual ~Mixer(); | |||
| }; | |||
| extern Mixer* mixer; | |||
| @@ -0,0 +1,471 @@ | |||
| /*******************************************************************************/ | |||
| /* 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 <dsp.h> | |||
| #include <string.h> | |||
| #include "debug.h" | |||
| #include <FL/Fl_Tabs.H> | |||
| #include "FL/Fl_Flowpack.H" | |||
| #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" | |||
| extern Mixer *mixer; | |||
| void | |||
| Mixer_Strip::get ( Log_Entry &e ) const | |||
| { | |||
| e.add( ":name", name() ); | |||
| // e.add( ":controllable", controllable() ); | |||
| // e.add( ":inputs", _in.size() ); | |||
| /* e.add( ":gain", gain_slider->value() ); */ | |||
| e.add( ":meter_point", prepost_button->value() ? "pre" : "post" ); | |||
| 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, ":controllable" ) ) | |||
| // controllable( atoi( v ) ); | |||
| else if ( ! strcmp( s, ":inputs" ) ) | |||
| configure_ports( atoi( v ) ); | |||
| /* else if ( ! strcmp( s, ":gain" ) ) */ | |||
| /* gain_slider->value( atof( v ) ); */ | |||
| else if ( ! strcmp( s, ":meter_point" ) ) | |||
| prepost_button->value( strcmp( v, "pre" ) == 0 ); | |||
| else if ( ! strcmp( s, ":color" ) ) | |||
| { | |||
| color( (Fl_Color)atoll( v ) ); | |||
| redraw(); | |||
| } | |||
| } | |||
| if ( ! mixer->contains( this ) ) | |||
| mixer->add( this ); | |||
| } | |||
| Mixer_Strip::Mixer_Strip( const char *strip_name, int channels ) : Fl_Group( 0, 0, 120, 600 ) | |||
| { | |||
| label( strdup( strip_name ) ); | |||
| init(); | |||
| color( (Fl_Color)rand() ); | |||
| // name( strdup( strip_name ) ); | |||
| configure_ports( channels ); | |||
| log_create(); | |||
| } | |||
| Mixer_Strip::Mixer_Strip() : Fl_Group( 0, 0, 120, 600 ) | |||
| { | |||
| init(); | |||
| log_create(); | |||
| } | |||
| Mixer_Strip::~Mixer_Strip ( ) | |||
| { | |||
| configure_ports( 0 ); | |||
| } | |||
| void Mixer_Strip::cb_handle(Fl_Widget* o) { | |||
| // parent()->parent()->damage( FL_DAMAGE_ALL, x(), y(), w(), h() ); | |||
| if ( o == close_button ) | |||
| ((Mixer*)parent())->remove( this ); | |||
| else if ( o == inputs_counter ) | |||
| configure_ports( ((Fl_Counter*)o)->value() ); | |||
| else if ( o == name_field ) | |||
| name( name_field->value() ); | |||
| /* else if ( o == controllable_button ) */ | |||
| /* { */ | |||
| /* controllable( controllable_button->value() ); */ | |||
| /* // configure_ports( channels() ); */ | |||
| /* } */ | |||
| else if ( o == prepost_button ) | |||
| { | |||
| if ( ((Fl_Button*)o)->value() ) | |||
| size( 300, h() ); | |||
| else | |||
| size( 120, h() ); | |||
| 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 ); | |||
| 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" ); | |||
| } | |||
| bool | |||
| Mixer_Strip::configure_ports ( int n ) | |||
| { | |||
| /* /\* figure out how many buffers we have to create *\/ */ | |||
| /* int required_buffers = chain->required_buffers(); */ | |||
| /* engine->lock(); */ | |||
| /* if ( chain_buffers > 0 ) */ | |||
| /* { */ | |||
| /* for ( int i = chain_buffers; --i; ) */ | |||
| /* { */ | |||
| /* delete chain_buffer[i]; */ | |||
| /* chain_buffer[i] = NULL; */ | |||
| /* } */ | |||
| /* delete chain_buffer; */ | |||
| /* chain_buffer = NULL; */ | |||
| /* chain_buffers = 0; */ | |||
| /* } */ | |||
| /* sample_t **buf = new sample_t*[required_buffers]; */ | |||
| /* for ( int i = 0; i < required_buffers; ++i ) */ | |||
| /* buf[i] = new sample_t[nframes]; */ | |||
| /* chain_buffers = required_buffers; */ | |||
| /* chain_buffer = buf; */ | |||
| /* engine->unlock(); */ | |||
| /* /\* FIXME: bogus *\/ */ | |||
| /* return true; */ | |||
| } | |||
| void | |||
| Mixer_Strip::process ( nframes_t nframes ) | |||
| { | |||
| THREAD_ASSERT( RT ); | |||
| /* sample_t *gain_buf = NULL; */ | |||
| /* float g = gain_slider->value(); */ | |||
| /* if ( _control && _control->connected() ) */ | |||
| /* { */ | |||
| /* gain_buf = (sample_t*)_control->buffer( nframes ); */ | |||
| /* /\* // bring it up to 0.0-2.0f *\/ */ | |||
| /* /\* for ( int i = nframes; i--; ) *\/ */ | |||
| /* /\* gain_buf[i] += 1.0f; *\/ */ | |||
| /* // apply gain from slider */ | |||
| /* buffer_apply_gain( gain_buf, nframes, g ); */ | |||
| /* /\* FIXME: bullshit! *\/ */ | |||
| /* _control_peak = gain_buf[0]; */ | |||
| /* } */ | |||
| /* else */ | |||
| /* { */ | |||
| /* _control_peak = 0; */ | |||
| /* } */ | |||
| /* for ( int i = channels(); i--; ) */ | |||
| /* { */ | |||
| /* if ( _in[i].connected()) */ | |||
| /* { */ | |||
| /* if ( gain_buf ) */ | |||
| /* buffer_copy_and_apply_gain_buffer( (sample_t*)_out[i].buffer( nframes ), (sample_t*)_in[i].buffer( nframes ), gain_buf, nframes ); */ | |||
| /* else */ | |||
| /* buffer_copy_and_apply_gain( (sample_t*)_out[i].buffer( nframes ), (sample_t*)_in[i].buffer( nframes ), nframes, g ); */ | |||
| /* sample_t *meter_buffer = prepost_button->value() == 1 ? (sample_t*)_in[i].buffer( nframes ) : (sample_t*)_out[i].buffer( nframes ); */ | |||
| /* /\* set peak value (in dB) *\/ */ | |||
| /* _peak[i] = 20 * log10( get_peak_sample( meter_buffer, nframes ) / 2.0f ); */ | |||
| /* } */ | |||
| /* else */ | |||
| /* { */ | |||
| /* buffer_fill_with_silence( (sample_t*)_out[i].buffer( nframes ), nframes ); */ | |||
| /* } */ | |||
| /* } */ | |||
| chain->process( nframes ); | |||
| } | |||
| /* update GUI with values from RT thread */ | |||
| void | |||
| Mixer_Strip::update ( void ) | |||
| { | |||
| THREAD_ASSERT( UI ); | |||
| } | |||
| void | |||
| Mixer_Strip::init ( ) | |||
| { | |||
| chain_buffers = 0; | |||
| chain_buffer = NULL; | |||
| box(FL_THIN_UP_BOX); | |||
| 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_Input *o = name_field = new Fl_Sometimes_Input( 2, 2, 144, 24 ); | |||
| o->color( color() ); | |||
| o->box( FL_FLAT_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 = 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->callback( ((Fl_Callback*)cb_handle), this ); | |||
| } // Fl_Button* o | |||
| o->end(); | |||
| } // Fl_Group* o | |||
| { Fl_Flip_Button* o = prepost_button = new Fl_Flip_Button(61, 183, 45, 22, "narrow/wide"); | |||
| o->type(1); | |||
| // o->box(FL_ROUNDED_BOX); | |||
| o->box( FL_THIN_DOWN_BOX ); | |||
| o->color((Fl_Color)106); | |||
| o->selection_color((Fl_Color)65); | |||
| o->labeltype(FL_NORMAL_LABEL); | |||
| o->labelfont(0); | |||
| o->labelsize(14); | |||
| o->labelcolor(FL_FOREGROUND_COLOR); | |||
| o->align(FL_ALIGN_CLIP); | |||
| o->callback( ((Fl_Callback*)cb_handle), this ); | |||
| o->when(FL_WHEN_RELEASE); | |||
| } // Fl_Flip_Button* o | |||
| // { 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_Tabs *o = new Fl_Tabs( 4, 104, 110, 330 ); | |||
| o->clip_children( 1 ); | |||
| o->box( FL_NO_BOX ); | |||
| { Fl_Group *o = new Fl_Group( 4, 114, 110, 330, "Fader" ); | |||
| o->labelsize( 9 ); | |||
| o->box( FL_NO_BOX ); | |||
| { Fl_Pack* o = fader_pack = new Fl_Pack(4, 116, 103, 330 ); | |||
| o->spacing( 20 ); | |||
| o->type( Fl_Pack::HORIZONTAL ); | |||
| o->end(); | |||
| Fl_Group::current()->resizable(o); | |||
| } // Fl_Group* o | |||
| o->end(); | |||
| Fl_Group::current()->resizable(o); | |||
| } | |||
| { Fl_Group *o = new Fl_Group( 4, 114, 110, 330, "Signal" ); | |||
| o->labelsize( 9 ); | |||
| o->hide(); | |||
| { Chain *o = chain = new Chain( 4, 116, 110, 330 ); | |||
| o->labelsize( 10 ); | |||
| o->align( FL_ALIGN_TOP ); | |||
| o->color( FL_RED ); | |||
| o->configure_outputs_callback( configure_outputs, this ); | |||
| o->name( name() ); | |||
| o->initialize_with_default(); | |||
| Fl_Group::current()->resizable(o); | |||
| } | |||
| o->end(); | |||
| } | |||
| o->end(); | |||
| Fl_Group::current()->resizable(o); | |||
| } | |||
| { Fl_Pack *o = new Fl_Pack( 2, 440, 114, 40 ); | |||
| o->spacing( 2 ); | |||
| o->type( Fl_Pack::VERTICAL ); | |||
| { 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( "Pan" ); | |||
| } | |||
| { Panner* o = new Panner(0, 0, 110, 90); | |||
| 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 | |||
| { | |||
| Controller_Module *m = new Controller_Module( 100, 24, "Inputs" ); | |||
| 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 ); | |||
| // controllable( true ); | |||
| { | |||
| Module *gain_module; | |||
| { | |||
| Module *m = gain_module = new Gain_Module( 50, 50, "Gain" ); | |||
| m->initialize(); | |||
| chain->insert( chain->module( chain->modules() - 1 ), m ); | |||
| } | |||
| { | |||
| Controller_Module *m = new Controller_Module( 100, 0, "Gain" ); | |||
| m->chain( chain ); | |||
| m->pad( false ); | |||
| m->connect_to( &gain_module->control_input[0] ); | |||
| m->size( 33, 0 ); | |||
| fader_pack->add( m ); | |||
| } | |||
| Module *meter_module; | |||
| { | |||
| Module *m = meter_module = new Meter_Module( 50, 50, "Meter" ); | |||
| chain->insert( chain->module( chain->modules() - 1 ), m ); | |||
| } | |||
| { | |||
| Meter_Indicator_Module *m = new Meter_Indicator_Module( 100, 0, "" ); | |||
| m->chain( chain ); | |||
| m->pad( false ); | |||
| m->connect_to( &meter_module->control_output[0] ); | |||
| m->size( 58, 0 ); | |||
| m->clip_children( 0 ); | |||
| fader_pack->add( m ); | |||
| fader_pack->resizable( m ); | |||
| } | |||
| chain->configure_ports(); | |||
| } | |||
| } | |||
| int | |||
| Mixer_Strip::handle ( int m ) | |||
| { | |||
| Logger log( this ); | |||
| static Fl_Color orig_color; | |||
| switch ( m ) | |||
| { | |||
| case FL_ENTER: | |||
| // orig_color = color(); | |||
| // color( FL_BLACK ); | |||
| redraw(); | |||
| return 1; | |||
| break; | |||
| case FL_LEAVE: | |||
| // color( orig_color ); | |||
| redraw(); | |||
| return 1; | |||
| break; | |||
| default: | |||
| return Fl_Group::handle( m ); | |||
| } | |||
| return 0; | |||
| } | |||
| @@ -0,0 +1,100 @@ | |||
| // generated by Fast Light User Interface Designer (fluid) version 1.0108 | |||
| #ifndef Mixer_Strip_H | |||
| #define Mixer_Strip_H | |||
| #include <FL/Fl.H> | |||
| #include "DPM.H" | |||
| #include "Panner.H" | |||
| #include <FL/Fl_Scalepack.H> | |||
| #include <FL/Fl_Pack.H> | |||
| #include <FL/Fl_Flip_Button.H> | |||
| #include <FL/Fl_Arc_Dial.H> | |||
| #include <FL/Boxtypes.H> | |||
| #include <FL/Fl_Group.H> | |||
| #include <FL/Fl_Box.H> | |||
| #include <FL/Fl_Button.H> | |||
| #include <FL/Fl_Value_Slider.H> | |||
| #include <FL/Fl_Counter.H> | |||
| #include <FL/Fl_Progress.H> | |||
| #include <FL/Fl_Input.H> | |||
| //#include "Fader.H" | |||
| #include <JACK/Port.H> | |||
| #include "../FL/Fl_Sometimes_Input.H" | |||
| #include "Loggable.H" | |||
| class Chain; | |||
| class Fl_Flowpack; | |||
| class Mixer_Strip : public Fl_Group, public Loggable { | |||
| public: | |||
| Mixer_Strip(int X, int Y, int W, int H, const char *L = 0); | |||
| Mixer_Strip( const char *name, int channels ); | |||
| Mixer_Strip(); /* for log create */ | |||
| ~Mixer_Strip(); | |||
| // Fl_Value_Slider *gain_slider; | |||
| Fl_Flip_Button *prepost_button; | |||
| Fl_Button *close_button; | |||
| Fl_Input *name_field; | |||
| Fl_Flowpack *controls_pack; | |||
| Chain *chain; | |||
| sample_t **chain_buffer; | |||
| int chain_buffers; | |||
| nframes_t nframes; | |||
| private: | |||
| unsigned _color; | |||
| void init ( ); | |||
| void cb_handle(Fl_Widget*); | |||
| static void cb_handle(Fl_Widget*, void*); | |||
| void update_port_names ( void ); | |||
| protected: | |||
| void get ( Log_Entry &e ) const; | |||
| void set ( Log_Entry &e ); | |||
| virtual int handle ( int m ); | |||
| public: | |||
| void color ( Fl_Color c ) | |||
| { | |||
| _color = c; | |||
| name_field->color( _color ); | |||
| name_field->redraw(); | |||
| } | |||
| Fl_Color color ( void ) const | |||
| { | |||
| return name_field->color(); | |||
| } | |||
| LOG_CREATE_FUNC( Mixer_Strip ); | |||
| Fl_Counter *inputs_counter; | |||
| void process ( unsigned int nframes ); | |||
| static void configure_outputs ( Fl_Widget *o, void *v ); | |||
| void configure_outputs ( void ); | |||
| bool configure_ports ( int n ); | |||
| void update ( void ); | |||
| // int channels ( void ) const { return _in.size(); } | |||
| void name ( const char *name ); | |||
| const char *name ( void ) const { return label(); } | |||
| }; | |||
| #endif | |||
| @@ -0,0 +1,169 @@ | |||
| /*******************************************************************************/ | |||
| /* 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. */ | |||
| /*******************************************************************************/ | |||
| #include "Module.H" | |||
| #include <FL/fl_draw.H> | |||
| #include <stdlib.h> | |||
| #include <string.h> | |||
| #include "Module_Parameter_Editor.H" | |||
| Module::~Module ( ) | |||
| { | |||
| for ( unsigned int i = 0; i < audio_input.size(); ++i ) | |||
| audio_input[i].disconnect(); | |||
| for ( unsigned int i = 0; i < audio_output.size(); ++i ) | |||
| audio_output[i].disconnect(); | |||
| for ( unsigned int i = 0; i < control_input.size(); ++i ) | |||
| control_input[i].disconnect(); | |||
| for ( unsigned int i = 0; i < control_output.size(); ++i ) | |||
| control_output[i].disconnect(); | |||
| audio_input.clear(); | |||
| audio_output.clear(); | |||
| control_input.clear(); | |||
| control_output.clear(); | |||
| } | |||
| void | |||
| Module::draw_box ( void ) | |||
| { | |||
| fl_color( FL_WHITE ); | |||
| int tw, th, tx, ty; | |||
| tw = w(); | |||
| th = h(); | |||
| ty = y(); | |||
| tx = x(); | |||
| // bbox( tx, ty, tw, th ); | |||
| fl_push_clip( tx, ty, tw, th ); | |||
| int spacing = w() / instances(); | |||
| for ( int i = instances(); i--; ) | |||
| { | |||
| fl_draw_box( box(), tx + (spacing * i), ty, tw / instances(), th, Fl::belowmouse() == this ? fl_lighter( color() ) : color() ); | |||
| } | |||
| if ( audio_input.size() && audio_output.size() ) | |||
| { | |||
| /* maybe draw control indicators */ | |||
| if ( control_input.size() ) | |||
| fl_draw_box( FL_ROUNDED_BOX, tx + 4, ty + 4, 5, 5, is_being_controlled() ? FL_YELLOW : fl_inactive( FL_YELLOW ) ); | |||
| if ( control_output.size() ) | |||
| fl_draw_box( FL_ROUNDED_BOX, tx + tw - 8, ty + 4, 5, 5, is_controlling() ? FL_YELLOW : fl_inactive( FL_YELLOW ) ); | |||
| } | |||
| fl_pop_clip(); | |||
| // box( FL_NO_BOX ); | |||
| Fl_Group::draw_children(); | |||
| } | |||
| void | |||
| Module::draw_label ( void ) | |||
| { | |||
| int tw, th, tx, ty; | |||
| bbox( tx, ty, tw, th ); | |||
| const char *lp = label(); | |||
| int l = strlen( label() ); | |||
| fl_color( FL_FOREGROUND_COLOR ); | |||
| char *s = NULL; | |||
| if ( l > 10 ) | |||
| { | |||
| s = new char[l]; | |||
| char *sp = s; | |||
| for ( ; *lp; ++lp ) | |||
| switch ( *lp ) | |||
| { | |||
| case 'i': case 'e': case 'o': case 'u': case 'a': | |||
| break; | |||
| default: | |||
| *(sp++) = *lp; | |||
| } | |||
| *sp = '\0'; | |||
| } | |||
| if ( l > 20 ) | |||
| fl_font( FL_HELVETICA, 10 ); | |||
| else | |||
| fl_font( FL_HELVETICA, 14 ); | |||
| fl_draw( s ? s : lp, tx, ty, tw, th, (Fl_Align)(FL_ALIGN_CENTER | FL_ALIGN_INSIDE) ); | |||
| if ( s ) | |||
| delete[] s; | |||
| } | |||
| #include "FL/test_press.H" | |||
| int | |||
| Module::handle ( int m ) | |||
| { | |||
| switch ( m ) | |||
| { | |||
| case FL_PUSH: | |||
| { | |||
| if ( test_press( FL_BUTTON1 ) ) | |||
| { | |||
| if ( _editor ) | |||
| { | |||
| _editor->show(); | |||
| } | |||
| else if ( ncontrol_inputs() ) | |||
| { | |||
| DMESSAGE( "Opening module parameters for \"%s\"", label() ); | |||
| _editor = new Module_Parameter_Editor( this ); | |||
| _editor->show(); | |||
| do { Fl::wait(); } | |||
| while ( _editor->shown() ); | |||
| DMESSAGE( "Module parameters for \"%s\" closed",label() ); | |||
| delete _editor; | |||
| _editor = NULL; | |||
| } | |||
| return 1; | |||
| } | |||
| break; | |||
| } | |||
| } | |||
| return Fl_Group::handle( m ); | |||
| } | |||
| @@ -0,0 +1,321 @@ | |||
| /*******************************************************************************/ | |||
| /* 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. */ | |||
| /*******************************************************************************/ | |||
| #pragma once | |||
| #include <FL/Fl.H> | |||
| #include <FL/Fl_Widget.H> | |||
| #include <FL/Fl_Button.H> | |||
| #include <FL/Fl_Group.H> | |||
| #include <stdlib.h> | |||
| #include "util/debug.h" | |||
| #include <vector> | |||
| #include "util/Thread.H" | |||
| class Chain; | |||
| class Module_Parameter_Editor; | |||
| class Module : public Fl_Group { | |||
| void init ( void ) | |||
| { | |||
| _editor = 0; | |||
| _chain = 0; | |||
| _instances = 1; | |||
| box( FL_UP_BOX ); | |||
| labeltype( FL_NO_LABEL ); | |||
| clip_children( 1 ); | |||
| } | |||
| int _ins; | |||
| int _outs; | |||
| int _instances; | |||
| unsigned long _nframes; | |||
| Chain *_chain; | |||
| Module_Parameter_Editor *_editor; | |||
| void cb_handle(Fl_Widget*); | |||
| static void cb_handle(Fl_Widget*, void*); | |||
| public: | |||
| class Port | |||
| { | |||
| /* char *type_names[] = { "Audio", "Control" }; */ | |||
| /* char *direction_names[] = { "Input", "Output" }; */ | |||
| public: | |||
| enum Direction { INPUT, OUTPUT }; | |||
| enum Type { AUDIO, CONTROL }; | |||
| /* hints for control ports (specifically control inputs) */ | |||
| struct Hints | |||
| { | |||
| enum Type { LINEAR, LOGARITHMIC, BOOLEAN, INTEGER }; | |||
| Type type; | |||
| bool ranged; | |||
| float minimum; | |||
| float maximum; | |||
| float default_value; | |||
| int dimensions; | |||
| Hints ( ) | |||
| { | |||
| type = LINEAR; | |||
| ranged = false; | |||
| minimum = 0; | |||
| maximum = 0; | |||
| default_value = 0.0f; | |||
| dimensions = 1; | |||
| } | |||
| }; | |||
| Hints hints; | |||
| Port ( Module *module, Direction direction, Type type, const char *name = 0 ) | |||
| { | |||
| _name = name; | |||
| _direction = direction; | |||
| _type = type; | |||
| _buf = 0; | |||
| _nframes = 0; | |||
| _connected = 0; | |||
| _module = module; | |||
| } | |||
| Port ( const Port& p ) | |||
| { | |||
| _name = p._name; | |||
| _direction = p._direction; | |||
| _type = p._type; | |||
| _buf = p._buf; | |||
| _nframes = p._nframes; | |||
| _connected = p._connected; | |||
| _module = p._module; | |||
| hints = p.hints; | |||
| } | |||
| virtual ~Port ( ) | |||
| { | |||
| // disconnect(); | |||
| } | |||
| const char *name ( void ) const { return _name; } | |||
| Type type ( void ) const { return _type; } | |||
| Direction direction ( void ) const { return _direction; } | |||
| Module * module ( void ) const { return _module; } | |||
| unsigned long nframes ( void ) const { return _nframes; } | |||
| void buffer ( void *buf, unsigned long nframes ) { _buf = buf; _nframes = nframes; }; | |||
| void *buffer ( void ) const { return _buf; } | |||
| void control_value_no_callback ( float f ) | |||
| { | |||
| THREAD_ASSERT( UI ); | |||
| if ( buffer() ) | |||
| { | |||
| *((float*)buffer()) = f; | |||
| } | |||
| } | |||
| void control_value ( float f ) | |||
| { | |||
| control_value_no_callback( f ); | |||
| _module->handle_control_changed( this ); | |||
| } | |||
| float control_value ( ) const | |||
| { | |||
| if ( buffer() ) | |||
| return *((float*)buffer()); | |||
| else | |||
| return 0.0f; | |||
| } | |||
| bool connected ( void ) const { return _connected; } | |||
| Port *connected_port ( void ) const | |||
| { | |||
| return _connected; | |||
| } | |||
| void connect_to ( Port *to ) | |||
| { | |||
| _buf = to->_buf; | |||
| to->_connected = this; | |||
| _connected = to; | |||
| } | |||
| void connect_to ( void *buf ) | |||
| { | |||
| _buf = buf; | |||
| // _connected = (Port*)0x01; | |||
| } | |||
| void disconnect ( void ) | |||
| { | |||
| if ( _connected && _connected != (void*)0x01 ) | |||
| { | |||
| _connected->_connected = NULL; | |||
| _connected = NULL; | |||
| } | |||
| else | |||
| _connected = NULL; | |||
| /* FIXME: do something! */ | |||
| } | |||
| private: | |||
| Port *_connected; | |||
| Type _type; | |||
| Direction _direction; | |||
| const char *_name; | |||
| void *_buf; | |||
| unsigned long _nframes; | |||
| Module *_module; | |||
| }; | |||
| void bbox ( int &X, int &Y, int &W, int &H ) | |||
| { | |||
| X = x() + 5; | |||
| Y = y() + 5; | |||
| W = w() - 10; | |||
| H = h() - 10; | |||
| } | |||
| Module ( int W, int H, const char *L = 0 ) : Fl_Group( 0, 0, W, H, L ) | |||
| { | |||
| init(); | |||
| } | |||
| Module ( ) : Fl_Group( 0, 0, 0, 50, "Unnamed" ) | |||
| { | |||
| init(); | |||
| } | |||
| virtual ~Module ( ); | |||
| // static Module * pick_plugin ( void ); | |||
| unsigned long nframes ( void ) const { return _nframes; } | |||
| void nframes ( unsigned long v ) { _nframes = v; } | |||
| int instances ( void ) const { return _instances; } | |||
| bool is_being_controlled ( void ) const | |||
| { | |||
| for ( unsigned int i = control_input.size(); i--; ) | |||
| if ( control_input[i].connected() ) | |||
| return true; | |||
| return false; | |||
| } | |||
| bool is_controlling ( void ) const | |||
| { | |||
| for ( unsigned int i = control_output.size(); i--; ) | |||
| if ( control_output[i].connected() ) | |||
| return true; | |||
| return false; | |||
| } | |||
| virtual const char *name ( void ) const = 0; | |||
| std::vector<Port> audio_input; | |||
| std::vector<Port> audio_output; | |||
| std::vector<Port> control_input; | |||
| std::vector<Port> control_output; | |||
| void add_port ( const Port &p ) | |||
| { | |||
| if ( p.type() == Port::AUDIO && p.direction() == Port::INPUT ) | |||
| audio_input.push_back( p ); | |||
| else if ( p.type() == Port::AUDIO && p.direction() == Port::OUTPUT ) | |||
| audio_output.push_back( p ); | |||
| else if ( p.type() == Port::CONTROL && p.direction() == Port::INPUT ) | |||
| control_input.push_back( p ); | |||
| else if ( p.type() == Port::CONTROL && p.direction() == Port::OUTPUT ) | |||
| control_output.push_back( p ); | |||
| } | |||
| int noutputs ( void ) const | |||
| { | |||
| return audio_output.size(); | |||
| } | |||
| int ninputs ( void ) const | |||
| { | |||
| return audio_input.size(); | |||
| } | |||
| int ncontrol_inputs ( void ) const | |||
| { | |||
| return control_input.size(); | |||
| } | |||
| int ncontrol_outputs ( void ) const | |||
| { | |||
| return control_output.size(); | |||
| } | |||
| Chain *chain ( void ) const { return _chain; } | |||
| void chain ( Chain * v ) { _chain = v; } | |||
| virtual bool initialize ( void ) { return true; } | |||
| /* for the given number of inputs, return how many outputs this | |||
| * plugin would have. -1 if this plugin can't support so many | |||
| * inputs. */ | |||
| virtual int can_support_inputs ( int n ) = 0; | |||
| /* called by the chain whenever we need to adjust our input | |||
| * channel configuration, but only if can_support_inputs() returns | |||
| * true */ | |||
| virtual bool configure_inputs ( int n ) = 0; | |||
| virtual void process ( void ) { } | |||
| /* called whenever the value of a control port is changed. | |||
| This can be used to take appropriate action from the GUI thread */ | |||
| virtual void handle_control_changed ( Port * ) { } | |||
| /* called whenever the name of the chain changes (usually because | |||
| * the name of the mixer strip changed). */ | |||
| virtual void handle_chain_name_changed () {} | |||
| protected: | |||
| void draw_connections ( void ); | |||
| void draw_label ( void ); | |||
| void draw_box ( void ); | |||
| virtual void draw ( void ) { Module::draw_box(); Module::draw_label(); } | |||
| virtual int handle ( int m ); | |||
| }; | |||
| @@ -0,0 +1,293 @@ | |||
| /*******************************************************************************/ | |||
| /* 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. */ | |||
| /*******************************************************************************/ | |||
| #include <FL/fl_draw.H> | |||
| #include <FL/Fl_Pack.H> | |||
| #include <FL/Fl_Value_Slider.H> | |||
| #include <FL/Fl_Box.H> | |||
| #include <FL/Fl_Counter.H> | |||
| #include "FL/Fl_Arc_Dial.H" | |||
| #include "FL/Boxtypes.H" | |||
| #include "FL/Fl_Flowpack.H" | |||
| #include "FL/Fl_Labelpad_Group.H" | |||
| #include "FL/Fl_Choice.H" | |||
| #include "Module.H" | |||
| #include "Module_Parameter_Editor.H" | |||
| #include "Controller_Module.H" | |||
| #include "Chain.H" | |||
| #include <string.h> | |||
| #include <stdlib.h> | |||
| #include <stdio.h> | |||
| #include <util/debug.h> | |||
| Module_Parameter_Editor::Module_Parameter_Editor ( Module *module ) : Fl_Double_Window( 0, 0, 800, 600 ) | |||
| { | |||
| _module = module; | |||
| _resized = false; | |||
| char lab[256]; | |||
| if ( strcmp( module->name(), module->label() ) ) | |||
| { | |||
| snprintf( lab, sizeof( lab ), "%s : %s", module->name(), module->label() ); | |||
| } | |||
| else | |||
| strcpy( lab, module->label() ); | |||
| char title[512]; | |||
| snprintf( title, sizeof( title ), "%s - %s - %s", "Mixer", module->chain()->name(), lab ); | |||
| label( title ); | |||
| { Fl_Pack *o = main_pack = new Fl_Pack( 0, y(), w(), h() - 10 ); | |||
| o->type( FL_VERTICAL ); | |||
| o->label( strdup( lab ) ); | |||
| o->labeltype( FL_SHADOW_LABEL ); | |||
| o->labelsize( 18 ); | |||
| o->align( (Fl_Align)(FL_ALIGN_TOP | FL_ALIGN_RIGHT | FL_ALIGN_INSIDE ) ); | |||
| { Fl_Pack *o = new Fl_Pack( 0, 0, 50, 25 ); | |||
| o->type( FL_HORIZONTAL ); | |||
| o->spacing( 20 ); | |||
| { Fl_Choice *o = mode_choice = new Fl_Choice( 0, 0, 200, 25 ); | |||
| o->add( "Knobs" ); | |||
| o->add( "Horizontal Sliders" ); | |||
| o->add( "Vertical Sliders" ); | |||
| o->value( 0 ); | |||
| o->when( FL_WHEN_CHANGED ); | |||
| o->callback( cb_mode_handle, this ); | |||
| } | |||
| /* { Fl_Box *o = new Fl_Box( 0, 0, 300, 25 ); */ | |||
| /* o->box( FL_ROUNDED_BOX ); */ | |||
| /* o->color( FL_YELLOW ); */ | |||
| /* o->label( strdup( lab ) ); */ | |||
| /* o->labeltype( FL_SHADOW_LABEL ); */ | |||
| /* o->labelsize( 18 ); */ | |||
| /* o->align( (Fl_Align)(FL_ALIGN_TOP | FL_ALIGN_RIGHT | FL_ALIGN_INSIDE ) ); */ | |||
| /* // Fl_Group::current()->resizable( o ); */ | |||
| /* } */ | |||
| o->end(); | |||
| } | |||
| { Fl_Group *o = new Fl_Group( 0, 0, w(), h() ); | |||
| { Fl_Flowpack *o = control_pack = new Fl_Flowpack( 50, 0, w() - 100, h() ); | |||
| /* o->box( FL_ROUNDED_BOX ); */ | |||
| /* o->color( FL_GRAY ); */ | |||
| o->vspacing( 10 ); | |||
| o->hspacing( 10 ); | |||
| o->end(); | |||
| } | |||
| o->resizable( 0 ); | |||
| o->end(); | |||
| } | |||
| o->end(); | |||
| } | |||
| end(); | |||
| // draw(); | |||
| make_controls(); | |||
| } | |||
| Module_Parameter_Editor::~Module_Parameter_Editor ( ) | |||
| { | |||
| } | |||
| void | |||
| Module_Parameter_Editor::make_controls ( void ) | |||
| { | |||
| Module *module = _module; | |||
| control_pack->clear(); | |||
| for ( unsigned int i = 0; i < module->control_input.size(); ++i ) | |||
| { | |||
| Fl_Widget *w; | |||
| Module::Port *p = &module->control_input[i]; | |||
| if ( p->hints.type == Module::Port::Hints::BOOLEAN ) | |||
| { | |||
| Fl_Button *o = new Fl_Button( 0, 0, 30, 30, p->name() ); | |||
| w = o; | |||
| o->selection_color( FL_GREEN ); | |||
| o->type( FL_TOGGLE_BUTTON ); | |||
| o->value( p->control_value() ); | |||
| } | |||
| else if ( p->hints.type == Module::Port::Hints::INTEGER ) | |||
| { | |||
| Fl_Counter *o = new Fl_Counter(0, 0, 58, 24, p->name() ); | |||
| w = o; | |||
| o->type(1); | |||
| o->step(1); | |||
| if ( p->hints.ranged ) | |||
| { | |||
| o->minimum( p->hints.minimum ); | |||
| o->maximum( p->hints.maximum ); | |||
| } | |||
| o->value( p->control_value() ); | |||
| } | |||
| else | |||
| { | |||
| if ( mode_choice->value() == 0 ) | |||
| { | |||
| Fl_Arc_Dial *o = new Fl_Arc_Dial( 0, 0, 50, 50, p->name() ); | |||
| w = o; | |||
| if ( p->hints.ranged ) | |||
| { | |||
| o->minimum( p->hints.minimum ); | |||
| o->maximum( p->hints.maximum ); | |||
| } | |||
| o->box( FL_BURNISHED_OVAL_BOX ); | |||
| o->color( fl_darker( fl_darker( FL_GRAY ) ) ); | |||
| o->selection_color( FL_WHITE ); | |||
| o->value( p->control_value() ); | |||
| } | |||
| else | |||
| { | |||
| Fl_Value_Slider *o = new Fl_Value_Slider( 0, 0, 120, 24, p->name() ); | |||
| w = o; | |||
| if ( p->hints.ranged ) | |||
| { | |||
| o->minimum( p->hints.minimum ); | |||
| o->maximum( p->hints.maximum ); | |||
| } | |||
| if ( mode_choice->value() == 1 ) | |||
| { | |||
| o->type( FL_HORIZONTAL ); | |||
| o->size( 120, 24 ); | |||
| } | |||
| else | |||
| { | |||
| o->type( FL_VERTICAL ); | |||
| o->size( 24, 120 ); | |||
| } | |||
| o->slider( FL_THIN_UP_BOX ); | |||
| o->color( fl_darker( fl_darker( FL_GRAY ) ) ); | |||
| o->selection_color( FL_WHITE ); | |||
| o->value( p->control_value() ); | |||
| } | |||
| } | |||
| Fl_Button *bound; | |||
| w->align(FL_ALIGN_TOP); | |||
| w->labelsize( 10 ); | |||
| w->callback( cb_value_handle, new callback_data( this, i ) ); | |||
| { Fl_Group *o = new Fl_Group( 0, 0, 50, 75 ); | |||
| { | |||
| Fl_Labelpad_Group *flg = new Fl_Labelpad_Group( w ); | |||
| { Fl_Button *o = bound = new Fl_Button( 0, 50, 14, 14 ); | |||
| o->selection_color( FL_YELLOW ); | |||
| o->type( 0 ); | |||
| o->labelsize( 8 ); | |||
| o->value( p->connected() ); | |||
| o->callback( cb_bound_handle, new callback_data( this, i ) ); | |||
| } | |||
| o->resizable( 0 ); | |||
| o->end(); | |||
| flg->position( o->x(), o->y() ); | |||
| bound->position( o->x(), flg->y() + flg->h() ); | |||
| o->size( flg->w(), flg->h() + bound->h() ); | |||
| o->init_sizes(); | |||
| } | |||
| control_pack->add( o ); | |||
| } | |||
| } | |||
| main_pack->size( control_pack->max_width() + 100, control_pack->h() + 50 ); | |||
| size( control_pack->max_width() + 100, control_pack->h() + 50 ); | |||
| } | |||
| void | |||
| Module_Parameter_Editor::cb_value_handle ( Fl_Widget *w, void *v ) | |||
| { | |||
| callback_data *cd = (callback_data*)v; | |||
| cd->base_widget->set_value( cd->port_number, ((Fl_Valuator*)w)->value() ); | |||
| } | |||
| void | |||
| Module_Parameter_Editor::cb_mode_handle ( Fl_Widget *w, void *v ) | |||
| { | |||
| ((Module_Parameter_Editor*)v)->make_controls(); | |||
| } | |||
| void | |||
| Module_Parameter_Editor::cb_bound_handle ( Fl_Widget *w, void *v ) | |||
| { | |||
| callback_data *cd = (callback_data*)v; | |||
| Fl_Button *fv = (Fl_Button*)w; | |||
| fv->value( 1 ); | |||
| cd->base_widget->bind_control( cd->port_number ); | |||
| } | |||
| void | |||
| Module_Parameter_Editor::bind_control ( int i ) | |||
| { | |||
| Module::Port *p = &_module->control_input[i]; | |||
| if ( p->connected() ) | |||
| /* can only bind once */ | |||
| return; | |||
| Controller_Module *o = new Controller_Module( 50, 50, p->name() ); | |||
| o->chain( _module->chain() ); | |||
| o->connect_to( p ); | |||
| _module->chain()->add_control( o ); | |||
| _module->redraw(); | |||
| } | |||
| void | |||
| Module_Parameter_Editor::set_value (int i, float value ) | |||
| { | |||
| _module->control_input[i].control_value( value ); | |||
| _module->handle_control_changed( &_module->control_input[i] ); | |||
| } | |||
| @@ -0,0 +1,62 @@ | |||
| /*******************************************************************************/ | |||
| /* 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. */ | |||
| /*******************************************************************************/ | |||
| #pragma once | |||
| #include <FL/Fl_Double_Window.H> | |||
| class Fl_Pack; | |||
| class Fl_Flowpack; | |||
| class Fl_Choice; | |||
| class Module; | |||
| class Module_Parameter_Editor : public Fl_Double_Window | |||
| { | |||
| Module *_module; | |||
| struct callback_data | |||
| { | |||
| Module_Parameter_Editor *base_widget; | |||
| int port_number; | |||
| callback_data ( Module_Parameter_Editor *base_widget, int port_number ) | |||
| { | |||
| this->base_widget = base_widget; | |||
| this->port_number = port_number; | |||
| } | |||
| }; | |||
| static void cb_value_handle ( Fl_Widget *w, void *v ); | |||
| static void cb_mode_handle ( Fl_Widget *w, void *v ); | |||
| static void cb_bound_handle ( Fl_Widget *w, void *v ); | |||
| void set_value (int i, float value ); | |||
| void bind_control ( int i ); | |||
| void make_controls ( void ); | |||
| Fl_Pack *main_pack; | |||
| Fl_Flowpack *control_pack; | |||
| Fl_Choice *mode_choice; | |||
| bool _resized; | |||
| public: | |||
| Module_Parameter_Editor ( Module *module ); | |||
| virtual ~Module_Parameter_Editor ( ); | |||
| }; | |||
| @@ -0,0 +1,283 @@ | |||
| /*******************************************************************************/ | |||
| /* Copyright (C) 2008 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. */ | |||
| /*******************************************************************************/ | |||
| #include "Panner.H" | |||
| #include <stdio.h> | |||
| #include <math.h> | |||
| /* 2D Panner widget. Supports various multichannel configurations. */ | |||
| /* multichannel layouts, in degrees */ | |||
| int Panner::_configs[][12] = | |||
| { | |||
| /* none, error condition? */ | |||
| { NONE }, | |||
| /* mono, panner disabled */ | |||
| { NONE }, | |||
| /* stereo */ | |||
| { L, R }, | |||
| /* stereo + mono */ | |||
| { L, R, C }, | |||
| /* quad */ | |||
| { FL, FR, RL, RR }, | |||
| /* 5.1 */ | |||
| { FL, FR, RL, RR, C }, | |||
| /* no such config */ | |||
| { NONE }, | |||
| /* 7.1 */ | |||
| { FL, FR, RL, RR, C, L, R }, | |||
| }; | |||
| /* speaker symbol */ | |||
| #define BP fl_begin_polygon() | |||
| #define EP fl_end_polygon() | |||
| #define BCP fl_begin_complex_polygon() | |||
| #define ECP fl_end_complex_polygon() | |||
| #define BL fl_begin_line() | |||
| #define EL fl_end_line() | |||
| #define BC fl_begin_loop() | |||
| #define EC fl_end_loop() | |||
| #define vv(x,y) fl_vertex(x,y) | |||
| static void draw_speaker ( Fl_Color col ) | |||
| { | |||
| fl_color(col); | |||
| BP; vv(0.2,0.4); vv(0.6,0.4); vv(0.6,-0.4); vv(0.2,-0.4); EP; | |||
| BP; vv(-0.6,0.8); vv(0.2,0.0); vv(-0.6,-0.8); EP; | |||
| fl_color( fl_darker( col ) ); | |||
| BC; vv(0.2,0.4); vv(0.6,0.4); vv(0.6,-0.4); vv(0.2,-0.4); EC; | |||
| BC; vv(-0.6,0.8); vv(0.2,0.0); vv(-0.6,-0.8); EC; | |||
| } | |||
| /** set X, Y, W, and H to the bounding box of point /p/ in screen coords */ | |||
| void | |||
| Panner::point_bbox ( const Point *p, int *X, int *Y, int *W, int *H ) const | |||
| { | |||
| int tx, ty, tw, th; | |||
| bbox( tx, ty, tw, th ); | |||
| tw -= pw(); | |||
| th -= ph(); | |||
| float px, py; | |||
| p->axes( &px, &py ); | |||
| *X = tx + ((tw / 2) * px + (tw / 2)); | |||
| *Y = ty + ((th / 2) * py + (th / 2)); | |||
| *W = pw(); | |||
| *H = ph(); | |||
| } | |||
| Panner::Point * | |||
| Panner::event_point ( void ) | |||
| { | |||
| for ( int i = _ins; i--; ) | |||
| { | |||
| int px, py, pw, ph; | |||
| Point *p = &_points[ i ]; | |||
| point_bbox( p, &px, &py, &pw, &ph ); | |||
| // printf( "%d, %d -- %d %d %d %d\n", Fl::event_x(), Fl::event_y(), px, py, pw, ph ); | |||
| if ( Fl::event_inside( px, py, pw, ph ) ) | |||
| return p; | |||
| } | |||
| return NULL; | |||
| } | |||
| void | |||
| Panner::draw ( void ) | |||
| { | |||
| draw_box(); | |||
| // draw_box( FL_FLAT_BOX, x(), y(), w(), h(), FL_BLACK ); | |||
| draw_label(); | |||
| if ( _bypassed ) | |||
| { | |||
| fl_color( 0 ); | |||
| fl_font( FL_HELVETICA, 12 ); | |||
| fl_draw( "(bypass)", x(), y(), w(), h(), FL_ALIGN_CENTER ); | |||
| return; | |||
| } | |||
| int tw, th, tx, ty; | |||
| bbox( tx, ty, tw, th ); | |||
| fl_push_clip( tx, ty, tw, th ); | |||
| fl_color( FL_WHITE ); | |||
| const int b = 10; | |||
| tx += b; | |||
| ty += b; | |||
| tw -= b * 2; | |||
| th -= b * 2; | |||
| fl_arc( tx, ty, tw, th, 0, 360 ); | |||
| if ( _configs[ _outs ][0] >= 0 ) | |||
| { | |||
| for ( int i = _outs; i--; ) | |||
| { | |||
| int a = _configs[ _outs ][ i ]; | |||
| Point p( 1.2f, (float)a ); | |||
| float px, py; | |||
| p.axes( &px, &py ); | |||
| fl_push_matrix(); | |||
| const int bx = tx + ((tw / 2) * px + (tw / 2)); | |||
| const int by = ty + ((th / 2) * py + (th / 2)); | |||
| fl_translate( bx, by ); | |||
| fl_scale( 5, 5 ); | |||
| a = 90 - a; | |||
| fl_rotate( a ); | |||
| draw_speaker( FL_WHITE ); | |||
| fl_rotate( -a ); | |||
| fl_pop_matrix(); | |||
| } | |||
| } | |||
| /* ensure that points are drawn *inside* the circle */ | |||
| for ( int i = _ins; i--; ) | |||
| { | |||
| Point *p = &_points[ i ]; | |||
| Fl_Color c = (Fl_Color)(10 + i); | |||
| int px, py, pw, ph; | |||
| point_bbox( p, &px, &py, &pw, &ph ); | |||
| /* draw point */ | |||
| fl_color( c ); | |||
| fl_pie( px, py, pw, ph, 0, 360 ); | |||
| /* draw echo */ | |||
| fl_color( c = fl_darker( c ) ); | |||
| fl_arc( px - 5, py - 5, pw + 10, ph + 10, 0, 360 ); | |||
| fl_color( c = fl_darker( c ) ); | |||
| fl_arc( px - 10, py - 10, pw + 20, ph + 20, 0, 360 ); | |||
| fl_color( c = fl_darker( c ) ); | |||
| fl_arc( px - 30, py - 30, pw + 60, ph + 60, 0, 360 ); | |||
| /* draw number */ | |||
| char pat[4]; | |||
| snprintf( pat, 4, "%d", i + 1 ); | |||
| fl_color( FL_BLACK ); | |||
| fl_font( FL_HELVETICA, ph + 2 ); | |||
| fl_draw( pat, px + 1, py + 1, pw - 1, ph - 1, FL_ALIGN_CENTER ); | |||
| /* draw line */ | |||
| /* fl_color( FL_WHITE ); */ | |||
| /* fl_line( bx + pw() / 2, by + ph() / 2, tx + (tw / 2), ty + (th / 2) ); */ | |||
| } | |||
| fl_pop_clip(); | |||
| } | |||
| /* return the current gain setting for the path in/out */ | |||
| Panner::Point | |||
| Panner::point( int i ) | |||
| { | |||
| return _points[ i ]; | |||
| } | |||
| int | |||
| Panner::handle ( int m ) | |||
| { | |||
| static Point *drag; | |||
| int r = Fl_Widget::handle( m ); | |||
| switch ( m ) | |||
| { | |||
| case FL_PUSH: | |||
| if ( Fl::event_button2() ) | |||
| { | |||
| _bypassed = ! _bypassed; | |||
| redraw(); | |||
| return 0; | |||
| } | |||
| else if ( Fl::event_button1() && ( drag = event_point() ) ) | |||
| return 1; | |||
| else | |||
| return 0; | |||
| case FL_RELEASE: | |||
| drag = NULL; | |||
| return 1; | |||
| case FL_DRAG: | |||
| { | |||
| float X = Fl::event_x() - x(); | |||
| float Y = Fl::event_y() - y(); | |||
| int tx, ty, tw, th; | |||
| bbox( tx, ty, tw, th ); | |||
| /* if ( _outs < 3 ) */ | |||
| /* drag->angle( (float)(X / (tw / 2)) - 1.0f, 0.0f ); */ | |||
| /* else */ | |||
| drag->angle( (float)(X / (tw / 2)) - 1.0f, (float)(Y / (th / 2)) - 1.0f ); | |||
| printf( "%f %f\n", drag->a, drag->d ); | |||
| redraw(); | |||
| return 1; | |||
| } | |||
| } | |||
| return r; | |||
| // return 0; | |||
| } | |||
| @@ -0,0 +1,172 @@ | |||
| /*******************************************************************************/ | |||
| /* Copyright (C) 2008 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. */ | |||
| /*******************************************************************************/ | |||
| #pragma once | |||
| #include <FL/Fl_Widget.H> | |||
| #include <FL/fl_draw.H> | |||
| #include <FL/Fl.H> | |||
| #include <math.h> | |||
| #include <vector> | |||
| using namespace std; | |||
| class Panner : public Fl_Widget | |||
| { | |||
| struct Point | |||
| { | |||
| /* axes */ | |||
| /* distance from center (from 0 to 1) */ | |||
| float d; | |||
| /* angle */ | |||
| float a; | |||
| Point ( ) : d( 0.0f ), a( 0.0f ) { } | |||
| Point ( float D, float A ) : d( D ), a( A ) { } | |||
| /** translate angle /a/ into x/y coords and place the result in /X/ and /Y/ */ | |||
| void | |||
| axes ( float *X, float *Y ) const | |||
| { | |||
| /* rotate */ | |||
| float A = ( 270 - a ) * ( M_PI / 180 ); | |||
| *X = -d * cosf( A ); | |||
| *Y = d * sinf( A ); | |||
| } | |||
| void | |||
| angle ( float X1, float Y1 ) | |||
| { | |||
| float X2, Y2; | |||
| Y2 = X2 = 0; | |||
| float t; | |||
| t = atan2( X2 - X1, Y2 - Y1 ); | |||
| a = t * (180 / M_PI); | |||
| if ( a < 0 ) | |||
| a = 360 + a; | |||
| a = 360 - a; | |||
| /* standard distance calculation */ | |||
| d = sqrt( pow( X2 - X1, 2 ) + pow( Y2 - Y1, 2 ) ); | |||
| if ( d > 1.0f ) | |||
| d = 1.0f; | |||
| } | |||
| /** return the distance between the point and that referenced by /p/ */ | |||
| float | |||
| distance ( const Point &p ) | |||
| { | |||
| /* first, transform point coords */ | |||
| float x1, y1, x2, y2; | |||
| axes( &x1, &y1 ); | |||
| p.axes( &x2, &y2 ); | |||
| /* standard distance calculation */ | |||
| return sqrt( pow( x1 - x2, 2 ) + pow( y1 - y2, 2 ) ); | |||
| } | |||
| }; | |||
| /* channel configuration */ | |||
| int _ins, | |||
| _outs; | |||
| bool _bypassed; | |||
| vector <Point> _points; | |||
| static int pw ( void ) { return 12; } | |||
| static int ph ( void ) { return 12; } | |||
| static int _configs[][12]; | |||
| void bbox ( int &X, int &Y, int &W, int &H ) const | |||
| { | |||
| W = w() - Fl::box_dw( box() ); | |||
| H = h() - Fl::box_dh( box() ); | |||
| X = x() + Fl::box_dx( box() ); | |||
| Y = y() + Fl::box_dy( box() ); | |||
| } | |||
| void point_bbox ( const Point *p, int *X, int *Y, int *W, int *H ) const; | |||
| Point * event_point ( void ); | |||
| Point angle_to_axes ( float a ); | |||
| enum { | |||
| NONE = -1, | |||
| R = 90, | |||
| L = 270, | |||
| C = 0, | |||
| FL = 315, | |||
| FR = 45, | |||
| RL = 225, | |||
| RR = 135, | |||
| }; | |||
| protected: | |||
| virtual void draw ( void ); | |||
| virtual int handle ( int ); | |||
| public: | |||
| Panner ( int X, int Y, int W, int H, const char *L = 0 ) : | |||
| Fl_Widget( X, Y, W, H, L ) | |||
| { | |||
| _bypassed = false; | |||
| _ins = 1; | |||
| _outs = 1; | |||
| // _ins = _outs = 4; | |||
| // _points.push_back( Point( 1, FL ) ); | |||
| _points.push_back( Point( 1, C ) ); | |||
| /* _points.push_back( Point( 1, FR ) ); */ | |||
| /* _points.push_back( Point( 1, RL ) ); */ | |||
| /* _points.push_back( Point( 1, RR ) ); */ | |||
| } | |||
| virtual ~Panner ( ) { } | |||
| Panner::Point point ( int i ); | |||
| }; | |||
| @@ -0,0 +1,577 @@ | |||
| /*******************************************************************************/ | |||
| /* 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. */ | |||
| /*******************************************************************************/ | |||
| /* Filter module. Can host LADPSA Plugins, or can be inherited from to make internal | |||
| modules with special features and appearance. */ | |||
| #include "Plugin_Module.H" | |||
| #include <Fl/fl_draw.H> | |||
| #include <FL/Fl_Group.H> | |||
| #include "util/debug.h" | |||
| #include <string.h> | |||
| #define HAVE_LIBLRDF 1 | |||
| #include "LADSPAInfo.h" | |||
| #include <vector> | |||
| #include <string> | |||
| #include <ladspa.h> | |||
| #include <stdlib.h> | |||
| #include <math.h> | |||
| #include "Engine/Engine.H" | |||
| static LADSPAInfo *ladspainfo; | |||
| /* keep this out of the header to avoid spreading ladspa.h dependency */ | |||
| struct Plugin_Module::ImplementationData | |||
| { | |||
| const LADSPA_Descriptor *descriptor; | |||
| // std::vector<LADSPA_Data*> m_LADSPABufVec; | |||
| LADSPA_Handle handle; | |||
| }; | |||
| Plugin_Module::Plugin_Module ( int , int , const char *L ) : Module( 50, 50, L ) | |||
| { | |||
| init(); | |||
| end(); | |||
| } | |||
| Plugin_Module::~Plugin_Module ( ) | |||
| { | |||
| } | |||
| /* void */ | |||
| /* Plugin_Module::detect_plugins ( void ) */ | |||
| /* { */ | |||
| /* LADPSAInfo *li = new LADSPAInfo(); */ | |||
| /* } */ | |||
| #include <FL/Fl_Menu_Button.H> | |||
| /* allow the user to pick a plugin */ | |||
| Plugin_Module * | |||
| Plugin_Module::pick_plugin ( void ) | |||
| { | |||
| /**************/ | |||
| /* build menu */ | |||
| /**************/ | |||
| Fl_Menu_Button *menu = new Fl_Menu_Button( 0, 0, 400, 400 ); | |||
| menu->type( Fl_Menu_Button::POPUP3 ); | |||
| Plugin_Module::Plugin_Info *pia = Plugin_Module::discover(); | |||
| for ( Plugin_Module::Plugin_Info *pi = pia; pi->path; ++pi ) | |||
| { | |||
| menu->add(pi->path, 0, NULL, pi, 0 ); | |||
| } | |||
| menu->popup(); | |||
| if ( menu->value() <= 0 ) | |||
| return NULL; | |||
| /************************/ | |||
| /* load selected plugin */ | |||
| /************************/ | |||
| Plugin_Module::Plugin_Info *pi = (Plugin_Module::Plugin_Info*)menu->menu()[ menu->value() ].user_data(); | |||
| Plugin_Module *m = new Plugin_Module( 50, 50 ); | |||
| // Plugin_Module *plugin = new Plugin_Module(); | |||
| m->load( pi ); | |||
| const char *plugin_name = pi->path; | |||
| char *label = strdup( rindex(plugin_name, '/') + 1 ); | |||
| m->label( label ); | |||
| delete[] pia; | |||
| return m; | |||
| } | |||
| void | |||
| Plugin_Module::init ( void ) | |||
| { | |||
| _idata = new Plugin_Module::ImplementationData(); | |||
| _active = false; | |||
| _crosswire = true; | |||
| _instances = 1; | |||
| // box( FL_ROUNDED_BOX ); | |||
| // box( FL_NO_BOX ); | |||
| align( (Fl_Align)FL_ALIGN_CENTER | FL_ALIGN_INSIDE ); | |||
| color( (Fl_Color)fl_color_average( FL_BLUE, FL_GREEN, 0.5f ) ); | |||
| int tw, th, tx, ty; | |||
| bbox( tx, ty, tw, th ); | |||
| } | |||
| #include "FL/test_press.H" | |||
| int | |||
| Plugin_Module::handle ( int m ) | |||
| { | |||
| switch ( m ) | |||
| { | |||
| case FL_ENTER: | |||
| case FL_LEAVE: | |||
| redraw(); | |||
| return 1; | |||
| break; | |||
| default: | |||
| return Module::handle( m ); | |||
| } | |||
| return 0; | |||
| } | |||
| /* There are two possible adaptations that can be made at Plugin_Module input to account for a mismatch | |||
| between channel configurations. | |||
| The two scenarios are as follows. | |||
| 1. The preceding module has fewer outputs than this module has inputs. If | |||
| the preceding module has 1 output (MONO) then it will be duplicated | |||
| for this module's addition inputs. If the preceding module has more | |||
| than one output, then the chain is in error. | |||
| 2. The preceding module has more outputs than this module has inputs | |||
| If this module has 1 output (MONO) then it will create the required number of | |||
| instances of its plugin. | |||
| Stereo plugins are never run with more than one instance. Mono | |||
| plugins will have their outputs brought up to stereo for plugins with | |||
| stereo input. | |||
| */ | |||
| int | |||
| Plugin_Module::can_support_inputs ( int n ) | |||
| { | |||
| /* this is the simple case */ | |||
| if ( plugin_ins() == n ) | |||
| return plugin_outs(); | |||
| /* e.g. MONO going into STEREO */ | |||
| /* we'll duplicate our inputs */ | |||
| else if ( n < plugin_ins() && | |||
| 1 == n ) | |||
| { | |||
| return plugin_outs(); | |||
| } | |||
| /* e.g. STEREO going into MONO */ | |||
| /* we'll run multiple instances of the plugin */ | |||
| else if ( n > plugin_ins() && | |||
| plugin_ins() == 1 && plugin_outs() == 1 ) | |||
| { | |||
| return plugin_outs() * n; | |||
| // instances( i ); | |||
| } | |||
| return -1; | |||
| } | |||
| bool | |||
| Plugin_Module::configure_inputs( int n ) | |||
| { | |||
| if ( 1 == n && plugin_ins() > 1 ) | |||
| { | |||
| _crosswire = true; | |||
| audio_input.clear(); | |||
| audio_input.push_back( Port( this, Port::INPUT, Port::AUDIO ) ); | |||
| } | |||
| /* audio_input.clear(); */ | |||
| /* audio_output.clear(); */ | |||
| /* for ( int i = 0; i < n; ++i ) */ | |||
| /* { */ | |||
| /* add_port( Port( Port::INPUT, Port::AUDIO ) ); */ | |||
| /* } */ | |||
| /* if ( n > plugin_ins() ) */ | |||
| /* { */ | |||
| /* /\* multiple instances *\/ */ | |||
| /* instances( n / plugin_ins() ); */ | |||
| /* } */ | |||
| /* else if ( n < plugin_ins() ) */ | |||
| /* { */ | |||
| /* /\* duplication of input *\/ */ | |||
| /* } */ | |||
| /* for ( int i = 0; i < plugin_outs() * instances(); ++i ) */ | |||
| /* { */ | |||
| /* add_port( Port( this, Port::OUTPUT, Port::AUDIO ) ); */ | |||
| /* } */ | |||
| if ( ! _active ) | |||
| activate(); | |||
| /* // _plugin->deactivate(); */ | |||
| /* /\* if ( _plugin->active() ) *\/ */ | |||
| /* /\* _plugin->activate(); *\/ */ | |||
| /* FIXME: do controls */ | |||
| return true; | |||
| } | |||
| /* return a list of available plugins */ | |||
| Plugin_Module::Plugin_Info * | |||
| Plugin_Module::discover ( void ) | |||
| { | |||
| if ( !ladspainfo ) | |||
| ladspainfo = new LADSPAInfo(); | |||
| std::vector<LADSPAInfo::PluginEntry> plugins = ladspainfo->GetMenuList(); | |||
| Plugin_Info* pi = new Plugin_Info[plugins.size() + 1]; | |||
| int j = 0; | |||
| for (std::vector<LADSPAInfo::PluginEntry>::iterator i=plugins.begin(); | |||
| i!=plugins.end(); i++, j++) | |||
| { | |||
| pi[j].path = i->Name.c_str(); | |||
| pi[j].id = i->UniqueID; | |||
| } | |||
| return pi; | |||
| } | |||
| bool | |||
| Plugin_Module::load ( Plugin_Module::Plugin_Info *pi ) | |||
| { | |||
| _idata->descriptor = ladspainfo->GetDescriptorByID( pi->id ); | |||
| _plugin_ins = _plugin_outs = 0; | |||
| if ( _idata->descriptor ) | |||
| { | |||
| if ( LADSPA_IS_INPLACE_BROKEN( _idata->descriptor->Properties ) ) | |||
| { | |||
| WARNING( "Cannot use this plugin because it is incapable of processing audio in-place" ); | |||
| return false; | |||
| } | |||
| else if ( ! LADSPA_IS_HARD_RT_CAPABLE( _idata->descriptor->Properties ) ) | |||
| { | |||
| WARNING( "Cannot use this plugin because it is incapable of hard real-time operation" ); | |||
| return false; | |||
| } | |||
| /* FIXME: bogus rate */ | |||
| if ( ! (_idata->handle = _idata->descriptor->instantiate( _idata->descriptor, engine->sample_rate() ) ) ) | |||
| { | |||
| WARNING( "Failed to load plugin" ); | |||
| return false; | |||
| } | |||
| // _idata->descriptor->activate( _idata->handle ); | |||
| MESSAGE( "Name: %s", _idata->descriptor->Name ); | |||
| for ( unsigned int i = 0; i < _idata->descriptor->PortCount; ++i ) | |||
| { | |||
| if ( LADSPA_IS_PORT_AUDIO( _idata->descriptor->PortDescriptors[i] ) ) | |||
| { | |||
| if ( LADSPA_IS_PORT_INPUT( _idata->descriptor->PortDescriptors[i] ) ) | |||
| { | |||
| add_port( Port( this, Port::INPUT, Port::AUDIO, _idata->descriptor->PortNames[ i ] ) ); | |||
| _plugin_ins++; | |||
| } | |||
| else if (LADSPA_IS_PORT_OUTPUT(_idata->descriptor->PortDescriptors[i])) | |||
| { | |||
| _plugin_outs++; | |||
| add_port( Port( this, Port::OUTPUT, Port::AUDIO, _idata->descriptor->PortNames[ i ] ) ); | |||
| } | |||
| } | |||
| } | |||
| MESSAGE( "Plugin has %i inputs and %i outputs", _plugin_ins, _plugin_outs); | |||
| for ( unsigned int i = 0; i < _idata->descriptor->PortCount; ++i ) | |||
| { | |||
| if ( LADSPA_IS_PORT_CONTROL( _idata->descriptor->PortDescriptors[i] ) ) | |||
| { | |||
| Port::Direction d; | |||
| if ( LADSPA_IS_PORT_INPUT( _idata->descriptor->PortDescriptors[i] ) ) | |||
| { | |||
| d = Port::INPUT; | |||
| } | |||
| else if ( LADSPA_IS_PORT_OUTPUT( _idata->descriptor->PortDescriptors[i] ) ) | |||
| { | |||
| d = Port::OUTPUT; | |||
| } | |||
| Port p( this, d, Port::CONTROL, _idata->descriptor->PortNames[ i ] ); | |||
| LADSPA_PortRangeHintDescriptor hd = _idata->descriptor->PortRangeHints[i].HintDescriptor; | |||
| if ( LADSPA_IS_HINT_BOUNDED_BELOW(hd) ) | |||
| { | |||
| p.hints.ranged = true; | |||
| p.hints.minimum = _idata->descriptor->PortRangeHints[i].LowerBound; | |||
| } | |||
| if ( LADSPA_IS_HINT_BOUNDED_ABOVE(hd) ) | |||
| { | |||
| p.hints.ranged = true; | |||
| p.hints.maximum = _idata->descriptor->PortRangeHints[i].UpperBound; | |||
| } | |||
| if ( LADSPA_IS_HINT_HAS_DEFAULT(hd) ) | |||
| { | |||
| float Max=1.0f, Min=-1.0f, Default=0.0f; | |||
| int Port=i; | |||
| // Get the bounding hints for the port | |||
| LADSPA_PortRangeHintDescriptor HintDesc=_idata->descriptor->PortRangeHints[Port].HintDescriptor; | |||
| if (LADSPA_IS_HINT_BOUNDED_BELOW(HintDesc)) | |||
| { | |||
| Min=_idata->descriptor->PortRangeHints[Port].LowerBound; | |||
| if (LADSPA_IS_HINT_SAMPLE_RATE(HintDesc)) | |||
| { | |||
| Min*=engine->sample_rate(); | |||
| } | |||
| } | |||
| if (LADSPA_IS_HINT_BOUNDED_ABOVE(HintDesc)) | |||
| { | |||
| Max=_idata->descriptor->PortRangeHints[Port].UpperBound; | |||
| if (LADSPA_IS_HINT_SAMPLE_RATE(HintDesc)) | |||
| { | |||
| Max*=engine->sample_rate(); | |||
| } | |||
| } | |||
| #ifdef LADSPA_VERSION | |||
| // We've got a version of the header that supports port defaults | |||
| if (LADSPA_IS_HINT_HAS_DEFAULT(HintDesc)) { | |||
| // LADSPA_HINT_DEFAULT_0 is assumed anyway, so we don't check for it | |||
| if (LADSPA_IS_HINT_DEFAULT_1(HintDesc)) { | |||
| Default = 1.0f; | |||
| } else if (LADSPA_IS_HINT_DEFAULT_100(HintDesc)) { | |||
| Default = 100.0f; | |||
| } else if (LADSPA_IS_HINT_DEFAULT_440(HintDesc)) { | |||
| Default = 440.0f; | |||
| } else { | |||
| // These hints may be affected by SAMPLERATE, LOGARITHMIC and INTEGER | |||
| if (LADSPA_IS_HINT_DEFAULT_MINIMUM(HintDesc) && | |||
| LADSPA_IS_HINT_BOUNDED_BELOW(HintDesc)) { | |||
| Default=_idata->descriptor->PortRangeHints[Port].LowerBound; | |||
| } else if (LADSPA_IS_HINT_DEFAULT_MAXIMUM(HintDesc) && | |||
| LADSPA_IS_HINT_BOUNDED_ABOVE(HintDesc)) { | |||
| Default=_idata->descriptor->PortRangeHints[Port].UpperBound; | |||
| } else if (LADSPA_IS_HINT_BOUNDED_BELOW(HintDesc) && | |||
| LADSPA_IS_HINT_BOUNDED_ABOVE(HintDesc)) { | |||
| // These hints require both upper and lower bounds | |||
| float lp = 0.0f, up = 0.0f; | |||
| float min = _idata->descriptor->PortRangeHints[Port].LowerBound; | |||
| float max = _idata->descriptor->PortRangeHints[Port].UpperBound; | |||
| if (LADSPA_IS_HINT_DEFAULT_LOW(HintDesc)) { | |||
| lp = 0.75f; | |||
| up = 0.25f; | |||
| } else if (LADSPA_IS_HINT_DEFAULT_MIDDLE(HintDesc)) { | |||
| lp = 0.5f; | |||
| up = 0.5f; | |||
| } else if (LADSPA_IS_HINT_DEFAULT_HIGH(HintDesc)) { | |||
| lp = 0.25f; | |||
| up = 0.75f; | |||
| } | |||
| if (LADSPA_IS_HINT_LOGARITHMIC(HintDesc)) { | |||
| p.hints.type = Port::Hints::LOGARITHMIC; | |||
| if (min==0.0f || max==0.0f) { | |||
| // Zero at either end means zero no matter | |||
| // where hint is at, since: | |||
| // log(n->0) -> Infinity | |||
| Default = 0.0f; | |||
| } else { | |||
| // Catch negatives | |||
| bool neg_min = min < 0.0f ? true : false; | |||
| bool neg_max = max < 0.0f ? true : false; | |||
| if (!neg_min && !neg_max) { | |||
| Default = exp(log(min) * lp + log(max) * up); | |||
| } else if (neg_min && neg_max) { | |||
| Default = -exp(log(-min) * lp + log(-max) * up); | |||
| } else { | |||
| // Logarithmic range has asymptote | |||
| // so just use linear scale | |||
| Default = min * lp + max * up; | |||
| } | |||
| } | |||
| } else { | |||
| Default = min * lp + max * up; | |||
| } | |||
| } | |||
| if (LADSPA_IS_HINT_SAMPLE_RATE(HintDesc)) { | |||
| Default *= engine->sample_rate(); | |||
| } | |||
| if (LADSPA_IS_HINT_INTEGER(HintDesc)) { | |||
| if ( p.hints.ranged && | |||
| 0 == p.hints.minimum && | |||
| 1 == p.hints.maximum ) | |||
| p.hints.type = Port::Hints::BOOLEAN; | |||
| else | |||
| p.hints.type = Port::Hints::INTEGER; | |||
| Default = floorf(Default); | |||
| } | |||
| if (LADSPA_IS_HINT_TOGGLED(HintDesc)){ | |||
| p.hints.type = Port::Hints::BOOLEAN; | |||
| } | |||
| } | |||
| } | |||
| #else | |||
| Default = 0.0f; | |||
| #endif | |||
| p.hints.default_value = Default; | |||
| } | |||
| float *control_value = new float; | |||
| *control_value = p.hints.default_value; | |||
| p.connect_to( control_value ); | |||
| add_port( p ); | |||
| _idata->descriptor->connect_port( _idata->handle, i, (LADSPA_Data*)control_input.back().buffer() ); | |||
| DMESSAGE( "Plugin has control port \"%s\" (default: %f)", _idata->descriptor->PortNames[ i ], p.hints.default_value ); | |||
| } | |||
| } | |||
| } | |||
| else | |||
| { | |||
| WARNING( "Failed to load plugin" ); | |||
| return false; | |||
| } | |||
| return true; | |||
| } | |||
| /* const char * */ | |||
| /* Plugin_Module::name ( void ) const */ | |||
| /* { */ | |||
| /* return _idata->descriptor->Name; */ | |||
| /* } */ | |||
| void | |||
| Plugin_Module::set_input_buffer ( int n, void *buf ) | |||
| { | |||
| for ( unsigned int i = 0; i < _idata->descriptor->PortCount; ++i ) | |||
| if ( LADSPA_IS_PORT_INPUT( _idata->descriptor->PortDescriptors[i] ) && | |||
| LADSPA_IS_PORT_AUDIO( _idata->descriptor->PortDescriptors[i] ) ) | |||
| if ( n-- == 0 ) | |||
| _idata->descriptor->connect_port( _idata->handle, i, (LADSPA_Data*)buf ); | |||
| } | |||
| void | |||
| Plugin_Module::set_output_buffer ( int n, void *buf ) | |||
| { | |||
| for ( unsigned int i = 0; i < _idata->descriptor->PortCount; ++i ) | |||
| if ( LADSPA_IS_PORT_OUTPUT( _idata->descriptor->PortDescriptors[i] ) && | |||
| LADSPA_IS_PORT_AUDIO( _idata->descriptor->PortDescriptors[i] ) ) | |||
| if ( n-- == 0 ) | |||
| _idata->descriptor->connect_port( _idata->handle, i, (LADSPA_Data*)buf ); | |||
| } | |||
| void | |||
| Plugin_Module::set_control_buffer ( int n, void *buf ) | |||
| { | |||
| for ( unsigned int i = 0; i < _idata->descriptor->PortCount; ++i ) | |||
| if ( LADSPA_IS_PORT_INPUT( _idata->descriptor->PortDescriptors[i] ) && | |||
| LADSPA_IS_PORT_CONTROL( _idata->descriptor->PortDescriptors[i] ) ) | |||
| if ( n-- == 0 ) | |||
| _idata->descriptor->connect_port( _idata->handle, i, (LADSPA_Data*)buf ); | |||
| } | |||
| void | |||
| Plugin_Module::activate ( void ) | |||
| { | |||
| if ( _active ) | |||
| FATAL( "Attempt to activate already active plugin" ); | |||
| if ( _idata->descriptor->activate ) | |||
| _idata->descriptor->activate( _idata->handle ); | |||
| _active = true; | |||
| } | |||
| void | |||
| Plugin_Module::deactivate( void ) | |||
| { | |||
| if ( _idata->descriptor->deactivate ) | |||
| _idata->descriptor->deactivate( _idata->handle ); | |||
| _active = false; | |||
| } | |||
| void | |||
| Plugin_Module::process ( ) | |||
| { | |||
| if ( _crosswire ) | |||
| { | |||
| for ( int i = 0; i < plugin_ins(); ++i ) | |||
| { | |||
| set_input_buffer( i, audio_input[0].buffer() ); | |||
| } | |||
| } | |||
| else | |||
| { | |||
| for ( unsigned int i = 0; i < audio_input.size(); ++i ) | |||
| { | |||
| set_input_buffer( i, audio_input[i].buffer() ); | |||
| } | |||
| } | |||
| for ( unsigned int i = 0; i < audio_output.size(); ++i ) | |||
| { | |||
| set_output_buffer( i, audio_output[i].buffer() ); | |||
| } | |||
| if ( _active ) | |||
| { | |||
| _idata->descriptor->run( _idata->handle, nframes() ); | |||
| } | |||
| } | |||
| @@ -0,0 +1,114 @@ | |||
| /*******************************************************************************/ | |||
| /* 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. */ | |||
| /*******************************************************************************/ | |||
| #pragma once | |||
| #include "Module.H" | |||
| class Plugin_Module : Module { | |||
| void init ( void ); | |||
| int _ins; | |||
| int _outs; | |||
| int _instances; | |||
| void bbox ( int &X, int &Y, int &W, int &H ) | |||
| { | |||
| X = x(); | |||
| Y = y() + 5; | |||
| W = w(); | |||
| H = h() - 10; | |||
| } | |||
| void cb_handle(Fl_Widget*); | |||
| static void cb_handle(Fl_Widget*, void*); | |||
| Fl_Button *close_button; | |||
| struct ImplementationData; | |||
| ImplementationData *_idata; | |||
| bool _active; | |||
| int _plugin_ins; | |||
| int _plugin_outs; | |||
| bool _crosswire; | |||
| struct Plugin_Info | |||
| { | |||
| const char *path; | |||
| unsigned long id; | |||
| Plugin_Info ( ) | |||
| { | |||
| path = 0; | |||
| id = 0; | |||
| } | |||
| }; | |||
| static Plugin_Info* discover ( void ); | |||
| bool load ( Plugin_Info * ); | |||
| void set_input_buffer ( int n, void *buf ); | |||
| void set_output_buffer ( int n, void *buf ); | |||
| void set_control_buffer ( int n, void *buf ); | |||
| void activate ( void ); | |||
| void deactivate ( void ); | |||
| void process ( unsigned long nframes ); | |||
| bool active ( void ) const { return _active; } | |||
| public: | |||
| Plugin_Module( int W, int H, const char *L = 0 ); | |||
| Plugin_Module ( ); | |||
| virtual ~Plugin_Module(); | |||
| static Plugin_Module * pick_plugin ( void ); | |||
| int plugin_ins ( void ) const { return _plugin_ins; } | |||
| int plugin_outs ( void ) const { return _plugin_outs; } | |||
| bool ins ( int i ); | |||
| int ins ( void ) const { return _ins; } | |||
| int outs ( void ) const { return plugin_outs() * _instances; } | |||
| bool controllable ( void ) const { return false; } | |||
| int instances ( void ) const { return _instances; } | |||
| void instances ( int i ) { _instances = i; } | |||
| void select_plugin ( unsigned long id ); | |||
| const char *name ( void ) const { return "Plugin"; } | |||
| int can_support_inputs ( int ); | |||
| bool configure_inputs ( int ); | |||
| void process ( void ); | |||
| protected: | |||
| // virtual void draw ( void ); | |||
| virtual int handle ( int ); | |||
| }; | |||
| @@ -0,0 +1,114 @@ | |||
| /*******************************************************************************/ | |||
| /* Copyright (C) 2008 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. */ | |||
| /*******************************************************************************/ | |||
| #include <FL/Fl.H> | |||
| #include <FL/Fl_Single_Window.H> | |||
| #include <FL/Fl_Single_Window.H> | |||
| #include <FL/Fl_Scroll.H> | |||
| #include <FL/Fl_Pack.H> | |||
| #include "Mixer_Strip.H" | |||
| #include <stdlib.h> | |||
| #include <unistd.h> | |||
| #include "DPM.H" | |||
| #include "Mixer.H" | |||
| #include "Engine/Engine.H" | |||
| #include "util/Thread.H" | |||
| #include "util/debug.h" | |||
| Engine *engine; | |||
| Mixer *mixer; | |||
| Fl_Single_Window *main_window; | |||
| #include <FL/Boxtypes.H> | |||
| #include "Loggable.H" | |||
| #include <FL/Fl_Tooltip.H> | |||
| int | |||
| main ( int argc, char **argv ) | |||
| { | |||
| Thread::init(); | |||
| Thread thread( "UI" ); | |||
| thread.set(); | |||
| Fl_Tooltip::color( FL_BLACK ); | |||
| Fl_Tooltip::textcolor( FL_YELLOW ); | |||
| Fl_Tooltip::size( 14 ); | |||
| Fl_Tooltip::hoverdelay( 0.1f ); | |||
| Fl::visible_focus( 0 ); | |||
| LOG_REGISTER_CREATE( Mixer_Strip ); | |||
| init_boxtypes(); | |||
| Fl::get_system_colors(); | |||
| Fl::scheme( "plastic" ); | |||
| // Fl::scheme( "gtk+" ); | |||
| /* Fl::foreground( 0xFF, 0xFF, 0xFF ); */ | |||
| /* Fl::background( 0x10, 0x10, 0x10 ); */ | |||
| MESSAGE( "Initializing JACK" ); | |||
| engine = new Engine(); | |||
| engine->init( "Non-Mixer" ); | |||
| Fl_Single_Window *o = main_window = new Fl_Single_Window( 1024, 768, "Mixer" ); | |||
| { | |||
| Fl_Widget *o = mixer = new Mixer( 0, 0, main_window->w(), main_window->h(), NULL ); | |||
| Fl_Group::current()->resizable(o); | |||
| } | |||
| o->end(); | |||
| o->show( argc, argv ); | |||
| { | |||
| engine->lock(); | |||
| if ( argc > 1 ) | |||
| { | |||
| char name[1024]; | |||
| snprintf( name, sizeof( name ), "%s/history", argv[1] ); | |||
| Loggable::open( name ); | |||
| } | |||
| else | |||
| { | |||
| WARNING( "Running without a project--nothing will be saved." ); | |||
| } | |||
| engine->unlock(); | |||
| } | |||
| Fl::run(); | |||
| delete engine; | |||
| MESSAGE( "Your fun is over" ); | |||
| } | |||
| @@ -0,0 +1,21 @@ | |||
| # -*- mode: makefile; -*- | |||
| all: Mixer | |||
| Mixer_SRCS := $(wildcard Mixer/*.C Mixer/*.fl Mixer/Engine/*.C) | |||
| Mixer_SRCS += util/debug.C util/Thread.C util/file.C | |||
| Mixer_SRCS:=$(Mixer_SRCS:.fl=.C) | |||
| Mixer_SRCS:=$(sort $(Mixer_SRCS)) | |||
| Mixer_OBJS:=$(Mixer_SRCS:.C=.o) | |||
| Mixer_LIBS := $(FLTK_LIBS) $(JACK_LIBS) $(LASH_LIBS) -llrdf | |||
| Mixer/mixer: $(Mixer_OBJS) FL | |||
| @ echo -n Linking mixer... | |||
| @ $(CXX) $(CXXFLAGS) $(Mixer_LIBS) $(Mixer_OBJS) -o $@ -LFL -lfl_widgets -Lnonlib -lnonlib && echo $(DONE) | |||
| Mixer: Mixer/mixer | |||
| Mixer_clean: | |||
| rm -f $(Mixer_OBJS) Mixer/mixer | |||
| @@ -27,5 +27,6 @@ require_command ar ar | |||
| require_command makedepend makedepend | |||
| require_package JACK 0.103.0 jack | |||
| require_package sndfile 1.0.17 sndfile | |||
| require_package lrdf 0.1.0 lrdf | |||
| end | |||