view release on metacpan or search on metacpan
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
=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
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.
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
=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 {