Aprel
Posts: 2
Joined: Thu Jan 22, 2015 6:04 am

Write data to gpio at 44.1 kbps

Thu Jan 22, 2015 6:58 am

I'm working on a lighting application based on audio, similar to a Christmas light setup with music. I'm going to set up the pi to drive a relay. The basic idea is I want the relay open during quiet parts of the audio and closed during loud parts. Here's the approach I have taken:

For each sample in the 44.1kHz audio file, if the amplitude is greater than the arbitrary threshold, write 1 to file; else, 0. This gives me a file of 0s and 1s equal in length to 44100 bits * seconds duration of the audio file. I want to "play" those 0s and 1s (simultaneous to the actual audio in speakers) to a gpio pin at 44.1kHz. How best can I accomplish this? Multiple calls to delay() would seem to lack the necessary precision.

FWIW the relay takes about 1ms to switch, so it can't operate at 44100 switches per second, but naturally there are long stretches of 0s and 1s from the audio file so switching is sparse. I considered converting the 0s and 1s to ON and OFF calls with delay() in-between, but I worry about drift because delay() is not guaranteed to return exactly after the number of ms specified. This could lead to loss of sync over long audio files.

I'm also aware that there are some really nice libraries for Christmas light setups. I'm using this project as mostly a learning experience. Also, the libraries I've seen use FFT or other automated means of controlling the lights, but I'd like to eventual jump from using amplitude as a delimiter, to making my own custom on-off cue files.

User avatar
Redrobes
Posts: 80
Joined: Mon Dec 26, 2011 9:19 pm
Location: S.W. UK
Contact: Website

Re: Write data to gpio at 44.1 kbps

Thu Jan 22, 2015 1:29 pm

What your saying you want to do is insane and if you go through with it then you will just end up destroying your relays. Even a bass bump would send a few hundred transitions to your relay.

What you need to do is to find the low pass filter of the RMS of the waveform (the waveforms power) and then set a threshold on it. The threshold value needs to be determined on test based on the sample values or have it as some kind of GUI parameter.

So for each sample you need to put the sample value into a double precision floating point then square it then average it then do the square root on it. Then take that waveform and put it through a low pass filter by adding a small amount of each of the result into an accumulator then if the value is higher than some threshold then set the IO line high else set it low.

Since your just using a threshold then there is no need to square root it and just use a threshold value which is the square of what you would have used. Since thats arbitrary then its no real difference.

So something like:

double thresh = 500.0;
double a = 0.0;

While( more samples )
{
double d = (double)sample;
a = ( 999.0 * a + (d*d) ) / 1000.0;
if( a > thresh )
{
gpio(1);
}
else
{
gpio(0);
}
}

Even with 1000 as the low pass that would give you up to about 20Hz of lighting which might still be too much so up that to 5000 or 10000 as required.

Aprel
Posts: 2
Joined: Thu Jan 22, 2015 6:04 am

Re: Write data to gpio at 44.1 kbps

Fri Jan 23, 2015 1:56 am

Redrobes wrote:then if the value is higher than some threshold then set the IO line high else set it low.
Redrobes wrote: While( more samples )
{
[// ...]
}
I know this code is simplified, but it exemplifies where I start to get lost coding this. This while loop will run as fast as the CPU will allow, but in my case, I need 44100 iterations per second. How do I code time-sensitive processing?

In the interest of limiting the processing that needs to be done on-the-fly, I can convert the data to ON-OFF metrics, e.g. program starts at time 0ms --> ON at 733 ms, OFF at 3280 ms, ON at 4127 ms, etc., but then how do I code this?

Thank you, Redrobes, for the advice so far.

Return to “C/C++”