rearrange files some a few cleanups, too.
This commit is contained in:
parent
ec4f175c2b
commit
f80dd24a8d
9 changed files with 16 additions and 13 deletions
356
embeddedcommon/DFT32.c
Normal file
356
embeddedcommon/DFT32.c
Normal 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
75
embeddedcommon/DFT32.h
Normal 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
409
embeddedcommon/embeddednf.c
Normal 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
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
82
embeddedcommon/embeddednf.h
Normal file
82
embeddedcommon/embeddednf.h
Normal 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
|
||||
|
333
embeddedcommon/embeddedout.c
Normal file
333
embeddedcommon/embeddedout.c
Normal 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);
|
||||
}
|
||||
|
27
embeddedcommon/embeddedout.h
Normal file
27
embeddedcommon/embeddedout.h
Normal 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
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue