Once again, I decided to put the old travel DVD player's screen to good use by using as an output device for the Arduino. Though the DVD mechanism is broken, the screen allows for standard NTSC composite video input.. runs on an attached rechargeable battery pack (can also power the Arduino.)
I make no guarantees, and you do so AT YOUR OWN RISK, but it should work with any TV or device that allows NTSC Video In. I am in no way responsible if you damage yourself or your equipment. This is a prototype built by an amateur, keep that in mind.
A brilliant bit of code, the TVout
http://www.arduino.cc/playground/Main/TVout library for Arduino, allows you to generate composite NTSC monochrome video with only two pins and two resistors. I generally leave the resolution at the default 128x96, which translates to 16x12 text with the default 8x8 font. Running under the defaults seems to give the least amount of trouble with this library, which is a work in progress. Note that due to this library being actively worked on, there's no guarantee the code I am using will work with other IDE or Library versions. This has been developed using version 0019, though I will be testing shortly on the most recent releases. In addition, though it should not matter, I am using a 5v Adafruit Boarduino. There should be no differences, as long as your Arduino is a 5v device. Also note that old versions of Arduino which use an Atmel ATmega168 won't be able to run this, they don't have enough memory.
The other piece of the puzzle is collecting and processing audio, so we have something to display on our little display.
The first piece- data collection- is fairly standard. I use an electret microphone (which alone only produces a few mV output, far too low for our Arduino to use directly) with a transistor amplifier as the signal source, which is then sampled via the ADC on the Analog 0 pin of the Arduino.
To do spectrum analysis however, you need to capture signal over time, then process that data with what is known as a Fourier Transformation. This magical process takes a signal and breaks it down into buckets based upon frequencies found within the sample. This produces a remarkably good picture of the signal.. and if displayed, functions as a visual spectrum analyzer if looped over and over.
In this project, I've used code posted by a user to the Arduino forums:
http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1286718155.
This post contains a library which performs both the sampling and the Fast Fourier Transformation completely in C in 8 bits, amazing fast considering that fact, and uses a few tricks to be really stingy on memory, which is at a premium on Arduino- especially with the TVout data space eating up quite a bit. Since the Atmega 328 only has 2k of RAM, every byte counts. Matrix math done like this is nothing short of awesome. Best of all, it's usable as a library. Cut and paste the .cpp and .h into a new folder named "FFT" in the Libraries directory. My Arduino project code is adapted from the original code from the forum-posted Arduino program.
So, to produce our desired outcome, we just need to get to get the whole show together and hope it can perform.. With only a handful of cheap components (a few dollars at most), it produces a perfectly usable and quite entertaining realtime 64-band video spectrum analyzer. Though not really "useful" for any real purposes, it makes an entertaining party display out of any television with a video input...
There are a lot of improvements which can be made- the first being the amplifier gain to make it a bit more responsive, and optimization of the FFT code. In reality, it's the drawing of the bars which takes the most time per sample/display cycle..not the matrix math!
Arduino Code:
#include <TVout.h>
#include <fix_fft.h>
TVout TV;
char im[128], data[128], lastpass[64];
char x=32, ylim=90;
int i=0,val;
void setup()
{
TV.begin(_NTSC,128,96); // Initialize TV output, 128x96.
TV.print_str(2,2," Realtime Arduino"); // TVout lib uses x,y for print
TV.print_str(2,11," Spectrum Analyzer"); // statements. 8x8 default font.
analogReference(DEFAULT); // Use default (5v) aref voltage.
for (int z=0; z<64; z++) {lastpass[z]=80;}; // fill the lastpass[] array with dummy data
};
void loop()
{
for (i=0; i < 128; i++){ // We don't go for clean timing here, it's
val = analogRead(0); // better to get somewhat dirty data fast
data[i] = val/4 -128; // than to get data that's lab-accurate
im[i] = 0; // but too slow, for this application.
};
fix_fft(data,im,7,0);
for (i=1; i< 64;i++){ // In the current design, 60Hz and noise
data[i] = sqrt(data[i] * data[i] + im[i] * im[i]); // in general are a problem. Future designs
TV.draw_line(i+x,lastpass[i],i+x,ylim,0); // and code may fix this, but for now, I
TV.draw_line(i+x,ylim,i+x,ylim-data[i],1); // skip displaying the 0-500hz band completely.
lastpass[i]=ylim-data[i]; // if you insist, initialize the loop with 0
}; // rather than 1.
};
The circuit required is a simple microphone and transistor amplifier, as well as two resistors connected to D8 and D9 to provide video signal. See the next blog post for the schematic.. drawing is a much bigger pain than you'd think!