The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.
package Audio::Nama; 
use Audio::Nama;
use Test::More tests => 126;
use File::Path qw(make_path remove_tree);
use File::Slurp;
use Cwd;

use strict;
use warnings;
no warnings qw(uninitialized);

our ($expected_setup_lines);

$ENV{NAMA_VERBOSE_TEST_OUTPUT} and diag ("TESTING $0\n");

$ENV{NAMA_VERBOSE_TEST_OUTPUT} and diag("working directory: ",cwd);

our $test_dir = "/tmp/nama-test";
$fx_cache->{fake} = read_file("t/data/fake_effects_cache.json");

cleanup_dirs();
setup_dirs();

sub cleanup_dirs { 	chdir('..'), remove_tree($test_dir) if -e $test_dir }
sub setup_dirs{ make_path("$test_dir/test/.wav", "$test_dir/untitled/.wav") }

$ENV{NAMA_VERBOSE_TEST_OUTPUT} and diag( qx(find $test_dir) );

apply_test_args();

$ENV{NAMA_VERBOSE_TEST_OUTPUT} and diag "options: @ARGV";
bootstrap_environment();
$config->{use_git} = 0;

$ENV{NAMA_VERBOSE_TEST_OUTPUT} and diag "Check representative variable from default .namarc";

is( $config->{mix_to_disk_format}, "s16_le,N,44100,i", "Read mix_to_disk_format");

# object id => type mappings
#
my @id_to_type = (
	1 						=> 'soundcard',
    Fluidsynth 				=> 'jack_client',
	"MPlayer [20120]:out_0" => 'jack_client',
	"drumkit.ports"			=> 'jack_ports_list',
	manual					=> 'jack_manual',
	jack					=> 'jack_manual',
	bus						=> 'bus',
	null					=> 'null',
	"loop,16"				=> 'loop',
	"loop,Master"			=> 'loop',
);

while( my($dest,$type) = splice @id_to_type, 0,2){
	is( dest_type($dest), $type, "$dest => $type");
}


is( ref $bn{Main}, q(Audio::Nama::SubBus), 'Bus initializtion');

# SKIP: { 
# my $cs_got = eval_iam('cs');
# my $cs_want = q(### Chain status (chainsetup 'command-line-setup') ###
# Chain "default" [selected] );
# is( $cs_got, $cs_want, "Evaluate Ecasound 'cs' command");
# }

my $test_project = 'test';

load_project(name => $test_project, create => 1);

$ENV{NAMA_VERBOSE_TEST_OUTPUT} and diag("project project dir: ".project_dir());
$ENV{NAMA_VERBOSE_TEST_OUTPUT} and diag("project project wav dir: ".this_wav_dir());

#diag(map{ $_->dump} values %Audio::Nama::Track::by_index );

is( project_dir(), "$test_dir/$test_project", "establish project directory");

force_jack();

### Unit Tests for Audio::Nama::IO.pm

my @io_test_data = split "\n\n",
my $yaml = q(---
-
  class: from_null
  ecs_string: -i:null
-
  class: to_null
  ecs_string: -o:null
-
  class: to_wav
  args:
    name: sax
    width: 1
    full_path: /foo/.wav/sax_1.wav
  ecs_string: -f:s16_le,1,44100,i -o:/foo/.wav/sax_1.wav
-
  class: from_wav
  args:
    playat_output: playat,5
    select_output: select,1,4
    modifiers: []
    full_path: test_dir/sax_1.wav
  ecs_string: /-i:playat,5,select,1,4,.+sax_\d+.wav/
-
  class: from_loop
  args:
    endpoint: sax_in
  ecs_string: -i:loop,sax_in
-
  class: to_loop
  args:
    endpoint: sax_out
  ecs_string: -o:loop,sax_out
-
  class: to_alsa_soundcard_device
  ecs_string: -o:alsa,default
-
  class: from_alsa_soundcard_device
  ecs_string: -i:alsa,default
-
  class: from_soundcard
  args:
    width: 1
    source_id: 2
    source_type: soundcard
  ecs_string: -i:jack_multi,system:capture_2
-
  class: to_soundcard
  args:
    width: 2
    send_id: 5
    send_type: soundcard
  ecs_string: -o:jack_multi,system:playback_5,system:playback_6
-
  class: to_jack_port
  args:
    width: 1
    port_name: sax
  ecs_string: -f:f32_le,1,44100 -o:jack,,sax_out
-
  class: from_jack_port
  args:
    port_name: sax
    width: 2
  ecs_string: -f:f32_le,2,44100 -i:jack,,sax_in
-
  class: from_jack_client
  args:
    source_id: Horgand
    source_type: jack_client
  ecs_string: -i:jack,Horgand
-
  class: to_jack_client
  args:
    send_id: system
    send_type: jack_client
  ecs_string: -o:jack,system
-
  class: to_jack_multi
  args:
    width: 2
    send_id: system
    send_type: jack_multi
  ecs_string: -o:jack_multi,system:playback_1,system:playback_2
-
  class: from_jack_multi
  args:
    width: 2
    source_id: Horgand
    source_type: jack_client
  ecs_string: -i:jack_multi,Horgand:out_1,Horgand:out_2
...);

