Bash One Liner - Compose Music From Entropy in /dev/urandom
2016-03-04 - By Robert Elder
Last Updated Dec 12, 2019: Fixed an off by one error (awk uses 1-based arrays!).
The Command
cat /dev/urandom | hexdump -v -e '/1 "%u\n"' | awk '{ split("0,2,4,5,7,9,11,12",a,","); for (i = 0; i < 1; i+= 0.0001) printf("%08X\n", 100*sin(1382*exp((a[$1%8+1]/12)*log(2))*i)) }' | xxd -r -p | aplay -c 2 -f S32_LE -r 16000
The above command was tested on Ubuntu 14.04. You should hear music play through your speakers.
Updated March 6: I only expected this to work on Ubuntu, but you can read the comments to find audio clips and versions of the command that people have created for Mac:
Explanation
The first part of the pipe
cat /dev/urandom
prints the contents of /dev/urandom to stdout. If you're a level 500 elite hacker like I am, you will note that the cryptographic quality of numbers from /dev/urandom is not the same as numbers from /dev/random. In our case, we don't want this operation to block, so we use /dev/urandom. See 'man 4 random'. (Updated March 6: In addition to the man page, see 1, 2)
Since the output of /dev/urandom is random binary bytes, we can use hexdump to format it into nice integers from 0-255:
hexdump -v -e '/1 "%u\n"'
Here is a sample of the output from hexdump:
cat /dev/urandom | hexdump -v -e '/1 "%u\n"' | head
135
16
156
54
115
156
116
20
59
191
The -v flag gets rid of the hexdump's default behaviour of replacing repeated lines with a '*' character. The -e flag uses '/1 "%u\n"' to print out the binary byte as an a formatted decimal number.
These integers in the range of 0-255 are then passed into awk, one line at a time:
awk '...'
The ... part is the following small program:
split("0,2,4,5,7,9,11,12",a,",");
for (i = 0; i < 1; i+= 0.0001) printf("%08X\n", 100*sin(1382*exp((a[$1%8+1]/12)*log(2))*i))
The line
split("0,2,4,5,7,9,11,12",a,",");
creates an array 'a' which encodes the relative number of semitones from the base note in a major musical scale. This array is useful in the print statement because the frequency in Hertz of a musical note with equal temperament can be calculated using the following formula: 440 * 2^(semitone distance / 12). 440Hz has arbitrarily been chosen as the reference note, and it represents the frequency of A4.
The for loop then prints formatted 4 byte hexadecimal numbers that represent the amplitude of the sound wave at a given point in time:
printf("%08X\n", 100*sin(1382*exp((a[$1%8+1]/12)*log(2))*i))
The formula
100*sin(1382*exp((a[$1%8+1]/12)*log(2))*i)
Can be broken down as follows:
100 # A scalar multiple used to control the volume.
*
sin(
1382 # =~ 440 * 3.14159
*
exp( # Awk doesn't seem to support arbitrary powers, so we use 2^x = e^(x*ln(2))
(
# NOTE: awk uses 1-based indexing...
a[$1%8+1] # Pick a semitone at random on the major scale
/
12 # Divide by 12 per the formula
)
*
log(2)
)
*
i # The counter in the for loop that counts from 0 to 1
)
The amount of time that each note is played for can be controlled by changing how fast the for loop counts up to 1. This number will also affect the perceived frequency.
The above formula makes a bit more sense if you think about it in the following way. If you did
for (i = 0; i < 1; i+= 0.0001) sin(i * 3.14159);
This will just calculate values on one cycle of a sine curve. If you then multiply 'i' by a frequency X of a musical note, each iteration of the for loop will calculate points on the X cycles of notes of the given frequency so the amplitude will change faster than the original 1 cycle per completion of the for loop.
The next part of the pipe uses xxd to convert the 8 byte hexadecimal values back into binary:
xxd -r -p
Which is then fed into aplay and turned into audible sound:
aplay -c 2 -f S32_LE -r 16000
Sad Music
If you want to make the music sound sad, you can change from the major scale to a minor scale:
cat /dev/urandom | hexdump -v -e '/1 "%u\n"' | awk '{ split("0,2,3,5,7,8,10,12",a,","); for (i = 0; i < 1; i+= 0.0001) printf("%08X\n", 100*sin(1382*exp((a[$1%8+1]/12)*log(2))*i)) }' | xxd -r -p | aplay -c 2 -f S32_LE -r 16000
The Fast Meme Transform: Convert Audio Into Linux Commands
Published 2018-02-10 |
$1.00 CAD |
A Surprisingly Common Mistake Involving Wildcards & The Find Command
Published 2020-01-21 |
A Guide to Recording 660FPS Video On A $6 Raspberry Pi Camera
Published 2019-08-01 |
The Most Confusing Grep Mistakes I've Ever Made
Published 2020-11-02 |
Use The 'tail' Command To Monitor Everything
Published 2021-04-08 |
An Overview of How to Do Everything with Raspberry Pi Cameras
Published 2019-05-28 |
An Introduction To Data Science On The Linux Command Line
Published 2019-10-16 |
Join My Mailing List Privacy Policy |
Why Bother Subscribing?
|