Audio-Nama

 view release on metacpan or  search on metacpan

lib/Audio/Nama.pm  view on Meta::CPAN

	map{ my $pid = $_; 
		 map{ my $signal = $_; 
			  kill $signal, $pid; 
			  sleeper(0.2);
			} (15,9);
		 waitpid $pid, 1;
	} @pids;
}
	
sub cleanup_exit {
	logsub((caller(0))[3]);
 	remove_riff_header_stubs();
	trigger_rec_cleanup_hooks();
	# for each process: 
	# - SIGINT (1st time)
	# - allow time to close down
	# - SIGINT (2nd time)
	# - allow time to close down
	# - SIGKILL
	#project_snapshot(); 
	Audio::Nama::Engine::sync_action('kill_and_reap');

lib/Audio/Nama/Assign.pm  view on Meta::CPAN


sub assign {
  # Usage: 
  # assign ( 
  # data 	=> $ref,
  # vars 	=> \@vars,
  # var_map => 1,
  #	class => $class
  #	);

	logsub((caller(0))[3]);
	
	my %h = @_; # parameters appear in %h
	my $class;
	logpkg(__FILE__,__LINE__,'logcarp',"didn't expect scalar here") if ref $h{data} eq 'SCALAR';
	logpkg(__FILE__,__LINE__,'logcarp',"didn't expect code here") if ref $h{data} eq 'CODE';
	# print "data: $h{data}, ", ref $h{data}, $/;

	if ( ref $h{data} !~ /^(HASH|ARRAY|CODE|GLOB|HANDLE|FORMAT)$/){
		# we guess object
		$class = ref $h{data}; 

lib/Audio/Nama/Assign.pm  view on Meta::CPAN

$fx_cache
$text
$gui
$midi
$help
$mastering
$project

);
sub assign_singletons {
	logsub((caller(0))[3]);
	my $ref = shift;
	my $data = $ref->{data} or die "expected data got undefined";
	my $class = $ref->{class} // 'Audio::Nama';
	$class .= '::'; # SKIP_PREPROC
	map {
		my $ident = $_;
		if( defined $data->{$ident}){
			my $type = ref $data->{$ident};
			$type eq 'HASH' or die "$ident: expect hash, got $type";
			map{ 

lib/Audio/Nama/Assign.pm  view on Meta::CPAN


{
	my $parse_re =  		# initialize only once
			qr/ ^ 			# beginning anchor
			([\%\@\$]) 		# first character, sigil
			([\w:]+)		# identifier, possibly perl namespace 
			(?:->\{(\w+)})?  # optional hash key for new hash-singleton vars
			$ 				# end anchor
			/x;
sub serialize {
	logsub((caller(0))[3]);

	my %h = @_;
	my @vars = @{ $h{vars} };
	my $class = $h{class};
	my $file  = $h{file};
	my $format = $h{format} // 'perl'; # default to Data::Dumper::Concise

 	$class //= "Audio::Nama";
	$class =~ /::$/ or $class .= '::'; # SKIP_PREPROC
	logpkg(__FILE__,__LINE__,'debug',"file: $file, class: $class\nvariables...@vars");

lib/Audio/Nama/Assign.pm  view on Meta::CPAN


	# now we serialize %state
	
	my $path = $h{file};

	serialize_and_write(\%state, $path, $format);
}
}

sub json_out {
	logsub((caller(0))[3]);
	my $data_ref = shift;
	my $type = ref $data_ref;
	croak "attempting to code wrong data type: $type"
		if $type !~ /HASH|ARRAY/;
	$to_json->encode($data_ref);
}

sub json_in {
	logsub((caller(0))[3]);
	my $json = shift;
	my $data_ref = decode_json($json);
	$data_ref
}

sub yaml_in {
	
	# logsub((caller(0))[3]);
	my $input = shift;
	my $yaml = $input =~ /\n/ # check whether file or text
		? $input 			# yaml text
		: do
			{
				logpkg(__FILE__,__LINE__,'debug',"filename: $input"); 
				read_file($input);	# file name
			};
	if ($yaml =~ /\t/){
		croak "YAML file: $input contains illegal TAB character.";

lib/Audio/Nama/CacheTrack.pm  view on Meta::CPAN

# track
# additional_time
# processing_time
# original_version
# output_wav
# orig_volume
# orig_pan
# bus - we are caching a bus

sub cache_track { # launch subparts if conditions are met
	logsub((caller(0))[3]);
	my $args = {}; # to pass params to routines involved in caching
	(my $track, $args->{additional_time}) = @_;

	my $bus = $track->is_mixing;
	my $obj; # track or bus
	my $name = $track->name;
	if( $track->off ){
		my $bus = $track->is_mixer && ! $track->playback_version;
		my $status = $bus ? MON : PLAY;
		throw(qq(Cannot cache track "$name" with status OFF. Set to $status and try again)); 

lib/Audio/Nama/CacheTrack.pm  view on Meta::CPAN

	# set WAV output format
	$g->set_vertex_attributes(
		$track->name, 
		{ format => signal_format($config->{cache_to_disk_format},$track->width),
			version => ($args->{track_result_version}),
		}
	); 
}

sub generate_cache_track_graph {
	logsub((caller(0))[3]);
	my $args = shift;
 	my $g = Audio::Nama::ChainSetup::initialize();
	$args->{graph} = $g;
	
	#   We route the signal thusly:
	#
	#   Target track --> CacheRecTrack --> wav_out
	#
	#   CacheRecTrack slaves to target target
	#     - same name

lib/Audio/Nama/CacheTrack.pm  view on Meta::CPAN

	my $from_path = join_path($args->{track}->dir, $from_name);

	$g->set_vertex_attributes(
		$args->{track}->name,
		{ full_path => $from_path }
	);

}

sub process_cache_graph {
	logsub((caller(0))[3]);
	my $g = shift;
	logpkg(__FILE__,__LINE__,'debug', "The graph after bus routing:\n$g");
	Audio::Nama::ChainSetup::prune_graph();
	logpkg(__FILE__,__LINE__,'debug', "The graph after pruning:\n$g");
	Audio::Nama::Graph::expand_graph($g); 
	logpkg(__FILE__,__LINE__,'debug', "The graph after adding loop devices:\n$g");
	Audio::Nama::Graph::add_inserts($g);
	logpkg(__FILE__,__LINE__,'debug', "The graph with inserts:\n$g");
	my $success = Audio::Nama::ChainSetup::process_routing_graph();
	if ($success) 
	{ 
		Audio::Nama::ChainSetup::write_chains();
		Audio::Nama::ChainSetup::remove_temporary_tracks();
	}
	$success
}

sub cache_engine_run {
	logsub((caller(0))[3]);
	my $args = shift;
	connect_transport()
		or throw("Couldn't connect engine! Aborting."), return;

	$args->{processing_time} = $setup->{audio_length} + $args->{additional_time};

	pager($args->{track}->name.": processing time: ". d2($args->{processing_time}). " seconds");
	pager("Starting cache operation. Please wait.");
	
	revise_prompt(" "); 

lib/Audio/Nama/CacheTrack.pm  view on Meta::CPAN

	# we try to set processing time this way
	ecasound_iam("cs-set-length $args->{processing_time}"); 

	ecasound_iam("start");

	# ensure that engine stops at completion time
	$setup->{cache_track_args} = $args;
 	start_event(poll_engine => timer(1, 0.5, \&poll_progress));
}
sub complete_caching {
	logsub((caller(0))[3]);
	my $args = shift;	
	my $name = $args->{track}->name;
	my @files = grep{/$name/} new_files_were_recorded();
	if (@files ){ 
		
		update_cache_map($args);	
		caching_cleanup($args);

	} else { throw("track cache operation failed!") }
	undef $setup->{cache_track_args};
}
sub update_cache_map {
	logsub((caller(0))[3]);
	my $args = shift;
	logpkg(__FILE__,__LINE__,'debug', "updating track cache_map");
	logpkg(__FILE__,__LINE__,'debug', "current track cache entries:",
		sub {
			join "\n","cache map", 
			map{($_->dump)} Audio::Nama::EffectChain::find(track_cache => 1)
		});

	my $track = $args->{track};

lib/Audio/Nama/ChainSetup.pm  view on Meta::CPAN

	@input_chains,	# list of input chain segments 
	@output_chains, # list of output chain segments
	@post_input,	# post-input chain operators
	@pre_output, 	# pre-output chain operators

	$chain_setup,	# final result as string
	);


sub remove_temporary_tracks {
	logsub((caller(0))[3]);
	map { logpkg(__FILE__,__LINE__,'debug',"removing temporary track ",$_->group.'/'.$_->name); $_->remove  } 
		grep{ $_->group eq 'Temp' } Audio::Nama::audio_tracks();
}
sub initialize {

	remove_temporary_tracks(); # we will generate them again
	$setup->{audio_length} = 0;  
	@io = (); 			# IO object list
	Audio::Nama::IO::initialize();
	$g = Graph->new(); 	

lib/Audio/Nama/ChainSetup.pm  view on Meta::CPAN

sub warn_missing_jack_clients {
	for my $track (Audio::Nama::audio_tracks()){
		$track->send_type =~ /jack_client/ and not $jack->{clients}->{$track->send_id}
		 	and Audio::Nama::throw("Track ".$track->name. qq(: JACK client ").$track->send_id.qq(" not found. Skipping aux send));
		$track->source_type eq 'jack_client' and not $jack->{clients}->{$track->source_id}
			and Audio::Nama::throw("Track ".$track->name. qq(: JACK client ").$track->source_id.qq(" not found. Skipping source connection));
	}
}

sub generate_setup_try {
	logsub((caller(0))[3]);

	my $extra_setup_code = shift;

	warn_missing_jack_clients();

	# start with bus routing
	
	map{ $_->apply($g) } Audio::Nama::Bus::all();

	logpkg(__FILE__,__LINE__,'debug',"Graph after bus routing:\n$g");

lib/Audio/Nama/ChainSetup.pm  view on Meta::CPAN

	}
}

sub add_paths_for_aux_sends { # not including Main

	# currently this routing is track-oriented 

	# we could add this to the Audio::Nama::Bus base class
	# then suppress it in Mixdown and Main groups

	logsub((caller(0))[3]);

	map {  Audio::Nama::Graph::add_path_for_aux_send($g, $_ ) } 
	grep {  $_->group !~ /Mixdown|Null/
			and $_->send_type 
			and $_->rec_status ne OFF } Audio::Nama::audio_tracks();
}


sub add_paths_from_Main {
	logsub((caller(0))[3]);

	if ($mode->mastering){
		$g->add_path(qw[Main Eq Low Boost]);
		$g->add_path(qw[Eq Mid Boost]);
		$g->add_path(qw[Eq High Boost]);
	}
	else { 
		$g->add_path('Main', output_node($tn{Main}->send_type)) if $tn{Main}->mon
			and ! $tn{Mixdown}->rec

		# tests require this, why not generated by
		# add_paths_for_aux_sends() ??
	}

}
sub add_paths_for_mixdown_handling {
	logsub((caller(0))[3]);
	my $final_leg_origin = $mode->mastering ? 'Boost' : 'Main';

	if ($tn{Mixdown}->rw eq REC ){
		# don't monitor via soundcard
		$g->delete_edge('Main','soundcard_out');
		$g->delete_edge('Boost','soundcard_out');
		my @p = ($final_leg_origin, ,'Mixdown', 'wav_out');
		$g->add_path(@p);
		$g->set_vertex_attributes('Mixdown', {
		  	format_template		=> $config->{mix_to_disk_format},

lib/Audio/Nama/ChainSetup.pm  view on Meta::CPAN

			my @e = ('wav_in','Mixdown',output_node($tn{Main}->send_type));
			$g->add_path(@e);
			$g->set_vertex_attributes('Mixdown', {
				send_type	=> $tn{Main}->send_type,
				send_id		=> $tn{Main}->send_id,
				chain			=> "Mixdown" }); 
		# no effects will be applied because effects are on chain 2
	}
}
sub prune_graph {
	logsub((caller(0))[3]);
	Audio::Nama::Graph::simplify_send_routing($g);
	logpkg(__FILE__,__LINE__,'debug',"Graph after simplify_send_routing:\n$g");
	Audio::Nama::Graph::remove_out_of_bounds_tracks($g) if Audio::Nama::edit_mode();
	logpkg(__FILE__,__LINE__,'debug',"Graph after remove_out_of_bounds_tracks:\n$g");
	Audio::Nama::Graph::recursively_remove_inputless_tracks($g);
	logpkg(__FILE__,__LINE__,'debug',"Graph after recursively_remove_inputless_tracks:\n$g");
	Audio::Nama::Graph::recursively_remove_outputless_tracks($g); 
	logpkg(__FILE__,__LINE__,'debug',"Graph after recursively_remove_outputless_tracks:\n$g");
}
# object based dispatch from routing graph
	
sub process_routing_graph {
	logsub((caller(0))[3]);

	# generate a set of IO objects from edges
	@io = map{ dispatch($_) } $g->edges;
	
	logpkg(__FILE__,__LINE__,'debug', sub{ join "\n",map $_->dump, @io });

	# sort chain_ids by attached input object
	# one line will show all with that one input
	# -a:3,5,6 -i:foo
	

lib/Audio/Nama/ChainSetup.pm  view on Meta::CPAN

	my ($a, $b) = @{$_[0]};
	#say "a: $a, b: $b";
	my ($name, $endpoint) = $tn{$a} ? @{$_[0]} : reverse @{$_[0]} ;
	my $direction = $tn{$a} ? 'output' : 'input';
	($name, $endpoint, $direction)
}
sub override {
	# data from edges has priority over data from vertexes
	# we specify $name, because it could be left or right 
	# vertex
	logsub((caller(0))[3]);
	my ($name, $edge) = @_;
	(override_from_vertex($name), override_from_edge($edge))
}
	
sub override_from_vertex {
	my $name = shift;
		warn("undefined graph\n"), return () unless (ref $g) =~ /Graph/;
		my $attr = $g->get_vertex_attributes($name);
		$attr ? %$attr : ();
}
sub override_from_edge {
	my $edge = shift;
		warn("undefined graph\n"), return () unless (ref $g) =~ /Graph/;
		my $attr = $g->get_edge_attributes(@$edge);
		$attr ? %$attr : ();
}
							
sub write_chains {

	logsub((caller(0))[3]);

	## write general options
	
	my @globals;
	my $format = signal_format($config->{devices}->{jack}->{signal_format},2); # HARDCODED XXX
	push @globals, $config->{ecasound_globals}->{common};
	push @globals, "-f:$format", join(',', 	'-G:jack',
										$config->{ecasound_jack_client_name},
										$config->{jack_transport_mode}
							) if $jack->{jackd_running};

lib/Audio/Nama/Config.pm  view on Meta::CPAN

# sub global_config {
# 	read_file( join_path($ENV{HOME}, config_file()));
# }

sub read_config {

	# read and process the configuration file
	#
	# use the embedded default file if none other is present
	
	logsub((caller(0))[3]);
	
	my $config_file = shift;
	
	my $yml = $config_file // get_data_section("default_namarc");
	strip_all( $yml );
	my %cfg = %{  yaml_in($yml) };
	logpkg(__FILE__,__LINE__,'debug', "config file:", Dumper \%cfg);
	*subst = \%{$cfg{abbreviations}}; # alias
	walk_tree(\%cfg);
	walk_tree(\%cfg); # second pass completes substitutions

lib/Audio/Nama/Config.pm  view on Meta::CPAN


loads initial_mix.json");

	$config->{use_git} = $config->{use_git} && git_executable_found() ? 1 : 0;

}

sub git_executable_found { qx(which git) }

sub walk_tree {
	#logsub((caller(0))[3]);
	my $ref = shift;
	map { substitute($ref, $_) } 
		grep {$_ ne q(abbreviations)} 
			keys %{ $ref };
}
sub substitute{
	my ($parent, $key)  = @_;
	my $val = $parent->{$key};
	#logpkg(__FILE__,__LINE__,'debug', qq(key: $key val: $val\n) );
	ref $val and walk_tree($val)

lib/Audio/Nama/EcasoundCleanup.pm  view on Meta::CPAN

}


package Audio::Nama;
use v5.36;
use Cwd;
use File::Spec::Functions qw(splitpath);
use Audio::Nama::Globals qw(:all);

sub rec_cleanup {  
	logsub((caller(0))[3]);
	logpkg(__FILE__,__LINE__,'debug',"transport still running, can't cleanup"), return if $this_engine->running;
	if( my (@files) = new_files_were_recorded() )
	{
		if( my @rec_tracks = Audio::Nama::ChainSetup::engine_wav_out_tracks() )
		{
			$project->{playback_position} = 0;
			$setup->{_last_rec_tracks} = \@rec_tracks;
		}

		if( grep /Mixdown/, @files) { 
				mixdown_postprocessing() ;
				mixplay();
		}
		post_rec_configure() 
	}
}
sub mixdown_postprocessing {
	logsub((caller(0))[3]);
	nama_cmd('mixplay');
	my ($oldfile) = $tn{Mixdown}->full_path =~ m{([^/]+)$};
	$oldfile = join_path('.wav',$oldfile);
	my $tag_name = join '-', $project->{name}, current_branch();
	my $version = $tn{Mixdown}->playback_version;

	# simplify the tagname basename 
	# 
	# 	untitled-master        -> untitled
	#   untitled-premix-branch -> untitled-premix

lib/Audio/Nama/EcasoundCleanup.pm  view on Meta::CPAN

		$comment .= "encoded " if $encoding;
		$comment .= "as $tag_name ";
		$comment .= "(commit $sha)" if $sha;
	}
	$tn{Mixdown}->add_system_version_comment($version, $comment);
	pager_newline($comment);	
	encode_mixdown_file($oldfile,$tag_name);
	chdir $was_in;
}
sub tag_mixdown_commit {
	logsub((caller(0))[3]);
	my ($name, $newfile, $mixdownfile) = @_;
	logpkg(__FILE__,__LINE__,'debug',"tag_mixdown_commit: @_");

	my ($sym) = $newfile =~ m([^/]+$);
	my ($mix) = $mixdownfile =~ m([^/]+$);

	# we want to tag the normal playback state
	
	local $quiet = 1;
	mixoff();

	my $msg = "State for $sym ($mix)";
	project_snapshot($msg);
	git('tag', $name, '-m', $mix);
}
sub delete_existing_mixdown_tag_and_convenience_encodings {
	logsub((caller(0))[3]);
	my $name = shift;
	logpkg(__FILE__,__LINE__,'debug',"name: $name");
		git('tag', '-d', $name);
		foreach( qw(mp3 ogg wav) ){
			my $file = join_path(project_dir(),"$name.$_");
			unlink $file if -e $file;
		}
	}
sub encode_mixdown_file {
	state $shell_encode_command = {

lib/Audio/Nama/EcasoundSetup.pm  view on Meta::CPAN

use v5.36;
our $VERSION = 1.0;
use Audio::Nama::Globals qw(:all);
use Audio::Nama::Log qw(logpkg logsub);
sub setup { 
	package Audio::Nama;
	no warnings 'uninitialized';
	my $self = shift;
	# return 1 if successful
	# catch errors from generate_setup_try() and cleanup
	logsub((caller(0))[3]);

	# extra argument (setup code) will be passed to generate_setup_try()
	my (@extra_setup_code) = @_;

	# save current track
	local $this_track;

	# prevent engine from starting an old setup
	
	ecasound_iam('cs-disconnect') if ecasound_iam('cs-connected');

lib/Audio/Nama/EcasoundSetup.pm  view on Meta::CPAN


### legacy ecasound support routines in root namespace 

package Audio::Nama;
use v5.36;
no warnings 'uninitialized';
sub find_duplicate_inputs { # in Main bus only

	%{$setup->{tracks_with_duplicate_inputs}} = ();
	%{$setup->{inputs_used}} = ();
	logsub((caller(0))[3]);
	map{	my $source = $_->source;
			$setup->{tracks_with_duplicate_inputs}->{$_->name}++ if $setup->{inputs_used}->{$source} ;
		 	$setup->{inputs_used}->{$source} //= $_->name;
	} 
	grep { $_->rw eq REC }
	map{ $tn{$_} }
	$bn{Main}->tracks(); # track names;
}
sub load_ecs {
	my $setup = shift;

lib/Audio/Nama/EcasoundSetup.pm  view on Meta::CPAN

	$setup eq $result or throw("$result: failed to select chain setup");
	logpkg(__FILE__,__LINE__,'debug',sub{map{ecasound_iam($_)} qw(cs es fs st ctrl-status)});
	1;
}
sub teardown_engine {
	ecasound_iam("cs-disconnect") if ecasound_iam("cs-connected");
	ecasound_iam("cs-remove") if ecasound_iam("cs-selected");
}

sub arm {
	logsub((caller(0))[3]);
	exit_preview_modes();
	request_setup();
	reconfigure_engine();
}

# substitute all live inputs by clock-sync'ed 
# Ecasound null device 'rtnull'

sub arm_rtnull {

lib/Audio/Nama/EcasoundSetup.pm  view on Meta::CPAN

	);

arm();

}
sub something_to_run { $en{ecasound}->valid_setup or midi_run_ready()  }

sub midi_run_ready { $config->{use_midi} and $en{midish} and $en{midish}->is_active }

sub connect_transport {
	logsub((caller(0))[3]);
	remove_riff_header_stubs();
	register_other_ports(); # we won't see Nama ports since Nama hasn't started
	load_ecs($file->chain_setup) or Audio::Nama::throw("failed to load chain setup"), return;
	$this_engine->valid_setup()
		or throw("Invalid chain setup, engine not ready."),return;
	find_op_offsets(); 
	setup_fades();
	apply_ops();
	ecasound_iam('cs-connect');
		#or throw("Failed to connect setup, engine not ready"),return;

lib/Audio/Nama/Effect.pm  view on Meta::CPAN

		{
			return 1 if $entry->{params}->[$_]->{dir} eq 'output' 
		}
}

sub registry_index {
	my $self = shift;
	$fx_cache->{full_label_to_index}->{ $self->type };
}
sub ecasound_controller_index { 
	logsub((caller(0))[3]);
	my $self = shift;
	my $n = $self->chain;
	my $id = $self->id;
	my $opcount = 0;
	logpkg(__FILE__,__LINE__,'debug', "id: $id, n: $n, ops: @{ $ti{$n}->ops }" );
	for my $op (@{ $ti{$n}->ops }) { 
		# increment only controllers
		next unless fxn($op)->is_controller;
		$opcount++;
		last if $op eq $id;
	} 
	$opcount;
}
sub ecasound_effect_index { 
	logsub((caller(0))[3]);
	my $self = shift;
	my $n = $self->chain;
	my $id = $self->id;
	my $opcount = 0;
	logpkg(__FILE__,__LINE__,'debug', "id: $id, n: $n, ops: @{ $ti{$n}->ops }" );
	for my $op (@{ $ti{$n}->ops }) { 
			my $fx = fxn($op);
 			next if $fx->is_controller;
			++$opcount;   # first index is 1
			last if $op eq $id

lib/Audio/Nama/Effect.pm  view on Meta::CPAN

		}
		else { $new_value = $value }
	logpkg(__FILE__,__LINE__,'debug', "id $op_id p: $parameter, sign: $sign value: $value");
	update_effect( 
		$op_id, 
		$parameter,
		$new_value);
	1
}
sub _remove_effect { 
	logsub((caller(0))[3]);
	my $self = shift;
	my $id = $self->id;
	my $n 		= $self->chain;
	my $parent 	= $self->parent;
	my $owns	= $self->owns;
	logpkg(__FILE__,__LINE__,'debug', "id: $id", ($parent ? ". parent: ".$parent->id : '' ));

	my $object = $parent ? q(controller) : q(chain operator); 
	logpkg(__FILE__,__LINE__,'debug', qq(ready to remove $object "$id" from track "$n"));

lib/Audio/Nama/Effect.pm  view on Meta::CPAN

		my @ops_list = @{$track->ops};
		my @new_list = grep  { $_ ne $id  } @ops_list;
		$track->{ops} =   [ @new_list ];
	}
	#set_current_op($this_track->ops->[0]);
	#set_current_param(1);
	delete $by_id{$self->id};
	return(); 
}
sub position_effect {
	#logsub((caller(0))[3]);
	my($self, $pos) = @_;

	my $op = $self->id;
	
	#Audio::Nama::pager("$op or $pos: controller not allowed, skipping.\n"), return 
	#	if grep{ fxn($_)->is_controller } $op, $pos;
	
	# first, modify track data structure
	
	my $track = $ti{$self->chain};

lib/Audio/Nama/Effect.pm  view on Meta::CPAN

	# easier to reconfigure the engine than to code for
	# repositioning ecasound effects.
	say join " - ",@new_op_list;
	@{$track->ops} = @new_op_list;
	Audio::Nama::request_setup();
	$this_track = $track;
	nama_cmd('show_track');
}

sub apply_op {
	logsub((caller(0))[3]);
	my $self = shift;
	local $config->{category} = 'ECI_FX';
	my $id = $self->id;
	logpkg(__FILE__,__LINE__,'debug', "id: $id");
	logpkg(__FILE__,__LINE__,'logcluck', "$id: expected effect entry not found!"), return
		if effect_entry_is_bad($id);
	my $code = $self->type;
	my $dad = $self->parent;
	my $chain = $self->chain; 
	logpkg(__FILE__,__LINE__,'debug', "chain: $chain, type: $code");

lib/Audio/Nama/Effect.pm  view on Meta::CPAN

#		user command: add_effect <effect_chain_name>
#		add_effect(effect_chain => $fxc) calls insert_effect() 
#		insert_effect()
#				* removes preceding operators 
#				* calls append_effect(effect_chain => $fxc) 
#					+ which calls $fxc->add
#					+ which calls append_effect() for each effect
#				* restores the operators
		 
sub add_effect {
	#logsub((caller(0))[3]);
	my $args = shift;
	my $added = _add_effect($args);
	$added->[0]->id
}
sub _add_effect {
	my $p = shift;
	logsub((caller(0))[3]);
	#logpkg(__FILE__,__LINE__,'debug',sub{ "add effect arguments - 0:\n".json_out($p)});
	
	set_chain_value($p);

	### We prohibit creating effects on the Mixdown track	

	### We check $track->forbid_user_ops
	### which is set on the Mixdown track,

	### An alternative would be giving each

lib/Audio/Nama/Effect.pm  view on Meta::CPAN

		
	if ($running){
		ecasound_iam('start');	
		sleeper(0.3);
		Audio::Nama::unmute();
		$ui->start_heartbeat;
	}
	$added;
}
sub modify_effect {
	logsub((caller(0))[3]);
	my ($op_id, $parameter, $sign, $value) = @_;
		# $parameter: one-based
	
	my $FX = fxn($op_id)
		or pager("$op_id: non-existing effect id. Skipping.\n"), return; 
	$FX->_modify_effect($parameter, $value, $sign);
}


sub modify_multiple_effects {
	logsub((caller(0))[3]);
	my ($op_ids, $parameters, $sign, $value) = @_;
	map{ my $op_id = $_;
		map{ 	my $parameter = $_;
				modify_effect($op_id, $parameter, $sign, $value);
				set_current_op($op_id);
				set_current_param($parameter);	
		} @$parameters;
	} @$op_ids;
}

sub remove_effect { 
	logsub((caller(0))[3]);
	my $id = shift;
	my $FX = fxn($id)
		or logpkg(__FILE__,__LINE__,'logcarp',"$id: does not exist, skipping...\n"), return;
	$FX->_remove_effect;
}

sub full_effect_code {
	# get text effect code from user input, which could be
	# - LADSPA Unique ID (number)
	# - LADSPA Label (el:something)

lib/Audio/Nama/Effect.pm  view on Meta::CPAN

		return [] unless defined $p->{default};
		push @$values, $p->{default};
	}
	$values
}
	

## Ecasound engine -- apply/remove chain operators

sub apply_ops {  # in addition to operators in .ecs file
	logsub((caller(0))[3]);
	for my $track ( Audio::Nama::audio_tracks() ) {
		my $n = $track->n;
 		next unless Audio::Nama::ChainSetup::is_ecasound_chain($n);
		logpkg(__FILE__,__LINE__,'debug', "chain: $n, offset: $fx->{offset}->{$n}");
		$this_engine->reset_ecasound_selections_cache();
		$this_engine->current_chain($n);
		$track->apply_ops;
	}
}

sub remove_op {
	# remove chain operator from Ecasound engine

	logsub((caller(0))[3]);
	local $config->{category} = 'ECI_FX';

	# only if engine is configured
	return unless $this_engine->valid_setup();

	my $id = shift;
	my $self = fxn($id);
	Audio::Nama::request_setup(), return if $self->is_channel_op;
	my $n = $self->chain;

lib/Audio/Nama/Effect.pm  view on Meta::CPAN


## synchronize Ecasound chain operator parameters 
#  with Nama effect parameter

sub update_ecasound_effect {
	local $config->{category} = 'ECI_FX';

	# update the parameters of the Ecasound chain operator
	# referred to by a Nama operator_id
	
	#logsub((caller(0))[3]);

	return unless $this_engine->valid_setup;
	#my $es = ecasound_iam("engine-status");
	#logpkg(__FILE__,__LINE__,'debug', "engine is $es");
	#return if $es !~ /not started|stopped|running/;

	my ($id, $param, $val) = @_;

	my $FX = fxn($id) or carp("$id: effect not found. skipping...\n"), return;
	$param++; # so the value at $p[0] is applied to parameter 1

lib/Audio/Nama/Effect.pm  view on Meta::CPAN

# parameters

sub update_effect {
	my ($id, $param, $val) = @_;
	return if ! defined fxn($id);
	fxn($id)->params->[$param] = $val;
	update_ecasound_effect( @_ );
}

sub sync_effect_parameters {
	logsub((caller(0))[3]);
	local $config->{category} = 'ECI_FX';

	# when a controller changes an effect parameter, the
	# parameter value can differ from Nama's value for that
	# parameter.
	#
	# this routine syncs them in prep for save_state()
	
 	return unless $this_engine->valid_setup();
   	push my @ops, ops_with_controller(), ops_with_read_only_params();

lib/Audio/Nama/Effect.pm  view on Meta::CPAN

	grep{ $_->has_read_only_param() }
	map{ fxn($_) }
	map{ @{ $_->ops } } 
	Audio::Nama::ChainSetup::engine_tracks();
}


sub find_op_offsets {

	local $config->{category} = 'ECI_FX';
	logsub((caller(0))[3]);
	my @op_offsets = grep{ /"\d+"/} split "\n",ecasound_iam("cs");
	logpkg(__FILE__,__LINE__,'debug', join "\n\n",@op_offsets);
	for my $output (@op_offsets){
		my $chain_id;
		($chain_id) = $output =~ m/Chain "(\w*\d+)"/;
		# "print chain_id: $chain_id\n";
		next if $chain_id =~ m/\D/; # skip id's containing non-digits
									# i.e. M1
		my $quotes = $output =~ tr/"//;
		logpkg(__FILE__,__LINE__,'debug', "offset: $quotes in $output");

lib/Audio/Nama/Effect.pm  view on Meta::CPAN

}
sub restore_effects {
	my($track, @ops) = @_;
	set_bypass_state($track, 'off', @ops);
}

sub set_bypass_state {
	
	local $config->{category} = 'ECI_FX';
	my($track, $bypass_state, @ops) = @_;
	logsub((caller(0))[3]);

	# only process ops that belong to this track
	@ops = intersect_with_track_ops_list($track,@ops);

	$this_engine->current_chain($track->n);
	$track->mute;

	foreach my $op ( @ops)
	{ 
		my $FX = fxn($op);

lib/Audio/Nama/EffectChain.pm  view on Meta::CPAN

	my ($ec) = Audio::Nama::EffectChain::find(
		unique => 1, 
		user   => 1, 
		name   => $name,
	);
	if( $ec ){ $ec->add($Audio::Nama::this_track) }
	else { Audio::Nama::throw("$name: effect chain not found") }
	1;
}
sub new_effect_profile {
	logsub((caller(0))[3]);
	my ($bunch, $profile) = @_;
	my @tracks = bunch_tracks($bunch);
	Audio::Nama::pager( qq(effect profile "$profile" created for tracks: @tracks) );
	map { 
		Audio::Nama::EffectChain->new(
			profile 	=> $profile,
			user		=> 1,
			global		=> 1,
			track_name	=> $_,
			ops_list	=> [ $tn{$_}->user_ops ],
			inserts_data => $tn{$_}->inserts,
		);
	} @tracks;
}
sub delete_effect_profile { 
	logsub((caller(0))[3]);
	my $name = shift;
	Audio::Nama::pager( qq(deleting effect profile: $name) );
	map{ $_->destroy} Audio::Nama::EffectChain::find( profile => $name );
}

sub apply_effect_profile {  # overwriting current effects
	logsub((caller(0))[3]);
	my ($profile) = @_;
	my @chains = Audio::Nama::EffectChain::find(profile => $profile);

	# add missing tracks 
	map{ Audio::Nama::pager( "adding track $_" ); add_track($_) } 
		grep{ !$tn{$_} } 
		map{ $_->track_name } @chains;	
	# add effect chains
	map{ $_->add } @chains;
}

lib/Audio/Nama/EffectsRegistry.pm  view on Meta::CPAN

use v5.36;
use Audio::Nama::Util qw(round);
no warnings 'uninitialized';

## register data about LADSPA plugins, and Ecasound effects and
#  presets (names, ids, parameters, hints) 

sub prepare_static_effects_data{
	my $source = shift; 
	
	logsub((caller(0))[3]);

	if (not is_test_script() ){
		logpkg(__FILE__,__LINE__,'debug', join "\n", "newplugins:", new_plugins());
		if (! $source and ($config->{opts}->{r} or new_plugins())){ 

			rename $file->effects_cache, $file->effects_cache . ".bak";
			print "Regenerating effects data cache\n";
		}
	}

lib/Audio/Nama/EffectsRegistry.pm  view on Meta::CPAN

	# timestamp that file was modified
	my $filename = shift;
	#print "file: $filename\n";
	my @s = stat $filename;
	$s[9];
}
sub initialize_effect_index {
	$fx_cache->{partial_label_to_full} = {};
}
sub generate_mappings_for_shortcuts {
	logsub((caller(0))[3]);
	map{ 
		my $code = $_;
 		
		# abbrevations for lv2: lv2-foo for elv2:http://something.com/other/foo
		if ( my ($suffix) = $code =~ /(?:elv2:).*?([^\/]+)$/ )
		{
			$fx_cache->{partial_label_to_full}->{"lv2-$suffix"} = $code;
		}
		else {
		my ($short) = $code =~ /:([-\w]+)/;

lib/Audio/Nama/EffectsRegistry.pm  view on Meta::CPAN

{ my %dispatch =
		(
			ctrl 	=> \&generate_help,
			lv2	 	=> \&generate_lv2_help,
			ladspa 	=> \&generate_ladspa_help,
			cop		=> \&generate_help,
			preset	=> \&generate_help,
		);
		
sub extract_effects_data {
	logsub((caller(0))[3]);
	my ($plugin_type, $lower, $upper, $regex, $separator, @lines) = @_;
	carp ("incorrect number of lines ", join ' ',$upper-$lower,scalar @lines)
		if $lower + @lines - 1 != $upper;
	logpkg(__FILE__,__LINE__,'debug',"lower: $lower upper: $upper  separator: $separator");
	logpkg(__FILE__,__LINE__,'debug', "lines: ". join "\n",@lines);
	logpkg(__FILE__,__LINE__,'debug', "regex: $regex");
	my $j = $lower - 1;
	while(my $line = shift @lines){
		$j++;
		$line =~ /$regex/ or 

lib/Audio/Nama/EffectsRegistry.pm  view on Meta::CPAN

		$fx_cache->{registry}->[$j]->{display} = qq(field);
		$fx_cache->{registry}->[$j]->{plugin_type} = $plugin_type;
		$fx_cache->{user_help}->[$j] = $dispatch{$plugin_type}->($line);
		map{ push @{$fx_cache->{registry}->[$j]->{params}}, {name => $_} } @p_names
			if @p_names;
	}

}
}
sub sort_ladspa_effects {
	logsub((caller(0))[3]);
#	print json_out($fx_cache->{split}); 
	my $aa = $fx_cache->{split}->{ladspa}{a};
	my $zz = $fx_cache->{split}->{ladspa}{z};
#	print "start: $aa end $zz\n";
	map{push @{$fx_cache->{ladspa_sorted}}, 0} ( 1 .. $aa ); # fills array slice [0..$aa-1]
	splice @{$fx_cache->{ladspa_sorted}}, $aa, 0,
		 sort { $fx_cache->{registry}->[$a]->{name} cmp $fx_cache->{registry}->[$b]->{name} } ($aa .. $zz) ;
	logpkg(__FILE__,__LINE__,'debug', "sorted array length: ". scalar @{$fx_cache->{ladspa_sorted}});
}		
sub run_external_ecasound_cmd {

lib/Audio/Nama/EffectsRegistry.pm  view on Meta::CPAN

	$output =~ s/\r//;
	$output =~ s/^.+?Registered \w+ plugins:\s*//s; # XXX HARDCODED for plugins only
	$output =~ s/^ecasound .+?\Z//ms;
	my @output = split "\n",$output;
	#splice @output, 0,8;
	#splice @output, -3,3;
	join "\n",@output;
}
sub read_in_effects_data {
	
	logsub((caller(0))[3]);


	#### LADSPA

	my $lr = run_external_ecasound_cmd('ladspa-register') ;

	logpkg(__FILE__,__LINE__,'debug',"ladpsa-register output:\n",$lr);

	my @ladspa =  split "\n", $lr;
	

lib/Audio/Nama/EffectsRegistry.pm  view on Meta::CPAN

		$fx_cache->{registry}->[ $fx_cache->{full_label_to_index}->{ $code } ] = $hashref;
	}
}
sub ladspa_path {
	$ENV{LADSPA_PATH} || q(/usr/lib/ladspa);
}
sub lv2_path {
	$ENV{LV2_PATH} || q(/usr/lib/lv2);
}
sub get_ladspa_hints{
	logsub((caller(0))[3]);
	my @dirs =  split ':', ladspa_path();
	my $data = '';
	my %seen = ();
	my @plugins = ladspa_plugin_list();
	#pager join $/, @plugins;

	# use these regexes to snarf data
	
	my $pluginre = qr/
	Plugin\ Name:       \s+ "([^"]+)" \s+

lib/Audio/Nama/EffectsRegistry.pm  view on Meta::CPAN

	}
	
	$resolution = d2( $resolution + 0.002) if $resolution < 1  and $resolution > 0.01;
	$resolution = dn ( $resolution, 3 ) if $resolution < 0.01;
	$resolution = int ($resolution + 0.1) if $resolution > 1 ;
	
	($beg, $end, $default, $resolution)

}
sub integrate_ladspa_hints {
	logsub((caller(0))[3]);
	map{ 
		my $i = $fx_cache->{full_label_to_index}->{$_};
		# print("$_ not found\n"), 
		if ($i) {
			$fx_cache->{registry}->[$i]->{params} = $fx_cache->{ladspa}->{$_}->{params};
			# we revise the number of parameters read in from ladspa-register
			$fx_cache->{registry}->[$i]->{count} = scalar @{$fx_cache->{ladspa}->{$_}->{params}};
			$fx_cache->{registry}->[$i]->{display} = $fx_cache->{ladspa}->{$_}->{display};
		}
	} keys %{$fx_cache->{ladspa}};

lib/Audio/Nama/Engine.pm  view on Meta::CPAN

our @ISA = 'Audio::Nama::Engine';
use Role::Tiny::With;
with 'Audio::Nama::EcasoundRun';

sub launch_ecasound_server {
	my $self = shift;
	Audio::Nama::pager_newline("Using Ecasound via Audio::Ecasound (libecasoundc)");
	$self->{audio_ecasound} = Audio::Ecasound->new();
}
sub ecasound_iam{
	#logsub((caller(0))[3]);
	my $self = shift;
	my $cmd = shift;
	my $category = Audio::Nama::munge_category(shift());
	
	logit(__LINE__,$category,'debug',"LibEcasound-ECI sent: $cmd");

	my (@result) = $self->{audio_ecasound}->eci($cmd);
	logit(__LINE__,$category, 'debug',"LibEcasound-ECI  got: @result") 
		if $result[0] and not $cmd =~ /register/ and not $cmd =~ /int-cmd-list/; 
	my $errmsg = $self->{audio_ecasound}->errmsg();

lib/Audio/Nama/EngineSetup.pm  view on Meta::CPAN

# ----------- Engine Setup and Teardown -----------

package Audio::Nama;
use v5.36; use Carp;

sub reconfigure_engine {

	logsub((caller(0))[3]);

	# skip if command line option is set
	
	return if $config->{opts}->{R};
	refresh_wav_cache();
	update_jack_client_list();
	refresh_tempo_map() if $config->{use_metronome};
	project_snapshot();
	Audio::Nama::Engine::sync_action('configure');
}

sub request_setup { 
	my ($package, $filename, $line) = caller();
    logpkg(__FILE__,__LINE__,'debug',"reconfigure requested in file $filename:$line");
	$setup->{changed}++
} 

sub generate_setup {Audio::Nama::Engine::sync_action('setup') }

sub start_transport { 
	logsub((caller(0))[3]);
	Audio::Nama::Engine::sync_action('start');

}

sub stop_transport { 

	logsub((caller(0))[3]); 
	Audio::Nama::Engine::sync_action('stop');
}
	
1;
__END__

lib/Audio/Nama/Git.pm  view on Meta::CPAN

	$config->{use_git} or warn("@_: git command, but git is not enabled.
You may want to set use_git: 1 in .namarc"), return;
	logpkg(__FILE__,__LINE__,'debug',"VCS command: git @_"); 
	$project->{repo}->run(@_) 
}
sub repo_git_dir { join_path(project_dir(),'.git') }
sub init_repo_obj { $project->{repo} = Git::Repository->new( work_tree => project_dir() ) }
sub create_repo { Git::Repository->run( init => project_dir() )}

sub initialize_project_repository {
	logsub((caller(0))[3]);
	Audio::Nama::throw("either a test script running or use_git: 0 is configured in .namarc. ",
		"No repo created for project ", project_dir()), 
		return if not $config->{use_git} or is_test_script();
	if (not -d repo_git_dir()){
		pager("Creating git repository in ". repo_git_dir()); 
		create_repo;
		init_repo_obj();
		create_file_stubs();
		git_commit('initialize_repository');
	} else {
		init_repo_obj();
		#git_commit('committing prior changes') if git_diff();
	}
}
sub git_diff { 
	my @files = @_; 
	my $diff = git('diff', @files); 
	$diff =~ /\S/ ? $diff : undef 
}
sub git_tag_exists {
	logsub((caller(0))[3]);
	my $tag = shift;
	grep { $tag eq $_ } git( 'tag','--list');
}

# on command "get foo", Nama opens a branch name 'foo-branch', 
# or returns to HEAD of existing branch 'foo-branch'

sub tag_branch { "$_[0]-branch" }

sub restore_state_from_vcs {
	logsub((caller(0))[3]);
	my $name = shift; # tag or branch
	
	# checkout branch if matching branch exists
	
    if (git_branch_exists($name)){
		pager_newline( qq($name: branch exists. Checking out branch $name.) );
		git_checkout($name);
		
	}

lib/Audio/Nama/Git.pm  view on Meta::CPAN

			git_create_branch($branch, $tag);
			
		}
	}
 	else { throw("$name: tag doesn't exist. Cannot checkout."), return  }

	restore_state_from_file();
}
 
sub project_snapshot {
	logsub((caller(0))[3]);
	return if $config->{opts}->{R}
		   or $this_engine->running and Audio::Nama::ChainSetup::really_recording();
	# we skip storing commands that do not affect project state

	save_state();
	reset_command_buffer(), return if not state_changed();

	return if not $config->{use_git} 
	  	   or not $project->{name} 
		   or not $project->{repo};

lib/Audio/Nama/Git.pm  view on Meta::CPAN

	scalar @{$project->{command_buffer}} and join("\n", 
		undef,
		(map{ $_->{command} } @{$project->{command_buffer}}),
		# context for first command of group
		"* track: $project->{command_buffer}->[0]->{context}->{track}",
		"* bus:   $project->{command_buffer}->[0]->{context}->{bus}",
		"* op: $project->{command_buffer}->[0]->{context}->{op}"
		) or undef
}
sub git_commit {
	logsub((caller(0))[3]);
	my $commit_message = shift;
	my @defaults = ($file->state_store, $file->midi_store, $file->tempo_map);
	my @files = scalar @_ ? @_ : @defaults;
	no warnings 'uninitialized';
	@files = @defaults if not @files;
	$commit_message .= command_buffer_contents();
	use utf8;
	git( add => @files);
	git( commit => '--quiet', '--message', $commit_message);
	reset_command_buffer();
}

sub git_checkout {
	logsub((caller(0))[3]);
	my ($branchname, @args) = @_;
	return unless $config->{use_git};

	my $exist_message = git_branch_exists($branchname)
				?  undef
				: "$branchname: branch does not exist.";
	my $dirty_tree_msg  = !! state_changed() 
		?  "You have changes to working files.
You cannot switch branches until you commit
these changes, or throw them away."

lib/Audio/Nama/Git.pm  view on Meta::CPAN

			$conjunction, 
			$exist_message, 
			"No action taken."), return
		if $dirty_tree_msg or $exist_message;

	git(checkout => $branchname, @args);

}

sub git_create_branch {
	logsub((caller(0))[3]);
	my ($branchname, $branchfrom) = @_;
	return unless $config->{use_git};
	# create new branch
	my @args;
	my $from_target;
	$from_target = "from $branchfrom" if $branchfrom;
	push @args, $branchname;
	push(@args, $branchfrom) if $branchfrom;
	pager("Creating branch $branchname $from_target");
	git(checkout => '-b', @args)
}

sub state_changed {  
	logsub((caller(0))[3]);
	return unless $config->{use_git};
	git_diff();
}

sub git_branch_exists { 
	logsub((caller(0))[3]);
	return unless $config->{use_git};
	my $branchname = shift;
	grep{ $_ eq $branchname } 
		map{ s/^\s+//; s/^\* //; $_}
		git("branch");
}

sub current_branch {
	logsub((caller(0))[3]);
	return unless $project->{repo};
	my ($b) = map{ /\* (\S+)/ } grep{ /\*/ } split "\n", git('branch');
	$b
}

sub git_sha {
	my $commit = shift || 'HEAD';
		my ($sha) = git(show => $commit) =~ /commit ([0-9a-f]{10})/;
		$sha
}
sub git_branch_display {
	logsub((caller(0))[3]);
	my $display = $Audio::Nama::project->{name};
	return $display unless $config->{use_git};
	my $cb = current_branch();
	$display .= ":$cb" if $cb and $cb ne 'master';
	$display
}
sub list_branches {
	pager_newline(
		"---Branches--- (asterisk marks current branch)",
		$project->{repo}->run('branch'),

lib/Audio/Nama/Grammar.pm  view on Meta::CPAN

# --------------------- Command Grammar ----------------------

package Audio::Nama;
use Audio::Nama::Effect  qw(:all);
use v5.36;

sub setup_grammar {

	### COMMAND LINE PARSER 

	logsub((caller(0))[3]);

	$text->{commands_yml} = get_data_section("commands_yml");
	$text->{commands_yml} = quote_yaml_scalars($text->{commands_yml});
	$text->{commands} = yaml_in( $text->{commands_yml}) ;
	map
	{ 
		my $full_name = $_; 
		my $shortcuts = $text->{commands}->{$full_name}->{short};
		my @shortcuts = ();
		@shortcuts = split " ", $shortcuts if $shortcuts;

lib/Audio/Nama/Grammar.pm  view on Meta::CPAN

	{
		map{ 'm'.$_, 1} grep{ !$skip{$_} } split " ", get_data_section("midi_commands")
	};
	for (keys %{$text->{midi_cmd}}){
		say "$_: midi command same as Nama command" if $text->{commands}->{$_}
	}

}
sub process_line {
	state $total_effects_count;
	logsub((caller(0))[3]);
	no warnings 'uninitialized';
	my ($user_input) = @_;
	logpkg(__FILE__,__LINE__,'debug',"user input: $user_input");
	if (defined $user_input and $user_input !~ /^\s*$/) {
		push $text->{command_history}->@*, $user_input;
		$text->{command_index}++;
		# convert hyphenated commands to underscore form
		while( my($from, $to) = each %{$text->{hyphenated_commands}} ){ $user_input =~ s/$from/$to/g }
			my $context = context();
			my $success = nama_cmd( $user_input );

lib/Audio/Nama/Graphical.pm  view on Meta::CPAN

# gui handling

# in the $gui variable, keys with leading _underscore
# indicate variables
#
# $gui->{_project_name}  # scalar/array/hash var
# $gui->{mw}             # Tk objects (widgets, frames, etc.)

sub init_gui {

	logsub((caller(0))[3]);

	init_palettefields(); # keys only


	### 	Tk root window 

	# Tk main window
 	$gui->{mw} = MainWindow->new;  
	get_saved_colors();
	$gui->{mw}->optionAdd('*font', 'Helvetica 12');

lib/Audio/Nama/Graphical.pm  view on Meta::CPAN

sub wh {
	my $widget = shift;
	$widget->update;
	my ($width,$height,$sign1,$xpos,$sign2,$ypos) 
		= $widget->geometry =~ /(\d+)x(\d+)([+-])(\d+)([+-])(\d+)/;
	$width,$height
}

sub transport_gui {
	my $ui = shift;
	logsub((caller(0))[3]);

	$gui->{engine_label} = $gui->{transport_frame}->Label(
		-text => 'TRANSPORT',
		-width => 12,
		)->pack(-side => 'left');;
	$gui->{engine_start} = $gui->{transport_frame}->Button->pack(-side => 'left');
	$gui->{engine_stop} = $gui->{transport_frame}->Button->pack(-side => 'left');

	$gui->{engine_stop}->configure(-text => "Stop",
	-command => sub { 

lib/Audio/Nama/Graphical.pm  view on Meta::CPAN

		$ui->project_label_configure(-background => $color);
		start_transport();
				});

#preview_button();
#mastering_button();

}
sub time_gui {
	my $ui = shift;
	logsub((caller(0))[3]);

	my $time_label = $gui->{clock_frame}->Label(
		-text => 'TIME', 
		-width => 12);
	#print "bg: $gui->{_nama_palette}->{ClockBackground}, fg:$gui->{_nama_palette}->{ClockForeground}\n";
	$gui->{clock} = $gui->{clock_frame}->Label(
		-text => '0:00', 
		-width => 8,
		-background => $gui->{_nama_palette}->{ClockBackground},
		-foreground => $gui->{_nama_palette}->{ClockForeground},

lib/Audio/Nama/Graphical.pm  view on Meta::CPAN

					$bn{Main}->set(version => $v); 
					$version->configure(-text => $v);
					Audio::Nama::reconfigure_engine();
					refresh();
					}

			);
 	}
}
sub track_gui { 
	logsub((caller(0))[3]);
	my $ui = shift;
	my $n = shift;
	pager("track_gui already generated"), return
		if defined $gui->{tracks}->{$n} ;
	return if $ti{$n}->hide;
	
	logpkg(__FILE__,__LINE__,'debug', "found index: $n");
	my @rw_items = @_ ? @_ : (
			[ 'command' => "REC",
				-foreground => 'red',

lib/Audio/Nama/Graphical.pm  view on Meta::CPAN

		)
	];

	$ui->refresh_track($n);

}

sub remove_track_gui {
 	my $ui = shift;
 	my $n = shift;
	logsub((caller(0))[3]);
	return unless $gui->{tracks_remove}->{$n};
 	map {$_->destroy  } @{ $gui->{tracks_remove}->{$n} };
	delete $gui->{tracks_remove}->{$n};
	delete $gui->{tracks}->{$n};
}

sub paint_mute_buttons {
	map{ $gui->{tracks}->{$_}{mute}->configure(
			-background 		=> $gui->{_nama_palette}->{Mute},

			)} grep { $ti{$_}->old_vol_level}# muted tracks
				map { $_->n } Audio::Nama::audio_tracks();  # track numbers
}

sub create_master_and_mix_tracks { 
	logsub((caller(0))[3]);


	my @rw_items = (
			[ 'command' => "MON",
				-command  => sub { 
						return if $this_engine->started();
						$tn{Main}->set(rw => "MON");
						$ui->refresh_track($tn{Main}->n);
			}],
			[ 'command' => "OFF", 

lib/Audio/Nama/Graphical.pm  view on Meta::CPAN

					$w->radiobutton(
						-label => $v,
						-value => $v,
						-command => 
		sub { $gui->{tracks}->{$n}->{version}->configure(-text=>$v) 
				unless $ti{$n}->rec }
					);
}

sub add_effect_gui {
		logsub((caller(0))[3]);
		my $ui = shift;
		my %p 			= %{shift()};
		my ($n,$code,$id,$parent,$parameter,$FX) =
			@p{qw(chain type id parent parameter self)};
		my $i = $fx_cache->{full_label_to_index}->{$code};

		logpkg(__FILE__,__LINE__,'debug', sub{json_out(\%p)});

		logpkg(__FILE__,__LINE__,'debug', "id: $id, parent: $parent");
		# $id is determined by effect_init, which will return the

lib/Audio/Nama/Graphical.pm  view on Meta::CPAN

	map{ $_->destroy } map{ $_->children } $gui->{fx_frame};
	#my @children = $gui->{group_frame}->children;
	#map{ $_->destroy  } @children[1..$#children];
	my @children = $gui->{track_frame}->children;
	# leave field labels (first row)
	map{ $_->destroy  } @children[11..$#children]; # fragile
	%{$gui->{marks}} and map{ $_->destroy } values %{$gui->{marks}};
}
sub remove_effect_gui { 
	my $ui = shift;
	logsub((caller(0))[3]);
	my $id = shift;
	my $FX = fxn($id);
	my $n = $FX->chain;
	logpkg(__FILE__,__LINE__,'debug', "id: $id, chain: $n");

	logpkg(__FILE__,__LINE__,'debug', "i have widgets for these ids: ", join " ",keys %{$gui->{fx}});
	logpkg(__FILE__,__LINE__,'debug', "preparing to destroy: $id");
	return unless defined $gui->{fx}->{$id};
	$gui->{fx}->{$id}->destroy();
	delete $gui->{fx}->{$id}; 

}

sub effect_button {
	logsub((caller(0))[3]);
	my ($n, $label, $start, $end) = @_;
	logpkg(__FILE__,__LINE__,'debug', "chain $n label $label start $start end $end");
	my @items;
	my $widget;
	my @indices = ($start..$end);
	if ($start >= $fx_cache->{split}->{ladspa}{a} and $start <= $fx_cache->{split}->{ladspa}{z}){
		@indices = ();
		@indices = @{$fx_cache->{ladspa_sorted}}[$start..$end];
		logpkg(__FILE__,__LINE__,'debug', "length sorted indices list: ",scalar @indices );
	logpkg(__FILE__,__LINE__,'debug', "Indices: @indices");

lib/Audio/Nama/Graphical.pm  view on Meta::CPAN

		-text => $label,
		-tearoff =>0,
		# -relief => 'raised',
		-menuitems => [@items],
	);
	$widget;
}

sub make_scale {
	
	logsub((caller(0))[3]);
	my $ref = shift;
	my %p = %{$ref};
# 	%p contains following:
# 	id   => operator id
# 	parent => parent widget, i.e. the frame
# 	p_num      => parameter number, starting at 0
# 	length       => length widget # optional 
	my $id = $p{id};
	my $FX = fxn($id) // $p{self};
	my $n = $FX->chain;

lib/Audio/Nama/Graphical.pm  view on Meta::CPAN

	$gui->{wwcanvas}->createLine(
			$xpos,0,
			$xpos,$config->{waveform_canvas_y},
			-fill => 'red',
			-width => 1,
			-tags => 'playback-indicator'
	);
}

sub get_saved_colors {
	logsub((caller(0))[3]);

	# aliases
	
	$gui->{_old_bg} = $gui->{_palette}{mw}{background};
	$gui->{_old_abg} = $gui->{_palette}{mw}{activeBackground};
	$gui->{_old_bg} //= '#d915cc1bc3cf';
	#print "pb: $gui->{_palette}{mw}{background}\n";


	my $pal = $file->gui_palette;

lib/Audio/Nama/Graphical.pm  view on Meta::CPAN

					-background => $gui->{_nama_palette}->{GroupBackground},
					-foreground => $gui->{_nama_palette}->{GroupForeground},
				);
				refresh();
			}
	}

}

sub colorchooser { 
	logsub((caller(0))[3]);
	my ($field, $initialcolor) = @_;
	logpkg(__FILE__,__LINE__,'debug', "field: $field, initial color: $initialcolor");
	my $new_color = $gui->{mw}->chooseColor(
							-title => $field,
							-initialcolor => $initialcolor,
							);
	#print "new color: $new_color\n";
	$new_color;
}
sub init_palettefields {

lib/Audio/Nama/Graphical.pm  view on Meta::CPAN

	my %rw_background =  (	REC  => $gui->{_nama_palette}->{RecBackground},
							PLAY  => $gui->{_nama_palette}->{MonBackground},
						 	MON => $gui->{_nama_palette}->{MonBackground},
							OFF  => $gui->{_nama_palette}->{OffBackground});

	$widget->configure( -background => $rw_background{$status} );
	$widget->configure( -foreground => $rw_foreground{$status} );
}
sub refresh_group { 
	# main group, in this case we want to skip null group
	logsub((caller(0))[3]);
	
	
		my $status;
		if ( 	grep{ $_->rec} 
				map{ $tn{$_} }
				$bn{Main}->tracks ){

			$status = REC

		}elsif(	grep{ $_->play} 

lib/Audio/Nama/Graphical.pm  view on Meta::CPAN


	croak "some crazy status |$status|\n" if $status !~ m/rec|mon|off/i;
		#logit(__LINE__,'Audio::Nama::Refresh','debug', "attempting to set $status color: ", $take_color{$status});

	set_widget_color( $gui->{group_rw}, $status) if $gui->{group_rw};
}
sub refresh_track {
	
	my $ui = shift;
	my $n = shift;
	logsub((caller(0))[3]);
	
	my $rec_status = $ti{$n}->rec_status;
	logit(__LINE__,'Audio::Nama::Refresh','debug', "track: $n rec_status: $rec_status");

	return unless $gui->{tracks}->{$n}; # hidden track
	
	# set the text for displayed fields

	$gui->{tracks}->{$n}->{rw}->configure(-text => $rec_status);
	$gui->{tracks}->{$n}->{ch_r}->configure( -text => 

lib/Audio/Nama/Initializations.pm  view on Meta::CPAN

	$setup->{_last_rec_tracks} = [];

	$mastering->{track_names} = [ qw(Eq Low Mid High Boost) ];

	init_wav_memoize() if $config->{memoize};

}

sub initialize_interfaces {
	
	logsub((caller(0))[3]);
	
	if ( $config->{opts}->{g}){
			Audio::Nama::Graphical::initialize_tk() and $ui = Audio::Nama::Graphical->new()
			or pager_newline( "Unable to load perl Tk module. Starting in console mode.")
	}
	if ( not defined $ui ){
		$ui = Audio::Nama::Text->new();
		$text->{loop} = IO::Async::Loop->new;
	}
	choose_sleep_routine();

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} 

lib/Audio/Nama/Jack.pm  view on Meta::CPAN


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

lib/Audio/Nama/Jack.pm  view on Meta::CPAN

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

lib/Audio/Nama/Latency.pm  view on Meta::CPAN

	remove_connections_to_wav_out($lg);

	# want to deal with specific ports,
	# so substitute them into the graph
	
	replace_terminals_by_jack_ports($lg);
}


sub propagate_latency {   
	logsub((caller(0))[3]);

	initialize_jack_graph();
	logpkg(__FILE__,__LINE__,'debug',"jack graph\n","$lg");
	parse_port_connections();
	start_latency_watcher();
	propagate_capture_latency();
	#propagate_playback_latency();
} 
sub propagate_capture_latency {

    my @sinks = grep{ $lg->is_sink_vertex($_) } $lg->vertices();

	logpkg(__FILE__,__LINE__,'debug',"recurse through latency graph starting at sinks: @sinks");
	latency_rememoize();
	map{ latency_of($lg,'capture',$_) } @sinks;
}

sub propagate_playback_latency {
	logsub((caller(0))[3]); 
 	logpkg(__FILE__,__LINE__,'debug',"jack graph\n","$lg");
    my @sources = grep{ $lg->is_source_vertex($_) } $lg->vertices();
 	logpkg(__FILE__,__LINE__,'debug',"recurse through latency graph starting at sources: @sources");
	latency_rememoize();
 	map{ latency_of($lg,'playback',$_) } @sources;
 }

sub predecessor_latency {
	scalar @_ > 2 and die "too many args to predecessor_latency: @_";
	my ($g, $v) = @_;

lib/Audio/Nama/Mark.pm  view on Meta::CPAN

}


# ---------- Mark and jump routines --------
{
package Audio::Nama;
use v5.36;
use Audio::Nama::Globals qw(:all);

sub drop_mark {
	logsub((caller(0))[3]);
	my $name = shift;
	my $here = ecasound_iam("getpos");

	if( my $mark = $Audio::Nama::Mark::by_name{$name}){
		pager("$name: a mark with this name exists already at: ", 
			colonize($mark->time));
		return
	}
	if( my ($mark) = grep { $_->time == $here} Audio::Nama::Mark::all()){
		pager( q(This position is already marked by "),$mark->name,q(") );
		 return 
	}

	my $mark = Audio::Nama::Mark->new( time => $here, 
							name => $name);

	$ui->marker($mark); # for GUI
}
sub mark { # GUI_CODE
	logsub((caller(0))[3]);
	my $mark = shift;
	my $pos = $mark->time;
	if ($gui->{_markers_armed}){ 
			$ui->destroy_marker($pos);
			$mark->remove;
		    arm_mark_toggle(); # disarm
	}
	else{ 

		set_position($pos);
	}
}

sub next_mark {
	logsub((caller(0))[3]);
	my $mark = next_mark_object();
	set_position($mark->time);
	$this_mark = $mark;
}
sub next_mark_object {
	my @marks = Audio::Nama::Mark::all();
	my $here = ecasound_iam("cs-get-position");
	for my $i ( 0..$#marks ){
		if ($marks[$i]->time - $here > 0.001 ){
			logpkg(__FILE__,__LINE__,'debug', "here: $here, future time: ", $marks[$i]->time);

lib/Audio/Nama/Mark.pm  view on Meta::CPAN

sub previous_mark_object {
	my @marks = Audio::Nama::Mark::all();
	my $here = ecasound_iam("cs-get-position");
	for my $i ( reverse 0..$#marks ){
		if ($marks[$i]->time < $here ){
			return $marks[$i];
		}
	}
}
sub previous_mark {
	logsub((caller(0))[3]);
	my $mark = previous_mark_object();
	set_position($mark->time);
	$this_mark = $mark;
}
	
sub modify_mark {
	my ($mark, $newtime, $quiet) = @_;
	$mark->set( time => $newtime );
	! $quiet && do {
	pager($mark->name, ": set to ", d2( $newtime), "\n");

lib/Audio/Nama/Mark.pm  view on Meta::CPAN

sub bump_mark_plus_1 { }
sub bump_mark_minus_point_1 { }
sub bump_mark_plus_point_1 { }
sub bump_mark_minus_point_01 { }
sub bump_mark_plus_point_01 { }


## jump playback head position

sub jump_to_start { 
	logsub((caller(0))[3]);
	return if Audio::Nama::ChainSetup::really_recording();
	jump( 0 );
}
sub jump_to_end { 
	logsub((caller(0))[3]);
	# ten seconds shy of end
	return if Audio::Nama::ChainSetup::really_recording();
	my $end = ecasound_iam('cs-get-length') - $config->{seek_end_margin} ;  
	jump($end);
} 
sub jump {
	return if Audio::Nama::ChainSetup::really_recording();
	my $delta = shift;
	logsub((caller(0))[3]);
	my $here = ecasound_iam('getpos');
	logpkg(__FILE__,__LINE__,'debug', "delta: $delta, here: $here");
	my $new_pos = $here + $delta;
	if ( $setup->{audio_length} )
	{
		$new_pos = $new_pos < $setup->{audio_length} 
			? $new_pos 
			: $setup->{audio_length} - 10
	}
	set_position_with_fade( $new_pos );
}
sub set_position_with_fade { fade_around(\&_set_position, @_) }

sub _set_position {
	logsub((caller(0))[3]);

    return if Audio::Nama::ChainSetup::really_recording(); # don't allow seek while recording

    my $seconds = shift;
    my $coderef = sub{ ecasound_iam("setpos $seconds") };

	$jack->{jackd_running} 
		?  Audio::Nama::stop_do_start( $coderef, $jack->{seek_delay} )
		:  $coderef->();

lib/Audio/Nama/Midi.pm  view on Meta::CPAN

use v5.36;
#use Audio::Nama::Log qw(logpkg);
use Carp;

{
my ($pid, $sel);
my @handles = my ($fh_midi_write, $fh_midi_read, $fh_midi_error) = map{ IO::Handle->new() } 1..3;
map{ $_->autoflush(1) } @handles;

sub start_midish_process {
	logsub((caller(0))[3]);
	my $executable = qx(which midish);
	chomp $executable;
	$executable or say("Midish not found!"), return;
	$pid = open3($fh_midi_write, $fh_midi_read, $fh_midi_error,"$executable -v")
		or warn "Midish failed to start!";

	$sel = IO::Select->new();
	$sel->add($fh_midi_read);
	$sel->add($fh_midi_error);
	midish_cmd( qq(print "Midish is ready.") );
	write_aux_midi_commands();
	midish_cmd( q(exec ").$file->aux_midi_commands.q(") );
	$pid
}
sub midish_cmd {
	my $command = shift;
	logsub((caller(0))[3]);
	return unless $config->{use_midi};
	
	print $fh_midi_write "$command\n";
	#say "applied midish command: $command";
	$project->{midi_history} //=[];
	push  @{ $project->{midi_history} },$command;

	my $length = 2**16;
	sleeper(0.05);
	my @result;

lib/Audio/Nama/Modes.pm  view on Meta::CPAN

# ----------- Modes: mastering, preview, doodle ---------

package Audio::Nama;
use v5.36;
{
sub set_preview_mode {

	# set preview mode, releasing doodle mode if necessary
	
	logsub((caller(0))[3]);

	# do nothing if already in 'preview' mode
	
	return if $mode->preview;
	disable_preview_modes();
	{
	no warnings 'uninitialized';
	$mode->{preview}++;
	}

	pager( <<'MSG');
Setting preview mode. Recording of audio files is disabled.

Type 'arm' to enable recording.
MSG

}
sub set_doodle_mode {

	logsub((caller(0))[3]);
	return if $this_engine->started() and Audio::Nama::ChainSetup::really_recording();
	disable_preview_modes();
	{
	no warnings 'uninitialized';
	$mode->{doodle}++;
	}

	$tn{Mixdown}->set(rw => OFF);
	
	# reconfigure_engine will generate setup and start transport
	
pager( <<'MSG' );
Setting doodle mode. Using live inputs only. Duplicate
inputs are excluded. Recording of audio files is disabled.

Exit using 'preview' or 'arm' commands
MSG
}
sub exit_preview_modes {
		logsub((caller(0))[3]);
		return unless $mode->{preview} or $mode->{doodle};
		disable_preview_modes();
		stop_transport();
		pager("Exiting preview/doodle mode");
}
sub disable_preview_modes {
	undef $mode->{preview};
	undef $mode->{doodle}; 
}

lib/Audio/Nama/Persistence.pm  view on Meta::CPAN

# ---------- Persistent State Support -------------


package Audio::Nama;
use File::Copy;
use v5.36; no warnings 'uninitialized';
use vars '$VERSION';


sub save_state {
	logsub((caller(0))[3]);
	my $filename = shift;
	my $path = $filename || $file->state_store();

		# remove extension if present
		
		$filename =~ s/\.json//;

		# append filename if warranted
		
		$filename = 

lib/Audio/Nama/Persistence.pm  view on Meta::CPAN

sub decode {

	my ($source, $suffix) = @_;
	$decode{$suffix} 
		or die qq(key $suffix: expecting one of).join q(,),keys %decode;
	$decode{$suffix}->($source);
}
}

sub restore_state_from_file {
	logsub((caller(0))[3]);
	my $filename = shift;
	$filename //= $file->state_store();

	initialize_marshalling_arrays();

	my $suffix = 'json';	
	my $path = $file->untracked_state_store;
	if (-r $path)
	{
		my $source = read_file($path);

lib/Audio/Nama/Persistence.pm  view on Meta::CPAN

				file => $file->global_effect_chains,
				format => $format,
				vars => \@global_effect_chain_vars, 
				class => 'Audio::Nama',
			);
	} $config->serialize_formats;

}
sub restore_global_effect_chains {

	logsub((caller(0))[3]);
		my $path =  $file->global_effect_chains;
		-r $path or return;
		my $source = read_file($path);
		throw("$path: empty file"), return unless $source;
		my $suffix = 'json';
		my $ref = decode($source, $suffix);
		assign(
				data => $ref,
				vars   => \@global_effect_chain_vars, 
				class => 'Audio::Nama');

lib/Audio/Nama/Project.pm  view on Meta::CPAN

			my ($vol, $dir, $lastdir) = File::Spec->splitpath($_); $lastdir
		} File::Find::Rule  ->directory()
							->maxdepth(1)
							->extras( { follow => 1} )
						 	->in( project_root());
	pager($projects);
}

sub initialize_project_data {

	logsub((caller(0))[3]);

	-d Audio::Nama::waveform_dir() or mkdir Audio::Nama::waveform_dir();
	$ui->destroy_widgets();
	$ui->project_label_configure(
		-text => uc $project->{name}, 
		-background => 'lightyellow',
		); 

	$gui->{tracks} = {};
	$gui->{fx} = {};

lib/Audio/Nama/Project.pm  view on Meta::CPAN


sub create_project_dirs {
	map{create_dir($_)} project_dir(), this_wav_dir(), waveform_dir() 
}
sub create_file_stubs {
		write_file($file->state_store, "{}\n") unless -e $file->state_store;
		write_file($file->midi_store,    "\n") unless -e $file->midi_store; 
		write_file($file->tempo_map,     "\n") unless -e $file->tempo_map;
}
sub load_project {
	logsub((caller(0))[3]);
	my %args = @_;
	logpkg(__FILE__,__LINE__,'debug', sub{json_out \%args});
	
	$project->{name} = $args{name};
	if (not $project->{name} or not -d project_dir() and not $args{create})
	{
		no warnings 'uninitialized';
		Audio::Nama::pager_newline(qq(Project "$project->{name}" not found. Loading project "untitled".)); 
		load_project(name => 'Untitled', create => 1);

lib/Audio/Nama/Project.pm  view on Meta::CPAN

	#$ui->global_version_buttons(); 
	#$ui->refresh_group;

	logpkg(__FILE__,__LINE__,'debug', "project_root: ", project_root());
	logpkg(__FILE__,__LINE__,'debug', "this_wav_dir: ", this_wav_dir());
	logpkg(__FILE__,__LINE__,'debug', "project_dir: ", project_dir());

 1;
}	
sub restore_state {
	logsub((caller(0))[3]);
		my $name = shift;

		if( ! $name  or $name =~ /.json$/ or !  $config->{use_git})
		{
			restore_state_from_file($name)
		}
		else { restore_state_from_vcs($name)  }
}
sub initialize_mixer {
	return if $tn{Main};

lib/Audio/Nama/Project.pm  view on Meta::CPAN

			send_type => 'soundcard',
			send_id => 1,
			); 
	$ui->create_master_and_mix_tracks();
}



sub dig_ruins {
	
	logsub((caller(0))[3]);
	return if user_tracks_present();
	logpkg(__FILE__,__LINE__,'debug', "looking for audio files");

	my $d = this_wav_dir();
	opendir my $wav, $d or carp "couldn't open directory $d: $!";

	# remove version numbers
	
	my @wavs = grep{s/(_\d+)?\.wav$//i} readdir $wav;

lib/Audio/Nama/Project.pm  view on Meta::CPAN

 
	map{add_track($_)}@wavs;

}

sub remove_riff_header_stubs {

	# 44 byte stubs left by a recording chainsetup that is 
	# connected by not started
	
	logsub((caller(0))[3]);
	

	logpkg(__FILE__,__LINE__,'debug', "this wav dir: ", this_wav_dir());
	return unless this_wav_dir();
         my @wavs = File::Find::Rule ->name( qr/\.wav$/i )
                                        ->file()
                                        ->size(44)
                                        ->extras( { follow => 1} )
                                     	->in( this_wav_dir() )
									if -d this_wav_dir();
    logpkg(__FILE__,__LINE__,'debug', join $/, @wavs);

	map { unlink $_ } @wavs; 
}

sub create_system_buses {
	logsub((caller(0))[3]);

	my $buses = q(
		Main 		Audio::Nama::SubBus send_type track send_id Main	# master fader track
		Mixdown		Audio::Nama::Bus									# mixdown track
		Mastering	Audio::Nama::Bus									# mastering network
		Insert		Audio::Nama::Bus									# auxiliary tracks for inserts
		Cooked		Audio::Nama::Bus									# for track caching
		Temp		Audio::Nama::Bus									# temp tracks while generating setup
        Null		Audio::Nama::Bus 									# unrouted for Main
		Midi		Audio::Nama::MidiBus	send_type null send_id null # all MIDI tracks

lib/Audio/Nama/Terminal.pm  view on Meta::CPAN

# $label->set_text("lehho");
#prompt(); 
}
 
sub print_to_terminal ($txt) {
	$vbox->add( Tickit::Widget::Static->new( text => $txt ));
	$scrollbox->scroll_to(1e5);
}

sub prompt { 
	logsub((caller(0))[3]);
		my $obj = shift;
		my $prompt = join ' ', 'nama', git_branch_display(), bus_track_display(),'> ';
		#$obj->set_text($prompt);
		#$obj->set_position(99);
}
sub next_command {
	$text->{command_index}++ unless $text->{command_index} == scalar $text->{command_history}->@*;
	print_command();
}
sub previous_command {

lib/Audio/Nama/Terminal.pm  view on Meta::CPAN

             GETC      => sub {  },
             CLOSE     => sub {  };
			
}
sub restore_stdout {
	select $old_output_fh;
	close FH;
}
=comment
sub prompt { 
	logsub((caller(0))[3]);
	join ' ', 'nama', git_branch_display(), bus_track_display(),'> '
}
=cut

sub end_of_list_sound { system( $config->{hotkey_beep} ) }

sub previous_track {
	end_of_list_sound(), return if $this_track->n == 1;
	do{ $this_track = $ti{$this_track->n - 1} } until !  $this_track->hide;
}

lib/Audio/Nama/Terminal.pm  view on Meta::CPAN

sub next_param {
	my $param = $this_track->param;
	$param < scalar @{ fxn($this_track->op)->params }
		? $project->{current_param}->{$this_track->op}++ 
		: end_of_list_sound()
}
{my $override;
sub revise_prompt {
}
=comment
	logsub((caller(0))[3]);
	# hack to allow suppressing prompt
	$override = ($_[0] eq "default" ? undef : $_[0]) if defined $_[0];
    $override//prompt()
=cut
}

sub detect_spacebar {
=comment
		if ( $config->{press_space_to_start} 
				and ($buffer eq $trigger)
				and ! ($mode->song or $mode->live) )
			toggle_transport();	
=cut
warn ("not implemented");
}
sub throw {
	logsub((caller(0))[3]);
	pager_newline(@_)
}
sub pagers { &pager_newline(join "",@_) } # pass arguments along

sub pager_newline { 

	# Add a newline if necessary to each line
	# push them onto the output buffer
	# print them to the screen
	

lib/Audio/Nama/Terminal.pm  view on Meta::CPAN

		
		$config->{use_pager} 
		and ! $config->{opts}->{T}
}
sub pager {

	# push array onto output buffer, add two newlines
	# and print on terminal or view in pager
	# as appropriate
	
	logsub((caller(0))[3]);
	my @output = @_;
	@output or return;
	chomp $output[-1];
	$output[-1] .= "\n\n";
	@output = map{"$_\n"} map{ split "\n"} @output;
	return unless scalar @output;
	print for @output;
}
sub file_pager {};
1;

lib/Audio/Nama/TrackIO.pm  view on Meta::CPAN


sub is_used {
	my $track = shift;      # Track is used if:
	my $bus = $track->bus;  # 
	$track->send_type       # It's sending its own signal
	or $track->{rw} eq REC  # It's recording its own signal
	or $track->used_by_another_track
	or ($bus and $bus->can('wantme') and $bus->wantme)  # A bus needs my signal
}
sub rec_status {
#	logsub((caller(0))[3]);
	my $track = shift;
	my $bus = $track->bus;

	return OFF if 0 # 	! ($track->engine_group eq $Audio::Nama::this_engine->name)
				or  $track->{rw} eq OFF
				or 	! $track->is_used
				and ! ($mode->doodle and ! $mode->eager and $setup->{tracks_with_duplicate_inputs}->{$track->name} ); 
	if ($track->{rw} ne PLAY) # e.g. MON or REC
	{
		return OFF if $track->source_type eq 'jack_client' 

lib/Audio/Nama/TrackIO.pm  view on Meta::CPAN

				if $track->project;
			$msg .= qq(.\n);
			$msg .= "Can't set a track alias to REC.\n";
		Audio::Nama::throw($msg);
		return;
	}
	$track->set_rw(REC);
}
sub rw_set {
	my $track = shift;
	logsub((caller(0))[3]);
	my ($bus, $rw) = @_;
	$track->set_rec, return if $rw eq REC;
	$track->set_rw($rw);
}
sub set_rw {
	my ($track, $setting) = @_;
	#my $already = $track->rw eq $setting ? " already" : "";
	$track->set(rw => $setting);
	my $status = $track->rec_status();
	Audio::Nama::pagers("Track ",$track->name, " set to $setting", 

lib/Audio/Nama/TrackUtils.pm  view on Meta::CPAN

package Audio::Nama;
use v5.36;

sub add_track {

	logsub((caller(0))[3]);
	my ($name, @params) = @_;
	my %vals = (name => $name, @params);
	my $class = $vals{class} // 'Audio::Nama::Track';
	{ no warnings 'uninitialized';	
	logpkg(__FILE__,__LINE__,'debug', "name: $name, ch_r: $gui->{_chr}, ch_m: $gui->{_chm}");
	}	
	Audio::Nama::throw("$name: track name already in use. Skipping."), return 
		if $tn{$name};
	Audio::Nama::throw("$name: reserved track name. Skipping"), return
	 	if grep $name eq $_, @{$mastering->{track_names}}; 



( run in 1.191 second using v1.01-cache-2.11-cpan-a9ef4e587e4 )