my @test = @{yaml_in($yaml)};


my $i;

for (@test) {
	my %t = %$_;
	$i++;
	$ENV{NAMA_VERBOSE_TEST_OUTPUT} and diag "IO.pm unit test $i";
	my $class = "Audio::Nama::IO::$t{class}";
	my $io = $class->new(%{$t{args}});
	my @keys = sort grep{ $_ ne 'class'} keys %t;
	if( $t{ecs_string} =~ m(^/)){
		like( $io->ecs_string, $t{ecs_string}, "$t{class} ecs_string");
	}else{
		is(   $io->ecs_string, $t{ecs_string}, "$t{class} ecs_string");
	}
}
	

force_alsa();

process_command('add sax');

like(ref $this_track, qr/Track/, "track creation"); 

is( $this_track->name, 'sax', "current track assignment");

my ($vol_id) = $this_track->vol;

ok(   (defined $vol_id and $Audio::Nama::Effect::by_id{$vol_id}) , "apply volume control");

process_command('add_effect time_reverb3');

like( this_op_o()->code, qr/time_reverb3/, "apply preset");

is (this_op_o()->track_effect_index, 0, "positioned before vol/pan faders");

process_command('add_effect decimator 1 2');

like( this_op_o()->code, qr/decimator/, "apply LADSPA effect");

is( this_op_o()->track_effect_index, 1, "position before faders, after other effects");

process_command('vol -2');

is( $this_track->vol_o->params->[0], -2, "modify effect" );

process_command(join " ", 'position_effect', this_op_o()->id, 'ZZZ');

is( $this_track->ops->[-1], this_op_o()->id, 
	'position effect at end, using ZZZ pseudo-id');

process_command(join " ", 'position_effect', this_op_o()->id, $vol_id);

is( $this_track->ops->[this_op_o()->track_effect_index + 1], $vol_id, 
	"position effect before another effect");

my $op_id = this_op_o()->id;
process_command("remove_effect $op_id");

ok( (not grep { $_ eq $op_id } @{$this_track->ops}), 'remove effect');

process_command('source 2');

is( $this_track->source_type, 'soundcard', "set soundcard input");
is( $this_track->source_id,  2, "set input channel");

process_command('send 5');

# track sax, source 2, send 5

is( $this_track->send_type, 'soundcard', 'set soundcard output');
is( $this_track->send_id, 5, 'set soundcard output');

# this is ALSA dependent (i.e. no JACK running)

my $io = Audio::Nama::IO->new(track => 'sax');

like( ref $io, qr/IO$/, 'IO base class object');

$io = Audio::Nama::IO::to_alsa_soundcard_device->new(track => 'sax'); 

is($io->ecs_string, '-o:alsa,default', 'IO to_alsa_soundcard_device 1');
is($io->ecs_extra,  ' -chmove:1,5', 'IO to_alsa_soundcard_device 2');

$io = Audio::Nama::IO::to_soundcard->new(track => 'sax'); 

is($io->ecs_string, '-o:alsa,default', 'IO to_soundcard 1');
is($io->ecs_extra, ' -chmove:1,5', 'IO to_soundcard 2');

force_jack();


$io = Audio::Nama::IO::from_soundcard->new(track => 'sax'); 
like (ref $io, qr/from_jack_multi/, 'sound system ALSA/JACK detection: input');
is($io->ecs_string, '-i:jack_multi,system:capture_2', 'IO from_soundcard: jack 1');
is($io->ecs_extra, '-chcopy:1,2', 'IO from_soundcard: jack 2');


$io = Audio::Nama::IO::to_soundcard->new(track => 'sax'); 
like (ref $io, qr/to_jack_multi/, 'sound system ALSA/JACK detection: output');

is($io->ecs_string, '-o:jack_multi,system:playback_5', 'IO to_soundcard: jack 1');
ok(! $io->ecs_extra, 'IO to_soundcard: jack 2');

$io = Audio::Nama::IO::to_null->new(track => 'sax', device_id => 'alsa,default');

is($io->device_id, 'alsa,default', 'value overrides method call');

process_command("sax; source Horgand; gen");
like( Audio::Nama::ChainSetup::ecasound_chain_setup(), qr/Horgand/, 'set JACK client as input');
process_command("sax; source jack; gen");
like( Audio::Nama::ChainSetup::ecasound_chain_setup(), qr/jack,,sax_in/, 'set JACK port for manual input');

process_command("sax; rec; source 2");


force_alsa();

process_command('3; nosend; gen');

$expected_setup_lines = <<EXPECTED;

-a:1 -i:loop,Master_in
-a:3,R3 -i:alsa,default

# post-input processing

-a:R3 -chmove:2,1
-a:3 -chmove:2,1 -chcopy:1,2

# audio outputs

-a:1 -o:alsa,default
-a:3 -o:loop,Master_in
-a:R3 -f:s16_le,1,44100,i -o:/tmp/nama-test/test/.wav/sax_1.wav
EXPECTED

check_setup('ALSA basic setup' );

force_jack();
process_command('gen');
$expected_setup_lines = <<EXPECTED;

# audio inputs

-a:1 -i:loop,Master_in
-a:3,R3 -i:jack_multi,system:capture_2

# post-input processing

-a:3 -chcopy:1,2

# audio outputs

-a:1 -o:jack_multi,system:playback_1,system:playback_2
-a:3 -o:loop,Master_in
-a:R3 -f:s16_le,1,44100,i -o:/tmp/nama-test/test/.wav/sax_1.wav

EXPECTED

check_setup('JACK basic setup' );

process_command('3; mon; gen');
$expected_setup_lines = <<EXPECTED;

-a:1 -i:loop,Master_in
-a:3 -i:jack_multi,system:capture_2

# post-input processing

-a:3 -chcopy:1,2

# audio outputs

-a:1 -o:jack_multi,system:playback_1,system:playback_2
-a:3 -o:loop,Master_in
EXPECTED

check_setup('JACK mon setup' );

force_alsa(); process_command('gen');
$expected_setup_lines = <<EXPECTED;

-a:1 -i:loop,Master_in
-a:3 -i:alsa,default

# post-input processing

-a:3 -chmove:2,1 -chcopy:1,2

# audio outputs

-a:1 -o:alsa,default
-a:3 -o:loop,Master_in

EXPECTED

check_setup('ALSA mon setup' );
process_command('Master; send 5;gen');

$expected_setup_lines = <<EXPECTED;

-a:1 -i:loop,Master_in
-a:3 -i:alsa,default

# post-input processing

-a:3 -chmove:2,1 -chcopy:1,2

# pre-output processing

-a:1  -chmove:2,6 -chmove:1,5

# audio outputs

-a:1 -o:alsa,default
-a:3 -o:loop,Master_in
EXPECTED

check_setup('ALSA send-Master-to-alternate-channel setup' );
force_jack(); process_command('gen');

$expected_setup_lines = <<EXPECTED;
-a:1 -i:loop,Master_in
-a:3 -i:jack_multi,system:capture_2

# post-input processing

-a:3 -chcopy:1,2

# audio outputs

-a:1 -o:jack_multi,system:playback_5,system:playback_6
-a:3 -o:loop,Master_in
EXPECTED
check_setup('JACK send-Master-to-alternate-channel setup' );

=comment
process_command('Mixdown; rec; gen');
$expected_setup_lines = <<EXPECTED;

