Add a lot of documentation, etc. for ColorChord Embedded.
This commit is contained in:
parent
96002c5583
commit
ec4f175c2b
36
DFT32.c
36
DFT32.c
|
@ -87,13 +87,22 @@ int main()
|
|||
|
||||
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)
|
||||
|
||||
//For
|
||||
//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]; //This is updated every time the DFT hits the octavecount, or 1/32 updates.
|
||||
|
||||
//
|
||||
uint16_t embeddedbins[FIXBINS];
|
||||
|
||||
//From: http://stackoverflow.com/questions/1100090/looking-for-an-efficient-integer-square-root-algorithm-for-arm-thumb2
|
||||
/**
|
||||
|
@ -158,13 +167,22 @@ void UpdateOutputBins32()
|
|||
|
||||
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 );
|
||||
goutbins[i] /= (78<<DFTIIR)*(1<<octave); //reasonable (but arbitrary amplification)
|
||||
//reasonable (but arbitrary amplification)
|
||||
goutbins[i] /= (78<<DFTIIR)*(1<<octave);
|
||||
#endif
|
||||
|
||||
uint32_t rmux = ( (isps) * (isps)) + ((ispc) * (ispc));
|
||||
rmux = SquareRootRounded( rmux ) << 1; //bump it up so we don't lose a lot of detail at high freqs.
|
||||
|
||||
//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;
|
||||
}
|
||||
}
|
||||
|
@ -182,7 +200,8 @@ static void HandleInt( int16_t sample )
|
|||
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];
|
||||
|
||||
|
@ -229,9 +248,7 @@ int SetupDFTProgressive32()
|
|||
{
|
||||
int i;
|
||||
int j;
|
||||
//Sdatspace = malloc(FIXBPERO*OCTAVES*8);
|
||||
//memset(Sdatspace,0,FIXBPERO*OCTAVES*8);
|
||||
//printf( "MS: %d\n", FIXBPERO*OCTAVES*8);
|
||||
|
||||
Sdonefirstrun = 1;
|
||||
|
||||
for( i = 0; i < BINCYCLE; i++ )
|
||||
|
@ -310,9 +327,6 @@ void DoDFTProgressive32( float * outbins, float * frequencies, int bins, const f
|
|||
return;
|
||||
}
|
||||
|
||||
|
||||
//printf( "SKIPPY\n" );
|
||||
|
||||
if( !Sdonefirstrun )
|
||||
{
|
||||
SetupDFTProgressive32();
|
||||
|
|
16
DFT32.h
16
DFT32.h
|
@ -28,19 +28,22 @@
|
|||
#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.
|
||||
//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.
|
||||
//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 );
|
||||
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:
|
||||
|
@ -53,9 +56,12 @@ void UpdateBins32( const uint16_t * frequencies );
|
|||
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.
|
||||
|
|
|
@ -6,7 +6,6 @@ What is ColorChord?
|
|||
|
||||
Chromatic Sound to Light Conversion System. It's really that simple. Unlike so many of the sound responsive systems out there, ColorChord looks at the chromatic properties of the sound. It looks for notes, not ranges. If it hears an "E" it doesn't care what octave it's in, it's an E. This provides a good deal more interesting patterns between instruments and music than would be available otherwise.
|
||||
|
||||
|
||||
Background
|
||||
----------
|
||||
|
||||
|
@ -21,7 +20,13 @@ Here's a video of it running: https://www.youtube.com/watch?v=UI4eqOP2AU0
|
|||
Current State of Affairs
|
||||
------------------------
|
||||
|
||||
Currently, ColorChord 2 is designed to run on Linux. It's not particularly tied to an architecture, but does pretty much need a dedicated FPU to achieve any decent performance. Right now there aren't very many output options available for it. The most interesting one used for debugging is a vornoi-diagram-like thing called "DisplayShapeDriver."
|
||||
Currently, ColorChord 2 is designed to run on Linux or Windows. It's not particularly tied to an architecture, but does pretty much need a dedicated FPU to achieve any decent performance. Right now there aren't very many output options available for it. The most interesting one used for debugging is a vornoi-diagram-like thing called "DisplayShapeDriver."
|
||||
|
||||
ColorChord: Embedded
|
||||
--------------------
|
||||
|
||||
There is work on an embedded version of ColorChord, which avoids floating point operations anywhere in the output pipeline. Though I have made efforts to port it to AVRs, it doesn't seem feasable to operate on AVRs in the normal sense, so I have retargeted my efforts to 32-bit systems.
|
||||
|
||||
|
||||
Building and Using
|
||||
------------------
|
||||
|
|
|
@ -66,7 +66,7 @@ int main()
|
|||
if( wf == 64 )
|
||||
{
|
||||
NewFrame();
|
||||
wf = 0;
|
||||
wf = 0;
|
||||
}
|
||||
}
|
||||
return 0;
|
43
embeddednf.c
43
embeddednf.c
|
@ -140,7 +140,6 @@ void HandleFrameInfo()
|
|||
//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++ )
|
||||
{
|
||||
|
@ -222,13 +221,10 @@ void HandleFrameInfo()
|
|||
if( thisfreq > 255-(1<<SEMIBITSPERBIN) )
|
||||
thisfreq = (1<<SEMIBITSPERBIN)*FIXBPERO - (256-thisfreq);
|
||||
|
||||
// printf( "---%3d /(%2d) %4d/%4d/%4d---", thisfreq, i,prev, this, next );
|
||||
|
||||
//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;
|
||||
|
@ -255,6 +251,8 @@ void HandleFrameInfo()
|
|||
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;
|
||||
|
@ -267,11 +265,10 @@ void HandleFrameInfo()
|
|||
if( closest_note_distance <= MAX_JUMP_DISTANCE )
|
||||
{
|
||||
//We found the note we need to augment.
|
||||
//XXX: TODO: Should we be IIRing this?
|
||||
|
||||
note_peak_freqs[closest_note_id] = thisfreq;
|
||||
marked_note = closest_note_id;
|
||||
}
|
||||
|
||||
//The note was not found.
|
||||
else if( lowest_found_free_note != -1 )
|
||||
{
|
||||
|
@ -279,11 +276,22 @@ void HandleFrameInfo()
|
|||
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_NERFING_BITS) + (this>>(AMP_1_NERFING_BITS-3));
|
||||
note_peak_amps2[marked_note] = note_peak_amps2[marked_note] - (note_peak_amps2[marked_note]>>AMP_2_NERFING_BITS) + (this>>(AMP_2_NERFING_BITS-3));
|
||||
|
||||
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>>(AMP_2_IIR_BITS-3));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -310,6 +318,8 @@ void HandleFrameInfo()
|
|||
|
||||
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;
|
||||
|
@ -343,23 +353,30 @@ void HandleFrameInfo()
|
|||
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_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_NERFING_BITS;
|
||||
note_peak_amps2[i] -= note_peak_amps2[i]>>AMP_2_NERFING_BITS;
|
||||
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;
|
||||
|
|
29
embeddednf.h
29
embeddednf.h
|
@ -19,27 +19,36 @@
|
|||
//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)
|
||||
//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.
|
||||
//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 AMP_1_NERFING_BITS 5
|
||||
#define AMP_2_NERFING_BITS 3
|
||||
//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 3
|
||||
|
||||
//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 128
|
||||
|
||||
//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 100
|
||||
|
||||
|
||||
|
@ -53,16 +62,14 @@
|
|||
extern uint16_t folded_bins[]; //[FIXBPERO] <- The folded fourier output.
|
||||
extern uint16_t fuzzed_bins[]; //[FIXBINS] <- The Full DFT after IIR, Blur and Taper
|
||||
|
||||
//[MAXNOTES] <- frequency of note; Note if it is == 255, then it means it is not set.
|
||||
//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.
|
||||
|
||||
//XXX: TODO: Consider doing the fuzz IIR on the folded bins. That way we can
|
||||
//save several bytes of RAM on not having to keep fuzzed_bins around.
|
||||
|
||||
void Init();
|
||||
void UpdateFreqs();
|
||||
void HandleFrameInfo();
|
||||
|
|
|
@ -255,7 +255,6 @@ uint32_t ECCtoHEX( uint8_t note, uint8_t sat, uint8_t val )
|
|||
hue = (uint16_t)(((uint32_t)(65536-renote)<<16) / (third<<1)) + (third>>1); // ((((65535-renote)>>8) * (uint32_t)(third>>8)) >> 1) + (third<<1);
|
||||
}
|
||||
hue >>= 8;
|
||||
// printf( "%d;", hue );
|
||||
|
||||
return EHSVtoHEX( hue, sat, val );
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue