Audio-Nama

 view release on metacpan or  search on metacpan

Changes  view on Meta::CPAN

1.064  August 6, 2010
      * fix Bug rt.cpan.org #60024: Audio::Nama::Assign::expand_tilde()

1.063  August 2, 2010

      * various minor improvements
      * fix Bug#591166 (Debian build support)

1.052  March 20, 2010 (summary)

      * prompt displays current bus and current track
      * configurable use of ea or eadb for volume control
      * Ladish Level 1 support 
      * big speedup by caching results of Track and Wav methods
      * generate setup using graph representation and IO objects
      * rewrite send- and sub-buses 
      * track caching (track freezing)
      * post-fader track inserts (send/receive) with wet/dry control
      * more flexible track input 
         + manual connection to JACK port
         + auto connect to list of JACK ports

bin/nama  view on Meta::CPAN


=head2 Audio device

Nama performs Audio IO via JACK or ALSA. Soundcard IO goes
via JACK, if running, with transparent fallback to ALSA.

Nama supports Ladish Level 1 session handling.

=head2 User interfaces

Nama has fully featured terminal command prompt, a Tk
GUI, and experimental OSC and remote-command modes.
 
The command prompt can run Nama commands, Ecasound
interactive-mode commands, commands for the midish MIDI
recorder/player, shell commands and perl code.  Commands and
filenames can be autocompleted using the TAB key. Command
history is available to browse with up and down arrows. 

The help system provides documentation and keyword search
covering Nama commands and effects-processing
plugins.

=head1 OPTIONS

bin/nama  view on Meta::CPAN

are used to enter parameters values for plugins without
hinted ranges. Any parameter label can be clicked to 
add a parameter controller.

=head2 Waveform Window

Provides a conventional view with waveform and playback head.

=head2 Terminal Window 

The command prompt is available the terminal window
and provides access to all of Nama's functions.

=head1 TEXT USER INTERFACE

Press the I<Enter> key if necessary to get the 
command prompt, which will look something like this:

=over 12

C<nama untitled sax ('h' for help)E<gt>>

=back

In this instance, 'sax' is the current track in the
'untitled' default project. 

When using buses, the bus is indicated before the track:

=over 12

C<nama untitled Strings/violin ('h' for help)E<gt>>

=back

At the prompt, you can enter Nama and Ecasound commands, Perl code
preceded by C<eval> or shell code preceded by C<!>.

Multiple commands on a single line are allowed if delimited
by semicolons. Usually the lines are split on semicolons and
the parts are executed sequentially, however if the line
begins with C<eval> or C<!> the entire line (up to double
semicolons ';;' if present) will be given to the
corresponding interpreter.

You can access command history using up-arrow/down-arrow.

bin/nama  view on Meta::CPAN

Nama lets you create new branches, starting at any snapshot.

To start a new branch called I<compressed-mix> starting at a
snapshot called I<initial-mix> you would say:

C<new-branch compressed-mix initial-mix>

If you want to go back to working on the master branch, use
C<branch master>.

You can also issue native git commands at the Nama prompt.

=head3 Git history example

All projects begin on the "master" branch. Because this is
the default branch, it is not displayed in the prompt.
Otherwise "master" is not special in any way.

In the graphs below, the letters indicate
named snapshots.

    create test-project
    ...
    save a
    ...
    save b

bin/nama  view on Meta::CPAN

=head4 B<uncache-track> (uncache unc) - Select the uncached track version. This restores effects, but not inserts.

=over 8

uncache-track 

=back

=head2 General commands

=head4 B<do-script> (do) - Execute Nama commands from a file in the main project's directory or in the Nama project root directory. A script is a list of Nama commands, just as you would type them on the Nama prompt.

=over 8

do-script <string:filename>

do prepare_my_drums # Execute the script prepare_my_drums.


=back

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

	load_project(name => shift @ARGV,
				 create => delete $config->{opts}->{c}); 
				 		 # remove option for next project load
	nama_cmd($config->{execute_on_project_load});
	nama_cmd($config->{opts}->{X});
	reconfigure_engine();
	if (not $ti{3}){ # no user tracks
		say "Enter command to begin or type 'h' for help.";
		$this_track = $tn{Main};
	}
	show_prompt();
	$ui->loop();
}

