In a time where we have instant access to high-definition, streaming music from almost any kind of device, it's hard to imagine that once upon a time computers could not play more than beeps. Now, a tiny computer like an Arduino can play back audio and even directly synthesize audio (with some help of sketches and libraries). What if we turn this high-definition music, or really any sound around us, into humbler beeps reminiscent of 8-bit from the past?
The idea
Build an Arduino, connected to a microphone. This microphone receives sound, transmits the frequencies to the Arduino, which then plays beeps at the same or similar frequencies. This idea turned out to be too simplistic and more parts were needed.
Large parts
1x Arduino Uno
1x Breadboard
1x Electret microphone MAX9814 (MAX4466 also works)
2x Thin plastic speakers (tip: it's better to use one speaker for each tone)
Smaller parts
1x 22 pF capacitor (but 33 pF is better)
2x 150 ohm resistor (between Arduino audio signal wires and speakers)
Many jumper wires
The build
I am still a beginner to Arduino, which means that I probably chose a topic that is way beyond my skill level. Here you can see pictures of the build in progress.
First try building something. Right now, there's a microphone that can transmit audio data to the Arduino, and a speaker to play sounds.
Second build: added quite a few wires, the MSGEQ7 came in today too. And it works, the Arduino is now displaying a nice array of 7 frequency volumes!
Final build: a transplant onto a smaller breadboard to fit into a small box made by Laurens. It's really hard to adjust the wiring in here! The microphone is taped to the lid.
Also did some soldering because I wanted to place the microphone further away from the speakers.
Sounds
The Arduino can play square waves, these are just beeps and I originally wanted the 8-bitify to sound nicer. In my search for something that could help me produce chiptune or 8-bit-like sounds, I found Mozzi. Unfortunately, I could not get Mozzi to work properly even after spending a whole evening on it... while it did produce the proper sounds, the volume was so soft that I had to put the speaker in my ear - and it only vibrated in my ear without making audible sound.
I then found the proper Note library. Arduino has a simplified version of this library that only lets you play one tone at a time, but the complete library lets you play up to three tones simultaneously on the Uno's Atmega328. Yay! It's still not as advanced as I'd liked it to be, but three tones is better than just one :)
Reading frequencies
If you use a microphone, it outputs a single number to the Arduino that represents volume. There's a way to separate this signal into various frequency readings using a Fourier transform. There are libraries that do this, but the ffft libraries and their accompanying sketches were too difficult to understand for me. If you're curious, visit the Piccolo by Adafruit. I spent several hours trying to figure out the library and code, but I gave up after a while.
An easier way is to invest in a MSGEQ7 which is often used for Arduino spectrum analyzers. It splits an audio signal into a direct current that represents its 7 different frequencies. These are 63Hz, 160Hz, 400Hz, 1kHz, 2.5kHz, 6.25kHz and 16kHz. You can then read the 'loudness' from 0 - 1023 for each of the seven frequencies with the Arduino (just the loudness, so not the actual frequency). J. Skoba wrote a great article about it and also shows a piece of code to get it working. In order to get a reading of all seven frequencies, I only had to strobe the pin for it 7 times (code courtesy of J. Skoba).
for (int i = 0; i < 7; i++) {
digitalWrite(strobePin, LOW);
delayMicroseconds(30); // to allow the output to settle
spectrumValue[i] = analogRead(analogPin)- 100;
}
digitalWrite(strobePin, HIGH);
}
Playing tones
The Tone library did not work (at first). The Tone library was last updated for Arduino... pre-1.0. Arduino is now at 1.6.x. I got a shitload of compiler errors before I got it working. All I had to do was open Tone.cpp in my libraries > Tone folder (in my sketches folder), copy the contents of Tone.cpp into an Arduino sketch to format it properly, copy-paste that back into Notepad... and then change "#include <wiring.h>" to "#include <Arduino.h>".
Preparing values
I call this at the bottom of void draw() so they get updated each loop.
void updFreqValues() {
value63hz = spectrumValue[0];
value160hz = spectrumValue[1];
value400hz = spectrumValue[2];
value1khz = spectrumValue[3];
}
Comparison and tone playing code
In order to play the right sounds, I compared the values of the four frequency bands I used (63 Hz to 1 kHz) to the tones that the Notes library can play. So if I want to play a tone at a frequency of 156 Hz, I play 'NOTE_DS3' through tone1. For one tone (tone1), I used two different frequency readings (63 Hz and 160 Hz) just to play an extra low tone because without it the 8-bitify was missing tones that were obviously there.
Tone1 has a maximum frequency of 164 Hz (NOTE_E3), tone2 has a maximum of 440 Hz (NOTE_A4) and tone3 has a maximum of 1 kHz (NOTE_C6). The tones aren't very accurate because most frequencies that are picked up by the microphone converge around the 400 Hz, but still sound very different. When 8-bitify plays sounds it still doesn't really sound right.
It's important to realize that I still don't know the exact frequencies being played as I only saw the volume for each frequency band. I noticed the volume always seemed to change in relation to the frequency. It was lower if the frequency was slightly lower than the frequency the MSGEQ7 is set at (so if it measures frequency at 160 Hz, and I play 140 Hz, the volume was softer). That's why I based the choices for the different tones on the volume levels.
I used the if-conditions to prevent tones from playing while it was already playing tones. Without that, the 8-bitify would probably make people bleed from the ears and cry.
void speakerTone() {
if (!tone1.isPlaying()) {
// 160 Hz
if (value160hz > 900) {
tone1.play(NOTE_E3, tonedura);
} else if (value160hz > 800) {
tone1.play(NOTE_DS3, tonedura);
} else if (value160hz > 650) {
tone1.play(NOTE_D3, tonedura);
} else if (value160hz > 500) {
tone1.play(NOTE_CS3, tonedura);
} else if (value63hz > 750 && value160hz < 500) {
tone1.play(NOTE_B2, tonedura);
}
}
if (!tone2.isPlaying() && !tone3.isPlaying()) {
// 400 Hz
if (value400hz > 900) {
tone2.play(NOTE_A4, tonedura);
} else if (value400hz > 800) {
tone2.play(NOTE_GS4, tonedura);
} else if (value400hz > 700) {
tone2.play(NOTE_G4 , tonedura);
} else if (value400hz > 600) {
tone2.play(NOTE_F4, tonedura);
} else if (value400hz > 500) {
tone2.play(NOTE_D4, tonedura);
}
// 1 kHz
if (value1khz > 900) {
tone3.play(NOTE_C6, tonedura);
} else if (value1khz > 800) {
tone3.play(NOTE_B5, tonedura);
} else if (value1khz > 700) {
tone3.play(NOTE_A5, tonedura);
} else if (value1khz > 600) {
tone3.play(NOTE_G5, tonedura);
}
}
Does it work?
It works! It doesn't sound great because of the audio signals converging around 400 Hz, so it basically sounds the same for different kinds of sounds. It works much better if you place it next to a proper audio set-up with speakers, sound from a mobile phone or laptop is too 'noisy' to be caught by the microphone properly. I haven't tried it with an audio jack.
The video below shows it playing a few songs! I think the first and last song played sound the best because it plays more different notes then :)
Downloads: Schematic and code
Download the code: _8bitify.ino. Also download this library and alter Tone.cpp per the instructions above.
Download the Fritzing schematic here, you can see a preview below.