|  | 
/*******************************************************************************/
/* 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_Group.H>
#include <stdlib.h>
#include "debug.h"
#include <vector>
#include "Thread.H"
#include "Loggable.H"
#include "JACK/Port.H"
#include "OSC/Endpoint.H"
class Chain;
class Module_Parameter_Editor;
class Fl_Menu_;
class Fl_Menu_Button;
class Fl_Button;
class Module : public Fl_Group, public Loggable {
    int _ins;
    int _outs;
    int _instances;
    nframes_t _nframes;
    Chain *_chain;
    bool _is_default;
    Module_Parameter_Editor *_editor;
    static nframes_t _sample_rate;
    static Module *_copied_module_empty;
    static char *_copied_module_settings;
    void init ( void );
    void insert_menu_cb ( const Fl_Menu_ *m );
    static void insert_menu_cb ( Fl_Widget *w, void *v );
    void menu_cb ( const Fl_Menu_ *m );
    static void menu_cb ( Fl_Widget *w, void *v );
    Fl_Menu_Button & menu ( void ) const;
    void copy ( void ) const;
    void paste_before ( void );
protected:
    volatile bool _bypass;
public:
    /* true if this module was added by default and not under normal user control */
    bool is_default ( void ) const { return _is_default; }
    void is_default ( bool v ) { _is_default = v; }
    
    virtual void update ( void ) {}
    class Port
    {
        /* char *type_names[] = { "Audio", "Control" }; */
        /* char *direction_names[] = { "Input", "Output" }; */
        void update_connected_port_buffer ( void )
            {
                if ( connected() )
                    connected_port()->_buf = _buf;
            }
    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;
            bool visible;
            Hints ( )
                {
                    type = LINEAR;
                    ranged = false;
                    minimum = 0;
                    maximum = 1;    /* FIXME: totally bogus, but some
                                     * plugins (SWH delays) don't
                                     * provide an upper bound. */
                    default_value = 0.0f;
                    dimensions = 1;
                    visible = true;
                }
        };
        static int osc_control_change_exact ( float v, void *user_data );
        static int osc_control_change_cv ( float v, void *user_data );
        void learn_osc ( void );
        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;
                _scaled_signal = 0;
                _unscaled_signal = 0;
            }
        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;
                _scaled_signal = p._scaled_signal;
                _unscaled_signal = p._unscaled_signal;
            }
        virtual ~Port ( )
            {
                //     change_osc_path( NULL );
                //   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; }
        nframes_t nframes ( void ) const { return _nframes; }
        void buffer ( void *buf, nframes_t nframes ) { _buf = buf; _nframes = nframes; };
        void *buffer ( void ) const { return _buf; }
        OSC::Signal *scaled_signal ( void ) { return _scaled_signal; }
        const char *osc_path ( )
            {
                if ( _scaled_signal )
                    return _scaled_signal->path();
                else
                    return NULL;
            }
        char *osc_number_path ( void );
        void update_osc_port ( )
            {
//                if ( INPUT == _direction )
                change_osc_path( generate_osc_path() );
            }
        void destroy_osc_port ( )
            {
                delete _unscaled_signal;
                delete _scaled_signal;
                
                _unscaled_signal = _scaled_signal = NULL;
            }
        void control_value_no_callback ( float f )
            {
              /* can also be called from the OSC thread */
                ASSERT( Thread::is( "UI" ) || Thread::is( "OSC" ),
                        "Function called from wrong thread! (is %s)",  Thread::current()->name() );
                if ( buffer() )
                {
                    *((float*)buffer()) = f;
                }
                if ( _scaled_signal )
                {
                    
                    if ( hints.ranged )
                    {
                        // scale value to range.
                        
                        float scale = hints.maximum - hints.minimum;
                        float offset = hints.minimum;
        
                        f =  ( f - offset ) / scale;
                    }
                    if ( f > 1.0 )
                        f = 1.0;
                    else if ( f < 0.0 )
                        f = 0.0;
                  
//                    _scaled_signal->value( f );
                }
            }
        void control_value ( float f )
            {
                control_value_no_callback( f );
                _module->handle_control_changed( this );
                if ( connected() )
                    connected_port()->_module->handle_control_changed( connected_port() );
            }
        float control_value ( ) const
            {
                if ( buffer() )
                    return *((float*)buffer());
                else
                    return 0.0f;
            }
        bool connected ( void ) const { return _connected; }
        bool connected_osc ( void ) const;
        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;
                update_connected_port_buffer();
            }
        void send_feedback ( void );
        void disconnect ( void )
            {
                if ( _connected && _connected != (void*)0x01 )
                {
                    _connected->_connected = NULL;
                    _connected = NULL;
                }
                else
                    _connected = NULL;
                /* FIXME: do something! */
            }
    private:
        char *generate_osc_path ( void );
        void change_osc_path ( char *path );
    
        Port *_connected;
        Type _type;
        Direction _direction;
        const char *_name;
        void *_buf;
        nframes_t _nframes;
        Module *_module;
        OSC::Signal *_scaled_signal;
        OSC::Signal *_unscaled_signal;
        static void handle_signal_connection_state_changed ( OSC::Signal *, void *o );
    };
    void bbox ( int &X, int &Y, int &W, int &H )
        {
            X += + 5;
            Y += 5;
            W -= 10;
            H -= 10;
        }
    Module ( int W, int H, const char *L = 0 );
    Module ( );
    Module ( bool is_default, int W, int H, const char *L = 0 );
    virtual ~Module ( );
    LOG_NAME_FUNC( Module );
    nframes_t nframes ( void ) const { return _nframes; }
    void resize_buffers ( nframes_t v ) { _nframes = v; }
    int instances ( void ) const { return _instances; }
    void instances ( int i ) { _instances = i; }
    bool is_being_controlled ( void ) const
        {
            for ( nframes_t i = control_input.size(); i--; )
                if ( control_input[i].connected() )
                    return true;
            return false;
        }
    bool is_controlling ( void ) const
        {
            for ( nframes_t i = control_output.size(); i--; )
                if ( control_output[i].connected() )
                    return true;
            return false;
        }
    bool
    is_being_controlled_osc ( void ) const
        {
            for ( nframes_t i = control_input.size(); i--; )
                if ( control_input[i].connected_osc() )
                    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();
        }
    int nvisible_control_inputs ( void ) const
        {
            int n = 0;
            for ( std::vector<Port>::const_iterator i = control_input.begin();
                  i != control_input.end();
                  i++ )
                if ( i->hints.visible )
                    n++;
            return n;
        }
    virtual bool bypass ( void ) const { return _bypass; }
    virtual void bypass ( bool v ) { _bypass = v; redraw(); }
    virtual bool bypassable ( void ) const
        {
            return ninputs() == noutputs() ||
                ( ninputs() == 1 && noutputs() == 2 );            
        }
    int control_input_port_index ( Port *p )
        {
            for ( nframes_t i = control_input.size(); i--; )
                if ( &control_input[i] == p )
                    return i;
            return -1;
        }
    int control_output_port_index ( Port *p )
        {
            for ( nframes_t i = control_output.size(); i--; )
                if ( &control_output[i] == p )
                    return i;
            return -1;
        }
    Chain *chain ( void ) const { return _chain; }
    void chain ( Chain * v );
    char *get_parameters ( void ) const;
    void set_parameters ( const char * );
    
    void send_feedback ( void );
    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 ( nframes_t ) = 0;
    /* called whenever the module is initialized or when the sample rate is changed at runtime */
    virtual void handle_sample_rate_change ( nframes_t sample_rate ) {}
        
    /* 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 ();
    virtual void handle_port_connection_change () {}
#define MODULE_CLONE_FUNC(class)                                \
    virtual Module *clone_empty ( void ) const                  \
        {                                                       \
            return new class ();                                \
        }
    virtual Module *clone_empty ( void ) const { return NULL; }
    Module *clone ( Chain *dest ) const;
    Module *clone ( void ) const;
protected:
    nframes_t sample_rate ( void ) const { return Module::_sample_rate; }
    void draw_connections ( void );
    void draw_label ( int X, int Y, int W, int H );
    void draw_box ( int X, int Y, int W, int H );
    virtual void draw ( void ) { Module::draw_box(x(),y(),w(),h()); Module::draw_label(x(),y(),w(),h()); }
    virtual int handle ( int m );
    virtual void get ( Log_Entry &e ) const;
    virtual void set ( Log_Entry &e );
public:
    static void set_sample_rate ( nframes_t srate ) { _sample_rate = srate; }
    void command_open_parameter_editor();
    virtual void command_activate ( void );
    virtual void command_deactivate ( void );
    virtual void command_remove ( void );
};
 |