sub bootstrap_environment {
	definitions();
	process_command_line_options();
	start_logging();
	setup_grammar();
	initialize_interfaces();
    redirect_stdout() unless  $config->{opts}->{T};

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

  example: |
      cache 10 # Cache the curent track and append 10 seconds extra time,
               # to allow a reverb or delay to fade away without being cut.
uncache_track:
  type: effect
  what: Select the uncached track version. This restores effects, but not inserts.
  short: uncache unc
  parameters: none
do_script:
  type: general
  what: Execute Nama commands from a file in the main project's directory or in the Nama project root directory. A script is a list of Nama commands, just as you would type them on the Nama prompt.
  short: do
  parameters: <string:filename>
  example: |
    do prepare_my_drums # Execute the script prepare_my_drums.
scan:
  type: general
  what: Re-read the project's .wav directory. Mainly useful for troubleshooting.
  parameters: none
add_fade:
  type: effect

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

#
# or, if you are running from your build directory, e.g.
#
#     perl -I ~/build/nama/lib customize.pl

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

my @user_customization = (

prompt => sub { 
	no warnings 'uninitialized';
	join ' ', 'nama', git_branch_display(), bus_track_display(), '> ' 
},

## user defined commands

commands => 
	{
		# usage: greet <name> <adjective>
		greet => sub { 

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

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

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

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

	pager($msg); 
}

sub caching_cleanup {
	my $args = shift;
		$args->{track}->set( rw => $args->{track_rw});
		$tn{Main}->set(rw => MON);
		$args->{track}->set( rw => PLAY);
		$ui->global_version_buttons(); # recreate
		$ui->refresh();
		revise_prompt("default"); 
}
sub poll_progress {
	my $args = $setup->{cache_track_args};
	print ".";
	my $status = ecasound_iam('engine-status'); 
	my $here   = ecasound_iam("getpos");
	update_clock_display();
	logpkg(__FILE__,__LINE__,'debug', "engine time:   ". d2($here));
	logpkg(__FILE__,__LINE__,'debug', "engine status:  $status");

	return unless 
		   $status =~ /finished|error|stopped/ 
		or $here > $args->{processing_time};

	pager("Done.");
	logpkg(__FILE__,__LINE__,'debug', engine_status(current_position(),2,1));
	#revise_prompt();
	stop_polling_cache_progress($args);
}
sub stop_polling_cache_progress {
	my $args = shift;
	stop_event('poll_engine');
	$ui->reset_engine_mode_color_display();
	complete_caching($args);

}

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

			}
 	} 
	return unless -r $filename;
	say("reading user customization file $filename");
	my %custom;
	unless (%custom = do $filename) {
		throw("couldn't parse $filename: $@\n") if $@;
		return;
	}
	logpkg(__FILE__,__LINE__,'debug','customization :', sub{Dumper \%custom });
	my $prompt;
	{ no warnings 'redefine';
		*prompt = $custom{prompt} if $custom{prompt};
	}
	my @commands = keys %{ $custom{commands} };
	for my $cmd(@commands){
		#my $coderef = gen_coderef($cmd,$custom{commands}{$cmd}) or next;
		$text->{user_command}->{$cmd} = $custom{commands}{$cmd};
	}
	$config->{alias}   = $custom{aliases};
}

