From 6d75249cb8d98e9f99a216318a07b08866783c04 Mon Sep 17 00:00:00 2001 From: cnlohr Date: Wed, 1 Apr 2015 00:21:01 -0400 Subject: [PATCH] The starts of Embedded ColorChord --- Makefile | 15 ++- OutputLinear.c | 4 +- OutputProminent.c | 90 ++++++++++++++ colorchord.exe | Bin 64000 -> 0 bytes default.conf | 13 +- dft.c | 310 ++++++++++++++++++++++++++++++++++------------ dft.h | 35 +++++- embeddedcc.c | 123 ++++++++++++++++++ main.c | 18 ++- quickwash.conf | 26 ++++ sound_pulse.c | 5 + 11 files changed, 545 insertions(+), 94 deletions(-) create mode 100644 OutputProminent.c delete mode 100755 colorchord.exe create mode 100644 embeddedcc.c create mode 100644 quickwash.conf diff --git a/Makefile b/Makefile index a1dce0a..c42a863 100644 --- a/Makefile +++ b/Makefile @@ -2,23 +2,30 @@ all : colorchord RAWDRAW:=DrawFunctions.o XDriver.o SOUND:=sound.o sound_alsa.o sound_pulse.o sound_null.o -OUTS := OutputVoronoi.o DisplayArray.o OutputLinear.o DisplayPie.o DisplayNetwork.o DisplayUSB2812.o DisplayDMX.o +OUTS := OutputVoronoi.o DisplayArray.o OutputLinear.o DisplayPie.o DisplayNetwork.o DisplayUSB2812.o DisplayDMX.o OutputProminent.o WINGCC:=i586-mingw32msvc-gcc -WINGCCFLAGS:= -O2 -ffast-math -Wl,--relax -Wl,--gc-sections -ffunction-sections -fdata-sections -s +WINGCCFLAGS:= -O2 -Wl,--relax -Wl,--gc-sections -ffunction-sections -fdata-sections -s WINLDFLAGS:=-lwinmm -lgdi32 -lws2_32 RAWDRAWLIBS:=-lX11 -lm -lpthread -lXinerama -lXext LDLIBS:=-lpthread -lasound -lm -lpulse-simple -lpulse -CFLAGS:=-g -Os -flto -Wall -ffast-math +CFLAGS:=-g -Os -flto -Wall EXTRALIBS:=-lusb-1.0 colorchord : os_generic.o main.o dft.o decompose.o filter.o color.o sort.o notefinder.o util.o outdrivers.o $(RAWDRAW) $(SOUND) $(OUTS) parameters.o chash.o gcc -o $@ $^ $(CFLAGS) $(LDLIBS) $(EXTRALIBS) $(RAWDRAWLIBS) +embeddedcc : os_generic.c embeddedcc.c dft.c + gcc -o $@ $^ $(CFLAGS) -DCCEMBEDDED $(LDFLAGS) $(EXTRALIBS) $(RAWDRAWLIBS) + +runembedded : embeddedcc + parec --format=u8 --rate=8000 --channels=1 --device=alsa_output.pci-0000_00_1b.0.analog-stereo.monitor | ./embeddedcc + + colorchord.exe : os_generic.c main.c dft.c decompose.c filter.c color.c sort.c notefinder.c util.c outdrivers.c DrawFunctions.c parameters.c chash.c WinDriver.c sound.c sound_null.c sound_win.c OutputVoronoi.c DisplayArray.c OutputLinear.c DisplayPie.c DisplayNetwork.c $(WINGCC) $(WINGCCFLAGS) -o $@ $^ $(WINLDFLAGS) clean : - rm -rf *.o *~ colorchord colorchord.exe + rm -rf *.o *~ colorchord colorchord.exe embeddedcc diff --git a/OutputLinear.c b/OutputLinear.c index 631db2c..6e3fbda 100644 --- a/OutputLinear.c +++ b/OutputLinear.c @@ -157,7 +157,9 @@ static void LEDUpdate(void * id, struct NoteFinder*nf) if( satQ > 1 ) satQ = 1; led->last_led_pos[i] = rledpos[ia]; led->last_led_amp[i] = sat; - int r = CCtoHEX( led->last_led_pos[i], 1.0, (led->steady_bright?sat:satQ) ); + float sendsat = (led->steady_bright?sat:satQ); + if( sendsat > 1 ) sendsat = 1; + int r = CCtoHEX( led->last_led_pos[i], 1.0, sendsat ); OutLEDs[i*3+0] = r & 0xff; OutLEDs[i*3+1] = (r>>8) & 0xff; diff --git a/OutputProminent.c b/OutputProminent.c new file mode 100644 index 0000000..aa5adcb --- /dev/null +++ b/OutputProminent.c @@ -0,0 +1,90 @@ +//Really basic driver, that just selects the most prominent color and washes all the LEDs with that. + +#include "outdrivers.h" +#include "notefinder.h" +#include +#include +#include "parameters.h" +#include +#include "color.h" +#include +#include +#include + +struct ProminentDriver +{ + int did_init; + int total_leds; + float satamp; +}; + +static void LEDUpdate(void * id, struct NoteFinder*nf) +{ + struct ProminentDriver * led = (struct ProminentDriver*)id; + + + //Step 1: Calculate the quantity of all the LEDs we'll want. + int totbins = nf->note_peaks;//nf->dists; + int i; + float selected_amp = 0; + float selected_note = 0; + +// if( totbins > led_bins ) totbins = led_bins; + + for( i = 0; i < totbins; i++ ) + { + float freq = nf->note_positions[i] / nf->freqbins; + float amp = nf->note_amplitudes2[i] * led->satamp; + if( amp > selected_amp ) + { + selected_amp = amp; + selected_note = freq; + } + } + + + + //Advance the LEDs to this position when outputting the values. + for( i = 0; i < led->total_leds; i++ ) + { + float sendsat = selected_amp; + if( sendsat > 1 ) sendsat = 1; + int r = CCtoHEX( selected_note, 1.0, sendsat ); + + OutLEDs[i*3+0] = r & 0xff; + OutLEDs[i*3+1] = (r>>8) & 0xff; + OutLEDs[i*3+2] = (r>>16) & 0xff; + } + +} + +static void LEDParams(void * id ) +{ + struct ProminentDriver * led = (struct ProminentDriver*)id; + + led->satamp = 2; RegisterValue( "satamp", PAFLOAT, &led->satamp, sizeof( led->satamp ) ); + led->total_leds = 4; RegisterValue( "leds", PAINT, &led->total_leds, sizeof( led->total_leds ) ); + + + printf( "Found Prominent for output. leds=%d\n", led->total_leds ); + +} + + +static struct DriverInstances * OutputProminent() +{ + struct DriverInstances * ret = malloc( sizeof( struct DriverInstances ) ); + memset( ret, 0, sizeof( struct DriverInstances ) ); + struct ProminentDriver * led = ret->id = malloc( sizeof( struct ProminentDriver ) ); + memset( led, 0, sizeof( struct ProminentDriver ) ); + + ret->Func = LEDUpdate; + ret->Params = LEDParams; + LEDParams( led ); + return ret; + +} + +REGISTER_OUT_DRIVER(OutputProminent); + + diff --git a/colorchord.exe b/colorchord.exe deleted file mode 100755 index f0899d3e9460e3719bfe4c618694a29f68d38927..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 64000 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 diff --git a/default.conf b/default.conf index f96c54e..502124b 100644 --- a/default.conf +++ b/default.conf @@ -17,7 +17,7 @@ buffer = 128 play = 0 rec = 1 channels = 2 -samplerate = 44100 +samplerate = 8000 wininput = 0 #Compiled version will default this. @@ -31,7 +31,7 @@ sourcename = alsa_output.pci-0000_00_1b.0.analog-stereo.monitor ################################## # How much to amplify the incoming signal. -amplify = 2.5 +amplify = 2.0 # What is the base note? I.e. the lowest note. # Note that it won't have very much impact until an octave up though! @@ -47,6 +47,13 @@ dft_q = 20.0000 dft_speedup = 1000.0000 octaves = 5 +# Should we use a progressive DFT? +# 0 = DFT Quick +# 1 = DFT Progressive +# 2 = DFT Progressive Integer +# 3 = DFT Progressive Integer Skippy +do_progressive_dft = 3 + filter_iter = 2 filter_strength = .5 @@ -63,7 +70,7 @@ note_attach_freq_iir = 0.3000 note_combine_distance = 0.5000 note_jumpability = 1.8000 note_minimum_new_distribution_value = 0.0200 -note_out_chop = 0.1000 +note_out_chop = 0.05000 #======================================================================= diff --git a/dft.c b/dft.c index c40ae19..bfa3d02 100644 --- a/dft.c +++ b/dft.c @@ -1,4 +1,3 @@ - #include "dft.h" #include #include @@ -6,6 +5,9 @@ #include #include + +#ifndef CCEMBEDDED + void DoDFT( float * outbins, float * frequencies, int bins, float * databuffer, int place_in_data_buffer, int size_of_data_buffer, float q ) { int i, j; @@ -88,10 +90,9 @@ static float * gbinqtys; static float * gbinqtyc; static float * phis; static float * gfrequencies; -static float * goutbins; static float * lastbins; static float * advances; - +static float * goutbins; static int gbins; static float gq; static float gspeedup; @@ -325,7 +326,7 @@ void DoDFTProgressiveInteger( float * outbins, float * frequencies, int bins, co - +#endif @@ -337,14 +338,6 @@ void DoDFTProgressiveInteger( float * outbins, float * frequencies, int bins, co ////////////////////////SKIPPY DFT -//Skippy DFT is a very ood one. - - - -#define OCTAVES 5 -#define FIXBPERO 24 -#define FIXBINS (FIXBPERO*OCTAVES) -#define BINCYCLE (1< +#include +#include + +int8_t Ssintable[512]; //Actually [sin][cos] pairs. + +int main() +{ + int i; + for( i = 0; i < 256; i++ ) + { + Ssintable[i*2+0] = (int8_t)((sinf( i / 256.0 * 6.283 ) * 127.0)); + Ssintable[i*2+1] = (int8_t)((cosf( i / 256.0 * 6.283 ) * 127.0)); + } + + printf( "const int8_t Ssintable[512] = {" ); + for( i = 0; i < 512; i++ ) + { + if( !(i & 0xf ) ) + { + printf( "\n\t" ); + } + printf( "%4d," ,Ssintable[i] ); + } + printf( "};\n" ); +} + */ + + + +uint16_t Sdatspace[FIXBINS*4]; //(advances,places,isses,icses) //For -static uint8_t Sdo_this_octave[BINCYCLE]; -static int16_t Saccum_octavebins[OCTAVES]; -static uint8_t Swhichoctaveplace; +uint8_t Sdo_this_octave[BINCYCLE]; +int16_t Saccum_octavebins[OCTAVES]; +uint8_t Swhichoctaveplace; +uint16_t embeddedbins[FIXBINS]; //This is updated every time the DFT hits the octavecount, or 1/32 updates. + +//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 HandleProgressiveIntSkippy( int8_t sample1 ) { @@ -390,27 +500,40 @@ void HandleProgressiveIntSkippy( int8_t sample1 ) { int16_t isps = Sdatspace[i*4+2]; int16_t ispc = Sdatspace[i*4+3]; - int16_t mux = ( (isps/256) * (isps/256)) + ((ispc/256) * (ispc/256)); // printf( "%d (%d %d)\n", mux, isps, ispc ); int octave = i / FIXBPERO; // mux >>= octave; - goutbins[i] = sqrt( mux ); -// goutbins[i]/=100.0; - goutbins[i]/=100*(1<>5; - Sdatspace[i*4+3] -= ispc>>5; - } +#ifndef CCEMBEDDED + uint32_t mux = ( (isps/256) * (isps/256)) + ((ispc/256) * (ispc/256)); + goutbins[i] = sqrt( mux ); + goutbins[i]/=25*(1<>= octave; + + Sdatspace[i*4+2] -= isps>>4; //XXX 4 is more responsive AND doesn't overflow as easily. + Sdatspace[i*4+3] -= ispc>>4; //XXX 4 is more responsive AND doesn't overflow as easily. + + //TRICKY: It is possible for the sin and cos accumulators to overflow, + //I DO NOT INTEND TO FIX THIS NOW! It could be easily fixed by using 32-bit integers, or + //by decreasing the quality a little bit, but it is an extreme case with a pure, full-volume sinewave. + } + return; } + for( i = 0; i < OCTAVES;i++ ) { Saccum_octavebins[i] += sample1; } uint16_t * ds = &Sdatspace[oct*FIXBPERO*4]; - int8_t * st; + const int8_t * st; sample1 = Saccum_octavebins[oct]>>(OCTAVES-oct); Saccum_octavebins[oct] = 0; @@ -436,13 +559,13 @@ void HandleProgressiveIntSkippy( int8_t sample1 ) //Add TS and TC to the datspace stuff. (24 instructions) tmp1 = (*ds); //Read out, sin component. 4 Accurate. // tmp1 -= tmp1>>4; //Subtract from the MSB (with carry) 2 -> 6 AS/IS: 7+7 = 14 - tmp1 += ts>>3; //Add MSBs with carry 2 -> 6 AS/IS: 6 + tmp1 += ts>>4; //Add MSBs with carry 2 -> 6 AS/IS: 6 *(ds++) = tmp1; //Store values back 4 tmp1 = *ds; //Read out, sin component. 4 // tmp1 -= tmp1>>4; //Subtract from the MSB (with carry) 2 -> 6 AS/IS: 7+7 = 14 - tmp1 += tc>>3; //Add MSBs with carry 2 -> 6 AS/IS: 6 + tmp1 += tc>>4; //Add MSBs with carry 2 -> 6 AS/IS: 6 *ds++ = tmp1; //Store values back 4 @@ -452,46 +575,82 @@ void HandleProgressiveIntSkippy( int8_t sample1 ) } } +void SetupDFTProgressiveIntegerSkippy() +{ + 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 < 256; i++ ) + { + Ssintable[i*2+0] = (int8_t)((sinf( i / 256.0 * 6.283 ) * 127.0)); + Ssintable[i*2+1] = (int8_t)((cosf( i / 256.0 * 6.283 ) * 127.0)); + } +*/ + 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< OCTAVES ) + { + fprintf( stderr, "Error: algorithm fault.\n" ); + exit( -1 ); + } + Sdo_this_octave[i] = OCTAVES-j-1; + } +} + +#ifndef CCEMBEDDED + +void UpdateBinsForProgressiveIntegerSkippy( const float * frequencies ) +{ + int i; + for( i = 0; i < FIXBINS; i++ ) + { + float freq = frequencies[(i%FIXBPERO) + (FIXBPERO*(OCTAVES-1))]; + Sdatspace[i*4] = (65536.0/freq);// / oneoveroctave; + } +} + +#endif + + +void UpdateBinsForProgressiveIntegerSkippyInt( const uint16_t * frequencies ) +{ + int i; + for( i = 0; i < FIXBINS; i++ ) + { + uint16_t freq = frequencies[i%FIXBPERO]; + Sdatspace[i*4] = freq;// / oneoveroctave; + } +} + +void Push8BitIntegerSkippy( int8_t dat ) +{ + HandleProgressiveIntSkippy( dat ); + HandleProgressiveIntSkippy( dat ); +} + + +#ifndef CCEMBEDDED + void DoDFTProgressiveIntegerSkippy( 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, j; + int i; static int last_place; -//printf( "SKIPPY\n" ); - - if( !Sdonefirstrun ) - { - memset( outbins, 0, bins * sizeof( float ) ); - goutbins = outbins; - //Sdatspace = malloc(FIXBPERO*OCTAVES*8); - //memset(Sdatspace,0,FIXBPERO*OCTAVES*8); - //printf( "MS: %d\n", FIXBPERO*OCTAVES*8); - Sdonefirstrun = 1; - for( i = 0; i < 256; i++ ) - { - Ssintable[i*2+0] = (int8_t)((sinf( i / 256.0 * 6.283 ) * 127.0)); - Ssintable[i*2+1] = (int8_t)((cosf( i / 256.0 * 6.283 ) * 127.0)); - } - - 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< OCTAVES ) - { - fprintf( stderr, "Error: algorithm fault.\n" ); - exit( -1 ); - } - Sdo_this_octave[i] = OCTAVES-j-1; - } - } + memset( outbins, 0, bins * sizeof( float ) ); + goutbins = outbins; memcpy( outbins, backupbins, FIXBINS*4 ); @@ -501,13 +660,16 @@ void DoDFTProgressiveIntegerSkippy( float * outbins, float * frequencies, int bi return; } - - for( i = 0; i < bins; i++ ) + +//printf( "SKIPPY\n" ); + + if( !Sdonefirstrun ) { - float freq = frequencies[(i%FIXBPERO) + (FIXBPERO*(OCTAVES-1))]; - Sdatspace[i*4] = (65536.0/freq);// / oneoveroctave; + SetupDFTProgressiveIntegerSkippy(); + Sdonefirstrun = 1; } + UpdateBinsForProgressiveIntegerSkippy( frequencies ); for( i = last_place; i != place_in_data_buffer; i = (i+1)%size_of_data_buffer ) { @@ -519,23 +681,9 @@ void DoDFTProgressiveIntegerSkippy( float * outbins, float * frequencies, int bi last_place = place_in_data_buffer; memcpy( backupbins, outbins, FIXBINS*4 ); - - //Extract bins. -/* - for( i = 0; i < bins; i++ ) - { - int16_t isps = Sdatspace[i*4+2]; - int16_t ispc = Sdatspace[i*4+3]; - int16_t mux = ( (isps/256) * (isps/256)) + ((ispc/256) * (ispc/256)); -// printf( "%d (%d %d)\n", mux, isps, ispc ); - outbins[i] = sqrt( mux )/100.0; - } -*/ - -// printf( "\n"); } - +#endif diff --git a/dft.h b/dft.h index 944b949..1589c22 100644 --- a/dft.h +++ b/dft.h @@ -1,10 +1,13 @@ #ifndef _DFT_H #define _DFT_H +#include + +//Warning: Most ColorChords are not available for ColorChord Embedded. +#ifndef CCEMBEDDED //There are several options here, the last few are selectable by modifying the do_progressive_dft flag. - //Do a DFT on a live audio ring buffer. It assumes new samples are added on in the + direction, older samples go negative. //Frequencies are as a function of the samplerate, for example, a frequency of 22050 is actually 2 Hz @ 44100 SPS //bins = number of frequencies to check against. @@ -22,10 +25,40 @@ void DoDFTProgressive( float * outbins, float * frequencies, int bins, const flo //This is fast enough to run on an ESP8266 void DoDFTProgressiveInteger( float * outbins, float * frequencies, int bins, const float * databuffer, int place_in_data_buffer, int size_of_data_buffer, float q, float speedup ); +#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 void DoDFTProgressiveIntegerSkippy( float * outbins, float * frequencies, int bins, const float * databuffer, int place_in_data_buffer, int size_of_data_buffer, float q, float speedup ); +//It's actually split into a few functions, which you can call on your own: +void SetupDFTProgressiveIntegerSkippy(); //Call at start. + +#ifndef CCEMBEDDED +void UpdateBinsForProgressiveIntegerSkippy( const float * frequencies ); //Update the frequencies +#endif + +void UpdateBinsForProgressiveIntegerSkippyInt( const uint16_t * frequencies ); +void Push8BitIntegerSkippy( int8_t dat ); //Call this to push on new frames of sound. + + +//You can # define these to be other things. +#ifndef OCTAVES +#define OCTAVES 5 +#endif + +#ifndef FIXBPERO +#define FIXBPERO 24 +#endif + +#define FIXBINS (FIXBPERO*OCTAVES) +#define BINCYCLE (1< + +#include "dft.h" +#define DFREQ 8000 +#define BASE_FREQ 55.0 + +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 }; + +#define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)])) + +/* The above table was generated using the following code: + +#include +#include + +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; +} +*/ + +void UpdateFreqs() +{ + uint16_t fbins[FIXBPERO]; + int i; + + BUILD_BUG_ON( sizeof(bf_table) != FIXBPERO*4 ); + + for( i = 0; i < FIXBPERO; i++ ) + { + float frq = ( bf_table[i] * BASE_FREQ ); + fbins[i] = ( 65536.0 ) / ( DFREQ ) * frq * 16; + } + + UpdateBinsForProgressiveIntegerSkippyInt( fbins ); +} + +void Init() +{ + //Step 1: Initialize the Integer DFT. + SetupDFTProgressiveIntegerSkippy(); + + //Step 2: Set up the frequency list. + UpdateFreqs(); +} + +void HandleFrameInfo() +{ + uint16_t folded_bins[FIXBPERO]; + + int i, j, k = 0; + + for( i = 0; i < FIXBPERO; i++ ) + folded_bins[i] = 0; + + for( j = 0; j < OCTAVES; j++ ) + { + for( i = 0; i < FIXBPERO; i++ ) + { + folded_bins[i] += embeddedbins[k++]; + } + } + + + + //XXX TODO Taper the first and last octaves. +// for( i = 0; i < freqbins; i++ ) +// { +// nf->outbins[i] *= (i+1.0)/nf->freqbins; +// } +// for( i = 0; i < freqbins; i++ ) +// { +// nf->outbins[freqs-i-1] *= (i+1.0)/nf->freqbins; +// } + + //We now have the system folded into one + + for( i = 0; i < FIXBPERO; i++ ) + { + printf( "%5d ", folded_bins[i] ); + } + printf( "\n" ); +} + +int main() +{ + int wf = 0; + int ci; + Init(); + while( ( ci = getchar() ) != EOF ) + { + int cs = ci - 0x80; + Push8BitIntegerSkippy( (int8_t)cs ); + //printf( "%d ", cs ); fflush( stdout ); + wf++; + if( wf == 64 ) + { + HandleFrameInfo(); + wf = 0; + } + } + return 0; +} + diff --git a/main.c b/main.c index 3664a82..57aae5d 100644 --- a/main.c +++ b/main.c @@ -44,7 +44,7 @@ int sample_channel = -1;REGISTER_PARAM( sample_channel, PAINT ); struct NoteFinder * nf; //Sound circular buffer -#define SOUNDCBSIZE 65536 +#define SOUNDCBSIZE 8096 #define MAX_CHANNELS 2 double VisTimeEnd, VisTimeStart; @@ -98,10 +98,15 @@ void SoundCB( float * out, float * in, int samplesr, int * samplesp, struct Soun for( j = 0; j < channelin; j++ ) { float f = in[i*channelin+j]; - if( f > -1 && f < 1 ) + if( f >= -1 && f <= 1 ) { fo += f; } + else + { + fo += (f>0)?1:-1; +// printf( "Sound fault A %d/%d %d/%d %f\n", j, channelin, i, samplesr, f ); + } } fo /= channelin; @@ -114,9 +119,13 @@ void SoundCB( float * out, float * in, int samplesr, int * samplesp, struct Soun float f = in[i*channelin+sample_channel]; if( f > -1 && f < 1 ) { - sound[soundhead] = f; - soundhead = (soundhead+1)%SOUNDCBSIZE; + f = (f>0)?1:-1; } + + //printf( "Sound fault B %d/%d\n", i, samplesr ); + sound[soundhead] = f; + soundhead = (soundhead+1)%SOUNDCBSIZE; + } } } @@ -374,6 +383,7 @@ int main(int argc, char ** argv) int thisy = sound[thissoundhead] * 128 + 128; thissoundhead = (thissoundhead-1+SOUNDCBSIZE)%SOUNDCBSIZE; for( i = 0; i < screenx; i++ ) { + if( thisy < 0 || thisy > 256 ) printf( "%d/%d\n", thisy,thissoundhead ); CNFGTackSegment( i, lasty, i+1, thisy ); lasty = thisy; thisy = sound[thissoundhead] * 128 + 128; thissoundhead = (thissoundhead-1+SOUNDCBSIZE)%SOUNDCBSIZE; diff --git a/quickwash.conf b/quickwash.conf new file mode 100644 index 0000000..ab598e8 --- /dev/null +++ b/quickwash.conf @@ -0,0 +1,26 @@ + +This is a vornoi thing: +outdrivers = DisplayArray, OutputProminent +lightx = 2 +lighty = 2 +leds = 4 +fromsides = 1 +shape_cutoff = 0.03 +satamp = 5.000 +amppow = 2.510 +distpow = 1.500 + +samplerate = 11025 +buffer = 64 + +sourcename = default + +amplify = 2.5 +note_attach_amp_iir = 0.9000 +note_attach_amp_iir2 = 0.550 +note_attach_freq_iir = 0.9000 +dft_iir = .6 +dft_q = 20.0000 +dft_speedup = 1000.0000 +note_jumpability = 1.0000 + diff --git a/sound_pulse.c b/sound_pulse.c index 513b53a..7033db8 100644 --- a/sound_pulse.c +++ b/sound_pulse.c @@ -267,6 +267,11 @@ void * InitSoundPulse( SoundCBType cb ) r->channelsRec = r->channelsPlay; r->sourceName = GetParameterS( "sourcename", NULL ); + if( strcmp( r->sourceName, "default" ) == 0 ) + { + r->sourceName = 0; + } + r->play = 0; r->rec = 0; r->buffer = GetParameterI( "buffer", 1024 );