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