-a:1 -i:loop,Master_in
-a:3 -i:jack_multi,system:capture_2
-a:Mixdown,J1 -i:loop,Master_out

# post-input processing

-a:3 -chcopy:1,2

# audio outputs

-a:1 -o:loop,Master_out
-a:3 -o:loop,Master_in
-a:J1 -o:jack_multi,system:playback_5,system:playback_6
-a:Mixdown -f:s16_le,2,44100,i -o:/tmp/nama-test/test/.wav/Mixdown_1.wav
EXPECTED

check_setup('JACK mixdown setup with main out' );
gen_alsa();

$expected_setup_lines = <<EXPECTED;
-a:1 -i:loop,Master_in
-a:3 -i:alsa,default
-a:Mixdown,J1 -i:loop,Master_out

# post-input processing

-a:3 -chmove:2,1 -chcopy:1,2

# pre-output processing

-a:J1  -chmove:2,6 -chmove:1,5

# audio outputs

-a:1 -o:loop,Master_out
-a:3 -o:loop,Master_in
-a:J1 -o:alsa,default
-a:Mixdown -f:s16_le,2,44100,i -o:/tmp/nama-test/test/.wav/Mixdown_1.wav
EXPECTED

check_setup('ALSA mixdown setup with main out' );

process_command('master_on');
$expected_setup_lines = <<EXPECTED;
-a:1 -i:loop,Master_in
-a:3 -i:alsa,default
-a:4 -i:loop,Master_out
-a:5,6,7 -i:loop,Eq_out
-a:8 -i:loop,Boost_in
-a:Mixdown,J8 -i:loop,Boost_out

# post-input processing

-a:3 -chmove:2,1 -chcopy:1,2

# pre-output processing

-a:J8  -chmove:2,6 -chmove:1,5

# audio outputs

-a:1 -o:loop,Master_out
-a:3 -o:loop,Master_in
-a:4 -o:loop,Eq_out
-a:5,6,7 -o:loop,Boost_in
-a:8 -o:loop,Boost_out
-a:J8 -o:alsa,default
-a:Mixdown -f:s16_le,2,44100,i -o:/tmp/nama-test/test/.wav/Mixdown_1.wav
EXPECTED
gen_alsa();
check_setup('Mixdown in mastering mode - ALSA');


process_command('Master; stereo'); # normal output width

$expected_setup_lines = <<EXPECTED;

-a:1 -i:loop,Master_in
-a:3 -i:jack_multi,system:capture_2
-a:4 -i:loop,Master_out
-a:5,6,7 -i:loop,Eq_out
-a:8 -i:loop,Boost_in
-a:Mixdown,J8 -i:loop,Boost_out

# post-input processing

-a:3 -chcopy:1,2

# audio outputs

-a:1 -o:loop,Master_out
-a:3 -o:loop,Master_in
-a:4 -o:loop,Eq_out
-a:5,6,7 -o:loop,Boost_in
-a:8 -o:loop,Boost_out
-a:J8 -o:jack_multi,system:playback_5,system:playback_6
-a:Mixdown -f:s16_le,2,44100,i -o:/tmp/nama-test/test/.wav/Mixdown_1.wav
EXPECTED
gen_jack();
check_setup('Mixdown in mastering mode - JACK');
=cut

process_command('mixoff; master_off');
process_command('for 4 5 6 7 8; remove_track quiet');
process_command('Master; send 1');
process_command('add_bus Horns; sax move_to_bus Horns; sax stereo');

$expected_setup_lines = <<EXPECTED;

-a:1 -i:loop,Master_in
-a:3 -i:alsa,default
-a:4 -i:loop,sax_out

# post-input processing

-a:3 -chmove:2,1 -chmove:3,2

# audio outputs

-a:1 -o:alsa,default
-a:3 -o:loop,sax_out
-a:4 -o:loop,Master_in
EXPECTED
gen_alsa();
check_setup('Bus - ALSA');
gen_jack();

$expected_setup_lines = <<EXPECTED;
-a:1 -i:loop,Master_in
-a:3 -i:jack_multi,system:capture_2,system:capture_3
-a:4 -i:loop,sax_out

# audio outputs

-a:1 -o:jack_multi,system:playback_1,system:playback_2
-a:3 -o:loop,sax_out
-a:4 -o:loop,Master_in
EXPECTED
check_setup('Bus - JACK');

