# ------------- Realtime control routines -----------
## loading and running the Ecasound engine
package Audio::Nama;
use Modern::Perl; use Carp;
no warnings 'uninitialized';
use Audio::Nama::Util qw(process_is_running);
# support both 'stop' and 'stop-sync' commands
{ my $stop_command = undef;
sub stop_command {
return unless engine_running();
return eval_iam($stop_command) if $stop_command;
$stop_command = 'stop-sync';
eval_iam($stop_command);
return unless engine_running();
$stop_command = 'stop';
eval_iam($stop_command);
}
}
sub valid_engine_setup {
eval_iam("cs-selected") and eval_iam("cs-is-valid");
}
sub engine_running {
eval_iam("engine-status") eq "running"
};
sub mixing_only {
my $i;
my $am_mixing;
for (Audio::Nama::ChainSetup::really_recording()){
$i++;
$am_mixing++ if /Mixdown/;
}
$i == 1 and $am_mixing
}
sub start_transport {
# set up looping event if needed
# mute unless recording
# start
# wait 0.5s
# unmute
# start heartbeat
# report engine status
# sleep 1s
logsub("&start_transport");
throw("\nCannot start. Engine is not configured.\n"),return
unless eval_iam("cs-connected");
pager("\n\nStarting at ", current_position()) unless $quiet;
schedule_wraparound();
mute();
start_midish_transport()
if $config->{use_midish} and $mode->{midish_transport_sync};
eval_iam('start');
# limit engine run time if we are in mixdown or edit mode,
# or if requested by user, set timer to specified time
# defaulting to the result of cs-get-length
limit_processing_time( $setup->{runtime_limit} || $setup->{audio_length})
if mixing_only()
or edit_mode()
or defined $setup->{runtime_limit};
# TODO and live processing
#$project->{events}->{post_start_unmute} = AE::timer(0.5, 0, sub{unmute()});
sleeper(0.5);
unmute();
sleeper(0.5);
$ui->set_engine_mode_color_display();
start_heartbeat();
engine_status() unless $quiet;
}
sub stop_transport {
logsub("&stop_transport");
# Since the playback position advances slightly during
# the fade, we restore the position to exactly where the
# stop command was issued.
my $pos;
$pos = eval_iam('getpos') if eval_iam('cs-connected')
and ! Audio::Nama::ChainSetup::really_recording();
mute();
stop_command();
stop_midish_transport()
if $config->{use_midish} and $mode->{midish_transport_sync};
disable_length_timer();
if ( ! $quiet ){
sleeper(0.5);
engine_status(current_position(),2,0);
}
unmute();
stop_heartbeat();
$ui->project_label_configure(-background => $gui->{_old_bg});
# restore exact position transport stop command was issued
set_position($pos) if $pos
}
sub toggle_transport {
if (engine_running()){ stop_transport() }
else { start_transport() }
}
sub transport_running { eval_iam('engine-status') eq 'running' }
sub disconnect_transport {
return if transport_running();
teardown_engine();
}
sub engine_is {
my $pos = shift;
"Engine is ". eval_iam("engine-status"). ( $pos ? " at $pos" : "" )
}
sub engine_status {
my ($pos, $before_newlines, $after_newlines) = @_;
pager("\n" x $before_newlines, engine_is($pos), "\n" x $after_newlines);
}
sub current_position {
my $pos = eval_iam("getpos");
colonize(int($pos || 0))
}
sub start_heartbeat {
$project->{events}->{poll_engine} = AE::timer(0, 1, \&Audio::Nama::heartbeat);
}
sub stop_heartbeat {
# the following test avoids double-tripping rec_cleanup()
# following manual stop
return unless $project->{events}->{poll_engine};
undef $project->{events}->{poll_engine};
$ui->reset_engine_mode_color_display();
rec_cleanup()
}
sub heartbeat {
# print "heartbeat fired\n";
my $here = eval_iam("getpos");
my $status = eval_iam('engine-status');
if( $status =~ /finished|error/ ){
engine_status(current_position(),2,1);
revise_prompt();
stop_heartbeat();
sleeper(0.2);
set_position(0);
}
#if $status =~ /finished|error|stopped/;
#print join " ", $status, colonize($here), $/;
my ($start, $end);
$start = Audio::Nama::Mark::loop_start();
$end = Audio::Nama::Mark::loop_end();
schedule_wraparound()
if $mode->{loop_enable}
and defined $start
and defined $end
and ! Audio::Nama::ChainSetup::really_recording();
update_clock_display();
}
sub update_clock_display {
$ui->clock_config(-text => current_position());
}
sub schedule_wraparound {
return unless $mode->{loop_enable};
my $here = eval_iam("getpos");
my $start = Audio::Nama::Mark::loop_start();
my $end = Audio::Nama::Mark::loop_end();
my $diff = $end - $here;
logpkg(__FILE__,__LINE__,'debug', "here: $here, start: $start, end: $end, diff: $diff");
if ( $diff < 0 ){ # go at once
set_position($start);
cancel_wraparound();
} elsif ( $diff < 3 ) { #schedule the move
wraparound($diff, $start);
}
}
sub cancel_wraparound {
$project->{events}->{wraparound} = undef;
}
sub limit_processing_time {
my $length = shift;
$project->{events}->{processing_time}
= AE::timer($length, 0, sub { Audio::Nama::stop_transport(); print prompt() });
}
sub disable_length_timer {
$project->{events}->{processing_time} = undef;
undef $setup->{runtime_limit};
}
sub wraparound {
package Audio::Nama;
my ($diff, $start) = @_;
#print "diff: $diff, start: $start\n";
$project->{events}->{wraparound} = undef;
$project->{events}->{wraparound} = AE::timer($diff,0, sub{set_position($start)});
}
sub ecasound_select_chain {
my $n = shift;
my $cmd = "c-select $n";
if(
# specified chain exists in the chain setup
Audio::Nama::ChainSetup::is_ecasound_chain($n)
# engine is configured
and eval_iam( 'cs-connected' ) =~ /$file->{chain_setup}->[0]/
){ eval_iam($cmd);
return 1
} else {
logpkg(__FILE__,__LINE__,'trace',
"c-select $n: attempted to select non-existing Ecasound chain");
return 0
}
}
sub stop_do_start {
my ($coderef, $delay) = @_;
engine_running() ? _stop_do_start( $coderef, $delay)
: $coderef->()
}
sub _stop_do_start {
my ($coderef, $delay) = @_;
stop_command();
my $result = $coderef->();
sleeper($delay) if $delay;
eval_iam('start');
$result
}
sub restart_ecasound {
pager_newline("killing ecasound processes @{$this_engine->{pids}}");
kill_my_ecasound_processes();
pager_newline(q(restarting Ecasound engine - your may need to use the "arm" command));
select_ecasound_interface();
reconfigure_engine('force');
}
sub kill_my_ecasound_processes {
my @signals = (15, 9);
map{ kill $_, @{$this_engine->{pids}}; sleeper(1)} @signals;
}
1;
__END__