aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorB. Watson <urchlay@slackware.uk>2024-04-07 12:19:48 -0400
committerB. Watson <urchlay@slackware.uk>2024-04-07 12:19:48 -0400
commitb899a7ed5865143145cc719a29c8aaa5c1c1989e (patch)
treecdd58d635ed98a86700b0c353af0a66d486e21b3
parent9e4b1679d1bbe221f4a4807c85acbbec30b5889e (diff)
downloadmisc-scripts-b899a7ed5865143145cc719a29c8aaa5c1c1989e.tar.gz
soxdial: new script, generate DTMF tones via sox.
-rwxr-xr-xsoxdial304
1 files changed, 304 insertions, 0 deletions
diff --git a/soxdial b/soxdial
new file mode 100755
index 0000000..9fec05d
--- /dev/null
+++ b/soxdial
@@ -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);
+}