sub gen_coderef {

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

	rec_cleanup() 
}
sub heartbeat {

	#	print "heartbeat fired\n";

	my $here   = $this_engine->ecasound_iam("getpos");
	my $status = $this_engine->ecasound_iam('engine-status');
	if( $status =~ /finished|error/ ){
		engine_status(current_position(),2,1);
		revise_prompt();
		stop_heartbeat(); 
		sleeper(0.2);
		delete $this_engine->{started};
		set_position(0);
	}
	#print join " ", $status, colonize($here), $/;
	my ($start, $end);
	$start  = Audio::Nama::Mark::loop_start();
	$end    = Audio::Nama::Mark::loop_end();
	schedule_wraparound() 

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

		cancel_wraparound();
	} elsif ( $diff < 3 ) { #schedule the move
		wraparound($diff, $start);
	}
}
sub cancel_wraparound {
	stop_event('wraparound');
}
sub limit_processing_time {
	my $length = shift;
 	start_event(processing_time => timer($length, 0, sub { Audio::Nama::stop_transport(); print prompt() }));
}
sub disable_length_timer {
	stop_event('processing_time');
	undef $setup->{runtime_limit};
}
sub wraparound {
	my ($diff, $start) = @_;
	#print "diff: $diff, start: $start\n";
	stop_event('wraparound');
	start_event(wraparound => timer($diff,0, sub{set_position($start)}));

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

		#$text->{term}->stuff_char(10);
		#	&{$text->{term_attribs}->{'callback_read_char'}}();
		}
	}
}
sub complete_edit_points {
	@{$setup->{edit_points}} = @_edit_points; # save to global
	ecasound_iam('stop');
	Audio::Nama::pager("\nEngine is stopped\n");
	detect_spacebar();
	print prompt(), " ";
}
}
sub set_edit_points {
	$tn{$this_edit->edit_name}->set(rw => OFF) if defined $this_edit;
	Audio::Nama::throw("You must use a playback-only mode to setup edit marks. Aborting"), 
		return 1 if Audio::Nama::ChainSetup::really_recording();
	Audio::Nama::throw("You need stop the engine first. Aborting"), 
		return 1 if $this_engine->started();
	Audio::Nama::pager("Ready to set edit points!");
	sleeper(0.2);

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

Press "Q" to quit.

Engine will start in 2 seconds.));
	initialize_edit_points();
 	$project->{events}->{set_edit_points} = timer(2, 0, 
	sub {
		reset_input_line();
		detect_keystroke_p();
		ecasound_iam('start');
		Audio::Nama::pager("\n\nEngine is running\n");
		print prompt();
	});
}
sub transfer_edit_points {
	Audio::Nama::throw("Use 'set_edit_points' command to specify edit region"), return
		 unless scalar @{$setup->{edit_points}};
	my $edit = shift;
	Audio::Nama::Mark->new( name => $edit->play_start_name, time => $setup->{edit_points}->[0]);
	Audio::Nama::Mark->new( name => $edit->rec_start_name,  time => $setup->{edit_points}->[1]);
	Audio::Nama::Mark->new( name => $edit->rec_end_name,    time => $setup->{edit_points}->[2]);
	@{$setup->{edit_points}} = ();

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

our @EXPORT_OK = qw(

$this_track
$this_bus
$this_bus_o
$this_mark
$this_edit
$this_sequence
$this_engine
$this_user
$prompt
%tn
%ti
%bn
%mn
%en
%fi
$g
$debug
$debug2
$quiet

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

	pronouns => [qw( 

$this_track
$this_bus
$this_bus_o
$this_mark
$this_edit
$this_sequence
$this_engine
$this_user
$prompt
%tn
%ti
%bn
%mn
%en
%fi
$g
$debug
$debug2
$quiet

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

		# from the index
		$this_track = $tn{Main} if ! $this_track or
			(ref $this_track and ! $tn{$this_track->name});
	}
	if (! $this_engine->started() ){
		my $result = check_fx_consistency();
		pagers("Inconsistency found in effects data",
			Dumper ($result)) if $result->{is_error};
	}
	my $output = delete $text->{output_buffer};
	revise_prompt();
}
sub context {
	return unless $this_track;
	my $context = {};
	$context->{track} = $this_track->name;
	$context->{bus}   = $this_bus;
	$context->{op}    = $this_track->op;
	$context
}
sub nama_cmd {

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

effect_info
effect_manipulation
marks
time_shifting
track_io
inserts
fades
group
bus
mixdown
prompt
normalization
track_caching
effect_chains
effect_profiles
advanced_transport
version_control
diagnostics
edits
advanced
                ) ;

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

track_basics => <<'TRACKBASICS',
   add-track, add          - Create a new track

      Example: add sax     - Add a new track 'sax', mono by default.
                           - Sax is selected, and receives next commands.
               source 3    - Use soundcard channel 3 as input
               rec         - Arm for recording an audio file (e.g. sax_1.wav)
               start       - Start engine, begin recording
               stop        - Stop engine, close file and queue for playback
     
               <SPACE> can be used at the prompt to start and stop engine
   
      Example: add piano; source synth; stereo; rec
   
      This line of commands prepares to record track 'piano' in stereo 
      width from JACK client 'synth'. Use of semicolons allow several 
      commands in one line of input.

   import-audio, import    - Import a .wav file, resampling if necessary
   remove-track            - Remove effects, parameters and GUI for current track
TRACKBASICS

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


mixdown => <<MIXDOWN,
   mixdown,    mxd             - enable mixdown 
   mixoff,     mxo             - disable mixdown 
   mixplay,    mxp             - playback a recorded mix 
   automix                     - normalize track vol levels, then mixdown
   master-on,  mr              - enter mastering mode
   master-off, mro             - leave mastering mode
MIXDOWN

prompt => <<PROMPT,
   The prompt displays the name of the project and currently selected track and
   bus (if other than Main.)

   nama allegro violin / Strings > 

   At the command prompt, you can enter several types
   of commands:

   Type                        Example
   ------------------------------------------------------------
   Nama commands               load somesong
   Ecasound commands           cs-is-valid
   Shell expressions           ! ls
   Perl code                   eval 2*3     # prints '6'

   Many commands in Nama operate on the currently selected track or 'current track'. 

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

			?  $config->{engine_buffersize}->{realtime}->{default}
			:  $config->{engine_buffersize}->{nonrealtime}->{default}
 	}
	sub globals_realtime {
		Audio::Nama::ChainSetup::setup_requires_realtime()
			? $config->{ecasound_globals}->{realtime}
			: $config->{ecasound_globals}->{nonrealtime}
	}
	} # end Audio::Nama::Config package

	$prompt = "nama ('h' for help)> ";

	$this_bus = 'Main';
	
	$setup->{_old_snapshot} = {};
	$setup->{_last_rec_tracks} = [];

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

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

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

		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 

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

	$root->add($scroller, valign => 'top', force_size => $lines - 2); 
	$root->add($entry, valign => 'top');
}

