package PDL::Demos::Sound;
use PDL::Demos;

use File::Which qw/which/;

our $player;
our $player_args;

# Find a suitable sound player and provide command line switches
# - aplay comes with ALSA (in Debian: package alsa-utils)
# - play comes with SoX (in Debian: package sox)
my %args = (
    'aplay' => ['--quiet'],     # we mostly use aplay's defaults
    'play'  => [
        '--no-show-progress',
        '--buffer'   => 256,    # makes it work on Win32
        '--type'     => 'raw',
        '--rate'     => 8000,   # the aplay default
        '--bits'     => 8,      # the aplay default
        '--channels' => 1,      # the aplay default
        '--encoding' => 'unsigned-integer',
        '-',                    # using STDIN
        '--channels' => 1,
        ($^O =~ /MSWin32/ ? ('--type' => 'waveaudio') : ()),
    ]
);
$args{sox} = [ @{$args{play}}, '--default' ];
FIND_EXE:
for my $candidate (qw(aplay play sox)) {
    which $candidate  and  do {
        $player = $candidate;
        $player_args = $args{$player};
        last FIND_EXE;
    }
}

sub init {
    return <<'EndOfText';
our $player = $PDL::Demos::Sound::player;
our $player_args = $PDL::Demos::Sound::player_args;
EndOfText
}

my $scale = <<'EndOfScale';

open (my $pipe, '|-', $player, @$player_args)
    or die "Can not start a sound player.  Demo failed.\n";
binmode $pipe;
my $n         = 4000;
my $samples   = pdl[0..$n-1];
my $raw_sound = byte(zeroes($n));
my $amplitude = 80;
for (8,9,10,32/3,12,40/3,15,16) {
    $raw_sound = byte($amplitude*(1+sin($samples*($_/8)*440*3.14/$n)));
    print $pipe ${$raw_sound->get_dataref};
}
close $pipe;
EndOfScale

my $chord = <<'EndOfChord';

open (my $pipe, '|-', $player, @$player_args)
    or die "Can not start a sound player.  Demo failed.\n";
binmode $pipe;
my $n         = 8000;
my $samples   = pdl[0..$n-1];
my $raw_sound = byte(zeroes($n));
my $amplitude = 30;
for (8,10,12,16) {
    $raw_sound += byte($amplitude*(1+sin($samples*($_/8)*440*6.28/$n)));
}
print $pipe ${$raw_sound->get_dataref};
close $pipe;
EndOfChord

my @demo = ([comment => "Listen to Perl and PDL: The A major scale\n"],
            [actnw   => $scale],
            [comment => "Listen to Perl and PDL: A Chord\n"],
            [actnw   => $chord],
        );
sub demo {
    return @demo if $PDL::Demos::Sound::player;
    ([comment => <<EndOfComment]);
This demo requires an external command to play the sound,
unfortunately no suitable candidate could be found.
The following players are supported:
 - 'aplay' (from the ALSA suite) comes with the package 'alsa-utils'
   on Linux
 - 'play' and 'sox' (from SoX) are available as package 'sox' on Linux,
   and can be obtained from https://sourceforge.net/projects/sox/
   for Microsoft Windows and MacOS.
EndOfComment
}

sub info {
    ('sound', 'Sound (requires a sound player)')
}

1;

__END__

=head1 NAME

PDL::Demos::Sound - play PDL-generated sounds

=head1 DESCRIPTION

This module is intended to be auto-discovered by the mechanisms of
L<PDL::Demos>.  That module also defines our interface.

The demo creates some simple sound waves (a scale and a chord) and
pipes them to an external sound player program.  It knows how to
invoke C<aplay> (from alsa-utils), C<play> and C<sox> (the latter two
from SoX - Sound eXchange).  All of these are available as packages in
Linux distributions, SoX is also available from
L<https://sourceforge.net/projects/sox/> for Microsoft Windows and
MacOS.

If no suitable player is discovered, the demo does not die but shows
an explanation where to obtain them.