Files

266 lines
6.7 KiB
Markdown

## To build:
``` <!---sh-->
make
```
### Bugs and (Mis)features:
The current status of this entry is:
> **STATUS: INABIAF - please DO NOT fix**
For more detailed information see [2013/endoh3 in bugs.html](../../bugs.html#2013_endoh3).
## To use:
``` <!---sh-->
./endoh3
```
This program plays sound. See the
FAQ on "[compile with sound](../../faq.html#sox)"
if you do not know how to set your
system up for this or if you want to verify that everything is oky.
## Try:
``` <!---sh-->
./try.sh
./try.sh ABC
./try.sh test.abc yankee.abc
./try.sh *.abc
```
The script will check for SoX first and next `padsp` from `PulseAudio` and
finally if neither are found if you have `ruby(1)` installed it'll use the
included ruby script to convert it to a WAV file for you to play.
If no args are passed to the script it will play [twinkle.abc](%%REPO_URL%%/2013/endoh3/twinkle.abc) and
the string `ABC`. Otherwise while there's a remaining arg if it's a file it will
run the program on the file. If it's not a file it'll run it as a string.
The `*.abc` files are provided as music samples.
## Judges' remarks:
This program can toot out a tune that is small enough to posted to most social
media platforms that have small message length limits.
This endoh3ram can toot out a tune that is small enough.
### A modern day (2023) note about the award 'Most tweetable 1-liner' and twitter:
The IOCCC no longer endorses the use of what was once twitter, or whatever it
might be called today. We recommend that you pick a responsible social media
platform to post the program's output on. Nevertheless the award of this entry
will remain the same for historical purposes.
## Author's remarks:
### Remarks
This is a sound synthesizer for a subset of [ABC music
notation](http://en.wikipedia.org/wiki/ABC_notation).
Try:
``` <!---sh-->
cc -o endoh3 endoh3.c
echo "CDEFGABc" | ./endoh3 > /dev/dsp
```
If `/dev/dsp` is not available on your system, use an OSS sound wrapper such
as `padsp` or `aoss`:
``` <!---sh-->
echo "CDEFGABc" | ./endoh3 | padsp tee /dev/dsp > /dev/null
```
If you are using Mac OS X, try [sox](http://sox.sourceforge.net/) like so:
``` <!---sh-->
echo "CDEFGABc" | ./endoh3 | sox -q -traw -r8000 -b8 -e unsigned-integer - -tcoreaudio
```
If that does not work, use the attached script to convert the output into a wave
file format:
``` <!---sh-->
echo "CDEFGABc" | ./endoh3 | ruby wavify.rb > cde.wav
```
and play `cde.wav`.
You can also enjoy some music scores that I attached. With `/dev/dsp` you can
do so like:
``` <!---sh-->
cat twinkle.abc | ./endoh3 > /dev/dsp
cat menuet.abc | ./endoh3 > /dev/dsp
```
but you should be able to modify it a way to meet your system requirements as
described above.
### Obfuscation
The following sequence of questions may be helpful to understand the `endoh3ram`
program:
- How does it convert ABC notes into the frequency?
- What is `89/84.`? I found it by using the [Stern-Brocot
tree](https://en.wikipedia.org/wiki/Stern-Brocot_tree).
- How does it generate a wave? Hint: it generates a saw wave.
### Limitation
The following features are supported:
- Notes:
- `C` `D` `E` `F` `G` `A` `B` (in low octave)
- `c` `d` `e` `f` `g` `a` `b` (in high octave)
- Rests: `z`
- Note lengths:
- `A` (an eighth note)
- `A2` (a quarter note)
- `A3` (a dotted quarter note)
- `A4` (a half note)
The following features are *NOT* supported:
- Sharp and flat: `^C` `_C`
- But it is possible to emit the half tones. Do you see how?
- Shorter note lengths than 1: `C1/2`
- But you can use `C0.5` instead.
- Higher and lower notes: `c'` `C,`
- Any meta fields: `T:title` `L:1/4`
- Bar line, repeat, ties and slurs, and everything else.
### Known Bugs
You can *NOT* write a note length immediately followed by a note `E`,
such as `C2E2`.
Can you figure out why?
A workaround is inserting a whitespace: `C2 E2`.
### Portability
I tested it on:
* Ubuntu 13.10 with gcc 4.7.3, clang 3.2, and tcc 0.9.25.
* Mac OS X 10.6 with gcc 4.2.1 and clang 1.7.
### Legal
I believe that all the scores that I attached are out-of-copyright.
I did write a score of the Happy Birthday song too.
(It does not matter because the song is out-of-copyright in my country.)
But I do not attach it to protect you from W\*\*\*\*r.
For the same reason, do not post a score that contains only `z1092`.
You know, it is ["the famous song"](http://en.wikipedia.org/wiki/4%E2%80%B233%E2%80%B3).
### NOTICE to those who wish for a greater challenge:
**If you want a greater challenge, don't read any further**:
just try to understand the program via the source.
If you get stuck, come back and read below for additional hints and information.
### How this entry works:
Here is a magical expression which I found by brute-force:
``` <!---c-->
(c % 32 + 5) * 9 / 5 % 13 + n / 32 * 12 - 22
```
Interestingly, it converts an ASCII number of ABC notes
to the corresponding tone number.
```
'C' (ASCII 67) => 3
'D' (ASCII 68) => 5
'E' (ASCII 69) => 7
'F' (ASCII 70) => 9
'G' (ASCII 71) => 10
'A' (ASCII 65) => 12
'B' (ASCII 66) => 14
'c' (ASCII 99) => 15
'd' (ASCII 100) => 17
'e' (ASCII 101) => 19
'f' (ASCII 102) => 20
'g' (ASCII 103) => 22
'a' (ASCII 97) => 24
'b' (ASCII 98) => 26
```
By the way, you can write a half-tone by the following characters.
```
'K' => 4 (= C#)
'L' => 6 (= D#)
'U' => 8 (= F#)
'V' => 11 (= G#)
'P' => 13 (= A#)
```
Note that the tone numbers simply enumerate the semi-tone steps. So we can
calculate the frequency easily: `pow(2, n / 12.0)`. Then, how can we calculate
`pow` without `math.h`? `pow(2, 1.0 / 12.0)` is approximated by `89/84.`. (I
found this approximation by using the [Stern-Brocot
tree](http://en.wikipedia.org/wiki/Stern%E2%80%93Brocot_tree).) We can gain the
frequency by multiplying the value by `n` times.
Finally, the following code generates a saw wave:
``` <!---c-->
for(c = 0; c < len; c++) putchar(a = n * D);
```
where `D` is a frequency and `a` is a variable whose type is `char`. By
assigning `float` to `char` variable, the implicit type conversion is performed
from `float` to `char` (modulo 256).
(Strictly speaking, this behavior is undefined according to 6.3.1.4 in C99; you
can replace it with `(long)(n*D)` if you are pedantic.)
All that was left was to combine and condense the components. The key is
squashing them into just one `for`-loop.
<!--
Copyright © 1984-2024 by Landon Curt Noll. All Rights Reserved.
You are free to share and adapt this file under the terms of this license:
Creative Commons Attribution-ShareAlike 4.0 International (CC BY-SA 4.0)
For more information, see:
https://creativecommons.org/licenses/by-sa/4.0/
-->