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 )