#!/usr/bin/env raku

use NativeCall;
use Audio::PortAudio;

=begin pod

=head1 NAME

play-sine - play a sinewave to the default output stream

=head1 SYNOPSIS

=begin code

play-sine [--frequency=<440>] [--samplerate=<44100>]

=end code

=head1 DESCRIPTION

This plays a constant (if somewhat lo-fi) sine wave to
the default portaudio device.

You can specify the frequency and the samplerate as
command line options.

This uses a rather inefficient method of generating the
sine wave by precomputing a buffer which is the product
of the length of one sine wave cycle in samples times
the buffer size to avoid discontinuity, the reason for this
is that at the current time any sensible method is not
quite fast enough to avoid buffer under-runs in the sound
card. Consequently it may take some time before it starts
making any sound.

In an ideal world it will use something like:

=begin code

use NativeCall;

my $samplerate = 44100;
my $frequency = 440;

sub gen-sin(Int $sample-rate, Int $frequency) {
    gather {
        loop {
            for (0 .. ($samplerate/$frequency)).map({ sin(($_/($samplerate/$frequency)) * (2 * pi))}) -> $val {
                take $val;
            }
        }
    }
}

my $chan = Channel.new;

start {
    for gen-sin($samplerate, $frequency).rotor(256) -> @a {
        my $c = CArray[num32].new(flat @a Z @a);
        $chan.send([$c, 256]);
    }
}


my $now = now;
react {
    whenever $chan {
        sleep 256/44100;
        say now - $now;
        $now = now;
    }
}

=end code

However at the current time that is only about a tenth as fast as it needs to
be :-\

=end pod

multi sub MAIN(:$frequency = 440, :$samplerate = 44100 ) {
	my Num @sin =  (0 .. ($samplerate/$frequency)).map({ sin(($_/($samplerate/$frequency)) * (2 * pi))});
	my @out = flat (@sin Z @sin) xx 512;

	my CArray @n;

	for @out.rotor(1024) -> @a {
		@n.append: CArray[num32].new(@a);
	}

	my $pa = Audio::PortAudio.new;
	my $st = $pa.open-default-stream(0,2, Audio::PortAudio::Float32, $samplerate, 512);

	$st.start;
	loop {
		for @n -> $b {
			$st.write($b, 512);
		}
	}
}

# vim: expandtab shiftwidth=4 ft=raku
