#!/usr/bin/perl

use strict;
use Audio::DSP;
use Math::BigInt;
use integer;

my ($filename) = @ARGV;

#$dsp->audiofile($filename);
open(SOURCE_WAV, '<:bytes', $filename);

# read header (44 bytes), the last 4 bytes is for data size
my $header;
my $tmp;
read(SOURCE_WAV, $header, 22);

# channel
my $chan;
read(SOURCE_WAV, $chan, 2);
$header .= $chan;
$chan = unpack('S', $chan);

# rate
my $rate;
read(SOURCE_WAV, $rate, 2);
$header .= $rate;
$rate = unpack('S', $rate);

read(SOURCE_WAV, $tmp, 6);
$header .= $tmp;

# bytes per sample
my $fmt;
read(SOURCE_WAV, $fmt, 2);
$header .= $fmt;
$fmt = 8 * unpack('S', $fmt);

read(SOURCE_WAV, $tmp, 6);
$header .= $tmp;

# size
my $size;
read(SOURCE_WAV, $size, 4);
$size = unpack('l', $size) / 2; # 16bit data


# a voice is defined as signal above $min_sound last for $min_sound_time and doesn't include silence last for $max_silent_time
my $min_sound = 100;
my $min_sound_sec = 10; # per second
my $max_silent_sec = 50; # per second
my $min_sound_unit = $rate / $min_sound_sec; # second
my $max_silent_unit = $rate / $max_silent_sec; # second
my $min_pitch = 80;
my $buf = 4096;

my $dsp = new Audio::DSP(buffer   => $buf,
		      channels => $chan,
		      format   => $fmt,
		      rate     => $rate);
$dsp->init() || die $dsp->errstr();

my $total = Math::BigInt->new(0);
my $avg_total = Math::BigInt->new(0);
my $silent_total = Math::BigInt->new(0);
my $silent_avg_total = Math::BigInt->new(0);
my $silent_begin = 0;
my $silent_end = 0;
my $is_sound = 0;
my $begin = 0;
my $end = 0;
my $sound_data = '';
my $file_num = 1;
$filename =~ /(.*)[.]wav$/;
#my $file_prefix = $1;
my $file_prefix = 'sound';

# read the first buffer
my $buf_end = -1;
my @dt;
my $pitch = 0;

sub expend_buffer {
  read(SOURCE_WAV, $tmp, $buf);
  push(@dt, unpack('s' x ($buf / 2), $tmp));
  $buf_end += $buf / 2;
}

# count 0 point throuh times, this can be a sign of pitch
sub count_pitch {
  my $time_span = 20;
  return 0 if ($end < $time_span);
  my $a = $dt[$end - $buf_end - 2];
  my $b = $dt[$end - $buf_end - 1];
  if ($b < 0 && $a > 0) {
    for ($end - $buf_end - $time_span .. $end - $buf_end - 3) {
      return 0 if ($dt[$_] < 0);
    }
  } elsif ($b > 0 && $a < 0) {
    for ($end - $buf_end - $time_span .. $end - $buf_end - 3) {
      return 0 if ($dt[$_] > 0);
    }
  } else {
    return 0;
  }
  $pitch++;
}

expend_buffer();

##### Main loop #####
while ($begin < $size) {
#  print "\rProgress: begin - $begin, end - $end";
  while ($total >= $avg_total && $end - $begin < $min_sound_unit && $end < $size) {
    count_pitch();
    $total += abs($dt[$end - $buf_end - 1]);
    $avg_total += $min_sound;
    $end++;
    if ($end > $buf_end) {
      expend_buffer();
    }
  }

  if ($end - $begin >= $min_sound_unit) {
    $is_sound = 1;
  } elsif ($is_sound) {
    $silent_begin = ($end + $begin) / 2;
    $silent_end = $silent_begin;
    $silent_total = 0;
    $silent_avg_total = 0;
    while ($silent_total <= $silent_avg_total && $silent_end - $silent_begin < $max_silent_unit && $silent_end < $size) {
      $silent_total += abs($dt[$silent_end - $buf_end - 1]);
      $silent_avg_total += $min_sound;
      $silent_end++;
      expend_buffer() if ($silent_end > $buf_end);
    }
    if ($silent_end - $silent_begin >= $max_silent_unit) {
      if ($pitch >= $min_pitch) {
	$sound_data .= pack('s' x (($end - $begin) / 2), @dt[0 .. ($end - $begin) / 2 - 1]);
	open(WAV_FILE, '>', "$file_prefix.$file_num.wav");
	print WAV_FILE $header;
	print WAV_FILE pack('l', length($sound_data));
	print WAV_FILE $sound_data;
	close(WAV_FILE);
	print "\tpitch: $pitch\n";
	system("ls -l $file_prefix.$file_num.wav");
	$file_num++;
	$dsp->dwrite($sound_data);
      }

      $sound_data = '';
      $is_sound = 0;
      shift @dt for (1 .. ($end - $begin));
      $begin = $end;
      $pitch = 0;
      $total = 0;
      $avg_total = 0;
    } else {
      $total += abs($dt[$end - $buf_end - 1]);
      $avg_total += $min_sound;
      $end++;
      expend_buffer() if ($end > $buf_end);
    }
  } else {
    $total = 0;
    $avg_total = 0;
    shift @dt for (1 .. ($end - $begin));
    $begin = $end;
    $pitch = 0;
  }

  if ($is_sound && $end - $begin >= $min_sound_unit) {
    for (1 .. ($end - $begin) / 2) {
      $sound_data .= pack('s', shift @dt);
    }
    $begin = ($begin + $end) / 2;
    $end = $begin;
    $total = 0;
    $avg_total = 0;
  }
}

# process the last sound part
if ($is_sound) {
  $sound_data .= shift @dt for (1 .. ($end - $begin) / 2);
  open(WAV_FILE, '>', "$file_prefix.$file_num.wav");
  print WAV_FILE $header;
  print WAV_FILE pack('l', length($sound_data));
  print WAV_FILE $sound_data;
  close(WAV_FILE);
  $dsp->dwrite($sound_data);
}

$dsp->close();
close(SOURCE_WAV);

