rearrange files some a few cleanups, too.

This commit is contained in:
cnlohr 2015-04-04 22:16:55 -04:00
parent ec4f175c2b
commit f80dd24a8d
9 changed files with 16 additions and 13 deletions

356
embeddedcommon/DFT32.c Normal file
View file

@ -0,0 +1,356 @@
#include "DFT32.h"
#include <string.h>
#ifndef CCEMBEDDED
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
static float * goutbins;
#endif
uint16_t embeddedbins32[FIXBINS];
//NOTES to self:
//
// Let's say we want to try this on an AVR.
// 24 bins, 5 octaves = 120 bins.
// 20 MHz clock / 4.8k sps = 4096 IPS = 34 clocks per bin = :(
// We can do two at the same time, this frees us up some
static uint8_t Sdonefirstrun;
//A table of precomputed sin() values. Ranging -1500 to +1500
//If we increase this, it may cause overflows elsewhere in code.
const int16_t Ssinonlytable[256] = {
0, 36, 73, 110, 147, 183, 220, 256,
292, 328, 364, 400, 435, 470, 505, 539,
574, 607, 641, 674, 707, 739, 771, 802,
833, 863, 893, 922, 951, 979, 1007, 1034,
1060, 1086, 1111, 1135, 1159, 1182, 1204, 1226,
1247, 1267, 1286, 1305, 1322, 1339, 1355, 1371,
1385, 1399, 1412, 1424, 1435, 1445, 1455, 1463,
1471, 1477, 1483, 1488, 1492, 1495, 1498, 1499,
1500, 1499, 1498, 1495, 1492, 1488, 1483, 1477,
1471, 1463, 1455, 1445, 1435, 1424, 1412, 1399,
1385, 1371, 1356, 1339, 1322, 1305, 1286, 1267,
1247, 1226, 1204, 1182, 1159, 1135, 1111, 1086,
1060, 1034, 1007, 979, 951, 922, 893, 863,
833, 802, 771, 739, 707, 674, 641, 607,
574, 539, 505, 470, 435, 400, 364, 328,
292, 256, 220, 183, 147, 110, 73, 36,
0, -36, -73, -110, -146, -183, -219, -256,
-292, -328, -364, -399, -435, -470, -505, -539,
-573, -607, -641, -674, -706, -739, -771, -802,
-833, -863, -893, -922, -951, -979, -1007, -1034,
-1060, -1086, -1111, -1135, -1159, -1182, -1204, -1226,
-1247, -1267, -1286, -1305, -1322, -1339, -1355, -1371,
-1385, -1399, -1412, -1424, -1435, -1445, -1454, -1463,
-1471, -1477, -1483, -1488, -1492, -1495, -1498, -1499,
-1500, -1499, -1498, -1495, -1492, -1488, -1483, -1477,
-1471, -1463, -1455, -1445, -1435, -1424, -1412, -1399,
-1385, -1371, -1356, -1339, -1322, -1305, -1286, -1267,
-1247, -1226, -1204, -1182, -1159, -1135, -1111, -1086,
-1060, -1034, -1007, -979, -951, -923, -893, -863,
-833, -802, -771, -739, -707, -674, -641, -608,
-574, -540, -505, -470, -435, -400, -364, -328,
-292, -256, -220, -183, -147, -110, -73, -37,};
/** The above table was created using the following code:
#include <math.h>
#include <stdio.h>
#include <stdint.h>
int16_t Ssintable[256]; //Actually, just [sin].
int main()
{
int i;
for( i = 0; i < 256; i++ )
{
Ssintable[i] = (int16_t)((sinf( i / 256.0 * 6.283 ) * 1500.0));
}
printf( "const int16_t Ssinonlytable[256] = {" );
for( i = 0; i < 256; i++ )
{
if( !(i & 0x7 ) )
{
printf( "\n\t" );
}
printf( "%6d," ,Ssintable[i] );
}
printf( "};\n" );
} */
uint16_t Sdatspace32A[FIXBINS*2]; //(advances,places)
int32_t Sdatspace32B[FIXBINS*2]; //(isses,icses)
//This is updated every time the DFT hits the octavecount, or 1/32 updates.
int32_t Sdatspace32BOut[FIXBINS*2]; //(isses,icses)
//Sdo_this_octave is a scheduling state for the running SIN/COS states for
//each bin. We have to execute the highest octave every time, however, we can
//get away with updating the next octave down every-other-time, then the next
//one down yet, every-other-time from that one. That way, no matter how many
//octaves we have, we only need to update FIXBPERO*2 DFT bins.
static uint8_t Sdo_this_octave[BINCYCLE];
static int32_t Saccum_octavebins[OCTAVES];
static uint8_t Swhichoctaveplace;
//
uint16_t embeddedbins[FIXBINS];
//From: http://stackoverflow.com/questions/1100090/looking-for-an-efficient-integer-square-root-algorithm-for-arm-thumb2
/**
* \brief Fast Square root algorithm, with rounding
*
* This does arithmetic rounding of the result. That is, if the real answer
* would have a fractional part of 0.5 or greater, the result is rounded up to
* the next integer.
* - SquareRootRounded(2) --> 1
* - SquareRootRounded(3) --> 2
* - SquareRootRounded(4) --> 2
* - SquareRootRounded(6) --> 2
* - SquareRootRounded(7) --> 3
* - SquareRootRounded(8) --> 3
* - SquareRootRounded(9) --> 3
*
* \param[in] a_nInput - unsigned integer for which to find the square root
*
* \return Integer square root of the input value.
*/
static uint16_t SquareRootRounded(uint32_t a_nInput)
{
uint32_t op = a_nInput;
uint32_t res = 0;
uint32_t one = 1uL << 30; // The second-to-top bit is set: use 1u << 14 for uint16_t type; use 1uL<<30 for uint32_t type
// "one" starts at the highest power of four <= than the argument.
while (one > op)
{
one >>= 2;
}
while (one != 0)
{
if (op >= res + one)
{
op = op - (res + one);
res = res + 2 * one;
}
res >>= 1;
one >>= 2;
}
/* Do arithmetic rounding to nearest integer */
if (op > res)
{
res++;
}
return res;
}
void UpdateOutputBins32()
{
int i;
int * ipt = &Sdatspace32BOut[0];
for( i = 0; i < FIXBINS; i++ )
{
int16_t isps = *(ipt++)>>16;
int16_t ispc = *(ipt++)>>16;
int octave = i / FIXBPERO;
//If we are running DFT32 on regular ColorChord, then we will need to
//also update goutbins[]... But if we're on embedded systems, we only
//update embeddedbins32.
#ifndef CCEMBEDDED
uint32_t mux = ( (isps) * (isps)) + ((ispc) * (ispc));
goutbins[i] = sqrtf( (float)mux );
//reasonable (but arbitrary amplification)
goutbins[i] /= (78<<DFTIIR)*(1<<octave);
#endif
uint32_t rmux = ( (isps) * (isps)) + ((ispc) * (ispc));
//bump up all outputs here, so when we nerf it by bit shifting by
//ctave we don't lose a lot of detail.
rmux = SquareRootRounded( rmux ) << 1;
embeddedbins32[i] = rmux >> octave;
}
}
static void HandleInt( int16_t sample )
{
int i;
uint16_t adv;
uint8_t localipl;
uint8_t oct = Sdo_this_octave[Swhichoctaveplace];
Swhichoctaveplace ++;
Swhichoctaveplace &= BINCYCLE-1;
if( oct > 128 )
{
//Special: This is when we can update everything.
//This gets run one out of every 1/(1<<OCTAVES) times.
//It handles updating part of the DFT.
int32_t * bins = &Sdatspace32B[0];
int32_t * binsOut = &Sdatspace32BOut[0];
for( i = 0; i < FIXBINS; i++ )
{
//First for the SIN then the COS.
int32_t val = *(bins);
*(binsOut++) = val;
*(bins++) -= val>>DFTIIR;
val = *(bins);
*(binsOut++) = val;
*(bins++) -= val>>DFTIIR;
}
return;
}
for( i = 0; i < OCTAVES;i++ )
{
Saccum_octavebins[i] += sample;
}
uint16_t * dsA = &Sdatspace32A[oct*FIXBPERO*2];
int32_t * dsB = &Sdatspace32B[oct*FIXBPERO*2];
sample = Saccum_octavebins[oct]>>(OCTAVES-oct);
Saccum_octavebins[oct] = 0;
for( i = 0; i < FIXBPERO; i++ )
{
adv = *(dsA++);
localipl = *(dsA) >> 8;
*(dsA++) += adv;
*(dsB++) += (Ssinonlytable[localipl] * sample);
//Get the cosine (1/4 wavelength out-of-phase with sin)
localipl += 64;
*(dsB++) += (Ssinonlytable[localipl] * sample);
}
}
int SetupDFTProgressive32()
{
int i;
int j;
Sdonefirstrun = 1;
for( i = 0; i < BINCYCLE; i++ )
{
// Sdo_this_octave =
// 4 3 4 2 4 3 4 ...
//search for "first" zero
for( j = 0; j <= OCTAVES; j++ )
{
if( ((1<<j) & i) == 0 ) break;
}
if( j > OCTAVES )
{
#ifndef CCEMBEDDED
fprintf( stderr, "Error: algorithm fault.\n" );
exit( -1 );
#endif
return -1;
}
Sdo_this_octave[i] = OCTAVES-j-1;
}
return 0;
}
void UpdateBins32( const uint16_t * frequencies )
{
int i;
for( i = 0; i < FIXBINS; i++ )
{
uint16_t freq = frequencies[i%FIXBPERO];
Sdatspace32A[i*2] = freq;// / oneoveroctave;
}
}
void PushSample32( int16_t dat )
{
HandleInt( dat );
HandleInt( dat );
}
#ifndef CCEMBEDDED
void UpdateBinsForDFT32( const float * frequencies )
{
int i;
for( i = 0; i < FIXBINS; i++ )
{
float freq = frequencies[(i%FIXBPERO) + (FIXBPERO*(OCTAVES-1))];
Sdatspace32A[i*2] = (65536.0/freq);// / oneoveroctave;
}
}
#endif
#ifndef CCEMBEDDED
void DoDFTProgressive32( float * outbins, float * frequencies, int bins, const float * databuffer, int place_in_data_buffer, int size_of_data_buffer, float q, float speedup )
{
static float backupbins[FIXBINS];
int i;
static int last_place;
memset( outbins, 0, bins * sizeof( float ) );
goutbins = outbins;
memcpy( outbins, backupbins, FIXBINS*4 );
if( FIXBINS != bins )
{
fprintf( stderr, "Error: Bins was reconfigured. skippy requires a constant number of bins.\n" );
return;
}
if( !Sdonefirstrun )
{
SetupDFTProgressive32();
Sdonefirstrun = 1;
}
UpdateBinsForDFT32( frequencies );
for( i = last_place; i != place_in_data_buffer; i = (i+1)%size_of_data_buffer )
{
int16_t ifr1 = (int16_t)( ((databuffer[i]) ) * 4095 );
HandleInt( ifr1 );
HandleInt( ifr1 );
}
UpdateOutputBins32();
last_place = place_in_data_buffer;
memcpy( backupbins, outbins, FIXBINS*4 );
}
#endif

