#!/usr/bin/env tclsh
# (C) Stephan Beyer <s-beyer@gmx.net>, GPLv2, 2006
# tested with Tcl8.4 and Snack 2.2!
# A start without any parameters gives information

# You may send feedback!

# Preferences
################

# an absolute value for the tolerance of "silence"
# this value can depend on the sample range
set tol 3

# the minimum silent time [in seconds] to get recognized as silence
set time 1.0

# output filename suffix, use a format-like %d anywhere. This will be 
# replaced by a number!
set outsuffix .part%03d


# Procedures
###############

proc sec {bytes} {
	global rate
	expr int(10.0*$bytes/$rate)/10.0
}

proc outname {} {
	global wavfile outsuffix count
	format %s/%s$outsuffix\.wav  [file dirname $wavfile] \
	  [file rootname [file tail $wavfile]]  $count
}


# Main
###########

puts "Silence detector and splitter \[release 20060706\]\n"

if {$argc == 0} {
	puts stderr "Usage: $argv0 file.wav \[seconds\]"
	puts stderr {}
	set wavfile file.wav
	puts stderr "$wavfile will be split (on silent gaps) into non-silent parts"
	for {set count 0} {$count <= 2} {incr count} {
		puts stderr "\t[outname]"
	}
	puts stderr "\t..."
	puts stderr "The optional argument `seconds' tells us, how long a silence has"
	puts stderr "to be until it is detected as silence. Default: $time\n"
	puts stderr "NO WARRANTY. Have fun. :)\t\t(C) Stephan Beyer, 2006, GNU GPLv2"
	exit 2;
} else {
	set wavfile [lindex $argv 0]
	if {$argc >= 2} {
		set time [lindex $argv 1]
		if {$argc > 2} {
			puts stderr "Warning: additional parameters are being ignored."
		}
	}
}

package require snack
snack::audio stop

puts "Loading $wavfile\..."
snack::sound snd -load $wavfile 

# sound data info
set soundinfo [snd info]
set length [lindex $soundinfo 0]
set rate [lindex $soundinfo 1]
set lensec [expr $length/$rate]
set channels [lindex $soundinfo  5]
puts "  Length: $length bytes"
puts "  Sample rate: $rate Hz"
puts "    plays [sec $length] seconds"
puts "  Sample range: [lindex $soundinfo 3] .. [lindex $soundinfo 2]"
puts "  Encoding: [lindex $soundinfo 4]"
puts "  Channels: $channels"
puts "  File format: [lindex $soundinfo 6]"
puts "  Header length: [lindex $soundinfo 7] Bytes"
puts {}

puts "Detecting silence..."

# $time, just in bytes
set nbytes [expr int($time*$rate)]
# if $silent > $nbytes -> silence found
set silent 0
#  $cen +/- $tol is the range of silence
set cen 0
# first sample of each silence found
# -1 => no silence found
set sBegin -1
set sEnd 0
# count of output files
set count 0

for {set i 0} {$i < $length} {incr i} {
	set sample [snd sample $i]
	# average over all channels
	set avg [expr ([join $sample +])/$channels]

	if {$i % $rate == 0} {
		puts -nonewline "\r  [expr $i/$rate]/$lensec"
		flush stdout
	}

	if {$avg >= $cen - $tol && $avg <= $cen + $tol} {
		incr silent
		if {$silent == $nbytes} {
			set sBegin [expr $i-$nbytes+1]
			puts "\r  * Silence starts at $sBegin ([sec $sBegin]s)"
			if {$sBegin >= 0} {
				puts "  * Writing non-silent part into `[outname]'"
				snd write [outname] -start $sEnd -end $sBegin
				incr count
			}
		}
	} {
		if {$sBegin != -1} {
			set sEnd [expr $i]
			puts "\r  * Silence ends at [expr $i] ([sec $sEnd]s)"
			set sBegin -1
		}
		set silent 0
		set beginat $avg
	}
}
if {$sBegin < 0 && $sEnd == 0} {
	puts "\r  No silence found!\tDidn't copy original file."
} elseif { $sEnd > 0} {
	# don't forget to write the last part, if it doesn't end with silence!
	puts "\r  * Writing non-silent part into `[outname]'"
	snd write [outname] -start $sEnd -end $sBegin
}
puts "Bye!"
exit 0