process_command('remove_bus Horns');
process_command('add_submix_cooked Vo 5');
$expected_setup_lines = <<EXPECTED;

-a:1,4 -i:loop,sax_out
-a:3 -i:jack_multi,system:capture_2,system:capture_3

# audio outputs

-a:1 -o:jack_multi,system:playback_1,system:playback_2
-a:3 -o:loop,sax_out
-a:4 -o:jack_multi,system:playback_5,system:playback_6
EXPECTED
gen_jack();
check_setup('Create submix with output at soundcard - JACK');
process_command('remove_bus Vo');
process_command('sax mono');

###### Timeline tests
#
# This table tests how the offset run mode affects
# other time displacing operations. 
#
# If you have a region, the offset could occur
# before the region, in the middle of the region
# or after the region. Code for each case
# is different. There is further an 
# interaction with playat. 
#
# The tests are exhaustive, all possible
# combinations are covered.
#
# The numbers indicate time positions.

# asterisk (*) indicates that no output is available
# for that specific field

{

$ENV{NAMA_VERBOSE_TEST_OUTPUT} and diag "Edit mode playat$ENV{NAMA_VERBOSE_TEST_OUTPUT} region endpoints adjustment";
my @tests = split "\n",<<TEST_DATA;
1 12 5 15 4   8  *  *  * 30 out_of_bounds_near region
2 12 5 15 23 26  *  *  * 30 out_of_bounds_far region
3 12 5 15 10 17  2  5 10 30 play_start_during_playat_delay region
4 12 5 15 13 21  0  6 14 30 play_start_within_region region
5 12 5 15 21 26  0 14 19 30 play_start_within_region region
6  0 5 15  5  9  0 10 14 30 play_start_within_region region
7  0 0  0  5  9  0  5  9 30 no_region_play_start_after_playat_delay no_playat
8  2 0  0  5  9  0  3  7 30 no_region_play_start_after_playat_delay
9  6 0  0  5  9  1  0  3 30 no_region_play_start_during_playat_delay
10 6 0  0  3  5  *  *  * 30 out_of_bounds_near no_region
11 6 0  0 40 49  *  *  * 30 out_of_bounds_far  no_region
12 6 0  0 34 40  0 28 30 30 no_region_play_start_after_playat_delay end_after_wav_length

TEST_DATA

foreach(@tests){

	#diag($_);
	my ($index, 
		$playat, 
		$region_start, 
		$region_end, 
		$edit_play_start,
		$edit_play_end, 
		$new_playat, 
		$new_region_start, 
		$new_region_end,
		$length,
		$case, 
		$comment,
	) = split " ", $_;

	my $args = {
		playat 			=> $playat, 
		region_start 	=> $region_start, 
		region_end 		=> $region_end, 
		edit_play_start => $edit_play_start,
		edit_play_end 	=> $edit_play_end,
		setup_length 	=> $length,
	};
	is( Audio::Nama::edit_case($args), $case, "$index: $case $comment");
	is( Audio::Nama::new_playat($args), $new_playat, "$index: new_playat: $case");
	is( Audio::Nama::new_region_start($args), $new_region_start, "$index: new_region_start: $case");
	is( Audio::Nama::new_region_end($args), $new_region_end, "$index: new_region_end: $case");
}
}



load_project(name => "$test_project-convert51", create => 1);

my $script = <<CONVERT51;
# Patrick Shirkey's stereo-to-5.1 converter script implemented in Nama
#
# This is the algorithm to convert a true stereo track to "fake" 5.1 surround. 
# You can change the outputs to suit your 5.1 routing
# 
# channel 1 - Left Front : left input
# channel 2 - Right Front : right input
# channel 3 - Center : left + right + (bandpass 200hz-16000hz) + (amplify 71%)
# channel 4 - LFE : left + right + (low pass 200hz) + (amplify 71%)
# channel 5 - Left Rear: inverse left + right  + (amplify 50%) + 20ms delay
# channel 6 - Right Rear: inverse channel 5


# create tracks
#            3       4       5      6       7          8        9   10  11     12
add_tracks Stereo L_front R_front Center Subwoofer L_inverted Right R-L L_rear R_rear
add_bunch all Stereo L_front R_front Center Subwoofer L_inverted Right R-L L_rear R_rear