75
embeddedcommon/DFT32.h Normal file
View file

@ -0,0 +1,75 @@
#ifndef _DFT32_H
#define _DFT32_H
#include <stdint.h>
//A 32-bit version of the DFT used for ColorChord.
//This header makes it convenient to use for an embedded system.
//The 32-bit DFT avoids some bit shifts, however it uses slightly
//more RAM and it uses a lot of 32-bit arithmatic.
//
//This is basically a clone of "ProgressiveIntegerSkippy" and changes
//made here should be backported there as well.
//You can # define these to be other things elsewhere.
#ifndef OCTAVES
#define OCTAVES 5
#endif
#ifndef FIXBPERO
#define FIXBPERO 24
#endif
#ifndef FIXBINS
#define FIXBINS (FIXBPERO*OCTAVES)
#endif
#ifndef BINCYCLE
#define BINCYCLE (1<<OCTAVES)
#endif
//You may increase this past 5 but if you do, the amplitude of your incoming
//signal must decrease. Increasing this value makes responses slower. Lower
//values are more responsive.
#ifndef DFTIIR
#define DFTIIR 6
#endif
//Everything the integer one buys, except it only calculates 2 octaves worth of
//notes per audio frame.
//This is sort of working, but still have some quality issues.
//It would theoretically be fast enough to work on an AVR.
//NOTE: This is the only DFT available to the embedded port of ColorChord
#ifndef CCEMBEDDED
void DoDFTProgressive32( float * outbins, float * frequencies, int bins,
const float * databuffer, int place_in_data_buffer, int size_of_data_buffer,
float q, float speedup );
#endif
//It's actually split into a few functions, which you can call on your own:
int SetupDFTProgressive32(); //Call at start. Returns nonzero if error.
void UpdateBins32( const uint16_t * frequencies );
//Call this to push on new frames of sound.
//Though it accepts an int16, it actually only takes -4095 to +4095. (13-bit)
//Any more and you will exceed the accumulators and it will cause an overflow.
void PushSample32( int16_t dat );
#ifndef CCEMBEDDED
//ColorChord regular uses this to pass in floats.
void UpdateBinsForDFT32( const float * frequencies ); //Update the frequencies
#endif
//This takes the current sin/cos state of ColorChord and output to
//embeddedbins32.
void UpdateOutputBins32();
//Whenever you need to read the bins, you can do it from here.
//These outputs are limited to 0..~2047, this makes it possible
//for you to process with uint16_t's more easily.
//This is updated every time the DFT hits the octavecount, or 1/32 updates.
extern uint16_t embeddedbins32[]; //[FIXBINS]
#endif

