Audio-LADSPA
view release on metacpan or search on metacpan
Network/Network.pm view on Meta::CPAN
# 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
#
# See the COPYING file for more information.
package Audio::LADSPA::Network;
use strict;
our $VERSION = "0.021";
use Audio::LADSPA;
use Graph::Directed;
use Carp;
use base qw(Class::Publisher);
sub new {
my ($class,%args) = @_;
my $self = bless {
graph => Graph::Directed->new,
sample_rate => $args{sample_rate} || 44100,
buffer_size => $args{buffer_size} || 1024,
run_order => undef,
plugin_by_uniqid => {},
%args,
},$class;
$self->notify_subscribers("new",$self);
return $self;
}
sub sample_rate {
my $self = shift;
return $self->{sample_rate};
}
sub buffer_size {
my $self = shift;
return $self->{buffer_size};
}
sub _make_plugin {
my $self = shift;
my $plugin;
if (@_ == 1) {
$plugin = shift;
unless (ref ($plugin)) {
$plugin = $plugin->new($self->{sample_rate});
}
}
else {
$plugin = Audio::LADSPA->plugin(@_)->new($self->{sample_rate});
}
$plugin->set_monitor($self); # register callbacks.
return $plugin;
}
sub graph {
return $_[0]->{graph};
}
sub add_plugin {
my ($self) = shift;
my $plugin = $self->_make_plugin(@_);
$self->graph->add_vertex("$plugin");
$self->graph->set_vertex_attribute("$plugin",'plugin',$plugin);
for ($plugin->ports()) {
$self->_connect_default($plugin,$_) unless $plugin->get_buffer($_);
}
$self->{plugin_by_uniqid}->{$plugin->get_uniqid} = $plugin;
$self->notify_subscribers("add_plugin",$plugin);
return $plugin;
}
sub plugins {
my ($self) = @_;
if (!$self->{run_order}) {
$self->{run_order} = [ map { my $p = $self->graph->get_vertex_attribute("$_",'plugin'); $p ? $p : () } $self->graph->toposort() ];
}
return @{$self->{run_order}};
}
sub has_plugin {
my ($self,$plugin) = @_;
return $self->graph->has_vertex("$plugin");
}
sub add_buffer {
my ($self,$buff) = @_;
if (!ref $buff) {
$buff = Audio::LADSPA::Buffer->new($buff);
}
$self->graph->add_vertex("$buff");
$self->graph->set_vertex_attribute("$buff",'buffer',$buff);
$self->notify_subscribers("add_buffer",$buff);
return $buff;
}
sub buffers {
my ($self) = @_;
return map { my $b = $self->graph->get_vertex_attribute("$_",'buffer'); $b ? $b : () } $self->graph->vertices();
}
sub has_buffer {
my ($self,$buffer) = @_;
return $self->graph->has_vertex("$buffer");
}
sub _connect_default {
my ($self,$plugin,$port) = @_;
croak "Logic error: port $port already connected" if ($plugin->get_buffer($port));
my $buffer;
if ($plugin->is_control($port)) {
Network/Network.pm view on Meta::CPAN
Audio::LADSPA::Network->add_subscriber('*',\&subscriber);
my $net = Audio::LADSPA::Network->new();
my $sine = $net->add_plugin( label => 'sine_fcac' );
my $delay = $net->add_plugin( label => 'delay_5s' );
my $play = $net->add_plugin('Audio::LADSPA::Plugin::Play');
$net->connect($sine,'Output',$delay,'Input');
$net->connect($delay,'Output',$play,'Input');
$sine->set('Frequency (Hz)' => 440); # set freq
$sine->set(Amplitude => 1); # set amp
$delay->set('Delay (Seconds)' => 1); # 1 sec delay
$delay->set('Dry/Wet Balance' => 0.2); # balance - 0.2
for ( 0 .. 100 ) {
$net->run(100);
}
$sine->set(Amplitude => 0); #just delay from now
for ( 0 .. 500 ) {
$net->run(100);
}
=head1 DESCRIPTION
This module makes it easier to create connecting Audio::LADSPA::Plugin
objects. It automatically keeps the sampling frequencies correct for all plugins,
adds control and audio buffers to unconnected plugins and prevents illegal connections.
It also implements an observable-type API via Class::Publisher that can be used to
recieve notifications of events in the network. Amongst other things, this makes
writing loosely coupled GUIs fairly straightforward.
=head1 CONSTRUCTOR
=head2 new
my $network = Audio::LADSPA::Network->new(
sample_rate => $sample_rate,
buffer_size => $buffer_size
);
Construct a new Audio::LADSPA::Network. The sample_rate and buffer_size arguments may be omitted.
The default sample_rate is 44100, the default buffer_size is 1024.
=head1 COMMON METHODS
=head2 add_plugin
my $plugin = $network->add_plugin( EXPR );
Adds a $plugin to the $network. All unconnected ports will be connected to new C<Audio::LADSPA::Buffer>s.
Control buffers will be initalized with the correct default value for the port.
EXPR can be an Audio::LADSPA::Plugin object, an Audio::LADSPA::Plugin classname or any
expression supported by L<< Audio::LADSPA->plugin()|Audio::LADSPA/plugin >>.
Any $plugin added to a $network will have its monitor set to that $network. This
means that a $plugin cannot be in more than 1 Audio::LADSPA::Network at any given time, and that
all callbacks from the $plugin are handled by the $network.
See also L<Audio::LADSPA::Plugin/SETTING CALLBACKS>.
=head2 has_plugin
if ($network->has_plugin($plugin)) {
# something interesting...
}
Check if a given $plugin object exists in the $network. $plugin must be an Audio::LADSPA::Plugin object.
=head2 plugins
my @plugins = $network->plugins();
Returns all @plugins in the $network in topological order - this means that the plugins will
be sorted so that if $plugin2 recieves input from $plugin1, $plugin1 will be before $plugin2
in the @plugins list.
Because the topological sorting is expensive (that is, for semi-realtime audio generation),
the result of this operation is cached as long as the network does not change.
=head2 connect
unless ($network->connect( $plugin1, $port1, $plugin2, $port2 )) {
warn "Connection failed";
}
Connect $port1 of $plugin1 with $port2 of $plugin2. Plugins are
added to the network and new buffers are created if needed.
This method will only connect input ports to output ports. If the output
port is a control port, the input port must be a control port, if the
output port is an audio port, the type of input port does not matter.
The order in which the plugins are specified does not matter.
I<There is currently no special handling of incompatible value ranges.
Well behaved plugins should accept any value on any port, even if they specify a
range. Note that generic 0dB audio signals should be in the range -1 .. 1,
but that control signals usually have completely different ranges.>
Returns true on success, false on failure.
B<< You are advised to use this method instead of $plugin->connect( $buffer ) >>.
I<Some> of the magic in this method also works for C<< $plugin->connect() >>, if the
plugin is already added to the network, but that might change if the implementation
changes. YMMV.
=head2 run
$network->run( $number_of_samples );
Call $plugin->run( $number_of_samples ) on all plugins in the $network.
Throws an exception when $number_of_samples is higher than the $buffer_size
specified at the L<constructor|CONSTRUCTOR>.
=head2 disconnect
$network->disconnect($plugin,$port);
( run in 1.560 second using v1.01-cache-2.11-cpan-cdf2f3d4e48 )