diff options
Diffstat (limited to 'soxdial')
-rwxr-xr-x | soxdial | 304 |
1 files changed, 304 insertions, 0 deletions
@@ -0,0 +1,304 @@ +#!/usr/bin/perl -w + +# constuct and execute a sox command to dial a phone number with DTMF tones. + +$VERSION = "0.0.1"; +($SELF = $0) =~ s,.*/,,; + +$|++; + +=pod + +=head1 NAME + +soxdial - generate DTMF (touchtone) audio + +=head1 SYNOPSIS + +B<soxdial> [I<global-options>] [ [I<dial-options>] [I<dial-string>] ... ] + +=head1 DESCRIPTION + +=head1 OPTIONS + +Note that option bundling is not supported. Use e.g. B<-p -n>, not B<-pn>. Also, +spaces are required between options and their arguments. Use e.g. B<-b 16>, +not B<-b16>. + +=head2 Global Options + +These options affect the entire output. They should only be given once, +before any dial strings or dial options. + +=over 4 + +=item B<-l> I<sec>, B<--length> I<sec> + +Sets the time each digit's tones are played. Default is 0.25. + +=item B<-o> I<output>, B<--output> I<output> + +Write sox's output to a file, rather than playing it. The file format +is determined by the filename extension. Use B<.wav> for RIFF WAVE +output, B<.flac> for FLAC, B<.ogg> for Ogg Vorbis, or anything else +(including no extension) for raw audio samples. The special filename B<-> +writes raw samples to standard output. + +=item B<-r>, B<--rate> I<rate> + +Set the bitrate. Default is 8000. You should probably stick with +standard bitrates such as 22050, 44100, 48000, etc, although this is +not enforced. + +=item B<-b>, B<--bits> I<bits> + +Set the bits per sample. Default is 8. The only other choice is 16. + +=item B<-p>, B<--print-cmd> + +Print the generated B<sox> command on stdout. + +=item B<-n>, B<--no-exec> + +Do not execute the generated B<sox> command. This option also +enables B<-p>. + +=item B<--help> + +Prints this help text, via B<perldoc>(1). + +=item B<--man> + +Prints this help text as a man page, via B<pod2man>(1). Suggested use: + + soxdial --man > soxdial.1 + +Then B<soxdial.1> can be installed in e.g. /usr/man/man1 or +/usr/share/man/man1 or wherever your OS keeps its man pages. + +=back + +=head2 Dial Options + +These options can be mixed freely with dial strings, and are applied +as they're found on the command line. Each one affects the rest of the +dial strings, until the same option is seen again. + +=over 4 + +=item B<-d> I<sec>, B<--delay> I<sec> + +Sets the delay between consecutive digits. Default is 0.1. + +=item B<-c> I<sec>, B<--comma> I<sec> + +Sets the delay added by commas in the dial strings. Default is 0.5. + +=item B<-x>, B<--extended> + +Allows the extended touchtone pad keys A, B, C, and D. B<Disables> +letter-to-number conversions. + +=item B<-a>, B<--alphabet> + +Disables extended touchtone pad keys A, B, C, and D; re-enables +letter-to-number conversions. This is the default; this option exists +to turn off a prior B<-x>, B<--extended> option. + +B<-t> I<sec>, B<--dialtone> I<sec> + +Play I<sec> of dialtone. TODO: This option is not yet implemented! + +=back + +=head2 Dial Strings + +These are the actual digits to be dialled. DTMF digits 0 to 9, #, +and * are supported. By default, letters are also supported, and will +be converted to digits according to the standard layout of touchtone +phones (e.g. A through C convert to 2, D through F are 3, etc). + +Q and Z weren't present on classic phones, but they will be converted +to 7 and 9, respectively (like modern cell phones). + +To add an extra delay between digits, use a comma. + +To play the extra DTMF tones for the extended 16-digit keypad (which has +A, B, C, and D keys), use the B<-x>, B<--extended> option. This disables +letter-to-number conversion, but it can be re-enabled later on the +command line with the B<-a>, B<--alphabet> option. + +All characters that aren't mentioned above, will be silently +ignored. This allows you to paste a phone number in the form B<(555) +555-1212> and have it work correctly. + +=head1 AUTHOR + +soxdial was written by B. Watson <urchlay@slackware.uk> and released +under the WTFPL: Do WTF you want with this. + +=cut + +%freqs = ( + 1 => [697, 1209], + 2 => [697, 1336], + 3 => [697, 1477], + 4 => [770, 1209], + 5 => [770, 1336], + 6 => [770, 1477], + 7 => [852, 1209], + 8 => [852, 1336], + 9 => [852, 1477], + '*' => [941, 1209], + 0 => [941, 1336], + '#' => [941, 1477], + A => [697, 1633], + B => [770, 1633], + C => [852, 1633], + D => [941, 1633], +); + +@freqs1 = (); +@freqs2 = (); +@delays = (); +$time = 0; +$pausetime = 0.5; +$digittime = 0.25; +$intertime = 0.1; +$bits = 8; +$rate = 8000; +$output = "-d"; + +sub letter2number { + my $l = uc shift; + return $l unless $l =~ /[A-Z]/; + for($l) { + if(/[A-O]/) { + return int((ord($_) - 65) / 3 + 2) . ""; + } elsif(/[P-S]/) { + return "7"; + } elsif(/[T-V]/) { + return "8"; + } else { # /[W-Z]/ + return "9"; + } + } +} + +# sox -n -d synth 0.25 sine 697 sine 1209 sine 770 sine 1477 delay 0 0 .35 .35 remix - +# ...plays DTMF 1 and 6, for 0.25 sec each, with a 0.10 sec delay between them. + +sub add_digit { + my $d = shift; + if($d eq ',') { + # pause + push @freqs1, 0; + push @freqs2, 0; + push @delays, $time; + + $time += $pausetime; + } else { + #print "time $time\n"; + #print "freq1 " . ($freqs{$d}->[0]) . "\n"; + #print "freq2 " . ($freqs{$d}->[1]) . "\n"; + + push @freqs1, ($freqs{$d}->[0]); + push @freqs2, ($freqs{$d}->[1]); + push @delays, $time; + + $time += $digittime; + $time += $intertime; + } +} + +sub make_sox_cmd { + if($output eq '-') { + $output = "-t raw $output"; + } + my $cmd = "sox -n -b$bits -r$rate -c1 $output "; + my $synth = " synth $digittime "; + my $delay = "delay "; + + for(0..$#freqs1) { + my $f1 = $freqs1[$_]; + my $f2 = $freqs2[$_]; + my $d = $delays[$_]; + + $synth .= "sine $f1 sine $f2 "; + $delay .= "$d $d "; + } + + return $cmd . $synth . $delay . " remix -"; +} + +# main() +for ($argc = 0; $argc < @ARGV; $argc++) { + $_ = $ARGV[$argc]; + + if(/--?version$/) { + print "$SELF $VERSION\n"; + exit 0; + } elsif(/^--?man$/) { + exec "pod2man --stderr -s6 -cUrchlaysStuff -r$VERSION -u $0"; + exit 1; + } elsif(/^--?(?:\?|h)/) { + exec "perldoc $0"; + exit 1; + } elsif(/^--?p(rint)?$/) { + $print_command = 1; + } elsif(/^--?n(oexec)?$/) { + $print_command = 1; + $noexec = 1; + } elsif(/^--?o(?:output)?$/) { + $output = $ARGV[++$argc]; + } elsif(/^--?b(?:its)?$/) { + $bits = $ARGV[++$argc]; + if($bits != 8 && $bits != 16) { + die "$SELF: bad -b/--bits, only 8 or 16 is allowed.\n"; + } + } elsif(/^--?r(?:ate)?$/) { + $rate = $ARGV[++$argc]; + } elsif(/^--?l(?:ength)?$/) { + if(@freqs1) { + die "$SELF: can't change length after a dial string\n"; + } + $digittime = $ARGV[++$argc]; + } elsif(/^--?(?:x|extended)$/) { + $extended = 1; + } elsif(/^--?a(?:lphabet)$/) { + $extended = 0; + } elsif(/^--?d(?:elay)?$/) { + $intertime = $ARGV[++$argc]; + } elsif(/^--?c(?:omma)?$/) { + $pausetime = $ARGV[++$argc]; + } elsif(/^--?(?:t|dialtone)$/) { + die "$SELF: -t/--dialtone option not yet implemented.\n"; + } else { + for (split "", $_) { + my $digit = uc $_; + if($extended) { + next if $digit !~ /[0-9,#*A-D]/; + } else { + $digit = letter2number($digit); + next if $digit !~ /[0-9,#*]/; + } + add_digit($digit); + } + } +} + +if(!@freqs1) { + die "$SELF: no digits to dial.\n"; +} + +my $cmd = make_sox_cmd(); + +if($print_command) { + print "$cmd\n"; +} else { + $cmd .= " 2>/dev/null"; +} + +if(!$noexec) { + system($cmd); +} |