409
embeddedcommon/embeddednf.c Normal file
View file

@ -0,0 +1,409 @@
#include "embeddednf.h"
#include <stdio.h>
uint16_t folded_bins[FIXBPERO];
uint16_t fuzzed_bins[FIXBINS];
uint8_t note_peak_freqs[MAXNOTES];
uint16_t note_peak_amps[MAXNOTES];
uint16_t note_peak_amps2[MAXNOTES];
uint8_t note_jumped_to[MAXNOTES];
static const float bf_table[24] = {
1.000000, 1.029302, 1.059463, 1.090508, 1.122462, 1.155353,
1.189207, 1.224054, 1.259921, 1.296840, 1.334840, 1.373954,
1.414214, 1.455653, 1.498307, 1.542211, 1.587401, 1.633915,
1.681793, 1.731073, 1.781797, 1.834008, 1.887749, 1.943064 };
/* The above table was generated using the following code:
#include <stdio.h>
#include <math.h>
int main()
{
int i;
#define FIXBPERO 24
printf( "const float bf_table[%d] = {", FIXBPERO );
for( i = 0; i < FIXBPERO; i++ )
{
if( ( i % 6 ) == 0 )
printf( "\n\t" );
printf( "%f, ", pow( 2, (float)i / (float)FIXBPERO ) );
}
printf( "};\n" );
return 0;
}
*/
#define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)]))
void UpdateFreqs()
{
uint16_t fbins[FIXBPERO];
int i;
BUILD_BUG_ON( sizeof(bf_table) != FIXBPERO*4 );
//Warning: This does floating point. Avoid doing this frequently. If you
//absolutely cannot have floating point on your system, you may precompute
//this and store it as a table. It does preclude you from changing
//BASE_FREQ in runtime.
for( i = 0; i < FIXBPERO; i++ )
{
float frq = ( bf_table[i] * BASE_FREQ );
fbins[i] = ( 65536.0 ) / ( DFREQ ) * frq * 16;
}
#ifdef USE_32DFT
UpdateBins32( fbins );
#else
UpdateBinsForProgressiveIntegerSkippyInt( fbins );
#endif
}
void Init()
{
int i;
//Set up and initialize arrays.
for( i = 0; i < MAXNOTES; i++ )
{
note_peak_freqs[i] = 255;
note_peak_amps[i] = 0;
note_peak_amps2[i] = 0;
}
for( i = 0; i < FIXBPERO; i++ )
{
folded_bins[i] = 0;
}
for( i = 0; i < FIXBINS; i++ )
{
fuzzed_bins[i] = 0;
}
//Step 1: Initialize the Integer DFT.
#ifdef USE_32DFT
SetupDFTProgressive32();
#else
SetupDFTProgressiveIntegerSkippy();
#endif
//Step 2: Set up the frequency list. You could do this multiple times
//if you want to change the loadout of the frequencies.
UpdateFreqs();
}
void HandleFrameInfo()
{
int i, j, k;
uint8_t hitnotes[MAXNOTES];
for( i = 0; i < MAXNOTES; i++ )
{
hitnotes[i] = 0;
}
#ifdef USE_32DFT
uint16_t * strens;
UpdateOutputBins32();
strens = embeddedbins32;
#else
uint16_t * strens = embeddedbins;
#endif
//Copy out the bins from the DFT to our fuzzed bins.
for( i = 0; i < FIXBINS; i++ )
{
fuzzed_bins[i] = (fuzzed_bins[i] + (strens[i]>>FUZZ_IIR_BITS) -
(fuzzed_bins[i]>>FUZZ_IIR_BITS));
}
//Taper first octave
for( i = 0; i < FIXBPERO; i++ )
{
uint32_t taperamt = (65536 / FIXBPERO) * i;
fuzzed_bins[i] = (taperamt * fuzzed_bins[i]) >> 16;
}
//Taper last octave
for( i = 0; i < FIXBPERO; i++ )
{
int newi = FIXBINS - i - 1;
uint32_t taperamt = (65536 / FIXBPERO) * i;
fuzzed_bins[newi] = (taperamt * fuzzed_bins[newi]) >> 16;
}
//Fold the bins from fuzzedbins into one octave.
for( i = 0; i < FIXBPERO; i++ )
folded_bins[i] = 0;
k = 0;
for( j = 0; j < OCTAVES; j++ )
{
for( i = 0; i < FIXBPERO; i++ )
{
folded_bins[i] += fuzzed_bins[k++];
}
}
//Now, we must blur the folded bins to get a good result.
//Sometimes you may notice every other bin being out-of
//line, and this fixes that. We may consider running this
//more than once, but in my experience, once is enough.
for( j = 0; j < FILTER_BLUR_PASSES; j++ )
{
//Extra scoping because this is a large on-stack buffer.
uint16_t folded_out[FIXBPERO];
uint8_t adjLeft = FIXBPERO-1;
uint8_t adjRight = 1;
for( i = 0; i < FIXBPERO; i++ )
{
uint16_t lbin = folded_bins[adjLeft]>>2;
uint16_t rbin = folded_bins[adjRight]>>2;
uint16_t tbin = folded_bins[i]>>1;
folded_out[i] = lbin + rbin + tbin;
//We do this funny dance to avoid a modulus operation. On some
//processors, a modulus operation is slow. This is cheap.
adjLeft++; if( adjLeft == FIXBPERO ) adjLeft = 0;
adjRight++; if( adjRight == FIXBPERO ) adjRight = 0;
}
for( i = 0; i < FIXBPERO; i++ )
{
folded_bins[i] = folded_out[i];
}
}
//Next, we have to find the peaks, this is what "decompose" does in our
//normal tool. As a warning, it expects that the values in foolded_bins
//do NOT exceed 32767.
{
uint8_t adjLeft = FIXBPERO-1;
uint8_t adjRight = 1;
for( i = 0; i < FIXBPERO; i++ )
{
int16_t prev = folded_bins[adjLeft];
int16_t next = folded_bins[adjRight];
int16_t this = folded_bins[i];
uint8_t thisfreq = i<<SEMIBITSPERBIN;
int16_t offset;
adjLeft++; if( adjLeft == FIXBPERO ) adjLeft = 0;
adjRight++; if( adjRight == FIXBPERO ) adjRight = 0;
if( this < MIN_AMP_FOR_NOTE ) continue;
if( prev > this || next > this ) continue;
if( prev == this && next == this ) continue;
//i is at a peak...
int32_t totaldiff = (( this - prev ) + ( this - next ));
int32_t porpdiffP = ((this-prev)<<16) / totaldiff; //close to 0 =
//closer to this side, 32768 = in the middle, 65535 away.
int32_t porpdiffN = ((this-next)<<16) / totaldiff;
if( porpdiffP < porpdiffN )
{
//Closer to prev.
offset = -(32768 - porpdiffP);
}
else
{
//Closer to next
offset = (32768 - porpdiffN);
}
//Need to round. That's what that extra +(15.. is in the center.
thisfreq += (offset+(1<<(15-SEMIBITSPERBIN)))>>(16-SEMIBITSPERBIN);
//In the event we went 'below zero' need to wrap to the top.
if( thisfreq > 255-(1<<SEMIBITSPERBIN) )
thisfreq = (1<<SEMIBITSPERBIN)*FIXBPERO - (256-thisfreq);
//Okay, we have a peak, and a frequency. Now, we need to search
//through the existing notes to see if we have any matches.
//If we have a note that's close enough, we will try to pull it
//closer to us and boost it.
int8_t lowest_found_free_note = -1;
int8_t closest_note_id = -1;
int16_t closest_note_distance = 32767;
for( j = 0; j < MAXNOTES; j++ )
{
uint8_t nf = note_peak_freqs[j];
if( nf == 255 )
{
if( lowest_found_free_note == -1 )
lowest_found_free_note = j;
continue;
}
int16_t distance = thisfreq - nf;
if( distance < 0 ) distance = -distance;
//Make sure that if we've wrapped around the right side of the
//array, we can detect it and loop it back.
if( distance > ((1<<(SEMIBITSPERBIN-1))*FIXBPERO) )
{
distance = ((1<<(SEMIBITSPERBIN))*FIXBPERO) - distance;
}
//If we find a note closer to where we are than any of the
//others, we can mark it as our desired note.
if( distance < closest_note_distance )
{
closest_note_id = j;
closest_note_distance = distance;
}
}
int8_t marked_note = -1;
if( closest_note_distance <= MAX_JUMP_DISTANCE )
{
//We found the note we need to augment.
note_peak_freqs[closest_note_id] = thisfreq;
marked_note = closest_note_id;
}
//The note was not found.
else if( lowest_found_free_note != -1 )
{
note_peak_freqs[lowest_found_free_note] = thisfreq;
marked_note = lowest_found_free_note;
}
//If we found a note to attach to, we have to use the IIR to
//increase the strength of the note, but we can't exactly snap
//it to the new strength.
if( marked_note != -1 )
{
hitnotes[marked_note] = 1;
note_peak_amps[marked_note] =
note_peak_amps[marked_note] -
(note_peak_amps[marked_note]>>AMP_1_IIR_BITS) +
(this>>(AMP_1_IIR_BITS-3));
note_peak_amps2[marked_note] =
note_peak_amps2[marked_note] -
(note_peak_amps2[marked_note]>>AMP_2_IIR_BITS) +
((this<<3)>>(AMP_2_IIR_BITS));
}
}
}
#if 0
for( i = 0; i < MAXNOTES; i++ )
{
if( note_peak_freqs[i] == 255 ) continue;
printf( "%d / ", note_peak_amps[i] );
}
printf( "\n" );
#endif
//Now we need to handle combining notes.
for( i = 0; i < MAXNOTES; i++ )
for( j = 0; j < i; j++ )
{
//We'd be combining nf2 (j) into nf1 (i) if they're close enough.
uint8_t nf1 = note_peak_freqs[i];
uint8_t nf2 = note_peak_freqs[j];
int16_t distance = nf1 - nf2;
if( nf1 == 255 || nf2 == 255 ) continue;
if( distance < 0 ) distance = -distance;
//If it wraps around above the halfway point, then we're closer to it
//on the other side.
if( distance > ((1<<(SEMIBITSPERBIN-1))*FIXBPERO) )
{
distance = ((1<<(SEMIBITSPERBIN))*FIXBPERO) - distance;
}
if( distance > MAX_COMBINE_DISTANCE )
{
continue;
}
int into;
int from;
if( note_peak_amps[i] > note_peak_amps[j] )
{
into = i;
from = j;
}
else
{
into = j;
from = i;
}
//We need to combine the notes. We need to move the new note freq
//towards the stronger of the two notes.
int16_t amp1 = note_peak_amps[into];
int16_t amp2 = note_peak_amps[from];
//0 to 32768 porportional to how much of amp1 we want.
uint32_t porp = (amp1<<15) / (amp1+amp2);
uint16_t newnote = (nf1 * porp + nf2 * (32768-porp))>>15;
//When combining notes, we have to use the stronger amplitude note.
//trying to average or combine the power of the notes looks awful.
note_peak_freqs[into] = newnote;
note_peak_amps[into] = (note_peak_amps[into]>note_peak_amps[from])?
note_peak_amps[into]:note_peak_amps[j];
note_peak_amps2[into] = (note_peak_amps2[into]>note_peak_amps2[from])?
note_peak_amps2[into]:note_peak_amps2[j];
note_peak_freqs[from] = 255;
note_peak_amps[from] = 0;
note_jumped_to[from] = i;
}
//For al lof the notes that have not been hit, we have to allow them to
//to decay. We only do this for notes that have not found a peak.
for( i = 0; i < MAXNOTES; i++ )
{
if( note_peak_freqs[i] == 255 || hitnotes[i] ) continue;
note_peak_amps[i] -= note_peak_amps[i]>>AMP_1_IIR_BITS;
note_peak_amps2[i] -= note_peak_amps2[i]>>AMP_2_IIR_BITS;
//In the event a note is not strong enough anymore, it is to be
//returned back into the great pool of unused notes.
if( note_peak_amps[i] < MINIMUM_AMP_FOR_NOTE_TO_DISAPPEAR )
{
note_peak_freqs[i] = 255;
note_peak_amps[i] = 0;
note_peak_amps2[i] = 0;
}
}
//We now have notes!!!
#if 0
for( i = 0; i < MAXNOTES; i++ )
{
if( note_peak_freqs[i] == 255 ) continue;
printf( "(%3d %4d %4d) ", note_peak_freqs[i], note_peak_amps[i], note_peak_amps2[i] );
}
printf( "\n") ;
#endif
#if 0
for( i = 0; i < FIXBPERO; i++ )
{
printf( "%4d ", folded_bins[i] );
}
printf( "\n" );
#endif
}

