Tuesday, August 21, 2012

Perfection never was a requirement, although some might say we desired it.

My proof of concept was complete, and I was excited.  Now, it was time to give the display some polish and make it actually look presentable.

Step one was getting rid of the LED flicker.  The olimex booster pack headers weren't quite compatible with our launchpad (the SPI TX pin to the olimex was sitting on an SPI RX pin on the launchpad), so originally I was having to use our software SPI driver to communicate with the booster pack's shift registers, as the software driver allowed for using generic GPIOs as opposed to SPI specific pins.  I already had most of the launchpad<--->olimex connectors blue wired though, so adding three more to allow my project to use the hardware SPI peripheral was no big deal, and made the display refresh function much less software intensive.  Next, I decided to allocate one of my unused timers to act as a display timer.  I set it up to throw an interrupt based on a predefined refresh rate macro, and update one column of the display every time the interrupt occurred.  The result was a much, much better looking display that had no flicker whatsoever.  I found that I needed to be careful about setting my refresh rate too high though; my signal processing was able to keep up pretty well, but the fact that I had no smoothing implemented in my software meant that if the refresh rate was too high, any sudden peaks in the frequency spectrum (like that resulting from the attack of a kick drum) would only appear as though the upper LEDs were briefly dimly illuminated.

The next step was cleaning up the fft results.  The biggest hurdle here was in figuring out how to calculate the "maximum loudness" value.  In my proof of concept, the power LED breakpoints were all calculated based on observed values and statically defined.  This was a bad approach as it did not account for any variance in  the volume of the incoming signal (like switching from a loud song to a soft song) and, more importantly, did not account for the fact that a tone at 200 kHz sounds much, much quieter than a tone of the same power at 12 kHz.  Also, the logarithmic frequency display meant that my lower frequency LED was the average of only 2 frequency bins, where my upper frequency LED was the average of about 400 frequency bins.  Overall, this caused the lower frequency LEDs to display much more power than the upper frequency LEDs.

I played around with a few different possible solutions for this, and finally came up with something that looked good to me.  I created an array of eight maximum power values, corresponding to the eight frequency ranges I was displaying.  Each time through the processing loop, I divided the current power value for the frequency range by the maximum power I'd observed on that range (updating the maximum power if the current power was bigger than the maximum, of course).  This gave me a normalized value between 0 and 1 of how "loud" that frequency range was in relation to what it had been in the past.  At that point, I just checked to see if the normalized value was greater than 1/8, 2/8, 3/8, ... and set the number of LEDs to illuminate for that range accordingly.  To account for frequency spikes or drastic changes in volume, I multiplied each maximum by a decay factor every time I went through my signal processing loop.

The resulting display looked much, much better.  The downside to this method was that I was displaying power values relative to previous values for that frequency range, as opposed to the power in one frequency range relative to the current power in all the other frequency ranges.  I'm not convinced this is the best method to use for computing the display values, but it is the best that I was able to come up with.

The final step to getting a good looking display was to figure out what frequency range I should display.  I found that even with my normalized power display method, every time a cymbal crash was made on the drums or a sibilant sound was made by a vocalist, the top three to four frequency LEDs would peak.  This was because I was displaying the entire range of human hearing (20 Hz to 22 kHz) scrunched into 8 LED ranges.  I probably should have used some scientific method to figure out which ranges to display, but I decided instead to use the trusty "try stuff until it looks good" approach.  I ended up finding that using 40 Hz as a minimum and about 12 kHz as a maximum gave me a really good looking LED display.  With these values, I was able to finally visually map which LEDs were corresponding to which instruments when a song was playing in real time.

After about two months of spending inordinate portions of my weekends and free evenings to blink LEDs, I had finally created something that I was proud of :)

No comments:

Post a Comment