| @@ -96,9 +96,10 @@ DONE := $(BOLD)$(GREEN)done$(SGR0) | |||||
| include FL/makefile.inc | include FL/makefile.inc | ||||
| include nonlib/makefile.inc | include nonlib/makefile.inc | ||||
| include Timeline/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? | # FIXME: isn't there a better way? | ||||
| $(OBJS): .config Makefile | $(OBJS): .config Makefile | ||||
| @@ -110,13 +111,14 @@ TAGS: $(SRCS) | |||||
| ifneq ($(CALCULATING),yes) | ifneq ($(CALCULATING),yes) | ||||
| @ echo -n Calculating dependencies... | @ echo -n Calculating dependencies... | ||||
| @ makedepend -f- -- $(CXXFLAGS) $(INCLUDES) -- $(SRCS) 2>/dev/null > .deps && echo $(DONE) | @ makedepend -f- -- $(CXXFLAGS) $(INCLUDES) -- $(SRCS) 2>/dev/null > .deps && echo $(DONE) | ||||
| # @ gcc -M $(CXXFLAGS) $(INCLUDES) $(SRCS) > .deps && echo $(DONE) | |||||
| endif | endif | ||||
| install: all | install: all | ||||
| @ echo -n "Installing..." | @ echo -n "Installing..." | ||||
| @ install Timeline/timeline $(prefix)/bin/non-daw | @ 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 $(SYSTEM_PATH) | ||||
| @ mkdir -p $(PIXMAP_PATH) | @ mkdir -p $(PIXMAP_PATH) | ||||
| @ cp pixmaps/*.png $(PIXMAP_PATH) | @ cp pixmaps/*.png $(PIXMAP_PATH) | ||||
| @@ -125,7 +127,7 @@ install: all | |||||
| ifneq ($(USE_DEBUG),yes) | ifneq ($(USE_DEBUG),yes) | ||||
| @ echo -n "Stripping..." | @ echo -n "Stripping..." | ||||
| @ strip $(prefix)/bin/non-daw | @ strip $(prefix)/bin/non-daw | ||||
| # @ strip $(prefix)/bin/non-mixer | |||||
| @ strip $(prefix)/bin/non-mixer | |||||
| @ echo "$(DONE)" | @ echo "$(DONE)" | ||||
| endif | endif | ||||
| @@ -134,7 +136,7 @@ clean_deps: | |||||
| .PHONEY: clean config depend 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: | dist: | ||||
| git archive --prefix=non-daw-$(VERSION)/ v$(VERSION) | bzip2 > non-daw-$(VERSION).tar.bz2 | 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_command makedepend makedepend | ||||
| require_package JACK 0.103.0 jack | require_package JACK 0.103.0 jack | ||||
| require_package sndfile 1.0.17 sndfile | require_package sndfile 1.0.17 sndfile | ||||
| require_package lrdf 0.1.0 lrdf | |||||
| end | end | ||||