diff --git a/2018/algmyr/README.md b/2018/algmyr/README.md index 8ff441835..4a80d609b 100644 --- a/2018/algmyr/README.md +++ b/2018/algmyr/README.md @@ -4,6 +4,10 @@ make ``` +To use this entry requires, in some invocations, a sound processing program. +This can be ALSA or SoX but we recommend the latter. See [FAQ 3.10 - How do I +compile and use an IOCCC winner that requires sound?](/faq.md#faq3_10). + ### Bugs and (Mis)features: @@ -19,31 +23,16 @@ For more detailed information see [2018 algmyr in bugs.md](/bugs.md#2018-algmyr) ## To use: ```sh -cat prog.c | ./prog # Printing garbage might break your font +cat prog.c | ./prog # Print garbage: might mess up your terminal ./prog > out.raw ./prog -d 2 0 out.raw # Decode the first (0th) channel out of two ``` -NOTE: this entry will crash if a file specified cannot be opened. - ### Try: ```sh -./prog -h | ./prog -d 1 0 - -# Who needs cat? -./prog prog.c | ./prog -d 1 0 - -# Assuming ALSA: -echo '<<<<<< /\_/\_/\ _-_-_-_ !!!!! :.:.:.: >>>>>> ****** ~~~~~~~' | - ./prog | aplay -c1 -fFLOAT_LE -r44100 - -# Assuming sox: -echo -n ' MENE MENE TEKEL UPHARSIN ' | ./prog |\ - sox -t raw -c 1 -r 44100 -L -e floating-point -b 32 - -n spectrogram -d 10 -X 300 - -# now open spectrogram.png with a graphics viewer or editor +./try.sh ``` @@ -55,11 +44,13 @@ or at least a handy and well-tested volume knob. Pixels are numbered, amplitudes are weighed, frequencies are divided, and the message can be displayed in fiery letters, but don't be alarmed. -How does decoding of the waveform work? Can you encode an arbitrary text in a sound sample? +How does decoding of the waveform work? Can you encode an arbitrary text in a +sound sample? ## Author's remarks: + ### What is this? To put it brief, this entry is akin to a sound based cat. It takes characters as @@ -67,18 +58,20 @@ input and outputs them into frequency space as audio. The entry also supports decoding the produced waveforms using the `-d` flag. Usage instructions is output if run with the `-h` flag, albeit not in an immediately legible format. + ### Details In normal operation, the entry reads characters from one or more sources. If the `-h` flag is given, usage instructions is used as a source. Else if file paths -are present as arguments those will be used. Else stdin is used. +are present as arguments those will be used. Else `stdin` is used. If a character is part of the supported subset of ASCII `{10, 32-126}` then that character is output into frequency space of an audio signal (44.1 kHz single precision float, endianness should depend on system endianness) through `stdout`. Any other character will be interpreted and outputted as a space. -Characters are printed using a (slightly modified) pixel font based on [gohufont](https://github.com/hchargois/gohufont). +Characters are printed using a (slightly modified) pixel font based on +[gohufont](https://github.com/hchargois/gohufont). Pixels are drawn using crosses in frequency space, achieved by simultaneous up and down chirps. @@ -93,12 +86,13 @@ characters. See image [spoiler.png](spoilers.png) for an example spectrogram. + ### Limitations There is no error checking done when opening files, so expect segfaults if you provide the wrong path to a file. -This program is bound to misbehave if int is less than 32 bits. +This program is bound to misbehave if `int` is less than 32 bits. Flags will only be interpreted correctly if they are the first argument. Reading a file named `-d` or `-h` is fully possible if it is not the first file @@ -109,32 +103,32 @@ behavior. Some erroneous arguments cause segfaults (negative number of channels, channel id outside valid range). One argument in particular causes an infinite loop printing whitespace. -#### Techniques: -##### Obfuscation: +### Obfuscation: A lot of the obfuscations done came naturally from trying to appease the size tool. To name some: -* One character names -* Merging of related (and not so related) arithmetic operations -* Short circuiting for flow control to replace some `if`s -* Some `#defines`, sadly +* One character names. +* Merging of related (and not so related) arithmetic operations. +* Short circuiting for flow control to replace some `if`s. +* Some `#defines`, sadly. On the intersection of size and obfuscation is the data encoding, which was quite fun to design. Some additional obfuscation was done to mathematical formulas. Somewhat known -expressions was transformed into equivalent but less recognizable counterparts. +expressions were transformed into equivalent but less recognizable counterparts. I have tried to avoid intentional red herrings in the code, although fishy statements are plentiful. + ### Data encoding: The large string in the beginning of the source file contains font data, encoded in base64 using characters in the range `' '` to `'_'`. The space was chosen as -the zero because the data contains a lot of zeroes, and quite helpfully the size +zero because the data contains a lot of zeroes, and quite helpfully the size tool ignores whitespace even in strings. Without this fact the program would have been too large and the decoding feature would have had to go. Fortunately this hole in the size spec was readily available. :) @@ -142,9 +136,10 @@ this hole in the size spec was readily available. :) Characters in the font (5x8 pixels, 40 bits) are packed into 7 base64 digits (which was a pain to decode in minimal code). + ### Math: -Note that even though floats are used for the waveforms, the standard math +Note that even though `float`s are used for the waveforms, the standard math library is not used at all! Most arithmetic is done purely on integers. The only major exception is generating a table of sine values which are interpolated to give high resolution waveforms. Can you see when the generation is triggered, @@ -157,13 +152,15 @@ function was used. Although the one used is quite well known, it is not in a form that should be familiar to anyone. What looks like a polynomial and quacks like a polynomial might not actually be a polynomial, at least not deep inside. + ### Printing a string: The program was written to deal exclusively with file descriptors, so how would -one go about printing a char array not from a file descriptor without adding -additional logic? Answer: `ungetc` into stdin, one char at a time (to be +one go about printing a `char` array not from a file descriptor without adding +additional logic? Answer: `ungetc(3)` into `stdin`, one `char` at a time (to be standards compliant). This is used to print the usage instructions. + ### Compilation warnings Compiling using @@ -179,7 +176,7 @@ clang -Wall -Wextra -pedantic ``` is a bit more pessimistic and gives `-Wempty-body` (I like loops without -bodies), `-Wstring-plus-int` (since apparently some people think string+int is a +bodies), `-Wstring-plus-int` (since apparently some people think string+`int` is a concatenation) and one of the weirder warnings I have seen from me splitting a negative constant with whitespace. Compiling using @@ -190,6 +187,7 @@ clang -Wall -Wextra -pedantic -Weverything produces a few page faults of warnings and makes me feel generally bad about myself. :( + ### Misc The reported size of the source code using the size tool is very intentionally diff --git a/2018/algmyr/try.sh b/2018/algmyr/try.sh new file mode 100755 index 000000000..4c0f8045a --- /dev/null +++ b/2018/algmyr/try.sh @@ -0,0 +1,60 @@ +#!/usr/bin/env bash +# +# try.sh - demonstrate IOCCC winner 2018/algmyr +# + +# make sure CC is set so that when we do make CC="$CC" it isn't empty. Doing it +# this way allows us to have the user specify a different compiler in an easy +# way. +if [[ -z "$CC" ]]; then + CC="cc" +fi + +make CC="$CC" all >/dev/null || exit 1 + +# clear screen after compilation so that only the entry is shown +clear + +echo "$ ./prog -h | ./prog -d 1 0" 1>&2 +./prog -h | ./prog -d 1 0 +echo 1>&2 + +# Who needs cat? Answer: more people than you think. +read -r -n 1 -p "Press any key to run: ./prog prog.c | ./prog -d 1 0 (space = next page, q = quit): " +echo 1>&2 +./prog prog.c | ./prog -d 1 0 | less -EXF +echo 1>&2 + +APLAY="$(type -P aplay)" +SOX="$(type -P sox)" + +if [[ -z "$SOX" && -z "$APLAY" ]]; then + echo "Missing both sox and aplay, cannot do this step." +elif [[ -n "$SOX" ]]; then +echo "$ echo -n ' MENE MENE TEKEL UPHARSIN ' | ./prog | \ +sox -t raw -c 1 -r 44100 -L -e floating-point -b 32 \ +- -n spectrogram -d 10 -X 300" 1>&2 + echo -n ' MENE MENE TEKEL UPHARSIN ' | ./prog |\ + sox -t raw -c 1 -r 44100 -L -e floating-point -b 32 - -n spectrogram -d 10 -X 300 + + echo "Now open spectrogram.png in an image viewer or editor." 1>&2 + read -r -n 1 -p "Press any key to continue: " + echo 1>&2 + echo "$ echo "IOCCC 2018" | ./prog | sox -t raw -c 1 -r 44100 -L -e floating-point -b 32 - ioccc.wav" + echo "IOCCC 2018" | ./prog | sox -t raw -c 1 -r 44100 -L -e floating-point -b 32 - ioccc.wav + echo "Now listen to ioccc.wav." 1>&2 + +else +echo '<<<<<< /\_/\_/\ _-_-_-_ !!!!! :.:.:.: >>>>>> ****** ~~~~~~~' | +./prog | aplay -c1 -fFLOAT_LE -r44100 + echo '<<<<<< /\_/\_/\ _-_-_-_ !!!!! :.:.:.: >>>>>> ****** ~~~~~~~' | + ./prog | aplay -c1 -fFLOAT_LE -r44100 + +echo "Now open spectrogram.png in an image viewer or editor." 1>&2 +fi +echo 1>&2 + +echo "$ echo "IOCCC 2018!" | ./prog | ./prog -d 1 0" 1>&2 +echo "IOCCC 2018!" | ./prog | ./prog -d 1 0 + + diff --git a/bugs.md b/bugs.md index 758e027d4..3fbe713de 100644 --- a/bugs.md +++ b/bugs.md @@ -3151,8 +3151,19 @@ There was no IOCCC in 2017. ### Source code: [2018/algmyr/algmyr.c](2018/algmyr/algmyr.c) ### Information: [2018/algmyr/README.md](2018/algmyr/README.md) -This entry is known to crash if a file cannot be opened. This is noted by the -author and is easy enough to fix but need not be. +The author wrote: + +* There is no error checking done when opening files, so expect segfaults if you +provide the wrong path to a file. +* This program is bound to misbehave if `int` is less than 32 bits. +* Flags will only be interpreted correctly if they are the first argument. Reading +a file named `-d` or `-h` is fully possible if it is not the first file +provided, even though I am not sure why you would want to do that. +* Providing invalid/unexpected arguments to the decode flag will result in odd +behavior. Some erroneous arguments cause segfaults (negative number of channels, +channel id outside valid range). One argument in particular causes an infinite +loop printing whitespace. + ## 2018 hou diff --git a/thanks-for-help.md b/thanks-for-help.md index b1207f514..62ccb2b28 100644 --- a/thanks-for-help.md +++ b/thanks-for-help.md @@ -3952,6 +3952,12 @@ He also added the [try.sh](/2015/yang/try.sh) script. # 2018 +## [2018/algmyr](/2018/algmyr/prog.c) ([README.md](/2018/algmyr/README.md)) + + +[Cody](#cody) added the [try.sh](/2018/algmyr/try.sh) script. + + ## [2018/bellard](/2018/bellard/prog.c) ([README.md](/2018/bellard/README.md)) [Cody](#cody) added explicit linking of libm (`-lm`) for systems that do not do this