Audio-Analyzer
view release on metacpan or search on metacpan
lib/Audio/Analyzer.pm view on Meta::CPAN
use constant FREQ_CACHE => 9;
use constant SCALER => 10;
use constant BYTES => 11;
use constant EOF_FOUND => 12;
use constant FILELESS_BUFFER => 13;
sub new {
my ($class, %opts) = @_;
my $self = [];
bless($self, $class);
$self->init(%opts);
return $self;
}
sub add_samples {
my ($self, @samples) = @_;
push(@{$self->[FILELESS_BUFFER]}, @samples);
return;
}
sub sample_buffer_size {
my ($self) = @_;
return scalar(@{$self->[FILELESS_BUFFER]});
}
sub next {
my ($self) = @_;
my @samples;
my $chunk;
if (defined($self->[INPUT])) {
my $pcm = $self->read_pcm;
if (! defined($pcm)) {
return undef;
}
@samples = $self->convert_pcm($pcm);
} else {
@samples = $self->get_fileless_samples;
return undef unless @samples;
}
my $channels = $self->split_channels(@samples);
$chunk = Audio::Analyzer::Chunk->new($self, $channels);
return $chunk;
}
sub progress {
my ($self) = @_;
my $bytes = $self->[BYTES];
my $input = $self->[INPUT];
my $size = (stat($input))[7];
return int($bytes / $size * 100);
}
sub freqs {
my ($self) = @_;
my $sample_rate = $self->[SAMPLE_RATE];
my $dft_size = $self->[DFT_SIZE];
my $freq_cache = $self->[FREQ_CACHE];
my @freqs;
if (defined($freq_cache)) {
return $freq_cache;
}
for(my $i = 0; $i < $dft_size / 2; $i++) {
$freqs[$i] = $i / $dft_size * $sample_rate;
}
$self->[FREQ_CACHE] = \@freqs;
return $self->[FREQ_CACHE];
}
#private interface starts here
sub init {
my ($self, %opts) = @_;
my $file;
my $dft_size;
my $seek_step;
my $channels;
my $bits_per_sample;
my $sample_rate;
my $read_size;
my $scaler;
my $fps;
if (exists($opts{'file'})) {
if (! defined($file = $opts{'file'})) {
croak "if the file option is given it must have a value";
}
} elsif (! defined $opts{fileless}) {
croak "no file was specified and fileless operation is not configured";
}
if (! defined($dft_size = $opts{'dft_size'})) {
$dft_size = DEFAULT_DFT_SIZE;
}
if (! defined($sample_rate = $opts{'sample_rate'})) {
$sample_rate = DEFAULT_SAMPLE_RATE;
}
if (! defined($channels = $opts{'channels'})) {
$channels = DEFAULT_CHANNELS;
}
if (defined($bits_per_sample = $opts{'bits_per_sample'})) {
if ($bits_per_sample != 8 && $bits_per_sample != 16) {
lib/Audio/Analyzer.pm view on Meta::CPAN
return \@split;
}
#converts PCM into floating point representation
sub convert_pcm {
my ($self, $pcm) = @_;
my $bytes_per_sample = $self->[BYTES_PER_SAMPLE];
my @samples;
if ($bytes_per_sample == 2) {
while(length($pcm) >= 2) {
my $sample = unpack('s<', substr($pcm, 0, 2, ''));
push(@samples, $sample);
}
} else {
die "8 bit PCM isn't implemented yet";
}
return @samples;
}
sub get_fileless_samples {
my ($self) = @_;
my $input = $self->[INPUT];
my $read_size = $self->[READ_SIZE];
my $fileless_buffer = $self->[FILELESS_BUFFER];
my $samples_needed = $read_size / $self->[BYTES_PER_SAMPLE];
if (defined $input) {
die "read_buffer() was called for fileless operation but there was an input file ref?";
}
return () unless scalar(@$fileless_buffer) >= $samples_needed;
return splice(@$fileless_buffer, 0, $samples_needed);
}
sub read_pcm {
my ($self) = @_;
my $input = $self->[INPUT];
my $read_size = $self->[READ_SIZE];
my $seek_step = $self->[SEEK_STEP];
my $bytes = $self->[BYTES];
my $EOF_found = $self->[EOF_FOUND];
my $buf;
my $ret;
my $rewind;
die "there was no input filehandle ref" unless defined $input;
$ret = read($input, $buf, $read_size);
if (! defined($ret)) {
die "could not read: $!";
} elsif ($ret == 0) {
return undef;
} elsif ($ret < $read_size) {
#hit the end and did not get enough data for the FFT - seek
#backwards a whole read_size and finish the last reading
#as best as possible
my $size = (stat($input))[7];
$self->[EOF_FOUND] = 1;
seek($input, $size - $read_size, 0) or die "could not seek: $!";
return $self->read_pcm;
}
$bytes += $seek_step;
$rewind = $read_size - $seek_step;
if ($rewind && ! $EOF_found) {
seek($input, $rewind * -1, 1) or die "could not seek: $!";
}
$self->[BYTES] = $bytes;
return $buf;
}
sub scaler {
my ($self) = @_;
return $self->[SCALER];
}
package Audio::Analyzer::Chunk;
our $VERSION = '0.02';
use strict;
use warnings;
sub new {
my ($class, $analyzer, $channels) = @_;
my $self = {};
$self->{analyzer} = $analyzer;
$self->{channels} = $channels;
bless($self, $class);
return $self;
}
sub pcm {
my ($self) = @_;
return $self->{channels};
}
sub fft {
my ($self) = @_;
my $channels = $self->{channels};
my @mags;
for(my $i = 0; $i < scalar(@$channels); $i++) {
$mags[$i] = $self->do_fft($channels->[$i]);
}
( run in 2.176 seconds using v1.01-cache-2.11-cpan-39bf76dae61 )