Audio-Nama
view release on metacpan or search on metacpan
lib/Audio/Nama/Jack.pm view on Meta::CPAN
# ------- Jack port connect routines -------
package Audio::Nama;
use v5.36;
use File::Slurp;
no warnings 'uninitialized';
# general functions
sub update_jack_client_list {
state $warn_count;
#logsub((caller(0))[3]);
# cache current JACK status
# skip if Ecasound is busy
return if $this_engine->started();
if( $jack->{jackd_running} = process_is_running('jackd') ){
# reset our clients data
$jack->{clients} = {};
$jack->{use_jacks}
? jacks_get_port_latency()
: parse_port_latency();
parse_ports_list();
my ($bufsize) = qx(jack_bufsize);
($jack->{periodsize}) = $bufsize =~ /(\d+)/;
my ($sample_rate) = qx(jack_samplerate);
chomp $sample_rate;
$project->{name}
and $sample_rate != $project->{sample_rate}
and ($warn_count == 1 or $warn_count % 8 == 0) # warn less often
and Audio::Nama::throw(qq(
JACK audio daemon sample rate is $sample_rate but sample rate for project "$project->{name}" is $project->{sample_rate}.
Please fix this problem before continuing (maybe restart jackd with --rate $project->{sample_rate}?))),
print prompt();
$warn_count++;
} else { }
}
sub client_port {
my $name = shift;
$name =~ /(.+?):([^:]+)$/;
=comment
$name =~ /
(?<client>.+?) # anything, non-greedy
: # a colon
(?<port>[^:]+$) # non-colon stuff to end
/x;
@+{qw(client port)}
=cut
$1, $2
}
sub jack_client_array {
# returns array of ports if client and direction exist
my ($name, $direction) = @_;
$jack->{clients}->{$name}{$direction} // []
}
sub jacks_get_port_latency {
logsub((caller(0))[3]);
delete $jack->{clients};
my $jc;
$jc = jacks::JsClient->new("watch latency", undef, $jacks::JackNullOption, 0);
my $plist = $jc->getPortNames(".");
for (my $i = 0; $i < $plist->length(); $i++) {
my $pname = $plist->get($i);
my ($client_name,$port_name) = client_port($pname);
logpkg(__FILE__,__LINE__,'debug',qq(client: $client_name, port: $port_name));
my $port = $jc->getPort($pname);
#my @connections = $jc->getAllConnections($client_name, $port_name);
#say for @connections;
my $platency = $port->getLatencyRange($jacks::JackPlaybackLatency);
my $pmin = $platency->min();
my $pmax = $platency->max();
logpkg(__FILE__,__LINE__,'debug',"$pname: playback Latency [ $pmin $pmax ]");
$jack->{clients}->{$client_name}->{$port_name}->{latency}->{playback}->{min}
= $pmin;
$jack->{clients}->{$client_name}->{$port_name}->{latency}->{playback}->{max}
= $pmax;
my $clatency = $port->getLatencyRange($jacks::JackCaptureLatency);
my $cmin = $clatency->min();
my $cmax = $clatency->max();
logpkg(__FILE__,__LINE__,'debug',"$pname: capture Latency [ $cmin $cmax ]");
$jack->{clients}->{$client_name}->{$port_name}->{latency}->{capture}->{min}
= $cmin;
$jack->{clients}->{$client_name}->{$port_name}->{latency}->{capture}->{max}
= $cmax;
}
}
sub parse_port_connections {
my $j = shift || qx(jack_lsp -c 2> /dev/null);
return unless $j;
# initialize
$jack->{connections} = {};
# convert to single lines
$j =~ s/\n\s+/ /sg;
my @lines = split "\n",$j;
#say for @ports;
for (@lines){
my ($port, @connections) = split " ", $_;
#say "$port @connections";
$jack->{connections}->{$port} = \@connections;
lib/Audio/Nama/Jack.pm view on Meta::CPAN
\Qport latency = \E
\d+ # don't capture
\Q frames\E
\s+
# port playback latency = [ 0 2048 ] frames
\Qport playback latency = [ \E
(?<playback_min>\d+)
\s+
(?<playback_max>\d+)
\Q ] frames\E
\s+
# port capture latency = [ 0 2048 ] frames
\Qport capture latency = [ \E
(?<capture_min>\d+)
\s+
(?<capture_max>\d+)
\Q ] frames\E
)x;
# convert to single lines
$j =~ s/\n\s+/ /sg;
my @ports = split "\n",$j;
map
{
/$port_latency_re/;
#logpkg(__FILE__,__LINE__,'debug', Dumper %+);
logpkg(__FILE__,__LINE__,'debug', "client: ",$+{client});
logpkg(__FILE__,__LINE__,'debug', "port: ",$+{port});
logpkg(__FILE__,__LINE__,'debug', "capture min: ", $+{capture_min});
logpkg(__FILE__,__LINE__,'debug', "capture max: ",$+{capture_max});
logpkg(__FILE__,__LINE__,'debug', "playback min: ",$+{playback_min});
logpkg(__FILE__,__LINE__,'debug', "playback max: ",$+{playback_max});
$jack->{clients}->{$+{client}}->{$+{port}}->{latency}->{capture}->{min}
= $+{capture_min};
$jack->{clients}->{$+{client}}->{$+{port}}->{latency}->{capture}->{max}
= $+{capture_max};
$jack->{clients}->{$+{client}}->{$+{port}}->{latency}->{playback}->{min}
= $+{playback_min};
$jack->{clients}->{$+{client}}->{$+{port}}->{latency}->{playback}->{max}
= $+{playback_max};
} @ports;
}
sub parse_ports_list {
# default to output of jack_lsp -p
logsub((caller(0))[3]);
my $j = shift || qx(jack_lsp -tp 2> /dev/null);
logpkg(__FILE__,__LINE__,'debug', "input: $j");
# convert to single lines
$j =~ s/\n\s+/ /sg;
# system:capture_1 alsa_pcm:capture_1 properties: output,physical,terminal,
#fluidsynth:left properties: output,
#fluidsynth:right properties: output,
map{
my ($direction) = /properties: (input|output)/;
s/properties:.+//;
my @port_aliases = /
\s* # zero or more spaces
([^:]+:[^:]+?) # non-colon string, colon, non-greedy non-colon string
(?=[-+.\w]+:|\s+$) # zero-width port name or spaces to end-of-string
/gx;
map {
s/ $//; # remove trailing space
# make entries for 'system' and 'system:capture_1'
push @{ $jack->{clients}->{$_}->{$direction} }, $_;
my ($client, $port) = /(.+?):(.+)/;
push @{ $jack->{clients}->{$client}->{$direction} }, $_;
} @port_aliases;
}
grep{ ! /^jack:/i } # skip spurious jackd diagnostic messages
grep{ ! /8 bit raw midi/ }
split "\n",$j;
}
# connect jack ports via jack-plumbing or jack_connect
sub jack_plumbing_conf {
join_path( $ENV{HOME} , '.jack-plumbing' )
}
sub initialize_jack_plumbing_conf { }
{ my $fh;
my $jack_plumbing_code = sub
{
my ($port1, $port2) = @_;
my $debug++;
my $config_line = qq{(connect $port1 $port2)};
say $fh $config_line; # $fh in lexical scope
logpkg(__FILE__,__LINE__,'debug', $config_line);
};
my $jack_connect_code = sub
{
my ($port1, $port2) = @_;
my $debug++;
my $cmd = qq(jack_connect $port1 $port2);
logpkg(__FILE__,__LINE__,'debug', $cmd);
system($cmd) == 0
( run in 1.003 second using v1.01-cache-2.11-cpan-f0ff5d10edf )