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