Skip to content

Instantly share code, notes, and snippets.

Created December 12, 2012 15:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save anonymous/15c7caabb09e3f7a3f78 to your computer and use it in GitHub Desktop.
Save anonymous/15c7caabb09e3f7a3f78 to your computer and use it in GitHub Desktop.
use strictures;
package audiofind;
use IO::All -binary;
use GD::Graph::mixed;
use List::Util ();
use PDL;
use 5.010;
use Moo;
use Capture::Tiny 'capture';
use Carp::Always;
use Devel::Comments;
use PDL::IO::FlexRaw;
$|++;
sub {
has $_ => is => 'lazy'
for qw( sample sample_source sample_file sample_start sample_length bit_rate sox global_offset );
}
->();
my $cmd = $ARGV[0];
audiofind->new->$cmd;
### done
sub _build_sample_file { $_[0]->raw_path( "sample" ) }
sub _build_sample_source { io( "sample_source.mp3" ) }
sub _build_sample_start { 17.6 - $_[0]->global_offset }
sub _build_sample_length { 0.8 }
sub _build_bit_rate { 1200 }
sub _build_sox { "C:/Program Files (x86)/sox-14-4-0/sox.exe" }
sub _build_global_offset { 10 }
sub _build_sample {
my ( $self ) = @_;
### sample file
return $self->_file_to_pdl_array( $self->sample_file ) if $self->sample_file->exists;
my $values = $self->raw_for( $self->sample_source );
### saving raw sample data to: $self->sample_file->name
my $bit_rate = $self->bit_rate;
my $start = $bit_rate * $self->sample_start;
my $end = $start + $bit_rate * $self->sample_length - 1;
my $sample = $values->slice( "$start:$end" );
io( $self->sample_file->name )->binary->print( pack 's*', $sample->list );
return $sample;
}
sub raw_for {
my ( $self, $file ) = @_;
my $raw_path = $self->raw_path( $file );
$self->generate_raw_for( $file ) if !$raw_path->exists;
my $raw = $self->_file_to_pdl_array( $raw_path );
return $raw;
}
sub raw_path { shift->generic_path( @_, "raw" ) }
sub corr_path { shift->generic_path( @_, "corr" ) }
sub png_path { shift->generic_path( @_, "png" ) }
sub generic_path {
my ( $self, $file, $type ) = @_;
return io->catfile( $type, $self->bit_rate, "$file.$type" );
}
sub generate_raw_for {
my ( $self, $file ) = @_;
### generating raw for: $file->name
my $sox = $self->sox;
my $bit_rate = $self->bit_rate;
my $out = $self->raw_path( $file );
io( $out->filepath )->mkpath;
my $start = 20 - $self->global_offset;
my $end = 50 - $self->global_offset;
my ( $stdout, $err, $ret ) = capture {
system qq["$sox" "$file" -t raw -c 1 -b 16 -r $bit_rate "$out" trim $start $end];
};
die $err || "sox failed" if $err or $ret;
return;
}
sub search {
my ( $self ) = @_;
### finding files to search sample in
my $filter = sub {
$_->filename =~ /\.mp3$/ && $_->filename !~ /(dialog|bonus)\.mp3$/ && !$self->corr_path( $_ )->exists;
};
my @mp3s = io->curdir->filter( $filter )->All_Files;
@mp3s = @mp3s[ 0 .. 10 ];
### number of files found: scalar @mp3s
### generating pdl data for sample
my $sample = $self->sample;
my $sample_size = $sample->nelem;
my $sample_norm = $sample - avg( $sample );
my $sample_condensed = sum( $sample_norm**2 );
for my $file ( @mp3s ) { ### |===[%] |
my $search_space = $self->raw_for( $file );
my $search_space_size = $search_space->nelem;
my $max = $search_space_size - 1 - $sample_size;
my @correlations;
for my $i ( 0 .. $max ) {
my $search_slice = $search_space->slice( $i . ":" . ( $i + $sample_size - 1 ) );
my $corr = cross_corr( $search_slice, $sample_norm, $sample_condensed );
push @correlations, $corr;
}
my $corr = $self->corr_path( $file );
io( $corr->filepath )->mkpath;
$corr->print( join "\n", @correlations );
}
}
sub cross_corr {
my ( $search_slice, $sample_norm, $sample_condensed ) = @_;
my $search_norm = $search_slice - avg( $search_slice );
my $denom = sqrt( sum( $search_norm**2 ) * $sample_condensed );
my $corr_coeffs = sum( $search_norm * $sample_norm ) / $denom;
return $corr_coeffs;
}
sub graph {
my ( $self ) = @_;
my $filter = sub {
$_->filename =~ /\.mp3$/
&& $_->filename !~ /(dialog|bonus)\.mp3$/
&& $self->corr_path( $_->name )->exists
&& !$self->png_path( $_->name )->exists;
};
my @mp3s = io->curdir->filter( $filter )->All_Files;
my $secs_per_tick = 10;
my @spacers = ( '' ) x ( $self->bit_rate * $secs_per_tick - 1 );
my @labels = map { ( $_ * $secs_per_tick + $self->global_offset, @spacers ) } 0 .. 3;
for my $file ( @mp3s ) { ### |===[%] |
my @vals = split "\n", $self->corr_path( $file->name )->all;
my $graph = GD::Graph::mixed->new( 1200, 600 );
$graph->set(
title => $file->name,
markers => [3],
types => [qw(points)],
marker_size => 1,
zero_axis => 1,
y_min_value => -1.01,
y_max_value => 1.01,
);
my $gd = $graph->plot( [ \@labels, \@vals ] ) or die $graph->error;
my $png = $self->png_path( $file );
io( $png->filepath )->mkpath;
$png->binary->print( $gd->png );
}
return;
}
sub _file_to_pdl_array {
my ( $self, $file ) = @_;
# create a header for a single vector; compute the length automatically
my $n_elements = $file->size / length( pack 's', 0 );
my %header = ( Type => 'short', NDims => 1, Dims => [$n_elements] );
my $data = readflex( $file->name, [ \%header ] );
return $data;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment