Implement Mixer.

This commit is contained in:
Jonathan Moore Liles 2009-12-24 18:59:39 -06:00
parent 55ebb9e107
commit eacbcc173d
35 changed files with 6516 additions and 5 deletions

View File

@ -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

583
Mixer/Chain.C Normal file
View File

@ -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();
}
}

104
Mixer/Chain.H Normal file
View File

@ -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 );
};

259
Mixer/Controller_Module.C Normal file
View File

@ -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;
}
}

81
Mixer/Controller_Module.H Normal file
View File

@ -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;
};

179
Mixer/DPM.C Normal file
View File

@ -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 ); */
}
}

81
Mixer/DPM.H Normal file
View File

@ -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 );
}
};

147
Mixer/Engine/Engine.C Normal file
View File

@ -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 )
{
}

66
Mixer/Engine/Engine.H Normal file
View File

@ -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;

98
Mixer/Gain_Module.C Normal file
View File

@ -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 ); */
}
}
}
}

40
Mixer/Gain_Module.H Normal file
View File

@ -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 );
};

187
Mixer/JACK_Module.C Normal file
View File

@ -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() );
}

58
Mixer/JACK_Module.H Normal file
View File

@ -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 );
};

791
Mixer/LADSPAInfo.C Normal file
View File

@ -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;
}

199
Mixer/LADSPAInfo.h Normal file
View File

@ -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__

138
Mixer/Meter.H Normal file
View File

@ -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(); }
};

View File

@ -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];
}
}

View File

@ -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;
};

201
Mixer/Meter_Module.C Normal file
View File

@ -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();
}
}

51
Mixer/Meter_Module.H Normal file
View File

@ -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 );
};

230
Mixer/Mixer.C Normal file
View File

@ -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;
}

63
Mixer/Mixer.H Normal file
View File

@ -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;

471
Mixer/Mixer_Strip.C Normal file
View File

@ -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;
}

100
Mixer/Mixer_Strip.H Normal file
View File

@ -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

169
Mixer/Module.C Normal file
View File

@ -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 );
}

321
Mixer/Module.H Normal file
View File

@ -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 );
};

View File

@ -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] );
}

View File

@ -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 ( );
};

283
Mixer/Panner.C Normal file
View File

@ -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;
}

172
Mixer/Panner.H Normal file
View File

@ -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 );
};

577
Mixer/Plugin_Module.C Normal file
View File

@ -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() );
}
}

114
Mixer/Plugin_Module.H Normal file
View File

@ -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 );
};

114
Mixer/main.C Normal file
View File

@ -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" );
}

21
Mixer/makefile.inc Normal file
View File

@ -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

1
configure vendored
View File

@ -27,5 +27,6 @@ require_command ar ar
require_command makedepend makedepend
require_package JACK 0.103.0 jack
require_package sndfile 1.0.17 sndfile
require_package lrdf 0.1.0 lrdf
end