View file

@ -0,0 +1,82 @@
#ifndef _EMBEDDEDNF_H
#define _EMBEDDEDNF_H
//Use a 32-bit DFT. It won't work for AVRs, but for any 32-bit systems where
//they can multiply quickly, this is the bees knees.
#define USE_32DFT
#ifndef DFREQ
#define DFREQ 8000
#endif
#define BASE_FREQ 55.0 // You may make this a float.
//The higher the number the slackier your FFT will be come.
#define FUZZ_IIR_BITS 1
//Notes are the individually identifiable notes we receive from the sound.
//We track up to this many at one time. Just because a note may appear to
//vaporize in one frame doesn't mean it is annihilated immediately.
#define MAXNOTES 12
//We take the raw signal off of the
#define FILTER_BLUR_PASSES 2
//Determines bit shifts for where notes lie. We represent notes with an
//uint8_t. We have to define all of the possible locations on the note line
//in this. note_frequency = 0..((1<<SEMIBITSPERBIN)*FIXBPERO-1)
#define SEMIBITSPERBIN 3
#define NOTERANGE ((1<<SEMIBITSPERBIN)*FIXBPERO)
//If there is detected note this far away from an established note, we will
//then consider this new note the same one as last time, and move the
//established note. This is also used when combining notes. It is this
//distance times two.
#define MAX_JUMP_DISTANCE 4
#define MAX_COMBINE_DISTANCE 7
//These control how quickly the IIR for the note strengths respond. AMP 1 is
//the response for the slow-response, or what we use to determine size of
//splotches, AMP 2 is the quick response, or what we use to see the visual
//strength of the notes.
#define AMP_1_IIR_BITS 5
#define AMP_2_IIR_BITS 2
//This is the amplitude, coming from folded_bins. If the value is below this
//it is considered a non-note.
#define MIN_AMP_FOR_NOTE 80
//If the strength of a note falls below this, the note will disappear, and be
//recycled back into the unused list of notes.
#define MINIMUM_AMP_FOR_NOTE_TO_DISAPPEAR 64
#ifdef USE_32DFT
#include "DFT32.h"
#else
#include "dft.h"
#endif
extern uint16_t folded_bins[]; //[FIXBPERO] <- The folded fourier output.
extern uint16_t fuzzed_bins[]; //[FIXBINS] <- The Full DFT after IIR, Blur and Taper
//frequency of note; Note if it is == 255, then it means it is not set. It is
//generally a value from
extern uint8_t note_peak_freqs[];
extern uint16_t note_peak_amps[]; //[MAXNOTES]
extern uint16_t note_peak_amps2[]; //[MAXNOTES] (Responds quicker)
extern uint8_t note_jumped_to[]; //[MAXNOTES] When a note combines into another one,
//this records where it went. I.e. if your note just disappeared, check this flag.
void Init();
void UpdateFreqs();
void HandleFrameInfo();
#endif

