timthetortoise said:
Whoa... so you're basically doing a binary write? That's... uh... creative?
Heheh. You should see my shell script version of Tetris. I'm working on a shell script version of Breakout, too.
That is some scary code.
I could have written this without using the one line of Perl, but I'd either have to use awk (pretty ugly) or a combination of bc (to convert to octal) and tr (to convert the octal to a byte, and at least in the bc/tr version, would have been about a tenth as fast.
BTW, not days as I originally thought. It looks like it should complete in about three hours even on my 400 MHz G3 PowerBook. Of course, that's for only a single second of audio....
Edit: had to remove the one line of Perl. Turns out that neither Perl nor the shell's echo builtin can echo a NULL byte. This frustrated my attempt to do that performance optimization, and also meant that the previous code skipped all the zero bytes. Oops!
The only way to make this work sanely was to write the bytes to a temp file directly with tr, then concatenate that onto the file.
The previous code also had some math bugs, both with the bc bits and the simple shell math. *sigh*
Oh, and Mac OS X (at least 10.3) apparently defines the SECONDS variable for me as a very large value, resulting in highly unexpected behavior....
Anyway, the version below works on both Linux and Mac OS X... in seconds instead of hours.
On a little endian box, the conversion command is:
sox -l -x -r 48000 -c 1 -u -t raw testsin.raw testsin.aiff
On a big endian box, the conversion command is
sox -l -r 48000 -c 1 -u -t raw testsin.raw testsin.aiff
#!/bin/sh
NSECONDS=1
SAMPLE_RATE=48000
BITS=32
FREQUENCY=1000
PERIOD=`echo "$SAMPLE_RATE / $FREQUENCY" | bc`
MAXVAL=`echo "(2 ^ ($BITS)) - 1" | bc`
OUTFILE=$1
SAMPLES=$(($NSECONDS * $SAMPLE_RATE))
if [ "x$OUTFILE" = "x" ] ; then
echo "Usage: sinewave.sh <filename>";
exit 0;
fi
# PI is 4*a(1)
writebyte()
{
if [ $1 -gt 256 ] ; then
echo "BAD VALUE: $1";
elif [ $1 -lt 0 ] ; then
echo "BAD VALUE: $1";
fi
VAL=$1
# if [ $1 -eq 0 ] ; then
# VAL=1 # gnu tr is buggy.
# fi
OCTVAL="$(echo "ibase=10; obase=8; $VAL" | bc)"
printf '.' | tr '.' "\\$OCTVAL" >> /tmp/dag_wavetable
}
echo "Generating wave table."
# echo "PERIOD: $PERIOD"
rm -f /tmp/dag_wavetable
I=0
while [ $I -lt $PERIOD ] ; do
# echo "$I < $SAMPLES" 1>&2
FLOATVAL=`echo "scale=20 ; s(($I / $PERIOD) * 8 * a(1))" | bc -l`
# echo "FLOATVAL IS $FLOATVAL"
# Convert to range of 0 to 1.0, then multiply times the maximum value.
SCALEDVAL=`echo "scale=20 ; (($FLOATVAL + 1.0000000000000000000000000000000000) / 2.0000000000000000000000000000000000000) * $MAXVAL.00000000000000000000000000" | bc`
# echo "SCALEDVAL IS $SCALEDVAL"
INTVAL=`echo "scale=0; $SCALEDVAL / 1" | bc`
# echo $INTVAL
if [ $BITS -eq 32 ] ; then
BYTE_0=`echo "$INTVAL / 16777216" | bc`
BYTE_1=`echo "($INTVAL / 65536) % 256" | bc`
BYTE_2=`echo "($INTVAL / 256) % 256" | bc`
BYTE_3=`echo "$INTVAL % 256" | bc`
writebyte $BYTE_0
writebyte $BYTE_1
writebyte $BYTE_2
writebyte $BYTE_3
# echo "$BYTE_0 $BYTE_1 $BYTE_2 $BYTE_3"
elif [ $BITS -eq 24 ] ; then
BYTE_0=`echo "$INTVAL / 65536" | bc`
BYTE_1=`echo "($INTVAL / 256) % 256" | bc`
BYTE_2=`echo "$INTVAL % 256" | bc`
writebyte $BYTE_0
writebyte $BYTE_1
writebyte $BYTE_2
elif [ $BITS -eq 16 ] ; then
BYTE_0=`echo "$INTVAL / 256" | bc`
BYTE_1=`echo "$INTVAL % 256" | bc`
writebyte $BYTE_0
writebyte $BYTE_1
else
writebyte $INTVAL
fi
I=$(($I + 1))
# echo "BUF: $(echo "$BUF" | sed 's/./\./g')"
done
echo "Writing file."
TEST_ECHO_N="$(echo -n "FOO")"
# Wipe the output file.
printf "" > $OUTFILE
# cat /tmp/dag_wavetable >> $OUTFILE
# exit 0
CYCLES=$(($FREQUENCY * $NSECONDS))
# echo "CYCLES: $CYCLES FREQUENCY: $FREQUENCY SECONDS: $NSECONDS"
COUNT=0
while [ $COUNT -lt $CYCLES ] ; do
cat /tmp/dag_wavetable >> $OUTFILE
# echo "$COUNT < $CYCLES"
COUNT=$(($COUNT + 1))
done
echo "Done."