Stereo stereo

# create a bus for summing (inverted L) + R
add_bus R-L

# we'll do our own routing for these tracks

Stereo move_to_bus null
R-L    move_to_bus null

# no recording to disk, remove volume and pan controls

for all; mon; remove_fader_effect vol; remove_fader_effect pan

# feed six tracks the stereo source

for L_front R_front Center Subwoofer L_inverted Right; source track Stereo

# prepare sources for R-L ('afx' is an abbreviation for # 'add_effect')

L_inverted afx chmute 2; afx ea -100
Right afx chmute 1

# feed these two tracks to R-L

for L_inverted Right; move_to_bus R-L

# sum R-L source channels to mono

R-L afx chmix 1

# Center: sum R+L to mono, output at channel 3

Center afx chmix 3; afx efh 200; afx efl 16000; afx ea 70

# Subwoofer: sum R+L to mono, bandpass, output at channel 4
Subwoofer afx chmix 4; afx efl 200 ; afx ea 70

# L_rear and R_rear get input from R-L 

for L_rear R_rear; source track R-L

# L_rear: output at channel 5

L_rear afx chmove 1 5

# R_rear: output at channel 6
R_rear afx ea -100;afx chmove 1 6


#   ### Patrick Shirkey's script ####
#   
#   #!/bin/bash
#   
#   ecasound -z:mixmode,sum -f:s16_le,2,48000 \
#   -a:1,2,3,4,5,6 -i $1 \
#   -a:1 -chmute:2 \
#   -a:2 -chmute:1 \
#   -a:3 -chmix:3 -efh:200 -efl:16000 -ea:70 \
#   -a:4 -chmix:4 -efl:200 -ea:70 \
#   -a:5 -chmute:2 -ea:-100 \
#   -a:6 -chmute:1 \
#   -a:5,6 -o loop,1 \
#   -a:7 -i loop,1 \
#   -a:7 -efh:200 -efl:16000 -etd:20,0,1,100,100 -ea:50 -o loop,2 \
#   -a:8,9 -i loop,2 \
#   -a:8 -chmix:5 \
#   -a:9 -chmix:6 -ea:-100 \
#   -a:1,2,3,4,8,9 -f:s16_le,6,48000 -o $2

CONVERT51

do_script($script);
$expected_setup_lines = <<EXPECTED;

# ecasound chainsetup file

# general

-z:mixmode,sum -G:jack,Nama,send -b 256 -z:db,100000 -z:nointbuf

# audio inputs

-a:1 -i:loop,Master_in
-a:10 -i:loop,R-L_in
-a:11,12 -i:loop,R-L_out
-a:3 -i:alsa,default
-a:4,5,6,7,8,9 -i:loop,Stereo_out

# post-input processing

-a:3

# audio outputs

-a:1 -o:alsa,default
-a:10 -o:loop,R-L_out
-a:3 -o:loop,Stereo_out
-a:4,5,6,7,11,12 -o:loop,Master_in
-a:8,9 -o:loop,R-L_in
EXPECTED

force_alsa();
process_command('gen');
check_setup('Stereo to 5.1 converter script' );

load_project(name => "$test_project-sendbus-cooked", create => 1);