View file

@ -0,0 +1,333 @@
#include "embeddedout.h"
//uint8_t ledArray[NUM_LIN_LEDS]; //Points to which notes correspond to these LEDs
uint8_t ledOut[NUM_LIN_LEDS*3];
uint16_t ledSpin;
uint16_t ledAmpOut[NUM_LIN_LEDS];
uint8_t ledFreqOut[NUM_LIN_LEDS];
uint8_t ledFreqOutOld[NUM_LIN_LEDS];
void UpdateLinearLEDs()
{
//Source material:
/*
extern uint8_t note_peak_freqs[];
extern uint16_t note_peak_amps[]; //[MAXNOTES]
extern uint16_t note_peak_amps2[]; //[MAXNOTES] (Responds quicker)
extern uint8_t note_jumped_to[]; //[MAXNOTES] When a note combines into another one,
*/
//Goal: Make splotches of light that are porportional to the strength of notes.
//Color them according to value in note_peak_amps2.
uint8_t i;
int8_t k;
uint16_t j, l;
uint32_t total_size_all_notes = 0;
int32_t porpamps[MAXNOTES]; //LEDs for each corresponding note.
uint8_t sorted_note_map[MAXNOTES]; //mapping from which note into the array of notes from the rest of the system.
uint8_t sorted_map_count = 0;
uint32_t note_nerf_a = 0;
for( i = 0; i < MAXNOTES; i++ )
{
if( note_peak_freqs[i] == 255 ) continue;
note_nerf_a += note_peak_amps[i];
}
note_nerf_a = ((note_nerf_a * NERF_NOTE_PORP)>>8);
for( i = 0; i < MAXNOTES; i++ )
{
uint16_t ist = note_peak_amps[i];
uint8_t nff = note_peak_freqs[i];
if( nff == 255 )
{
continue;
}
if( ist < note_nerf_a )
{
continue;
}
#if SORT_NOTES
for( j = 0; j < sorted_map_count; j++ )
{
if( note_peak_freqs[ sorted_note_map[j] ] > nff )
{
break;
}
}
for( k = sorted_map_count; k > j; k-- )
{
sorted_note_map[k] = sorted_note_map[k-1];
}
sorted_note_map[j] = i;
#else
#endif
sorted_note_map[sorted_map_count] = i;
sorted_map_count++;
}
#if 0
for( i = 0; i < sorted_map_count; i++ )
{
printf( "%d: %d: %d /", sorted_note_map[i], note_peak_freqs[sorted_note_map[i]], note_peak_amps[sorted_note_map[i]] );
}
printf( "\n" );
#endif
uint16_t local_peak_amps[MAXNOTES];
uint16_t local_peak_amps2[MAXNOTES];
uint8_t local_peak_freq[MAXNOTES];
//Make a copy of all of the variables into local ones so we don't have to keep double-dereferencing.
for( i = 0; i < sorted_map_count; i++ )
{
//printf( "%5d ", local_peak_amps[i] );
local_peak_amps[i] = note_peak_amps[sorted_note_map[i]] - note_nerf_a;
local_peak_amps2[i] = note_peak_amps2[sorted_note_map[i]];
local_peak_freq[i] = note_peak_freqs[sorted_note_map[i]];
}
// printf( "\n" );
for( i = 0; i < sorted_map_count; i++ )
{
uint16_t ist = local_peak_amps[i];
porpamps[i] = 0;
total_size_all_notes += local_peak_amps[i];
}
if( total_size_all_notes == 0 )
{
for( j = 0; j < NUM_LIN_LEDS * 3; j++ )
{
ledOut[j] = 0;
}
return;
}
uint32_t porportional = (uint32_t)(NUM_LIN_LEDS<<8)/((uint32_t)total_size_all_notes);
uint16_t total_accounted_leds = 0;
for( i = 0; i < sorted_map_count; i++ )
{
porpamps[i] = (local_peak_amps[i] * porportional) >> 8;
total_accounted_leds += porpamps[i];
}
int16_t total_unaccounted_leds = NUM_LIN_LEDS - total_accounted_leds;
int addedlast = 1;
do
{
for( i = 0; i < sorted_map_count && total_unaccounted_leds; i++ )
{
porpamps[i]++; total_unaccounted_leds--;
addedlast = 1;
}
} while( addedlast && total_unaccounted_leds );
//Put the frequencies on a ring.
j = 0;
for( i = 0; i < sorted_map_count; i++ )
{
while( porpamps[i] > 0 )
{
ledFreqOut[j] = local_peak_freq[i];
ledAmpOut[j] = (local_peak_amps2[i]*NOTE_FINAL_AMP)>>8;
j++;
porpamps[i]--;
}
}
//This part totally can't run on an embedded system.
#if LIN_WRAPAROUND
uint16_t midx = 0;
uint32_t mqty = 100000000;
for( j = 0; j < NUM_LIN_LEDS; j++ )
{
uint32_t dqty;
uint16_t localj;
dqty = 0;
localj = j;
for( l = 0; l < NUM_LIN_LEDS; l++ )
{
int32_t d = (int32_t)ledFreqOut[localj] - (int32_t)ledFreqOutOld[l];
if( d < 0 ) d *= -1;
if( d > (NOTERANGE>>1) ) { d = NOTERANGE - d + 1; }
dqty += ( d * d );
localj++;
if( localj == NUM_LIN_LEDS ) localj = 0;
}
if( dqty < mqty )
{
mqty = dqty;
midx = j;
}
}
ledSpin = midx;
#else
ledSpin = 0;
#endif
j = ledSpin;
for( l = 0; l < NUM_LIN_LEDS; l++, j++ )
{
if( j >= NUM_LIN_LEDS ) j = 0;
ledFreqOutOld[l] = ledFreqOut[j];
uint16_t amp = ledAmpOut[j];
if( amp > 255 ) amp = 255;
uint32_t color = ECCtoHEX( ledFreqOut[j], 255, amp );
ledOut[l*3+0] = ( color >> 0 ) & 0xff;
ledOut[l*3+1] = ( color >> 8 ) & 0xff;
ledOut[l*3+2] = ( color >>16 ) & 0xff;
}
/* j = ledSpin;
for( i = 0; i < sorted_map_count; i++ )
{
while( porpamps[i] > 0 )
{
uint16_t amp = ((uint32_t)local_peak_amps2[i] * NOTE_FINAL_AMP) >> 8;
if( amp > 255 ) amp = 255;
uint32_t color = ECCtoHEX( local_peak_freq[i], 255, amp );
ledOut[j*3+0] = ( color >> 0 ) & 0xff;
ledOut[j*3+1] = ( color >> 8 ) & 0xff;
ledOut[j*3+2] = ( color >>16 ) & 0xff;
j++;
if( j == NUM_LIN_LEDS ) j = 0;
porpamps[i]--;
}
}*/
//Now, we use porpamps to march through the LEDs, coloring them.
/* j = 0;
for( i = 0; i < sorted_map_count; i++ )
{
while( porpamps[i] > 0 )
{
uint16_t amp = ((uint32_t)local_peak_amps2[i] * NOTE_FINAL_AMP) >> 8;
if( amp > 255 ) amp = 255;
uint32_t color = ECCtoHEX( local_peak_freq[i], 255, amp );
ledOut[j*3+0] = ( color >> 0 ) & 0xff;
ledOut[j*3+1] = ( color >> 8 ) & 0xff;
ledOut[j*3+2] = ( color >>16 ) & 0xff;
j++;
porpamps[i]--;
}
}*/
}
uint32_t ECCtoHEX( uint8_t note, uint8_t sat, uint8_t val )
{
uint16_t hue = 0;
uint16_t third = 65535/3;
uint16_t scalednote = note;
uint32_t renote = ((uint32_t)note * 65536) / NOTERANGE;
//Note is expected to be a vale from 0..(NOTERANGE-1)
//renote goes from 0 to the next one under 65536.
if( renote < third )
{
//Yellow to Red.
hue = (third - renote) >> 1;
}
else if( renote < (third<<1) )
{
//Red to Blue
hue = (third-renote);
}
else
{
//hue = ((((65535-renote)>>8) * (uint32_t)(third>>8)) >> 1) + (third<<1);
hue = (uint16_t)(((uint32_t)(65536-renote)<<16) / (third<<1)) + (third>>1); // ((((65535-renote)>>8) * (uint32_t)(third>>8)) >> 1) + (third<<1);
}
hue >>= 8;
return EHSVtoHEX( hue, sat, val );
}
uint32_t EHSVtoHEX( uint8_t hue, uint8_t sat, uint8_t val )
{
#define SIXTH1 43
#define SIXTH2 85
#define SIXTH3 128
#define SIXTH4 171
#define SIXTH5 213
uint16_t or = 0, og = 0, ob = 0;
hue -= SIXTH1; //Off by 60 degrees.
//TODO: There are colors that overlap here, consider
//tweaking this to make the best use of the colorspace.
if( hue < SIXTH1 ) //Ok: Yellow->Red.
{
or = 255;
og = 255 - ((uint16_t)hue * 255) / (SIXTH1);
}
else if( hue < SIXTH2 ) //Ok: Red->Purple
{
or = 255;
ob = (uint16_t)hue*255 / SIXTH1 - 255;
}
else if( hue < SIXTH3 ) //Ok: Purple->Blue
{
ob = 255;
or = ((SIXTH3-hue) * 255) / (SIXTH1);
}
else if( hue < SIXTH4 ) //Ok: Blue->Cyan
{
ob = 255;
og = (hue - SIXTH3)*255 / SIXTH1;
}
else if( hue < SIXTH5 ) //Ok: Cyan->Green.
{
og = 255;
ob = ((SIXTH5-hue)*255) / SIXTH1;
}
else //Green->Yellow
{
og = 255;
or = (hue - SIXTH5) * 255 / SIXTH1;
}
uint16_t rv = val;
if( rv > 128 ) rv++;
uint16_t rs = sat;
if( rs > 128 ) rs++;
//or, og, ob range from 0...255 now.
//Need to apply saturation and value.
or = (or * val)>>8;
og = (og * val)>>8;
ob = (ob * val)>>8;
//OR..OB == 0..65025
or = or * rs + 255 * (256-rs);
og = og * rs + 255 * (256-rs);
ob = ob * rs + 255 * (256-rs);
//printf( "__%d %d %d =-> %d\n", or, og, ob, rs );
or >>= 8;
og >>= 8;
ob >>= 8;
return or | (og<<8) | ((uint32_t)ob<<16);
}

View file

@ -0,0 +1,27 @@
#ifndef _EMBEDDEDOUT_H
#define _EMBEDDEDOUT_H
#include "embeddednf.h"
//Controls brightness
#define NOTE_FINAL_AMP 12 //Number from 0...255
//Controls, basically, the minimum size of the splotches.
#define NERF_NOTE_PORP 15 //value from 0 to 255
#define NUM_LIN_LEDS 296
#define LIN_WRAPAROUND 0 //Whether the output lights wrap around.
#define SORT_NOTES 0 //Whether the notes will be sorted.
extern uint8_t ledArray[];
extern uint8_t ledOut[]; //[NUM_LIN_LEDS*3]
void UpdateLinearLEDs();
uint32_t ECCtoHEX( uint8_t note, uint8_t sat, uint8_t val );
uint32_t EHSVtoHEX( uint8_t hue, uint8_t sat, uint8_t val ); //hue = 0..255 // TODO: TEST ME!!!
#endif