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