sub create_entry_widget {

	my $do_command = sub { my ( $self, $line ) = @_; 
							print_to_terminal($line); 
							$line =~ s/^.+?>\s*//;
							process_line($line); 
							show_prompt();
						}; 

	$entry = Tickit::Widget::Entry->new( 
		text 	 => prompt(),
		on_enter => $do_command,
	);

	show_prompt();
}
sub setup_key_bindings {

	Tickit::Widget::Entry::Plugin::Completion->apply($entry, 
		gen_words => \&gen_words, 
		use_popup => 0, 
		ignore_case => 1); 

	my $backspace  = sub { 
		my $stop_pos = length prompt();
		$entry->text_delete( $entry->position - 1, 1 ) 
			unless $entry->position <= $stop_pos 
	};
	my $left = sub { 
		my $stop_pos = length prompt();
		$entry->set_position( $entry->position - 1 ) 
			unless $entry->position <= $stop_pos 
	};

	my $spacebar = sub {
		if ( $config->{press_space_to_start}
				and $entry->position == length prompt()
				and ! ($mode->song or $mode->live) )
		{ toggle_transport() }
		else { $entry->on_text(' ') }
	};

	$entry->bind_keys( 
	'Up' 		=> sub { previous_command() }, 
	'Down'		=> sub { next_command()     }, 
	'Left'		=> $left,
	'C-a'	  	=> sub { $entry->set_position( length prompt() ) },
	'Home'  	=> sub { $entry->set_position( length prompt() ) },
	'C-k'		=> sub { $entry->text_delete(  $entry->position, 999) },
	'C-u'   	=> sub { $entry->text_delete(  
							length prompt(), 
							$entry->position - length prompt() ) },
	'C-h'   	=> $backspace,
	'Backspace' => $backspace,
    ' '			=> $spacebar,
	'C-z'		=> \&suspend,
	); 

}

sub suspend
{
	$term->pause;
	kill STOP => $$;
	$term->resume;
}

sub show_prompt {
	$entry->set_text(prompt());
	$entry->set_position(99); 
}
 
sub print_to_terminal (@text) {
	return unless defined $scroller;
	chomp for @text;
	$scroller->push( Tickit::Widget::Scroller::Item::Text->new( join ' ', @text ));
	$scroller->scroll_to_bottom;
}

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

}
our ($old_output_fh);
sub redirect_stdout {
	open(FH, '>', '/dev/null') or die; 
	FH->autoflush;
	$old_output_fh = select FH;
   	tie *FH, 'Tie::Simple', '', 
     		WRITE     => sub {  },

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

	$param > 1  ? set_current_param($this_track->param - 1)
				: end_of_list_sound()
}
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 throw {
	logsub((caller(0))[3]);
	pager_newline(@_)
}
sub pagers { &pager_newline(join "",@_) } # pass arguments along

sub pager_newline { 



( run in 2.904 seconds using v1.01-cache-2.11-cpan-6aa56a78535 )