do_script(' add mic
            add guitar
            for 3 4; mon
            add_submix_cooked ear 7
');
$expected_setup_lines = <<EXPECTED;
# general

-z:mixmode,sum -G:jack,Nama,send -G:jack,Nama,send -b 1024 -z:nodb -z:intbuf

# audio inputs

-a:1,5 -i:loop,mic_out
-a:1,6 -i:loop,guitar_out
-a:3,4 -i:alsa,default

# post-input processing

-a:3  -chcopy:1,2
-a:4  -chcopy:1,2

# pre-output processing

-a:5  -chmove:2,8 -chmove:1,7
-a:6  -chmove:2,8 -chmove:1,7

# audio outputs

-a:1,5,6 -o:alsa,default
-a:3 -o:loop,mic_out
-a:4 -o:loop,guitar_out
EXPECTED
force_alsa();
process_command('gen');
check_setup('Submix - ALSA');

force_jack();
process_command('gen');

$expected_setup_lines = <<EXPECTED;
# general

-z:mixmode,sum -G:jack,Nama,send -G:jack,Nama,send -b 1024 -z:nodb -z:intbuf -f:f32_le,2,44100

# audio inputs

-a:1,5 -i:loop,mic_out
-a:1,6 -i:loop,guitar_out
-a:3,4 -i:jack_multi,system:capture_1

# post-input processing

-a:3 -chcopy:1,2
-a:4 -chcopy:1,2

# audio outputs

-a:1 -o:jack_multi,system:playback_1,system:playback_2
-a:3 -o:loop,mic_out
-a:4 -o:loop,guitar_out
-a:5,6 -o:jack_multi,system:playback_7,system:playback_8
EXPECTED
check_setup('Submix, AKA add_submix_cooked - JACK');

load_project(name => "add_submix_raw", create => 1);

process_command("add_tracks mic guitar; for 3 4; mon;; 4 source 2; stereo; add_submix_raw raw-user 7"); 
$expected_setup_lines = <<EXPECTED;


# general

-z:mixmode,sum -G:jack,Nama,send -G:jack,Nama,send -b 1024 -z:nodb -z:intbuf -f:f32_le,2,44100

# audio inputs

-a:1 -i:loop,Master_in
-a:3,5 -i:jack_multi,system:capture_1
-a:4,6 -i:jack_multi,system:capture_2,system:capture_3

# post-input processing

-a:3 -chcopy:1,2
-a:5 -chcopy:1,2

# audio outputs

-a:1 -o:jack_multi,system:playback_1,system:playback_2
-a:3,4 -o:loop,Master_in
-a:5,6 -o:jack_multi,system:playback_7,system:playback_8
EXPECTED

force_jack();
process_command('gen');
check_setup('Submix, raw - JACK');

force_alsa();
process_command('gen');
$expected_setup_lines = <<EXPECTED;
# audio inputs

-a:1 -i:loop,Master_in
-a:3,4,5,6 -i:alsa,default

# post-input processing

-a:3  -chcopy:1,2
-a:4 -chmove:2,1 -chmove:3,2
-a:5  -chcopy:1,2
-a:6 -chmove:2,1 -chmove:3,2

# pre-output processing

-a:5  -chmove:2,8 -chmove:1,7
-a:6  -chmove:2,8 -chmove:1,7

# audio outputs

-a:1,5,6 -o:alsa,default
-a:3,4 -o:loop,Master_in
EXPECTED
check_setup('Send Bus, Raw - ALSA');

force_jack();
load_project(name => "$test_project-add_insert_post", create => 1);

process_command("add sax; mon; gen");
process_command("add_insert post jconvolver; gen");
$expected_setup_lines = <<EXPECTED;

# general

-z:mixmode,sum -G:jack,Nama,send -G:jack,Nama,send -b 8192 -z:nodb -z:intbuf -f:f32_le,2,44100

# audio inputs

-a:1 -i:loop,Master_in
-a:3 -i:jack_multi,system:capture_1
-a:4 -i:jack,jconvolver
-a:J3,5 -i:loop,sax_insert_post

# post-input processing

-a:3 -chcopy:1,2

# audio outputs

-a:1 -o:jack_multi,system:playback_1,system:playback_2
-a:3 -o:loop,sax_insert_post
-a:4,5 -o:loop,Master_in
-a:J3 -o:jack,jconvolver
EXPECTED

check_setup('JACK client as postfader insert');

load_project(name => "add_insert_pre", create => 1);
process_command("add sax; mon; add_insert pre jconvolver; gen");
$expected_setup_lines = <<EXPECTED;

# general

-z:mixmode,sum -G:jack,Nama,send -G:jack,Nama,send -b 1024 -z:nodb -z:intbuf -f:f32_le,2,44100

# audio inputs

-a:1 -i:loop,Master_in
-a:3 -i:loop,sax_insert_pre
-a:4 -i:jack,jconvolver
-a:5,6 -i:jack_multi,system:capture_1

# audio outputs

-a:1 -o:jack_multi,system:playback_1,system:playback_2
-a:3 -o:loop,Master_in
-a:4,5 -o:loop,sax_insert_pre
-a:6 -o:jack,jconvolver
EXPECTED
check_setup('JACK client as pre-fader insert');

load_project(name => "add_insert_via_soundcard-postfader", create => 1);
process_command("add sax; mon; source 2; add_insert post 5; gen");
$expected_setup_lines = <<EXPECTED;
-a:1 -i:loop,Master_in
-a:3 -i:jack_multi,system:capture_2
-a:4 -i:jack_multi,system:capture_7,system:capture_8
-a:J3,5 -i:loop,sax_insert_post

# post-input processing

-a:3 -chcopy:1,2

# audio outputs

-a:1 -o:jack_multi,system:playback_1,system:playback_2
-a:3 -o:loop,sax_insert_post
-a:4,5 -o:loop,Master_in
-a:J3 -o:jack_multi,system:playback_5,system:playback_6

EXPECTED
check_setup('Insert via soundcard, postfader - JACK');

force_alsa();
process_command("gen");
$expected_setup_lines = <<EXPECTED;

# general

-z:mixmode,sum -G:jack,Nama,send -G:jack,Nama,send -b 1024 -z:nodb -z:intbuf

# audio inputs

-a:1 -i:loop,Master_in
-a:3,4 -i:alsa,default
-a:J3,5 -i:loop,sax_insert_post

# post-input processing

-a:3 -chmove:2,1 -chcopy:1,2
-a:4 -chmove:7,1 -chmove:8,2 

# pre-output processing

-a:J3  -chmove:2,6 -chmove:1,5

# audio outputs

-a:1,J3 -o:alsa,default
-a:3 -o:loop,sax_insert_post
-a:4,5 -o:loop,Master_in
EXPECTED
check_setup('Insert via soundcard, postfader - ALSA');

load_project(name => "add_insert_via_soundcard_pre", create => 1);
process_command("add sax; mon; source 2; add_insert pre 5; gen");
$expected_setup_lines = <<EXPECTED;

# general

-z:mixmode,sum -G:jack,Nama,send -G:jack,Nama,send -b 1024 -z:nodb -z:intbuf

# audio inputs

-a:1 -i:loop,Master_in
-a:3 -i:loop,sax_insert_pre
-a:4,5,6 -i:alsa,default

# post-input processing

-a:4 -chmove:7,1 
-a:5 -chmove:2,1 
-a:6 -chmove:2,1 

# pre-output processing

-a:6  -chmove:1,5

# audio outputs

-a:1,6 -o:alsa,default
-a:3 -o:loop,Master_in
-a:4,5 -o:loop,sax_insert_pre
EXPECTED
check_setup('Hardware insert via soundcard, prefader  - ALSA');
gen_jack();
$expected_setup_lines = <<EXPECTED;
# general

-z:mixmode,sum -G:jack,Nama,send -G:jack,Nama,send -b 1024 -z:nodb -z:intbuf -f:f32_le,2,44100

# audio inputs

-a:1 -i:loop,Master_in
-a:3 -i:loop,sax_insert_pre
-a:4 -i:jack_multi,system:capture_7
-a:5,6 -i:jack_multi,system:capture_2

# audio outputs

-a:1 -o:jack_multi,system:playback_1,system:playback_2
-a:3 -o:loop,Master_in
-a:4,5 -o:loop,sax_insert_pre
-a:6 -o:jack_multi,system:playback_5
EXPECTED
check_setup('Hardware insert via soundcard, prefader  - JACK');

sub gen_alsa { force_alsa(); process_command('gen')}
sub gen_jack { force_jack(); process_command('gen')}
sub force_alsa { $config->{opts}->{A} = 1; $config->{opts}->{J} = 0; $jack->{jackd_running} = 0; }
sub force_jack{ $config->{opts}->{A} = 0; $config->{opts}->{J} = 1; $jack->{jackd_running} = 1; }
sub setup_content {
	my @lines = split "\n", shift;
	my %setup;
	for (@lines){
		next unless /^-a:/;
		s/\s*$//;
		$setup{$_}++;
	}
	\%setup;
}
sub check_setup {
	my $test_name = shift;
	is( json_out(setup_content(Audio::Nama::ChainSetup::ecasound_chain_setup())), 
		json_out(setup_content($expected_setup_lines)), 
		$test_name);
}


cleanup_dirs();
1;
__END__