A more conventional way to terminate a UNIX program is to have it terminate when it hits EOF. In addition to being more conventional, it also allows you to feed a set of frequencies into a pipe or to read them from a file without the danger or going into an infinite read loop if the input to your script is redirected from a file that doesn't contain QUIT
on an even numbered line:
function keyboard() {
printf "Press <CR> to continue, or ctl-D or QUIT<CR> to exit:- ";
if ( getline != 1 || $1 == "QUIT" ) { exit 0 }
}
Furthermore, note that the keyboard()
function does not take any input parameters (neither in your original code where it declares a local variable in the function declaration nor in the above which uses NR
, NF
, $0
, and $1
through $NF
as local variables instead of COMMAND
), so the call in main()
to keyboard()
should be:
keyboard();
instead of:
keyboard( COMMAND );
When looking for a restricted range of numbers, it would also be nice if you told your users what the range of allowable numbers is (or at least print a warning if you set RATE
in setrate()
because FREQ
was out of range. And, there is no need to define hard-coded values for FREQ
and RATE
in your function before reading a value for FREQ
and calculating the value for RATE
based on that:
function setrate( RATE )
{
cls();
BYTES=8;
printf("Enter frequency required (%d <= frequency <= %d):- ",
int((4000 + BYTES - 1) / BYTES), ((48000 + BYTES - 1) / BYTES))
getline FREQ;
RATE=(BYTES*FREQ);
if ( RATE <= 4000 ) { RATE=4000 };
if ( RATE >= 48000 ) { RATE=48000 };
return RATE;
}
If I was writing this, I would probably get rid of the keyboard()
function completely, and have setrate()
exit if EOF is found or 0 is entered for the frequency. That way you can just feed your program a file containing the frequencies you want it to process if you'd like to feed it data non-interactively (without having to worry about putting the frequencies you want on odd lines and empty even numbered lines (except for the last even numbered line that must contain only QUIT
):
function setrate( FREQ, RATE )
{
cls()
BYTES = 8
printf("Enter frequency to continue or ctl-D to exit.\n")
printf("Valid frequency range: %d <= frequency <= %d:- ",
int((4000 + BYTES - 1) / BYTES), ((48000 + BYTES - 1) / BYTES))
if(getline FREQ != 1 || (FREQ + 0) == 0) {
print "Good bye."
exit 0
}
RATE = (BYTES * FREQ)
if(RATE <= 4000) RATE=4000
if(RATE >= 48000) RATE=48000
return RATE
}
Note that when I write functions in awk
I use the convention that parameters immediately follow the open parenthesis in the declaration and local variables declared in the function definition are separated from parameters (if there are any) by a tab.
Note also that since BYTES
is not declared as a variable in the function definition line, it is a global variable. Therefore, it would be better if it were defined once in your BEGIN clause instead of redefining its constant value every time you call the function. (The same applies to LOOPS
in function main()
.)
And note that the syntax for assigning values to variables in awk
does not require that there be no spaces surrounding the equal sign operator (although that is a requirement in the shell programming language). So, if you're going to surround all of the other awk
operators with spaces, you should be consistent and do it that way in assignments as well.
But, if you are trying to learn how to write an awk
program, take advantage of the inherent looping awk
structure instead of trying to use the awk
command language to write something that looks like a shell script or a C program. Consider something more like:
#!/bin/sh
awk '
# Now generate a simple sinewave and run the _main_ code.
BEGIN { # Initialize variables.
BYTES = 8
LOOPS = 2;
MaxRATE = 48000
MinRATE = 4000
SINEWAVE8 = "Op}pN- -";
TMPFILE = "/tmp/sinewave8.raw"
COMMANDpt1 = "~/sox-14.4.2/sox -q -b " BYTES " -e unsigned-integer -r "
COMMANDpt2 = " " TMPFILE " -d trim 0 00:01 > /dev/null 2>&1"
# Create waveform file.
for(LOOP = 1; LOOP <= 16; ++LOOP) {
SINEWAVE8 = SINEWAVE8 SINEWAVE8
}
printf("%s", SINEWAVE8) > TMPFILE
# Prompt user for first input.
prompt()
}
# Clear the terminal window.
function cls() {
printf "\x1B[2J\x1B[H";
}
# Prompt user for input.
function prompt() {
# Clear the screen.
cls()
# Issue the prompt.
printf("Enter frequency to continue or ctl-D to exit.\n")
printf("Valid frequency range: %d <= frequency <= %d:- ",
int((MinRATE + BYTES - 1) / BYTES), ((MaxRATE + BYTES - 1) / BYTES))
}
# Process a line of user supplied input.
{ # Exit if input frequency is 0 or non-numeric.
if($1 + 0 == 0) exit
# Convert input frequency to rate.
RATE = BYTES * $1
if(RATE <= MinRATE) {
RATE = MinRATE
}
if(RATE >= MaxRATE) {
RATE = MaxRATE
}
# Print the waveform.
for(LOOP = 1; LOOP <= LOOPS; ++LOOP) {
system(COMMANDpt1 RATE COMMANDpt2)
}
# Prompt for the next input frequency.
prompt()
}
END { # Clear screen and print exit message.
cls()
print "Goodbye."
# Remove waveform file.
exit system("rm -rf " TMPFILE)
}'
Note that I do not have sox
installed on my system, so when I run this, it immediately clears the screen and prints a new prompt after system()
reports that it can't find sox
. If sox
doesn't include a delay after producing its output, you might want to add a sleep
command after the sox
command in the command string you pass to system()
.