I'm using Win32::Sound::WaveOut to create some simple sound sequences. I've written the following module to smooth the interface with my application:
package SoundPlayer;
use strict;
use Win32::Sound;
my $Rate = 8000;
sub new {
my $invocant = shift;
my $class = ref($invocant) || $invocant;
my $self = {player => Win32::Sound::WaveOut->new($Rate, 8, 1)};
bless $self, $class;
return $self
}
sub play {
my $self = shift;
my $player = $self->{player};
$self->stop;
my $silence = pack('C', 128);
my $data = '';
foreach (@_) {
my ($freq, $dur) = @$_;
$dur *= $Rate;
if ($freq == 0) {
$data .= $silence x ($dur)
} else {
foreach (0 .. $dur - 1) {
my $v = 128 + 127 * triangle($_ * $freq / $Rate * 256)
+;
$data .= pack('C', $v)
}
}
}
$player->Unload;
$player->Load($data);
$player->Write;
}
sub playBites {
my $self = shift;
my $tempo = shift;
my @sequence;
while (@_) {
my $bite = shift;
foreach my $note ($bite->sequence) {
my ($freq, $dur) = @$note;
$dur /= $tempo;
push @sequence, [int($freq), int($dur) / 1000]
}
}
$self->play(@sequence)
}
sub done {
my $self = shift;
return $self->{player}->Status
}
sub stop {
my $self = shift;
my $player = $self->{player};
$player->Reset;
$player->Unload
}
#---[Functions for different waveforms]------------------------------
sub _duo {
my $x = shift;
return (sin($x) + 0.5 * sin(3 * $x) + 0.25 * sin(5 * $x)) / 1.75
}
sub _square {
my $x = (int(shift) % 256);
return $x < 128 ? -1 : 1
}
sub _triangle {
my $x = (int(shift) % 256);
return $x < 128 ? $x / 128 - 0.5 : (256 - $x) / 128 - 0.5
}
sub _cliptriangle {
my $x = shift;
my $y = _triangle($x) * 1.25;
return $y > 1 ? 1 : $y < -1 ? -1 : $y
}
1;
It works fine, and I get the sounds I want. But I'm having a problem when long-duration sequences are followed those of shorter durations. Once the shorter sequence has finished, the remainder of the longer sequence continues playing until it has finished. Apparently the buffer is not getting cleared properly between sequences. But I'm at a loss how to remedy the situation (short of filling the ends of short sequences with silence -- a bad option). Any ideas?