From 13d0f17b11c345ecefd8c47bc8a193fc59ade78c Mon Sep 17 00:00:00 2001 From: cnlohr Date: Tue, 6 Jan 2015 22:51:39 -0500 Subject: [PATCH] add a bunch of files --- DisplayOUTDriver.c | 169 +++++++++++++++++ DisplayShapeDriver.c | 171 +++++++++++++++++ DrawFunctions.c | 273 +++++++++++++++++++++++++++ DrawFunctions.h | 54 ++++++ LEDOUTDriver.c | 197 +++++++++++++++++++ Makefile | 16 ++ TODO | 7 + WinDriver.c | 226 ++++++++++++++++++++++ XDriver.c | 258 +++++++++++++++++++++++++ chash.c | 439 +++++++++++++++++++++++++++++++++++++++++++ chash.h | 53 ++++++ color.c | 105 +++++++++++ color.h | 12 ++ decompose.c | 215 +++++++++++++++++++++ decompose.h | 12 ++ default.conf | 19 ++ dft.c | 75 ++++++++ dft.h | 16 ++ filter.c | 57 ++++++ filter.h | 11 ++ main.c | 309 ++++++++++++++++++++++++++++++ notefinder.c | 350 ++++++++++++++++++++++++++++++++++ notefinder.h | 93 +++++++++ os_generic.c | 342 +++++++++++++++++++++++++++++++++ os_generic.h | 82 ++++++++ outdrivers.c | 70 +++++++ outdrivers.h | 35 ++++ parameters.c | 290 ++++++++++++++++++++++++++++ parameters.h | 52 +++++ sort.c | 59 ++++++ sort.h | 16 ++ sound.c | 106 +++++++++++ sound.h | 36 ++++ sound_alsa.c | 339 +++++++++++++++++++++++++++++++++ sound_null.c | 44 +++++ sound_pulse.c | 372 ++++++++++++++++++++++++++++++++++++ util.c | 58 ++++++ util.h | 10 + 38 files changed, 5048 insertions(+) create mode 100644 DisplayOUTDriver.c create mode 100644 DisplayShapeDriver.c create mode 100644 DrawFunctions.c create mode 100644 DrawFunctions.h create mode 100644 LEDOUTDriver.c create mode 100644 Makefile create mode 100644 TODO create mode 100644 WinDriver.c create mode 100644 XDriver.c create mode 100644 chash.c create mode 100644 chash.h create mode 100644 color.c create mode 100644 color.h create mode 100644 decompose.c create mode 100644 decompose.h create mode 100644 default.conf create mode 100644 dft.c create mode 100644 dft.h create mode 100644 filter.c create mode 100644 filter.h create mode 100644 main.c create mode 100644 notefinder.c create mode 100644 notefinder.h create mode 100644 os_generic.c create mode 100644 os_generic.h create mode 100644 outdrivers.c create mode 100644 outdrivers.h create mode 100644 parameters.c create mode 100644 parameters.h create mode 100644 sort.c create mode 100644 sort.h create mode 100644 sound.c create mode 100644 sound.h create mode 100644 sound_alsa.c create mode 100644 sound_null.c create mode 100644 sound_pulse.c create mode 100644 util.c create mode 100644 util.h diff --git a/DisplayOUTDriver.c b/DisplayOUTDriver.c new file mode 100644 index 0000000..973b6ca --- /dev/null +++ b/DisplayOUTDriver.c @@ -0,0 +1,169 @@ +#include "outdrivers.h" +#include "notefinder.h" +#include +#include "parameters.h" +#include +#include +#include +#include "color.h" +#include "DrawFunctions.h" + +//Uses: note_amplitudes2[note] for how many lights to use. +//Uses: note_amplitudes_out[note] for how bright it should be. + +#define MAX_LEDS_PER_NOTE 512 + +extern short screenx, screeny; + +struct LINote +{ + float ledexp; + float lednrm; //How many LEDs should we have? + int nrleds; //How many LEDs do we have? + int ledids[MAX_LEDS_PER_NOTE]; +}; + +struct DPODriver +{ + int xn; + int yn; + float cutoff; + float satamp; + float pow; + + int note_peaks; + struct LINote * notes; + int numallocleds; + int unallocleds[MAX_LEDS_PER_NOTE]; +}; + +int GetAnLED( struct DPODriver * d ) +{ + if( d->numallocleds ) + { + return d->unallocleds[--d->numallocleds]; + } + + int i; + //Okay, now we have to go search for one. + for( i = 0; i < d->note_peaks; i++ ) + { + struct LINote * n = &d->notes[i]; + if( n->lednrm < n->nrleds && n->nrleds > 0 ) + { + return n->ledids[--n->nrleds]; + } + } + return -1; +} + +static void DPOUpdate(void * id, struct NoteFinder*nf) +{ + int i; + struct DPODriver * d = (struct DPODriver*)id; + + int tleds = d->xn * d->yn; + + if( d->note_peaks != nf->note_peaks ) + { + d->note_peaks = nf->note_peaks; + if( d->notes ) free( d->notes ); + d->notes = malloc( sizeof( struct LINote ) * nf->note_peaks ); + memset( d->notes, 0, sizeof( struct LINote ) * nf->note_peaks ); + + d->numallocleds = tleds; + for( i = 0; i < d->numallocleds; i++ ) + d->unallocleds[i] = i; + + } + + + float totalexp = 0; + + for( i = 0; i < d->note_peaks; i++ ) + { + struct LINote * l = &d->notes[i]; + l->ledexp = pow( nf->note_amplitudes2[i], d->pow ) - d->cutoff; + if( l->ledexp < 0 ) l->ledexp = 0; + totalexp += l->ledexp; + } + + for( i = 0; i < d->note_peaks; i++ ) + { + struct LINote * l = &d->notes[i]; + l->lednrm = l->ledexp * tleds / totalexp; + } + + //Relinquish LEDs + for( i = 0; i < d->note_peaks; i++ ) + { + struct LINote * l = &d->notes[i]; + while( l->lednrm < l->nrleds && l->nrleds > 0 ) + { + d->unallocleds[d->numallocleds++] = l->ledids[--l->nrleds]; + } + } + + //Add LEDs + for( i = 0; i < d->note_peaks; i++ ) + { + struct LINote * l = &d->notes[i]; + while( l->lednrm > l->nrleds ) + { + int led = GetAnLED( d ); + if( led < 0 ) break; + l->ledids[l->nrleds++] = led; + } + } + + float cw = ((float)screenx) / d->xn; + float ch = ((float)screeny) / d->yn; + + for( i = 0; i < d->note_peaks; i++ ) + { + struct LINote * l = &d->notes[i]; + int j; + float sat = nf->note_amplitudes_out[i] * d->satamp; + if( sat > 1 ) sat = 1; + CNFGDialogColor = CCtoHEX( nf->note_positions[i] / nf->freqbins, 1.0, sat ); + for( j = 0; j < l->nrleds; j++ ) + { + int id = l->ledids[j]; + float x = (id % d->xn) * cw; + float y = (id / d->xn) * ch; + + CNFGDrawBox( x, y, x+cw, y+ch ); + } + } +} + +static void DPOParams(void * id ) +{ + struct DPODriver * d = (struct DPODriver*)id; + d->xn = GetParameterI( "lightx", 16 ); + d->yn = GetParameterI( "lighty", 9 ); + d->cutoff = GetParameterF( "cutoff", .05 ); + d->satamp = GetParameterF( "satamp", 3 ); + d->pow = GetParameterF( "pow", 2.1 ); + d->note_peaks = 0; + if( d->xn * d->yn >= MAX_LEDS_PER_NOTE ) + { + fprintf( stderr, "ERROR: Cannot have more lights than %d\n", MAX_LEDS_PER_NOTE ); + exit (-1); + } +} + +static struct DriverInstances * DisplayOutDriver(const char * parameters) +{ + struct DriverInstances * ret = malloc( sizeof( struct DriverInstances ) ); + struct DPODriver * d = ret->id = malloc( sizeof( struct DPODriver ) ); + memset( d, 0, sizeof( struct DPODriver ) ); + ret->Func = DPOUpdate; + ret->Params = DPOParams; + DPOParams( d ); + return ret; +} + +REGISTER_OUT_DRIVER(DisplayOutDriver); + + diff --git a/DisplayShapeDriver.c b/DisplayShapeDriver.c new file mode 100644 index 0000000..a5a5e16 --- /dev/null +++ b/DisplayShapeDriver.c @@ -0,0 +1,171 @@ +#include "outdrivers.h" +#include "notefinder.h" +#include +#include "parameters.h" +#include +#include +#include "color.h" +#include +#include "DrawFunctions.h" + +//Uses: note_amplitudes2[note] for how many lights to use. +//Uses: note_amplitudes_out[note] for how bright it should be. + +#define MAX_LEDS_PER_NOTE 512 + +extern short screenx, screeny; + +struct LINote +{ + float x, y; //In screen space. + float ledexp; + float lednrm; //How many LEDs should we have? +}; + +struct DPODriver +{ + int xn; + int yn; + float cutoff; + float satamp; + float amppow; //For amplitudes + float distpow; //for distances + int note_peaks; + int from_sides; + + struct LINote * notes; +}; + +static void DPOUpdate(void * id, struct NoteFinder*nf) +{ + int i; + struct DPODriver * d = (struct DPODriver*)id; + + int tleds = d->xn * d->yn; + + if( d->note_peaks != nf->note_peaks ) + { + d->note_peaks = nf->note_peaks; + if( d->notes ) free( d->notes ); + d->notes = malloc( sizeof( struct LINote ) * nf->note_peaks ); + memset( d->notes, 0, sizeof( struct LINote ) * nf->note_peaks ); + } + + + float totalexp = 0; + + for( i = 0; i < d->note_peaks; i++ ) + { + struct LINote * l = &d->notes[i]; + l->ledexp = pow( nf->note_amplitudes2[i], d->amppow ) - d->cutoff; + if( l->ledexp < 0 ) l->ledexp = 0; + totalexp += l->ledexp; + if( d->from_sides ) + { + float angle = nf->note_positions[i] / nf->freqbins * 6.28318; +// float angle = nf->enduring_note_id[i]; + float cx = screenx/2; + float cy = screeny/2; + float tx = sin( angle ) * cx + cx; + float ty = cos( angle ) * cy + cy; + l->x = l->x * .9 + tx * .1; + l->y = l->y * .9 + ty * .1; + if( nf->enduring_note_id[i] == 0 ) + { + l->x = cx; + l->y = cy; + } + } + else + { + srand( nf->enduring_note_id[i] ); + l->x = rand()%screenx; + l->y = rand()%screenx; + } + } + + for( i = 0; i < d->note_peaks; i++ ) + { + struct LINote * l = &d->notes[i]; + l->lednrm = l->ledexp * tleds / totalexp; + } + + + + float cw = ((float)screenx) / d->xn; + float ch = ((float)screeny) / d->yn; + + int x, y; + for( y = 0; y < d->yn; y++ ) + for( x = 0; x < d->xn; x++ ) + { + float lx = (x+.5) * cw; + float ly = (y+.5) * ch; + float kx = (x) * cw; + float ky = (y) * ch; + + int bestmatch = -1; + float bestmatchval = 0; + for( i = 0; i < d->note_peaks; i++ ) + { + struct LINote * l = &d->notes[i]; + float distsq = (lx-l->x)*(lx-l->x)+(ly-l->y)*(ly-l->y); + float dist; + if( d->distpow == 1.0 ) + dist = distsq; + else if( d->distpow == 2.0 ) + dist = sqrtf(distsq); + else + dist = powf(distsq,1.0); + + float match = l->ledexp / dist; + if( match > bestmatchval ) + { + bestmatch = i; + bestmatchval = match; + } + } + + if( bestmatch != -1 ) + { + float sat = nf->note_amplitudes_out[bestmatch] * d->satamp; + if( sat > 1.0 ) sat = 1.0; + CNFGColor ( CCtoHEX( nf->note_positions[bestmatch] / nf->freqbins, 1.0, sat ) ); + } + else + { + CNFGColor ( 0 ); + } + CNFGTackRectangle( kx, ky, kx+cw, ky+ch ); + + } + CNFGColor ( 0xffffff ); +} + +static void DPOParams(void * id ) +{ + struct DPODriver * d = (struct DPODriver*)id; + d->xn = GetParameterI( "lightx", 160 ); + d->yn = GetParameterI( "lighty", 90 ); + d->cutoff = GetParameterF( "cutoff", .01 ); + d->satamp = GetParameterF( "satamp", 5 ); + d->amppow = GetParameterF( "amppow", 2.51 ); + d->distpow = GetParameterF( "distpow", 1.5 ); + d->from_sides = GetParameterI( "fromsides", 1 ); //If 0, will make sources random locations. + d->note_peaks = 0; +} + +static struct DriverInstances * DisplayShapeDriver(const char * parameters) +{ + struct DriverInstances * ret = malloc( sizeof( struct DriverInstances ) ); + struct DPODriver * d = ret->id = malloc( sizeof( struct DPODriver ) ); + memset( d, 0, sizeof( struct DPODriver ) ); + ret->Func = DPOUpdate; + ret->Params = DPOParams; + DPOParams( d ); + return ret; +} + +REGISTER_OUT_DRIVER(DisplayShapeDriver); + + diff --git a/DrawFunctions.c b/DrawFunctions.c new file mode 100644 index 0000000..c7c994a --- /dev/null +++ b/DrawFunctions.c @@ -0,0 +1,273 @@ +/* +Copyright (c) 2010, 2011 Charles Lohr + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include "DrawFunctions.h" +#include + +int CNFGPenX, CNFGPenY; +uint32_t CNFGBGColor; +uint32_t CNFGLastColor; +uint32_t CNFGDialogColor; //background for boxes + +const unsigned short FontCharMap[256] = { + 65535, 0, 10, 20, 32, 44, 56, 68, 70, 65535, 65535, 80, 92, 65535, 104, 114, + 126, 132, 138, 148, 156, 166, 180, 188, 200, 206, 212, 218, 224, 228, 238, 244, + 65535, 250, 254, 258, 266, 278, 288, 302, 304, 310, 316, 324, 328, 226, 252, 330, + 332, 342, 348, 358, 366, 372, 382, 392, 400, 410, 420, 424, 428, 262, 432, 436, + 446, 460, 470, 486, 496, 508, 516, 522, 536, 542, 548, 556, 568, 572, 580, 586, + 598, 608, 622, 634, 644, 648, 654, 662, 670, 682, 692, 700, 706, 708, 492, 198, + 714, 716, 726, 734, 742, 750, 760, 768, 782, 790, 794, 802, 204, 810, 820, 384, + 828, 836, 844, 850, 860, 864, 872, 880, 890, 894, 902, 908, 916, 920, 928, 934, + 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, + 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, + 944, 948, 960, 970, 986, 996, 1000, 1016, 1020, 1026, 1034, 1042, 364, 1046, 1056, 1058, + 1066, 1072, 1080, 932, 1092, 1100, 68, 1110, 1114, 1116, 1124, 1132, 1142, 1154, 1170, 1180, + 1192, 1204, 1218, 1234, 1248, 1264, 1276, 1290, 1300, 1310, 1322, 1334, 1342, 1350, 1360, 1370, + 1384, 1396, 1406, 1416, 1428, 1442, 1454, 1458, 1472, 1480, 1488, 1498, 1508, 1520, 1530, 1544, + 1556, 1568, 1582, 1598, 1612, 1628, 1642, 1654, 1666, 1678, 1692, 1706, 1710, 1714, 1720, 1726, + 1738, 1752, 1762, 1772, 1784, 1798, 1810, 1816, 1826, 1836, 1846, 1858, 1870, 1880, 1890, 65535, }; + +const unsigned char FontCharData[1902] = { + 0x00, 0x01, 0x20, 0x21, 0x03, 0x23, 0x23, 0x14, 0x14, 0x83, 0x00, 0x01, 0x20, 0x21, 0x04, 0x24, + 0x24, 0x13, 0x13, 0x84, 0x01, 0x21, 0x21, 0x23, 0x23, 0x14, 0x14, 0x03, 0x03, 0x01, 0x11, 0x92, + 0x11, 0x22, 0x22, 0x23, 0x23, 0x14, 0x14, 0x03, 0x03, 0x02, 0x02, 0x91, 0x01, 0x21, 0x21, 0x23, + 0x23, 0x01, 0x03, 0x21, 0x03, 0x01, 0x12, 0x94, 0x03, 0x23, 0x13, 0x14, 0x23, 0x22, 0x22, 0x11, + 0x11, 0x02, 0x02, 0x83, 0x12, 0x92, 0x12, 0x12, 0x01, 0x21, 0x21, 0x23, 0x23, 0x03, 0x03, 0x81, + 0x03, 0x21, 0x21, 0x22, 0x21, 0x11, 0x03, 0x14, 0x14, 0x23, 0x23, 0x92, 0x01, 0x10, 0x10, 0x21, + 0x21, 0x12, 0x12, 0x01, 0x12, 0x14, 0x03, 0xa3, 0x02, 0x03, 0x03, 0x13, 0x02, 0x12, 0x13, 0x10, + 0x10, 0xa1, 0x01, 0x23, 0x03, 0x21, 0x02, 0x11, 0x11, 0x22, 0x22, 0x13, 0x13, 0x82, 0x00, 0x22, + 0x22, 0x04, 0x04, 0x80, 0x20, 0x02, 0x02, 0x24, 0x24, 0xa0, 0x01, 0x10, 0x10, 0x21, 0x10, 0x14, + 0x14, 0x03, 0x14, 0xa3, 0x00, 0x03, 0x04, 0x04, 0x20, 0x23, 0x24, 0xa4, 0x00, 0x20, 0x00, 0x02, + 0x02, 0x22, 0x10, 0x14, 0x20, 0xa4, 0x01, 0x21, 0x21, 0x23, 0x23, 0x03, 0x03, 0x01, 0x20, 0x10, + 0x10, 0x14, 0x14, 0x84, 0x03, 0x23, 0x23, 0x24, 0x24, 0x04, 0x04, 0x83, 0x01, 0x10, 0x10, 0x21, + 0x10, 0x14, 0x14, 0x03, 0x14, 0x23, 0x04, 0xa4, 0x01, 0x10, 0x21, 0x10, 0x10, 0x94, 0x03, 0x14, + 0x23, 0x14, 0x10, 0x94, 0x02, 0x22, 0x22, 0x11, 0x22, 0x93, 0x02, 0x22, 0x02, 0x11, 0x02, 0x93, + 0x01, 0x02, 0x02, 0xa2, 0x02, 0x22, 0x22, 0x11, 0x11, 0x02, 0x02, 0x13, 0x13, 0xa2, 0x11, 0x22, + 0x22, 0x02, 0x02, 0x91, 0x02, 0x13, 0x13, 0x22, 0x22, 0x82, 0x10, 0x13, 0x14, 0x94, 0x10, 0x01, + 0x20, 0x91, 0x10, 0x14, 0x20, 0x24, 0x01, 0x21, 0x03, 0xa3, 0x21, 0x10, 0x10, 0x01, 0x01, 0x23, + 0x23, 0x14, 0x14, 0x03, 0x10, 0x94, 0x00, 0x01, 0x23, 0x24, 0x04, 0x03, 0x03, 0x21, 0x21, 0xa0, + 0x21, 0x10, 0x10, 0x01, 0x01, 0x12, 0x12, 0x03, 0x03, 0x14, 0x14, 0x23, 0x02, 0xa4, 0x10, 0x91, + 0x10, 0x01, 0x01, 0x03, 0x03, 0x94, 0x10, 0x21, 0x21, 0x23, 0x23, 0x94, 0x01, 0x23, 0x11, 0x13, + 0x21, 0x03, 0x02, 0xa2, 0x02, 0x22, 0x11, 0x93, 0x31, 0xc0, 0x03, 0xa1, 0x00, 0x20, 0x20, 0x24, + 0x24, 0x04, 0x04, 0x00, 0x12, 0x92, 0x01, 0x10, 0x10, 0x14, 0x04, 0xa4, 0x01, 0x10, 0x10, 0x21, + 0x21, 0x22, 0x22, 0x04, 0x04, 0xa4, 0x00, 0x20, 0x20, 0x24, 0x24, 0x04, 0x12, 0xa2, 0x00, 0x02, + 0x02, 0x22, 0x20, 0xa4, 0x20, 0x00, 0x00, 0x02, 0x02, 0x22, 0x22, 0x24, 0x24, 0x84, 0x20, 0x02, + 0x02, 0x22, 0x22, 0x24, 0x24, 0x04, 0x04, 0x82, 0x00, 0x20, 0x20, 0x21, 0x21, 0x12, 0x12, 0x94, + 0x00, 0x04, 0x00, 0x20, 0x20, 0x24, 0x04, 0x24, 0x02, 0xa2, 0x00, 0x02, 0x02, 0x22, 0x22, 0x20, + 0x20, 0x00, 0x22, 0x84, 0x11, 0x11, 0x13, 0x93, 0x11, 0x11, 0x13, 0x84, 0x20, 0x02, 0x02, 0xa4, + 0x00, 0x22, 0x22, 0x84, 0x01, 0x10, 0x10, 0x21, 0x21, 0x12, 0x12, 0x13, 0x14, 0x94, 0x21, 0x01, + 0x01, 0x04, 0x04, 0x24, 0x24, 0x22, 0x22, 0x12, 0x12, 0x13, 0x13, 0xa3, 0x04, 0x01, 0x01, 0x10, + 0x10, 0x21, 0x21, 0x24, 0x02, 0xa2, 0x00, 0x04, 0x04, 0x14, 0x14, 0x23, 0x23, 0x12, 0x12, 0x02, + 0x12, 0x21, 0x21, 0x10, 0x10, 0x80, 0x23, 0x14, 0x14, 0x03, 0x03, 0x01, 0x01, 0x10, 0x10, 0xa1, + 0x00, 0x10, 0x10, 0x21, 0x21, 0x23, 0x23, 0x14, 0x14, 0x04, 0x04, 0x80, 0x00, 0x04, 0x04, 0x24, + 0x00, 0x20, 0x02, 0x92, 0x00, 0x04, 0x00, 0x20, 0x02, 0x92, 0x21, 0x10, 0x10, 0x01, 0x01, 0x03, + 0x03, 0x14, 0x14, 0x23, 0x23, 0x22, 0x22, 0x92, 0x00, 0x04, 0x20, 0x24, 0x02, 0xa2, 0x00, 0x20, + 0x10, 0x14, 0x04, 0xa4, 0x00, 0x20, 0x20, 0x23, 0x23, 0x14, 0x14, 0x83, 0x00, 0x04, 0x02, 0x12, + 0x12, 0x21, 0x21, 0x20, 0x12, 0x23, 0x23, 0xa4, 0x00, 0x04, 0x04, 0xa4, 0x04, 0x00, 0x00, 0x11, + 0x11, 0x20, 0x20, 0xa4, 0x04, 0x00, 0x00, 0x22, 0x20, 0xa4, 0x01, 0x10, 0x10, 0x21, 0x21, 0x23, + 0x23, 0x14, 0x14, 0x03, 0x03, 0x81, 0x00, 0x04, 0x00, 0x10, 0x10, 0x21, 0x21, 0x12, 0x12, 0x82, + 0x01, 0x10, 0x10, 0x21, 0x21, 0x23, 0x23, 0x14, 0x14, 0x03, 0x03, 0x01, 0x04, 0x93, 0x00, 0x04, + 0x00, 0x10, 0x10, 0x21, 0x21, 0x12, 0x12, 0x02, 0x02, 0xa4, 0x21, 0x10, 0x10, 0x01, 0x01, 0x23, + 0x23, 0x14, 0x14, 0x83, 0x00, 0x20, 0x10, 0x94, 0x00, 0x04, 0x04, 0x24, 0x24, 0xa0, 0x00, 0x03, + 0x03, 0x14, 0x14, 0x23, 0x23, 0xa0, 0x00, 0x04, 0x04, 0x24, 0x14, 0x13, 0x24, 0xa0, 0x00, 0x01, + 0x01, 0x23, 0x23, 0x24, 0x04, 0x03, 0x03, 0x21, 0x21, 0xa0, 0x00, 0x01, 0x01, 0x12, 0x12, 0x14, + 0x12, 0x21, 0x21, 0xa0, 0x00, 0x20, 0x20, 0x02, 0x02, 0x04, 0x04, 0xa4, 0x10, 0x00, 0x00, 0x04, + 0x04, 0x94, 0x01, 0xa3, 0x10, 0x20, 0x20, 0x24, 0x24, 0x94, 0x00, 0x91, 0x02, 0x04, 0x04, 0x24, + 0x24, 0x22, 0x23, 0x12, 0x12, 0x82, 0x00, 0x04, 0x04, 0x24, 0x24, 0x22, 0x22, 0x82, 0x24, 0x04, + 0x04, 0x03, 0x03, 0x12, 0x12, 0xa2, 0x20, 0x24, 0x24, 0x04, 0x04, 0x02, 0x02, 0xa2, 0x24, 0x04, + 0x04, 0x02, 0x02, 0x22, 0x22, 0x23, 0x23, 0x93, 0x04, 0x01, 0x02, 0x12, 0x01, 0x10, 0x10, 0xa1, + 0x23, 0x12, 0x12, 0x03, 0x03, 0x14, 0x14, 0x23, 0x23, 0x24, 0x24, 0x15, 0x15, 0x84, 0x00, 0x04, + 0x03, 0x12, 0x12, 0x23, 0x23, 0xa4, 0x11, 0x11, 0x12, 0x94, 0x22, 0x22, 0x23, 0x24, 0x24, 0x15, + 0x15, 0x84, 0x00, 0x04, 0x03, 0x13, 0x13, 0x22, 0x13, 0xa4, 0x02, 0x04, 0x02, 0x13, 0x12, 0x14, + 0x12, 0x23, 0x23, 0xa4, 0x02, 0x04, 0x03, 0x12, 0x12, 0x23, 0x23, 0xa4, 0x02, 0x05, 0x04, 0x24, + 0x24, 0x22, 0x22, 0x82, 0x02, 0x04, 0x04, 0x24, 0x25, 0x22, 0x22, 0x82, 0x02, 0x04, 0x03, 0x12, + 0x12, 0xa2, 0x22, 0x02, 0x02, 0x03, 0x03, 0x23, 0x23, 0x24, 0x24, 0x84, 0x11, 0x14, 0x02, 0xa2, + 0x02, 0x04, 0x04, 0x14, 0x14, 0x23, 0x24, 0xa2, 0x02, 0x03, 0x03, 0x14, 0x14, 0x23, 0x23, 0xa2, + 0x02, 0x03, 0x03, 0x14, 0x14, 0x12, 0x13, 0x24, 0x24, 0xa2, 0x02, 0x24, 0x04, 0xa2, 0x02, 0x03, + 0x03, 0x14, 0x22, 0x23, 0x23, 0x85, 0x02, 0x22, 0x22, 0x04, 0x04, 0xa4, 0x20, 0x10, 0x10, 0x14, + 0x14, 0x24, 0x12, 0x82, 0x10, 0x11, 0x13, 0x94, 0x00, 0x10, 0x10, 0x14, 0x14, 0x04, 0x12, 0xa2, + 0x01, 0x10, 0x10, 0x11, 0x11, 0xa0, 0x03, 0x04, 0x04, 0x24, 0x24, 0x23, 0x23, 0x12, 0x12, 0x83, + 0x10, 0x10, 0x11, 0x94, 0x21, 0x10, 0x10, 0x01, 0x01, 0x02, 0x02, 0x13, 0x13, 0x22, 0x10, 0x93, + 0x11, 0x00, 0x00, 0x04, 0x04, 0x24, 0x24, 0x23, 0x02, 0x92, 0x01, 0x02, 0x11, 0x21, 0x22, 0x23, + 0x03, 0x13, 0x02, 0x11, 0x11, 0x22, 0x22, 0x13, 0x13, 0x82, 0x00, 0x11, 0x11, 0x20, 0x11, 0x14, + 0x02, 0x22, 0x03, 0xa3, 0x10, 0x12, 0x13, 0x95, 0x20, 0x00, 0x00, 0x02, 0x02, 0x11, 0x11, 0x22, + 0x02, 0x13, 0x13, 0x22, 0x22, 0x24, 0x24, 0x84, 0x00, 0x00, 0x20, 0xa0, 0x20, 0x10, 0x10, 0x11, + 0x11, 0xa1, 0x10, 0x21, 0x20, 0x21, 0x21, 0x11, 0x11, 0x90, 0x11, 0x02, 0x02, 0x13, 0x21, 0x12, + 0x12, 0xa3, 0x01, 0x21, 0x21, 0xa2, 0x10, 0x20, 0x20, 0x21, 0x21, 0x11, 0x12, 0x10, 0x11, 0xa2, + 0x00, 0xa0, 0x01, 0x10, 0x10, 0x21, 0x21, 0x12, 0x12, 0x81, 0x02, 0x22, 0x11, 0x13, 0x03, 0xa3, + 0x01, 0x10, 0x10, 0x21, 0x21, 0x03, 0x03, 0xa3, 0x01, 0x10, 0x10, 0x21, 0x21, 0x12, 0x12, 0x02, + 0x12, 0x23, 0x23, 0x83, 0x02, 0x05, 0x04, 0x14, 0x14, 0x23, 0x22, 0xa4, 0x14, 0x10, 0x10, 0x01, + 0x01, 0x12, 0x10, 0x20, 0x20, 0xa4, 0x14, 0x15, 0x15, 0x85, 0x20, 0xa1, 0x10, 0x20, 0x20, 0x21, + 0x21, 0x11, 0x11, 0x90, 0x01, 0x12, 0x12, 0x03, 0x11, 0x22, 0x22, 0x93, 0x00, 0x01, 0x02, 0x20, + 0x12, 0x13, 0x13, 0x23, 0x22, 0xa4, 0x00, 0x01, 0x02, 0x20, 0x12, 0x22, 0x22, 0x13, 0x13, 0x14, + 0x14, 0xa4, 0x00, 0x10, 0x10, 0x11, 0x11, 0x01, 0x11, 0x02, 0x02, 0x20, 0x12, 0x13, 0x13, 0x23, + 0x22, 0xa4, 0x10, 0x10, 0x11, 0x12, 0x12, 0x03, 0x03, 0x14, 0x14, 0xa3, 0x04, 0x02, 0x02, 0x11, + 0x11, 0x22, 0x22, 0x24, 0x03, 0x23, 0x10, 0xa1, 0x04, 0x02, 0x02, 0x11, 0x11, 0x22, 0x22, 0x24, + 0x03, 0x23, 0x01, 0x90, 0x04, 0x02, 0x02, 0x11, 0x11, 0x22, 0x22, 0x24, 0x03, 0x23, 0x01, 0x10, + 0x10, 0xa1, 0x04, 0x02, 0x02, 0x11, 0x11, 0x22, 0x22, 0x24, 0x03, 0x23, 0x01, 0x10, 0x10, 0x11, + 0x11, 0xa0, 0x04, 0x02, 0x02, 0x11, 0x11, 0x22, 0x22, 0x24, 0x03, 0x23, 0x00, 0x00, 0x20, 0xa0, + 0x04, 0x02, 0x02, 0x11, 0x11, 0x22, 0x22, 0x24, 0x03, 0x23, 0x00, 0x20, 0x20, 0x11, 0x11, 0x80, + 0x00, 0x04, 0x02, 0x22, 0x00, 0x11, 0x10, 0x14, 0x10, 0x20, 0x14, 0xa4, 0x23, 0x14, 0x14, 0x03, + 0x03, 0x01, 0x01, 0x10, 0x10, 0x21, 0x14, 0x15, 0x15, 0x85, 0x02, 0x22, 0x02, 0x04, 0x04, 0x24, + 0x03, 0x13, 0x00, 0x91, 0x02, 0x22, 0x02, 0x04, 0x04, 0x24, 0x03, 0x13, 0x11, 0xa0, 0x02, 0x22, + 0x02, 0x04, 0x04, 0x24, 0x03, 0x13, 0x01, 0x10, 0x10, 0xa1, 0x02, 0x22, 0x02, 0x04, 0x04, 0x24, + 0x03, 0x13, 0x00, 0x00, 0x20, 0xa0, 0x02, 0x22, 0x12, 0x14, 0x04, 0x24, 0x00, 0x91, 0x02, 0x22, + 0x12, 0x14, 0x04, 0x24, 0x11, 0xa0, 0x02, 0x22, 0x12, 0x14, 0x04, 0x24, 0x01, 0x10, 0x10, 0xa1, + 0x02, 0x22, 0x12, 0x14, 0x04, 0x24, 0x20, 0x20, 0x00, 0x80, 0x00, 0x10, 0x10, 0x21, 0x21, 0x23, + 0x23, 0x14, 0x14, 0x04, 0x04, 0x00, 0x02, 0x92, 0x04, 0x02, 0x02, 0x24, 0x24, 0x22, 0x01, 0x10, + 0x10, 0x11, 0x11, 0xa0, 0x02, 0x22, 0x22, 0x24, 0x24, 0x04, 0x04, 0x02, 0x00, 0x91, 0x02, 0x22, + 0x22, 0x24, 0x24, 0x04, 0x04, 0x02, 0x11, 0xa0, 0x02, 0x22, 0x22, 0x24, 0x24, 0x04, 0x04, 0x02, + 0x11, 0x20, 0x00, 0x91, 0x02, 0x22, 0x22, 0x24, 0x24, 0x04, 0x04, 0x02, 0x01, 0x10, 0x10, 0x11, + 0x11, 0xa0, 0x01, 0x21, 0x21, 0x24, 0x24, 0x04, 0x04, 0x01, 0x00, 0x00, 0x20, 0xa0, 0x01, 0x23, + 0x03, 0xa1, 0x01, 0x10, 0x10, 0x21, 0x21, 0x23, 0x23, 0x14, 0x14, 0x03, 0x03, 0x01, 0x03, 0xa1, + 0x01, 0x04, 0x04, 0x24, 0x24, 0x21, 0x11, 0xa0, 0x01, 0x04, 0x04, 0x24, 0x24, 0x21, 0x00, 0x91, + 0x02, 0x04, 0x04, 0x24, 0x24, 0x22, 0x01, 0x10, 0x10, 0xa1, 0x01, 0x04, 0x04, 0x24, 0x24, 0x21, + 0x00, 0x00, 0x20, 0xa0, 0x01, 0x02, 0x02, 0x13, 0x13, 0x14, 0x13, 0x22, 0x22, 0x21, 0x11, 0xa0, + 0x00, 0x04, 0x01, 0x11, 0x11, 0x22, 0x22, 0x13, 0x13, 0x83, 0x00, 0x05, 0x00, 0x10, 0x10, 0x21, + 0x21, 0x12, 0x02, 0x22, 0x22, 0x24, 0x24, 0x84, 0x02, 0x04, 0x04, 0x24, 0x24, 0x22, 0x23, 0x12, + 0x12, 0x02, 0x00, 0x91, 0x02, 0x04, 0x04, 0x24, 0x24, 0x22, 0x23, 0x12, 0x12, 0x02, 0x11, 0xa0, + 0x02, 0x04, 0x04, 0x24, 0x24, 0x22, 0x23, 0x12, 0x12, 0x02, 0x01, 0x10, 0x10, 0xa1, 0x02, 0x04, + 0x04, 0x24, 0x24, 0x22, 0x23, 0x12, 0x12, 0x02, 0x01, 0x10, 0x10, 0x11, 0x11, 0xa0, 0x02, 0x04, + 0x04, 0x24, 0x24, 0x22, 0x23, 0x12, 0x12, 0x02, 0x01, 0x01, 0x21, 0xa1, 0x02, 0x04, 0x04, 0x24, + 0x24, 0x22, 0x23, 0x12, 0x12, 0x02, 0x01, 0x10, 0x10, 0x21, 0x21, 0x81, 0x02, 0x13, 0x02, 0x04, + 0x04, 0x24, 0x12, 0x14, 0x12, 0x22, 0x13, 0x23, 0x22, 0xa3, 0x03, 0x04, 0x04, 0x24, 0x03, 0x12, + 0x12, 0x22, 0x14, 0x15, 0x15, 0x85, 0x24, 0x04, 0x04, 0x02, 0x02, 0x22, 0x22, 0x23, 0x23, 0x13, + 0x00, 0x91, 0x24, 0x04, 0x04, 0x02, 0x02, 0x22, 0x22, 0x23, 0x23, 0x13, 0x11, 0xa0, 0x24, 0x04, + 0x04, 0x02, 0x02, 0x22, 0x22, 0x23, 0x23, 0x13, 0x01, 0x10, 0x10, 0xa1, 0x24, 0x04, 0x04, 0x02, + 0x02, 0x22, 0x22, 0x23, 0x23, 0x13, 0x01, 0x01, 0x21, 0xa1, 0x12, 0x14, 0x00, 0x91, 0x12, 0x14, + 0x11, 0xa0, 0x12, 0x14, 0x01, 0x10, 0x10, 0xa1, 0x12, 0x14, 0x01, 0x01, 0x21, 0xa1, 0x00, 0x22, + 0x11, 0x20, 0x02, 0x22, 0x22, 0x24, 0x24, 0x04, 0x04, 0x82, 0x02, 0x04, 0x03, 0x12, 0x12, 0x23, + 0x23, 0x24, 0x01, 0x10, 0x10, 0x11, 0x11, 0xa0, 0x02, 0x04, 0x04, 0x24, 0x24, 0x22, 0x22, 0x02, + 0x00, 0x91, 0x02, 0x04, 0x04, 0x24, 0x24, 0x22, 0x22, 0x02, 0x11, 0xa0, 0x02, 0x04, 0x04, 0x24, + 0x24, 0x22, 0x22, 0x02, 0x01, 0x10, 0x10, 0xa1, 0x02, 0x04, 0x04, 0x24, 0x24, 0x22, 0x22, 0x02, + 0x01, 0x10, 0x10, 0x11, 0x11, 0xa0, 0x02, 0x04, 0x04, 0x24, 0x24, 0x22, 0x22, 0x02, 0x01, 0x01, + 0x21, 0xa1, 0x11, 0x11, 0x02, 0x22, 0x13, 0x93, 0x02, 0x04, 0x04, 0x24, 0x24, 0x22, 0x22, 0x02, + 0x04, 0xa2, 0x02, 0x04, 0x04, 0x14, 0x14, 0x23, 0x24, 0x22, 0x00, 0x91, 0x02, 0x04, 0x04, 0x14, + 0x14, 0x23, 0x24, 0x22, 0x11, 0xa0, 0x02, 0x04, 0x04, 0x14, 0x14, 0x23, 0x24, 0x22, 0x01, 0x10, + 0x10, 0xa1, 0x02, 0x04, 0x04, 0x14, 0x14, 0x23, 0x24, 0x22, 0x01, 0x01, 0x21, 0xa1, 0x02, 0x03, + 0x03, 0x14, 0x22, 0x23, 0x23, 0x05, 0x11, 0xa0, 0x00, 0x04, 0x02, 0x11, 0x11, 0x22, 0x22, 0x13, + 0x13, 0x82, 0x02, 0x03, 0x03, 0x14, 0x22, 0x23, 0x23, 0x05, 0x01, 0x01, 0x21, 0xa1, }; + + + +void CNFGDrawText( const char * text, int scale ) +{ + const unsigned char * lmap; + float iox = (float)CNFGPenX; + float ioy = (float)CNFGPenY; + + int place = 0; + unsigned short index; + int bQuit = 0; + while( text[place] ) + { + unsigned char c = text[place]; + + switch( c ) + { + case 9: + iox += 12 * scale; + break; + case 10: + iox = (float)CNFGPenX; + ioy += 6 * scale; + break; + default: + index = FontCharMap[c]; + if( index == 65535 ) + { + iox += 3 * scale; + break; + } + + lmap = &FontCharData[index]; + do + { + int x1 = (int)((((*lmap) & 0x70)>>4)*scale + iox); + int y1 = (int)(((*lmap) & 0x0f)*scale + ioy); + int x2 = (int)((((*(lmap+1)) & 0x70)>>4)*scale + iox); + int y2 = (int)(((*(lmap+1)) & 0x0f)*scale + ioy); + lmap++; + CNFGTackSegment( x1, y1, x2, y2 ); + bQuit = *lmap & 0x80; + lmap++; + } while( !bQuit ); + + iox += 3 * scale; + } + place++; + } +} + + +void CNFGDrawBox( int x1, int y1, int x2, int y2 ) +{ + unsigned lc = CNFGLastColor; + CNFGColor( CNFGDialogColor ); + CNFGTackRectangle( x1, y1, x2, y2 ); + CNFGColor( lc ); + CNFGTackSegment( x1, y1, x2, y1 ); + CNFGTackSegment( x2, y1, x2, y2 ); + CNFGTackSegment( x2, y2, x1, y2 ); + CNFGTackSegment( x1, y2, x1, y1 ); +} + +void CNFGGetTextExtents( const char * text, int * w, int * h, int textsize ) +{ + int charsx = 0; + int charsy = 1; + int charsline = 0; + const char * s; + + for( s = text; *s; s++ ) + { + if( *s == '\n' ) + { + charsline = 0; + if( *(s+1) ) + charsy++; + } + else + { + charsline++; + if( charsline > charsx ) + charsx = charsline; + } + } + + *w = charsx * textsize * 3 + textsize; + *h = charsy * textsize * 6; +} + +void CNFGDrawTextbox( int x, int y, const char * text, int textsize ) +{ + int w; + int h; + + CNFGGetTextExtents( text, &w, &h, textsize ); + + CNFGDrawBox( x, y, x + w, y + h ); + CNFGPenX = x + textsize; + CNFGPenY = y + textsize; + CNFGDrawText( text, textsize ); +} diff --git a/DrawFunctions.h b/DrawFunctions.h new file mode 100644 index 0000000..8a77ba0 --- /dev/null +++ b/DrawFunctions.h @@ -0,0 +1,54 @@ +//Copyright (c) 2011 <>< Charles Lohr - Under the MIT/x11 or NewBSD License you choose. + +#ifndef _DRAWFUCNTIONS_H +#define _DRAWFUCNTIONS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +typedef struct { + short x, y; +} RDPoint; + +extern int CNFGPenX, CNFGPenY; +extern uint32_t CNFGBGColor; +extern uint32_t CNFGLastColor; +extern uint32_t CNFGDialogColor; //background for boxes + +void CNFGDrawText( const char * text, int scale ); +void CNFGDrawBox( int x1, int y1, int x2, int y2 ); +void CNFGGetTextExtents( const char * text, int * w, int * h, int textsize ); +void CNFGDrawTextbox( int x, int y, const char * text, int textsize ); //ignores pen. + +//To be provided by driver. +uint32_t CNFGColor( uint32_t RGB ); +void CNFGUpdateScreenWithBitmap( unsigned long * data, int w, int h ); +void CNFGTackPixel( short x1, short y1 ); +void CNFGTackSegment( short x1, short y1, short x2, short y2 ); +void CNFGTackRectangle( short x1, short y1, short x2, short y2 ); +void CNFGTackPoly( RDPoint * points, int verts ); +void CNFGClearFrame(); +void CNFGSwapBuffers(); + +void CNFGGetDimensions( short * x, short * y ); +void CNFGSetup( const char * WindowName, int w, int h ); +void CNFGSetupFullscreen( const char * WindowName, int screen_number ); +void CNFGHandleInput(); + + +//You must provide: +void HandleKey( int keycode, int bDown ); +void HandleButton( int x, int y, int button, int bDown ); +void HandleMotion( int x, int y, int mask ); + + +#ifdef __cplusplus +}; +#endif + + +#endif + diff --git a/LEDOUTDriver.c b/LEDOUTDriver.c new file mode 100644 index 0000000..e849356 --- /dev/null +++ b/LEDOUTDriver.c @@ -0,0 +1,197 @@ +#include "outdrivers.h" +#include "notefinder.h" +#include +#include +#include "parameters.h" +#include +#include +#include "color.h" +#include +#include + +struct LEDOutDriver +{ + struct libusb_device_handle *devh; + int did_init; + int total_leds; + float light_siding; + float * last_led_pos; + float * last_led_amp; + float led_floor; + int led_bins; + float ampoff; + uint8_t * last_leds; + volatile int readyFlag; +}; + + +static void * LEDOutThread( void * v ) +{ + struct LEDOutDriver * led = (struct LEDOutDriver*)v; + while(1) + { + if( led->readyFlag ) + { + int r = libusb_control_transfer( led->devh, + 0x40, //reqtype + 0xA5, //request + 0x0100, //wValue + 0x0000, //wIndex + led->last_leds, + (led->total_leds*3)+1, + 1000 ); + if( r < 0 ) led->did_init = 0; + led->readyFlag = 0; + } + usleep(100); + } + return 0; +} + +static void LEDUpdate(void * id, struct NoteFinder*nf) +{ + struct LEDOutDriver * led = (struct LEDOutDriver*)id; + + if( !led->did_init ) + { + led->did_init = 1; + if( libusb_init(NULL) < 0 ) + { + fprintf( stderr, "Error: Could not initialize libUSB\n" ); + exit( -99 ); + } + + led->devh = libusb_open_device_with_vid_pid( NULL, 0xabcd, 0xf003 ); + + if( !led->devh ) + { + fprintf( stderr, "Error: Cannot find device.\n" ); + exit( -98 ); + } + } + + + + + //Step 1: Calculate the quantity of all the LEDs we'll want. + int totbins = nf->dists; + int i, j; + float binvals[totbins]; + float binpos[totbins]; + float totalbinval = 0; + +// if( totbins > led_bins ) totbins = led_bins; + + + + for( i = 0; i < totbins; i++ ) + { + binpos[i] = nf->note_positions[i] / nf->freqbins; + binvals[i] = pow( nf->note_amplitudes[i], led->light_siding ); + totalbinval += binvals[i]; + } + + float rledpos[led->total_leds]; + float rledamp[led->total_leds]; + int rbinout = 0; + + for( i = 0; i < totbins; i++ ) + { + int nrleds = (int)((binvals[i] / totalbinval) * led->total_leds); + if( nrleds < 40 ) nrleds = 0; + for( j = 0; j < nrleds && rbinout < led->total_leds; j++ ) + { + rledpos[rbinout] = binpos[i]; + rledamp[rbinout] = binvals[i]; + rbinout++; + } + } + + for( ; rbinout < led->total_leds; rbinout++ ) + { + rledpos[rbinout] = rledpos[rbinout-1]; + rledamp[rbinout] = rledamp[rbinout-1]; + } + + //Now we have to minimize "advance". + int minadvance = 0; +// float mindiff = 1e20; +/* + //Uncomment this for a rotationally continuous surface. + for( i = 0; i < total_leds; i++ ) + { + float diff = 0; + diff = 0; + for( j = 0; j < total_leds; j+=4 ) + { + int r = (j + i) % total_leds; + diff += (last_led_pos[j] - rledpos[r])*(last_led_pos[j] - rledpos[r]); + } + if( diff < mindiff ) + { + mindiff = diff; + minadvance = i; + } + } +*/ +// printf( "MA: %d %f\n", minadvance, mindiff ); + + while( led->readyFlag ) usleep(100); + + //Advance the LEDs to this position when outputting the values. + for( i = 0; i < led->total_leds; i++ ) + { + int ia = ( i + minadvance + led->total_leds ) % led->total_leds; + led->last_led_pos[i] = rledpos[ia]; + led->last_led_amp[i] = rledamp[ia] * led->ampoff; + int r = CCtoHEX( led->last_led_pos[i], 1.0, led->last_led_amp[i] ); + led->last_leds[i*3+0] = (r>>8) & 0xff; + led->last_leds[i*3+1] = r & 0xff; + led->last_leds[i*3+2] = (r>>16) & 0xff; + } + + led->readyFlag = 1; +} + +static void LEDParams(void * id ) +{ + struct LEDOutDriver * led = (struct LEDOutDriver*)id; + + led->total_leds = GetParameterI( "leds", 300 ); + led->ampoff = GetParameterF( "amp", 1.0 ); + led->light_siding = GetParameterF( "light_siding", 1.4 ); + + printf( "Found LEDs for output. leds=%d\n", led->total_leds ); + + if( led->last_led_pos ) free( led->last_led_pos ); + led->last_led_pos = malloc( sizeof( float ) * led->total_leds ); + + if( led->last_led_amp ) free( led->last_led_amp ); + led->last_led_amp = malloc( sizeof( float ) * led->total_leds ); + + led->led_floor = GetParameterF( "led_floor", .1 ); + led->led_bins = GetParameterF( "led_bins", 12 ); + + if( led->last_leds ) free( led->last_leds ); + led->last_leds = malloc( led->total_leds * 3 ); + +} + + +static struct DriverInstances * LEDOutDriver() +{ + struct DriverInstances * ret = malloc( sizeof( struct DriverInstances ) ); + memset( ret, 0, sizeof( struct DriverInstances ) ); + struct LEDOutDriver * led = ret->id = malloc( sizeof( struct LEDOutDriver ) ); + ret->Func = LEDUpdate; + ret->Params = LEDParams; + OGCreateThread( LEDOutThread, led ); + led->readyFlag = 0; + LEDParams( led ); + return ret; + +} + +REGISTER_OUT_DRIVER(LEDOutDriver); + + diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..86da060 --- /dev/null +++ b/Makefile @@ -0,0 +1,16 @@ +all : colorchord + +RAWDRAW:=DrawFunctions.o XDriver.o +SOUND:=sound.o sound_alsa.o sound_pulse.o sound_null.o +OUTS:=LEDOUTDriver.o DisplayOUTDriver.o DisplayShapeDriver.o parameters.o chash.o +RAWDRAWLIBS:=-lX11 -lm -lpthread -lXinerama -lXext +LDLIBS:=-lpthread -lasound -lm -lpulse-simple -lpulse +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) + gcc -o $@ $^ $(CFLAGS) $(LDLIBS) $(EXTRALIBS) $(RAWDRAWLIBS) + + +clean : + rm -rf *.o *~ colorchord diff --git a/TODO b/TODO new file mode 100644 index 0000000..bc904bc --- /dev/null +++ b/TODO @@ -0,0 +1,7 @@ +Still to do: + + +Try this: +* Separate "LED Selection" from "output" algorithms. +* For light finding, pick lights off the peaks to get number of lights to use. +* For light shifting (for 1d-looping light systems) shift the centers of the notes, then vernoi between the notes. diff --git a/WinDriver.c b/WinDriver.c new file mode 100644 index 0000000..4b054ed --- /dev/null +++ b/WinDriver.c @@ -0,0 +1,226 @@ +//Copyright (c) 2011 <>< Charles Lohr - Under the MIT/x11 or NewBSD License you choose. +//Portion from: http://en.wikibooks.org/wiki/Windows_Programming/Window_Creation + + +#include "DrawFunctions.h" +#include +#include +#include //for alloca + +static HINSTANCE lhInstance; +static HWND lsHWND; +static HDC lsHDC; +static HBITMAP lsBackBitmap; +static HDC lsWindowHDC; +static HBRUSH lsHBR; +static HPEN lsHPEN; +static HBRUSH lsClearBrush; +static unsigned int lsLastWidth; +static unsigned int lsLastHeight; + +static void InternalHandleResize() +{ + DeleteObject( lsBackBitmap ); + lsBackBitmap = CreateCompatibleBitmap( lsHDC, lsLastWidth, lsLastHeight ); + SelectObject( lsHDC, lsBackBitmap ); + +} + +uint32_t CNFGColor( uint32_t RGB ) +{ + CNFGLastColor = RGB; + + DeleteObject( lsHBR ); + lsHBR = CreateSolidBrush( RGB ); + SelectObject( lsHDC, lsHBR ); + + DeleteObject( lsHPEN ); + lsHPEN = CreatePen( PS_SOLID, 0, RGB ); + SelectObject( lsHDC, lsHPEN ); + + return RGB; +} + +void CNFGTackSegment( short x1, short y1, short x2, short y2 ) +{ + POINT pt[2] = { {x1, y1}, {x2, y2} }; + Polyline( lsHDC, pt, 2 ); + SetPixel( lsHDC, x1, y1, CNFGLastColor ); + SetPixel( lsHDC, x2, y2, CNFGLastColor ); +} + +void CNFGTackRectangle( short x1, short y1, short x2, short y2 ) +{ + RECT r; + if( x1 < x2 ) { r.left = x1; r.right = x2; } + else { r.left = x2; r.right = x1; } + if( y1 < y2 ) { r.top = y1; r.bottom = y2; } + else { r.top = y2; r.bottom = y1; } + FillRect( lsHDC, &r, lsHBR ); +} + +void CNFGClearFrame() +{ + RECT r = { 0, 0, lsLastWidth, lsLastHeight }; + DeleteObject( lsClearBrush ); + lsClearBrush = CreateSolidBrush( CNFGBGColor ); + SelectObject( lsHDC, lsClearBrush ); + + FillRect( lsHDC, &r, lsClearBrush ); +} + +void CNFGSwapBuffers() +{ + int thisw, thish; + RECT r; + BitBlt( lsWindowHDC, 0, 0, lsLastWidth, lsLastHeight, lsHDC, 0, 0, SRCCOPY ); + UpdateWindow( lsHWND ); + + //Check to see if the window is closed. + if( !IsWindow( lsHWND ) ) + { + exit( 0 ); + } + + GetClientRect( lsHWND, &r ); + thisw = r.right - r.left; + thish = r.bottom - r.top; + if( thisw != lsLastWidth || thish != lsLastHeight ) + { + lsLastWidth = thisw; + lsLastHeight = thish; + InternalHandleResize(); + } +} + +void CNFGTackPoly( RDPoint * points, int verts ) +{ + int i; + POINT * t = (POINT*)alloca( sizeof( POINT ) * verts ); + for( i = 0; i < verts; i++ ) + { + t[i].x = points[i].x; + t[i].y = points[i].y; + } + Polygon( lsHDC, t, verts ); +} + + +void CNFGTackPixel( short x1, short y1 ) +{ + SetPixel( lsHDC, x1, y1, CNFGLastColor ); +} + +void CNFGGetDimensions( short * x, short * y ) +{ + *x = lsLastWidth; + *y = lsLastHeight; +} + +//This was from the article +LRESULT CALLBACK MyWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch(msg) + { + case WM_DESTROY: + PostQuitMessage(0); + return 0; + } + return DefWindowProc(hwnd, msg, wParam, lParam); +} + +//This was from the article, too... well, mostly. +void CNFGSetup( const char * name_of_window, int width, int height ) +{ + static LPSTR szClassName = "MyClass"; + RECT client, window; + WNDCLASS wnd; + int w, h, wd, hd; + HINSTANCE hInstance = GetModuleHandle(NULL); + + lsLastWidth = width; + lsLastHeight = height; + + wnd.style = CS_HREDRAW | CS_VREDRAW; //we will explain this later + wnd.lpfnWndProc = MyWndProc; + wnd.cbClsExtra = 0; + wnd.cbWndExtra = 0; + wnd.hInstance = hInstance; + wnd.hIcon = LoadIcon(NULL, IDI_APPLICATION); //default icon + wnd.hCursor = LoadCursor(NULL, IDC_ARROW); //default arrow mouse cursor + wnd.hbrBackground = (HBRUSH)(COLOR_BACKGROUND); + wnd.lpszMenuName = NULL; //no menu + wnd.lpszClassName = szClassName; + + if(!RegisterClass(&wnd)) //register the WNDCLASS + { + MessageBox(NULL, "This Program Requires Windows NT", "Error", MB_OK); + } + + lsHWND = CreateWindow(szClassName, + name_of_window, //name_of_window, + WS_OVERLAPPEDWINDOW, //basic window style + CW_USEDEFAULT, + CW_USEDEFAULT, //set starting point to default value + lsLastWidth, + lsLastHeight, //set all the dimensions to default value + NULL, //no parent window + NULL, //no menu + hInstance, + NULL); //no parameters to pass + + + lsWindowHDC = GetDC( lsHWND ); + lsHDC = CreateCompatibleDC( lsWindowHDC ); + lsBackBitmap = CreateCompatibleBitmap( lsWindowHDC, lsLastWidth, lsLastHeight ); + SelectObject( lsHDC, lsBackBitmap ); + + lsClearBrush = CreateSolidBrush( CNFGBGColor ); + lsHBR = CreateSolidBrush( 0xFFFFFF ); + lsHPEN = CreatePen( PS_SOLID, 0, 0xFFFFFF ); + + ShowWindow(lsHWND, 1); //display the window on the screen + + //Once set up... we have to change the window's borders so we get the client size right. + GetClientRect( lsHWND, &client ); + GetWindowRect( lsHWND, &window ); + w = ( window.right - window.left); + h = ( window.bottom - window.top); + wd = w - client.right; + hd = h - client.bottom; + MoveWindow( lsHWND, window.left, window.top, lsLastWidth + wd, lsLastHeight + hd, 1 ); + + InternalHandleResize(); +} + +void CNFGHandleInput() +{ + int ldown = 0; + + MSG msg; + while( PeekMessage( &msg, lsHWND, 0, 0xFFFF, 1 ) ) + { + TranslateMessage(&msg); + + switch( msg.message ) + { + case WM_MOUSEMOVE: + HandleMotion( (msg.lParam & 0xFFFF), (msg.lParam>>16) & 0xFFFF, ( (msg.wParam & 0x01)?1:0) | ((msg.wParam & 0x02)?2:0) | ((msg.wParam & 0x10)?4:0) ); + break; + case WM_LBUTTONDOWN: HandleButton( (msg.lParam & 0xFFFF), (msg.lParam>>16) & 0xFFFF, 1, 1 ); break; + case WM_RBUTTONDOWN: HandleButton( (msg.lParam & 0xFFFF), (msg.lParam>>16) & 0xFFFF, 2, 1 ); break; + case WM_MBUTTONDOWN: HandleButton( (msg.lParam & 0xFFFF), (msg.lParam>>16) & 0xFFFF, 3, 1 ); break; + case WM_LBUTTONUP: HandleButton( (msg.lParam & 0xFFFF), (msg.lParam>>16) & 0xFFFF, 1, 0 ); break; + case WM_RBUTTONUP: HandleButton( (msg.lParam & 0xFFFF), (msg.lParam>>16) & 0xFFFF, 2, 0 ); break; + case WM_MBUTTONUP: HandleButton( (msg.lParam & 0xFFFF), (msg.lParam>>16) & 0xFFFF, 3, 0 ); break; + case WM_KEYDOWN: + case WM_KEYUP: + HandleKey( tolower( msg.wParam ), (msg.message==WM_KEYDOWN) ); + break; + default: + DispatchMessage(&msg); + break; + } + } +} + diff --git a/XDriver.c b/XDriver.c new file mode 100644 index 0000000..192843c --- /dev/null +++ b/XDriver.c @@ -0,0 +1,258 @@ +//Copyright (c) 2011 <>< Charles Lohr - Under the MIT/x11 or NewBSD License you choose. +//portions from +//http://www.xmission.com/~georgeps/documentation/tutorials/Xlib_Beginner.html + +#define HAS_XINERAMA + +#include "DrawFunctions.h" + +#include +#include +#include +#include +#include +#ifdef HAS_XINERAMA +#include +#include +#endif +#include +#include + +XWindowAttributes CNFGWinAtt; +Display *CNFGDisplay; +Window CNFGWindow; +Pixmap CNFGPixmap; +GC CNFGGC; +GC CNFGWindowGC; +int FullScreen = 0; + +void CNFGGetDimensions( short * x, short * y ) +{ + *x = CNFGWinAtt.width; + *y = CNFGWinAtt.height; +} + +static void InternalLinkScreenAndGo( const char * WindowName ) +{ + XGetWindowAttributes( CNFGDisplay, CNFGWindow, &CNFGWinAtt ); + + XSelectInput (CNFGDisplay, CNFGWindow, KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | ExposureMask | PointerMotionMask ); + XSetStandardProperties( CNFGDisplay, CNFGWindow, WindowName, WindowName, None, NULL, 0, NULL ); + + CNFGWindowGC = XCreateGC(CNFGDisplay, CNFGWindow, 0, 0); + + CNFGPixmap = XCreatePixmap( CNFGDisplay, CNFGWindow, CNFGWinAtt.width, CNFGWinAtt.height, CNFGWinAtt.depth ); + CNFGGC = XCreateGC(CNFGDisplay, CNFGPixmap, 0, 0); +} + +void CNFGSetupFullscreen( const char * WindowName, int screen_no ) +{ +#ifdef HAS_XINERAMA + XineramaScreenInfo *screeninfo = NULL; + int screens; + int event_basep, error_basep, a, b; + CNFGDisplay = XOpenDisplay(NULL); + int screen = XDefaultScreen(CNFGDisplay); + int xpos, ypos; + + if (!XShapeQueryExtension(CNFGDisplay, &event_basep, &error_basep)) + { + fprintf( stderr, "X-Server does not support shape extension" ); + exit( 1 ); + } + + Visual * visual = DefaultVisual(CNFGDisplay, screen); + CNFGWinAtt.depth = DefaultDepth(CNFGDisplay, screen); + + if (XineramaQueryExtension(CNFGDisplay, &a, &b ) && + (screeninfo = XineramaQueryScreens(CNFGDisplay, &screens)) && + XineramaIsActive(CNFGDisplay) && screen_no >= 0 && + screen_no < screens ) { + + CNFGWinAtt.width = screeninfo[screen_no].width; + CNFGWinAtt.height = screeninfo[screen_no].height; + xpos = screeninfo[screen_no].x_org; + ypos = screeninfo[screen_no].y_org; + } else + { + CNFGWinAtt.width = XDisplayWidth(CNFGDisplay, screen); + CNFGWinAtt.height = XDisplayHeight(CNFGDisplay, screen); + xpos = 0; + ypos = 0; + } + if (screeninfo) + XFree(screeninfo); + + + XSetWindowAttributes setwinattr; + setwinattr.override_redirect = 1; + setwinattr.save_under = 1; + setwinattr.event_mask = StructureNotifyMask | SubstructureNotifyMask | ExposureMask | ButtonPressMask | ButtonReleaseMask | ButtonPressMask | PointerMotionMask | ButtonMotionMask | EnterWindowMask | LeaveWindowMask |KeyPressMask |KeyReleaseMask | SubstructureNotifyMask | FocusChangeMask; + setwinattr.border_pixel = 0; + + CNFGWindow = XCreateWindow(CNFGDisplay, XRootWindow(CNFGDisplay, screen), + xpos, ypos, CNFGWinAtt.width, CNFGWinAtt.height, + 0, CNFGWinAtt.depth, InputOutput, visual, CWBorderPixel | CWEventMask | CWOverrideRedirect | CWSaveUnder, &setwinattr); + + XMapWindow(CNFGDisplay, CNFGWindow); + XSetInputFocus( CNFGDisplay, CNFGWindow, RevertToParent, CurrentTime ); + XFlush(CNFGDisplay); + FullScreen = 1; +//printf( "%d %d %d %d\n", xpos, ypos, CNFGWinAtt.width, CNFGWinAtt.height ); + InternalLinkScreenAndGo( WindowName ); +/* + setwinattr.override_redirect = 1; + XChangeWindowAttributes( + CNFGDisplay, CNFGWindow, + CWBorderPixel | CWEventMask | CWOverrideRedirect, &setwinattr); +*/ +#else + CNFGSetup( WindowName, 640, 480 ); +#endif +} + + +void CNFGSetup( const char * WindowName, int w, int h ) +{ + CNFGDisplay = XOpenDisplay(NULL); + XGetWindowAttributes( CNFGDisplay, RootWindow(CNFGDisplay, 0), &CNFGWinAtt ); + + int depth = CNFGWinAtt.depth; + CNFGWindow = XCreateWindow(CNFGDisplay, RootWindow(CNFGDisplay, 0), 1, 1, w, h, 0, depth, InputOutput, CopyFromParent, 0, 0 ); + XMapWindow(CNFGDisplay, CNFGWindow); + XFlush(CNFGDisplay); + + InternalLinkScreenAndGo( WindowName ); +} + +void CNFGHandleInput() +{ + static int ButtonsDown; + XEvent report; + + int bKeyDirection = 1; + int r; + while( (r=XCheckMaskEvent( CNFGDisplay, KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | ExposureMask | PointerMotionMask , &report )) ) + { +// XEvent nev; +// XPeekEvent(CNFGDisplay, &nev); + + //printf( "EVENT %d\n", report.type ); + //XMaskEvent(CNFGDisplay, KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | ExposureMask, &report); + + bKeyDirection = 1; + switch (report.type) + { + case NoExpose: + break; + case Expose: + XGetWindowAttributes( CNFGDisplay, CNFGWindow, &CNFGWinAtt ); + if( CNFGPixmap ) XFreePixmap( CNFGDisplay, CNFGPixmap ); + CNFGPixmap = XCreatePixmap( CNFGDisplay, CNFGWindow, CNFGWinAtt.width, CNFGWinAtt.height, CNFGWinAtt.depth ); + if( CNFGGC ) XFreeGC( CNFGDisplay, CNFGGC ); + CNFGGC = XCreateGC(CNFGDisplay, CNFGPixmap, 0, 0); + break; + case KeyRelease: + bKeyDirection = 0; + case KeyPress: + HandleKey( XLookupKeysym(&report.xkey, 0), bKeyDirection ); + break; + case ButtonRelease: + bKeyDirection = 0; + case ButtonPress: + HandleButton( report.xbutton.x, report.xbutton.y, report.xbutton.button, bKeyDirection ); + ButtonsDown = (ButtonsDown & (~(1<>1 ); + break; + default: + printf( "Event: %d\n", report.type ); + } + } +} + + +void CNFGUpdateScreenWithBitmap( unsigned long * data, int w, int h ) +{ + static XImage *xi; + static int depth; + static int lw, lh; + + if( !xi ) + { + int screen = DefaultScreen(CNFGDisplay); +// Visual * visual = DefaultVisual(CNFGDisplay, screen); + depth = DefaultDepth(CNFGDisplay, screen)/8; +// xi = XCreateImage(CNFGDisplay, DefaultVisual( CNFGDisplay, DefaultScreen(CNFGDisplay) ), depth*8, ZPixmap, 0, (char*)data, w, h, 32, w*4 ); +// lw = w; +// lh = h; + } + + if( lw != w || lh != h ) + { + if( xi ) free( xi ); + xi = XCreateImage(CNFGDisplay, DefaultVisual( CNFGDisplay, DefaultScreen(CNFGDisplay) ), depth*8, ZPixmap, 0, (char*)data, w, h, 32, w*4 ); + lw = w; + lh = h; + } + +// ls = lw * lh; + + XPutImage(CNFGDisplay, CNFGWindow, CNFGWindowGC, xi, 0, 0, 0, 0, w, h ); +} + + +#ifndef RASTERIZER + + +uint32_t CNFGColor( uint32_t RGB ) +{ + unsigned char red = RGB & 0xFF; + unsigned char grn = ( RGB >> 8 ) & 0xFF; + unsigned char blu = ( RGB >> 16 ) & 0xFF; + CNFGLastColor = RGB; + unsigned long color = (red<<16)|(grn<<8)|(blu); + XSetForeground(CNFGDisplay, CNFGGC, color); + return color; +} + +void CNFGClearFrame() +{ + XGetWindowAttributes( CNFGDisplay, CNFGWindow, &CNFGWinAtt ); + XSetForeground(CNFGDisplay, CNFGGC, CNFGColor(CNFGBGColor) ); + XFillRectangle(CNFGDisplay, CNFGPixmap, CNFGGC, 0, 0, CNFGWinAtt.width, CNFGWinAtt.height ); +} + +void CNFGSwapBuffers() +{ + XCopyArea(CNFGDisplay, CNFGPixmap, CNFGWindow, CNFGWindowGC, 0,0,CNFGWinAtt.width,CNFGWinAtt.height,0,0); + XFlush(CNFGDisplay); + if( FullScreen ) + XSetInputFocus( CNFGDisplay, CNFGWindow, RevertToParent, CurrentTime ); +} + +void CNFGTackSegment( short x1, short y1, short x2, short y2 ) +{ + XDrawLine( CNFGDisplay, CNFGPixmap, CNFGGC, x1, y1, x2, y2 ); + XDrawPoint( CNFGDisplay, CNFGPixmap, CNFGGC, x2, y2 ); +} + +void CNFGTackPixel( short x1, short y1 ) +{ + XDrawPoint( CNFGDisplay, CNFGPixmap, CNFGGC, x1, y1 ); +} + +void CNFGTackRectangle( short x1, short y1, short x2, short y2 ) +{ + XFillRectangle(CNFGDisplay, CNFGPixmap, CNFGGC, x1, y1, x2-x1, y2-y1 ); +} + +void CNFGTackPoly( RDPoint * points, int verts ) +{ + XFillPolygon(CNFGDisplay, CNFGPixmap, CNFGGC, (XPoint *)points, 3, Convex, CoordModeOrigin ); +} + +#endif + diff --git a/chash.c b/chash.c new file mode 100644 index 0000000..79fa68c --- /dev/null +++ b/chash.c @@ -0,0 +1,439 @@ +#include "chash.h" +#include +#include +#include + +#define I_AM_LITTLE (((union { unsigned x; unsigned char c; }){1}).c) + +static unsigned long GetStrHash( const char * c ) +{ + int place = 0; + unsigned int hashSoFar = 0; + + if( !c ) return 0; + + do + { + unsigned char cc = (*c); + unsigned long this = cc; + this <<= (((place++)&3)<<3); + hashSoFar += this; + } while( *(c++) ); + + return hashSoFar; +} + + +static unsigned long GeneralUsePrimes[] = { 7, 13, 37, 73, 131, 229, 337, 821, + 2477, 4594, 8941, 14797, 24953, 39041, 60811, 104729, 151909, + 259339, 435637, 699817, 988829, 1299827 }; + +struct chash * GenerateHashTable( int allowMultiple ) +{ + int bucketplace = 0; + int count = GeneralUsePrimes[bucketplace]; + struct chash * ret = malloc( sizeof( struct chash ) ); + ret->entryCount = 0; + ret->allowMultiple = allowMultiple; + ret->bucketCountPlace = bucketplace; + ret->buckets = malloc( sizeof( struct chashentry ) * count ); + memset( ret->buckets, 0, sizeof( struct chashentry ) * count ); + return ret; +} + +void ** HashTableInsert( struct chash * hash, const char * key, int dontDupKey ) +{ + int buckets = GeneralUsePrimes[hash->bucketCountPlace]; + int thisHash = GetStrHash( key ) % buckets; + int multi = hash->allowMultiple; + struct chashentry * thisEntry; + struct chashentry * overextndEntry = &hash->buckets[buckets]; + int i; + + //Cannot insert null! + if( !key ) + { + return 0; + } + + //Half full? + if( hash->entryCount > (buckets>>1) ) + { + //Resize! + int newbucketcount = GeneralUsePrimes[hash->bucketCountPlace + 1]; + struct chashentry * newbuckets = malloc( sizeof( struct chashentry ) * newbucketcount ); + struct chashentry * oldbuckets = hash->buckets; + overextndEntry = &newbuckets[newbucketcount]; + + memset( newbuckets, 0, sizeof( struct chashentry ) * newbucketcount ); + + for( i = 0; i < buckets; i++ ) + { + struct chashentry * oe = &oldbuckets[i]; + int newhash; + struct chashentry * ne; + + //Empty? Continue! + if( !oe->key ) continue; + + newhash = GetStrHash( oe->key ) % newbucketcount; + ne = &newbuckets[newhash]; + while( ne->key ) + { + ne++; + if( ne == overextndEntry ) ne = &newbuckets[0]; + } + + ne->key = oe->key; + ne->value = oe->value; + } + + //Replacing done, now update all. + + hash->buckets = newbuckets; + free( oldbuckets ); + hash->bucketCountPlace++; + buckets = newbucketcount; + thisHash = GetStrHash( key ) % buckets; + } + + thisEntry = &hash->buckets[thisHash]; + + while( thisEntry->key ) + { + + //If we aren't allowing multiple entries, say so! + if( !multi && strcmp( thisEntry->key, key ) == 0 ) + { + return &thisEntry->value; + } + + thisEntry++; + if( thisEntry == overextndEntry ) thisEntry = &hash->buckets[0]; + } + + thisEntry->value = 0; + if( dontDupKey ) + { + thisEntry->key = key; + } + else + { + thisEntry->key = strdup( key ); + } + thisEntry->hash = thisHash; + + hash->entryCount++; + + return &thisEntry->value; +} + +//Re-process a range; populated is the number of elements we expect to run into. +//If we removed some, it will be less length by that much. +//NOTE: This function doesn't range check diddily. +static void RedoHashRange( struct chash * hash, int start, int length, int populated ) +{ + int i; + int buckets = GeneralUsePrimes[hash->bucketCountPlace]; + struct chashentry * thisEntry = &hash->buckets[start]; + struct chashentry * overEntry = &hash->buckets[buckets]; + struct chashentry * copyEntry; + + struct chashlist * resort = alloca( sizeof( struct chashlist ) + sizeof( struct chashentry ) * (populated) ); + resort->length = populated; + copyEntry = &resort->items[0]; + + for( i = 0; i < length; i++ ) + { + if( thisEntry->key ) + { + copyEntry->key = thisEntry->key; + copyEntry->value = thisEntry->value; + copyEntry->hash = thisEntry->hash; + copyEntry++; + thisEntry->key = 0; + } + + thisEntry++; + if( thisEntry == overEntry ) thisEntry = &hash->buckets[0]; + } + + hash->entryCount -= populated; + + copyEntry = &resort->items[0]; + + for( i = 0; i < populated; i++ ) + { + *HashTableInsert( hash, copyEntry->key, 1 ) = copyEntry->value; + copyEntry++; + } + +} + + + +int HashTableRemove( struct chash * hash, const char * key ) +{ + int buckets = GeneralUsePrimes[hash->bucketCountPlace]; + int thisHash = GetStrHash( key ) % buckets; + int multi = hash->allowMultiple; + struct chashentry * thisEntry = &hash->buckets[thisHash]; + struct chashentry * overEntry = &hash->buckets[buckets]; + int startentry = thisHash; + int entriesSearched = 0; + int removedEntries = 0; + + //Search the list for matches, search until we have an empty spot. + while( thisEntry->key ) + { + if( strcmp( thisEntry->key, key ) == 0 ) + { + free( (char*)thisEntry->key ); + thisEntry->key = 0; + removedEntries++; + if( !multi ) + { + break; + } + } + + thisEntry++; + if( thisEntry == overEntry ) thisEntry = &hash->buckets[0]; + entriesSearched++; + } + + if( removedEntries == 0 ) + return 0; + + hash->entryCount -= removedEntries; + + RedoHashRange( hash, startentry, entriesSearched, entriesSearched-removedEntries ); + + return removedEntries; +} + +int HashTableRemoveSpecific( struct chash * hash, const char * key, void * value ) +{ + int buckets = GeneralUsePrimes[hash->bucketCountPlace]; + int thisHash = GetStrHash( key ) % buckets; + int multi = hash->allowMultiple; + struct chashentry * thisEntry = &hash->buckets[thisHash]; + struct chashentry * overEntry = &hash->buckets[buckets]; + int startentry = thisHash; + int entriesSearched = 0; + int removedEntries = 0; + + //Search the list for matches, search until we have an empty spot. + while( thisEntry->key ) + { + if( strcmp( thisEntry->key, key ) == 0 && thisEntry->value == value ) + { + free( (char*)thisEntry->key ); + thisEntry->key = 0; + removedEntries++; + if( !multi ) + { + break; + } + } + + thisEntry++; + if( thisEntry == overEntry ) thisEntry = &hash->buckets[0]; + entriesSearched++; + } + + if( removedEntries == 0 ) + return 0; + + hash->entryCount -= removedEntries; + + RedoHashRange( hash, startentry, entriesSearched, entriesSearched-removedEntries ); + + return removedEntries; +} + + +void HashDestroy( struct chash * hash, int deleteKeys ) +{ + int buckets = GeneralUsePrimes[hash->bucketCountPlace]; + int i; + + if( deleteKeys ) + { + struct chashentry * start = &hash->buckets[0]; + for( i = 0; i < buckets; i++ ) + { + free( (char*) (start++)->key ); + } + } + + free( hash->buckets ); + + free( hash ); +} + +void ** HashUpdateEntry( struct chash * hash, const char * key ) +{ + int buckets = GeneralUsePrimes[hash->bucketCountPlace]; + int thisHash = GetStrHash( key ) % buckets; + struct chashentry * thisEntry = &hash->buckets[thisHash]; + struct chashentry * overEntry = &hash->buckets[buckets]; + + while( thisEntry->key ) + { + //We allow null keys. + if( !key && !thisEntry->key ) + { + return &thisEntry->value; + } + + if( key && thisEntry->key && strcmp( thisEntry->key, key ) == 0 ) + { + return &thisEntry->value; + } + + thisEntry++; + if( thisEntry == overEntry ) thisEntry = &hash->buckets[0]; + } + + return 0; +} + +void * HashGetEntry( struct chash * hash, const char * key ) +{ + void ** v = HashUpdateEntry( hash, key ); + if( v ) + return *v; + return 0; +} + + +struct chashlist * HashGetAllEntries( struct chash * hash, const char * key ) +{ + int retreser = 3; + int buckets = GeneralUsePrimes[hash->bucketCountPlace]; + int thisHash = GetStrHash( key ) % buckets; + int multi = hash->allowMultiple; + struct chashentry * thisEntry = &hash->buckets[thisHash]; + struct chashentry * overEntry = &hash->buckets[buckets]; + struct chashlist * ret = malloc( sizeof( struct chashlist ) + sizeof( struct chashentry ) * retreser ); + + ret->length = 0; + + while( thisEntry->key ) + { + if( strcmp( thisEntry->key, key ) == 0 ) + { + ret->items[ret->length].key = thisEntry->key; + ret->items[ret->length].value = thisEntry->value; + ret->items[ret->length].hash = thisEntry->hash; + ret->length++; + + if( !multi ) + { + return ret; + } + + if( ret->length == retreser ) + { + retreser *= 2; + ret = realloc( ret, sizeof( struct chashlist ) + sizeof( struct chashentry ) * retreser ); + } + } + + thisEntry++; + if( thisEntry == overEntry ) thisEntry = &hash->buckets[0]; + } + + if( ret->length == 0 ) + { + free( ret ); + return 0; + } + + return ret; +} + + +static void merge( struct chashentry ** tmpitems, int start, int middle, int end ) +{ + int aptr = start; + int bptr = middle; + int i = 0; + int count = end-start+1; + struct chashentry * ret[count]; + + if( count == 1 ) return; + + while( i < count ) + { + if( bptr == end+1 || ( aptr != middle && strcmp( tmpitems[aptr]->key, tmpitems[bptr]->key ) < 0 ) ) + { + ret[i++] = tmpitems[aptr]; + aptr++; + } + else + { + ret[i++] = tmpitems[bptr]; + bptr++; + } + } + + for( i = 0; i < count; i++ ) + { + tmpitems[i+start] = ret[i]; + } +} + + +static void merge_sort( struct chashentry ** tmpitems, int start, int end ) +{ + int middle = (end+start)/2; + + if( end <= start ) + return; + + merge_sort( tmpitems, start, middle ); + merge_sort( tmpitems, middle+1, end ); + merge( tmpitems, start, middle+1, end ); +} + + +struct chashlist * HashProduceSortedTable( struct chash * hash ) +{ + struct chashentry ** tmpitems = alloca( sizeof( struct chashlist * ) * hash->entryCount ); //temp list of pointers. + struct chashentry * thisEntry = &hash->buckets[0]; + struct chashlist * ret = malloc( sizeof( struct chashlist ) + sizeof( struct chashentry ) * hash->entryCount ); + int i; + int index = 0; + int buckets = GeneralUsePrimes[hash->bucketCountPlace]; + + ret->length = hash->entryCount; + + if( hash->entryCount == 0 ) + { + return ret; + } + + //Move the table into tmp. + for( i = 0; i < buckets; i++, thisEntry++ ) + { + if( !thisEntry->key ) continue; + + tmpitems[index++] = thisEntry; + } + + //Sort tmpitems + merge_sort( tmpitems, 0, index-1 ); + + for( i = 0; i < hash->entryCount; i++ ) + { + ret->items[i].key = (tmpitems[i])->key; + ret->items[i].value = (tmpitems[i])->value; + ret->items[i].hash = (tmpitems[i])->hash; + } + + return ret; +} + + diff --git a/chash.h b/chash.h new file mode 100644 index 0000000..36e73d1 --- /dev/null +++ b/chash.h @@ -0,0 +1,53 @@ +#ifndef _CHASH_H +#define _CHASH_H + + +struct chashentry +{ + const char * key; + void * value; + unsigned long hash; +}; + +struct chashlist +{ + int length; + struct chashentry items[0]; +}; + + +struct chash +{ + int entryCount; + int allowMultiple; + int bucketCountPlace; + struct chashentry * buckets; +}; + +struct chash * GenerateHashTable( int allowMultiple ); + +void ** HashTableInsert( struct chash * hash, const char * key, int dontDupKey ); + +//returns # of entries removed. +int HashTableRemove( struct chash * hash, const char * key ); + +//returns 1 if entry was removed, 0 otherwise. +int HashTableRemoveSpecific( struct chash * hash, const char * key, void * value ); + +void HashDestroy( struct chash * hash, int deleteKeys ); + +//Gets a pointer to the pointer to day, allowing you to read or modify the entry. +//NOTE: This only returns one of the elements if multiple keys are allowed. +void ** HashUpdateEntry( struct chash * hash, const char * key ); + +//Returns the entry itself, if none found, returns 0. +void * HashGetEntry( struct chash * hash, const char * key ); + +//Get list of entries that match a given key. +struct chashlist * HashGetAllEntries( struct chash * hash, const char * key ); + +//Can tolerate an empty list. +struct chashlist * HashProduceSortedTable( struct chash * hash ); + + +#endif diff --git a/color.c b/color.c new file mode 100644 index 0000000..0724393 --- /dev/null +++ b/color.c @@ -0,0 +1,105 @@ +#include "color.h" +#include + +uint32_t CCtoHEX( float note, float sat, float value ) +{ + float hue = 0.0; + note = fmodf( note, 1.0 ); + note *= 12; + if( note < 4 ) + { + //Needs to be YELLOW->RED + hue = (4 - note) / 24.0; + } + else if( note < 8 ) + { + // [4] [8] + //Needs to be RED->BLUE + hue = ( 4 - note ) / 12.0; + } + else + { + // [8] [12] + //Needs to be BLUE->YELLOW + hue = ( 12 - note ) / 8.0 + 1./6.; + } + return HSVtoHEX( hue, sat, value ); +} + + +//0/6: RED +//1/6: YELLOW +//2/6: GREEN +//3/6: CYAN +//4/6: BLUE +//5/6: PURPLE +//6/6: RED +uint32_t HSVtoHEX( float hue, float sat, float value ) +{ + + float pr = 0; + float pg = 0; + float pb = 0; + + short ora = 0; + short og = 0; + short ob = 0; + + float ro = fmod( hue * 6, 6. ); + + float avg = 0; + + ro = fmod( ro + 6 + 1, 6 ); //Hue was 60* off... + + if( ro < 1 ) //yellow->red + { + pr = 1; + pg = 1. - ro; + } else if( ro < 2 ) + { + pr = 1; + pb = ro - 1.; + } else if( ro < 3 ) + { + pr = 3. - ro; + pb = 1; + } else if( ro < 4 ) + { + pb = 1; + pg = ro - 3; + } else if( ro < 5 ) + { + pb = 5 - ro; + pg = 1; + } else + { + pg = 1; + pr = ro - 5; + } + + //Actually, above math is backwards, oops! + pr *= value; + pg *= value; + pb *= value; + + avg += pr; + avg += pg; + avg += pb; + + pr = pr * sat + avg * (1.-sat); + pg = pg * sat + avg * (1.-sat); + pb = pb * sat + avg * (1.-sat); + + ora = pr * 255; + og = pb * 255; + ob = pg * 255; + + if( ora < 0 ) ora = 0; + if( ora > 255 ) ora = 255; + if( og < 0 ) og = 0; + if( og > 255 ) og = 255; + if( ob < 0 ) ob = 0; + if( ob > 255 ) ob = 255; + + return (ob<<16) | (og<<8) | ora; +} diff --git a/color.h b/color.h new file mode 100644 index 0000000..b766129 --- /dev/null +++ b/color.h @@ -0,0 +1,12 @@ +#ifndef _COLOR_H +#define _COLOR_H + +#include + +//note = 0..1 (Root is YELLOW); Val = 0..1 +//NOTE: CC is _NOT_ HUE!!! +uint32_t CCtoHEX( float note, float sat, float value ); +uint32_t HSVtoHEX( float hue, float sat, float value ); + +#endif + diff --git a/decompose.c b/decompose.c new file mode 100644 index 0000000..fca34cf --- /dev/null +++ b/decompose.c @@ -0,0 +1,215 @@ +#include "decompose.h" +#include +#include +#include +#include + +#define SQRT2PI 2.506628253 + +#ifdef TURBO_DECOMPOSE + +int DecomposeHistogram( float * histogram, int bins, float * out_means, float * out_amps, float * out_sigmas, int max_dists, double default_sigma, int iterations ) +{ + //Step 1: Find the actual peaks. + + int i; + int peak = 0; + for( i = 0; i < bins; i++ ) + { + float offset = 0; + float prev = histogram[(i-1+bins)%bins]; + float this = histogram[i]; + float next = histogram[(i+1)%bins]; + + if( prev > this || next > this ) continue; + if( prev == this && next == this ) continue; + + //i is at a peak... + float totaldiff = (( this - prev ) + ( this - next )); + float porpdiffP = (this-prev)/totaldiff; //close to 0 = closer to this side... 0.5 = in the middle ... 1.0 away. + float porpdiffN = (this-next)/totaldiff; + + if( porpdiffP < porpdiffN ) + { + //Closer to prev. + offset = -(0.5 - porpdiffP); + } + else + { + offset = (0.5 - porpdiffN); + } + + out_means[peak] = i + offset; + + //XXX XXX TODO Examine difference or relationship of "this" and "totaldiff" + out_amps[peak] = this * 4; + //powf( totaldiff, .8) * 10;//powf( totaldiff, .5 )*4; // + out_sigmas[peak] = default_sigma; + peak++; + } + + for( i = peak; i < max_dists; i++ ) + { + out_means[i] = -1; + out_amps[i] = 0; + out_sigmas[i] = default_sigma; + } + + return peak; +} + +//Yick: Doesn't match.. I guess it's only for debugging, right? +float CalcHistAt( float pt, int bins, float * out_means, float * out_amps, float * out_sigmas, int cur_dists ) +{ + int i; + float mark = 0.0; + for( i = 0; i < cur_dists; i++ ) + { + float amp = out_amps[i]; + float mean = out_means[i]; + float var = out_sigmas[i]; + + float x = mean - pt; + if( x < - bins / 2 ) x += bins; + if( x > bins / 2 ) x -= bins; + float nrm = amp / (var * SQRT2PI ) * expf( - ( x * x ) / ( 2 * var * var ) ); + mark += nrm; + } + return mark; +} + + +#else + +float AttemptDecomposition( float * histogram, int bins, float * out_means, float * out_amps, float * out_sigmas, int cur_dists ); +float CalcHistAt( float pt, int bins, float * out_means, float * out_amps, float * out_sigmas, int cur_dists ); + + +int DecomposeHistogram( float * histogram, int bins, float * out_means, float * out_amps, float * out_sigmas, int max_dists, double default_sigma, int iterations ) +{ + //NOTE: The sigma may change depending on all sorts of things, maybe? + + int sigs = 0; + int i; + + int went_up = 0; + + float vhist = histogram[bins-1]; + + for( i = 0; i <= bins; i++ ) + { + float thishist = histogram[i%bins]; + if( thishist > vhist ) + { + went_up = 1; + } + else if( went_up) + { + went_up = 0; + out_amps[sigs] = thishist / (default_sigma + 1); + out_sigmas[sigs] = default_sigma; + out_means[sigs] = i-0.5; + sigs++; + } + vhist = thishist; + } + if( sigs == 0 ) + return 0; + + int iteration; + float errbest = AttemptDecomposition( histogram, bins, out_means, out_amps, out_sigmas, sigs ); + int dropped[bins]; + for( i = 0; i < bins; i++ ) + { + dropped[i] = 0; + } + + for( iteration = 0; iteration < iterations; iteration++ ) + { + if( dropped[iteration%sigs] ) continue; + //Tweak with the values until they are closer to what we want. + float backup_mean = out_means[iteration%sigs]; + float backup_amps = out_amps[iteration%sigs]; + float backup_sigmas = out_sigmas[iteration%sigs]; + + float mute = 20. / (iteration+20.); +#define RANDFN ((rand()%2)-0.5)*mute +//#define RANDFN ((rand()%10000)-5000.0) / 5000.0 * mute +// out_sigmas[iteration%sigs] += RANDFN; + out_means[iteration%sigs] += RANDFN; + out_amps[iteration%sigs] += RANDFN; + float err = AttemptDecomposition( histogram, bins, out_means, out_amps, out_sigmas, sigs ); + if( err > errbest ) + { + out_means[iteration%sigs] = backup_mean; + out_amps[iteration%sigs] = backup_amps; + out_sigmas[iteration%sigs] = backup_sigmas; + } + else + { + if( out_amps[iteration%sigs] < 0.01 ) + { + dropped[iteration%sigs] = 1; + out_amps[iteration%sigs] = 0.0; + } + errbest = err; + } + + } + +// printf( "%f / %f = %f\n", origerr, errbest, origerr/errbest ); + + return sigs; +} + +float CalcHistAt( float pt, int bins, float * out_means, float * out_amps, float * out_sigmas, int cur_dists ) +{ + int i; + float mark = 0.0; + for( i = 0; i < cur_dists; i++ ) + { + float amp = out_amps[i]; + float mean = out_means[i]; + float var = out_sigmas[i]; + + float x = mean - pt; + if( x < - bins / 2 ) x += bins; + if( x > bins / 2 ) x -= bins; + float nrm = amp / (var * SQRT2PI ) * expf( - ( x * x ) / ( 2 * var * var ) ); + mark += nrm; + } + return mark; +} + +float AttemptDecomposition( float * histogram, int bins, float * out_means, float * out_amps, float * out_sigmas, int cur_dists ) +{ + int i, j; + float hist[bins]; + memcpy( hist, histogram, sizeof(hist) ); + for( i = 0; i < cur_dists; i++ ) + { + float amp = out_amps[i]; + float mean = out_means[i]; + float var = out_sigmas[i]; + + for( j = 0; j < bins; j++ ) + { + float x = mean - j; + if( x < - bins / 2 ) x += bins; + if( x > bins / 2 ) x -= bins; + float nrm = amp / (var * SQRT2PI ) * expf( - ( x * x ) / ( 2 * var * var ) ); + hist[j] -= nrm; + } + } + + float remain = 0; + for( j = 0; j < bins; j++ ) + { + remain += hist[j] * hist[j]; + } + return remain; +} + + +#endif + diff --git a/decompose.h b/decompose.h new file mode 100644 index 0000000..6c5b455 --- /dev/null +++ b/decompose.h @@ -0,0 +1,12 @@ +#ifndef _DECOMPOSE_H +#define _DECOMPOSE_H + +//Decompose a histogram into a series of normal distributions. +int DecomposeHistogram( float * histogram, int bins, float * out_means, float * out_amps, float * out_sigmas, int max_dists, double default_sigma, int iterations ); +float CalcHistAt( float pt, int bins, float * out_means, float * out_amps, float * out_sigmas, int cur_dists ); + + +#define TURBO_DECOMPOSE + +#endif + diff --git a/default.conf b/default.conf new file mode 100644 index 0000000..e6683d9 --- /dev/null +++ b/default.conf @@ -0,0 +1,19 @@ +play=0 +record=1 +samplerate=44100 +buffer=128 +sound_source=PULSE +sourcename=alsa_output.pci-0000_00_1b.0.analog-stereo.monitor +#default + + +amplify=2 +dft_iir=0.7 +dft_q=10 +note_attach_amp_iir=.3 +note_attach_freq_iir=.4 +note_attach_amp_iir2=.2 + +displayname=DisplayShapeDriver +fromsides=1 + diff --git a/dft.c b/dft.c new file mode 100644 index 0000000..1326363 --- /dev/null +++ b/dft.c @@ -0,0 +1,75 @@ +#include "dft.h" +#include +#include + + +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; + for( i = 0; i < bins; i++ ) + { + float freq = frequencies[i]; + float phi = 0; + int sampleplace = place_in_data_buffer; + float advance = 3.14159*2.0/freq; + + float binqtys = 0; + float binqtyc = 0; + + for( j = 0; j <= freq * q; j++ ) + { + float sample = databuffer[sampleplace]; + sampleplace = (sampleplace-1+size_of_data_buffer)%size_of_data_buffer; +//printf( "%d\n", sampleplace ); + float sv = sin( phi ) * sample; + float cv = cos( phi ) * sample; + + binqtys += sv; + binqtyc += cv; + + phi += advance; + } + + float amp = sqrtf( binqtys * binqtys + binqtyc * binqtyc ); + outbins[i] = amp / freq / q; + } +} + +void DoDFTQuick( float * outbins, float * frequencies, int bins, const float * databuffer, int place_in_data_buffer, int size_of_data_buffer, float q, float speedup ) +{ + int i, j; + + for( i = 0; i < bins; i++ ) + { + float freq = frequencies[i]; + float phi = 0; + int ftq = freq * q; + int sampleplace = place_in_data_buffer; + float advance = 3.14159*2.0/freq; + + float binqtys = 0; + float binqtyc = 0; + + int skip = floor( ftq / speedup ); + if( skip == 0 ) skip = 1; + advance *= skip; + + for( j = 0; j <= ftq; j += skip ) + { + float sample = databuffer[sampleplace]; + sampleplace = (sampleplace-skip+size_of_data_buffer)%size_of_data_buffer; +//printf( "%d\n", sampleplace ); + float sv = sinf( phi ) * sample; + float cv = cosf( phi ) * sample; + + binqtys += sv; + binqtyc += cv; + + phi += advance; + } + + float amp = sqrtf( binqtys * binqtys + binqtyc * binqtyc ); + outbins[i] = amp / freq / q * skip; + } +} + diff --git a/dft.h b/dft.h new file mode 100644 index 0000000..7fedc79 --- /dev/null +++ b/dft.h @@ -0,0 +1,16 @@ +#ifndef _DFT_H +#define _DFT_H + +//XXX WARNING: TODO: the last two parameters are a double due to a compiler bug. + +//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. +void DoDFT( float * outbins, float * frequencies, int bins, float * databuffer, int place_in_data_buffer, int size_of_data_buffer, float q ); + +//Skip many of the samples on lower frequencies; TODO: Need to fix the nyquist problem where high frequencies show low-frequency components. +//Speedup = target number of data points +void DoDFTQuick( float * outbins, float * frequencies, int bins, const float * databuffer, int place_in_data_buffer, int size_of_data_buffer, float q, float speedup ); + +#endif + diff --git a/filter.c b/filter.c new file mode 100644 index 0000000..23ab838 --- /dev/null +++ b/filter.c @@ -0,0 +1,57 @@ +#include "filter.h" +#include +#include + + +void FilterFoldedBinsIIRTWOPASS( float * folded, int bins, float iir ) +{ + int i; + float inv = 1.0 - iir; + float v = 0.0; + + //Go through the data once, forward. + for( i = 0; i < bins; i++ ) + { + v = v * iir + folded[i] * inv; + } + + //Start applying filter on second pass. + for( i = 0; i < bins; i++ ) + { + folded[i] = v * iir + folded[i] * inv; + } + + //Go backwards. + v = 0; + for( i = bins-1; i >= 0; i-- ) + { + v = v * iir + folded[i] * inv; + } + + //Start applying filter on second pass. + for( i = bins-1; i >= 0; i-- ) + { + folded[i] = v * iir + folded[i] * inv; + } + +} + + +void FilterFoldedBinsBlob( float * folded, int bins, float strength, int iter ) +{ + float tmp[bins]; + int i; + int j; + for( j = 0; j < iter; j++ ) + { + memcpy( tmp, folded, sizeof( tmp ) ); + for( i = 0; i < bins; i++ ) + { + float left = tmp[(i-1+bins)%bins]; + float right = tmp[(i-1+bins)%bins]; + folded[i] = folded[i] * (1.-strength) + (left + right) * strength * 0.5; + } + } + +} + diff --git a/filter.h b/filter.h new file mode 100644 index 0000000..f8581c3 --- /dev/null +++ b/filter.h @@ -0,0 +1,11 @@ +#ifndef _FILTER_H +#define _FILTER_H + + +//Perform a two-pass filter on the data, circularly. Once right, then left. +void FilterFoldedBinsIIRTWOPASS( float * folded, int bins, float strength ); + +void FilterFoldedBinsBlob( float * folded, int bins, float strength, int iter ); + + +#endif diff --git a/main.c b/main.c new file mode 100644 index 0000000..603c01f --- /dev/null +++ b/main.c @@ -0,0 +1,309 @@ +#include +#include "color.h" +#include +#include +#include "sound.h" +#include "os_generic.h" +#include "DrawFunctions.h" +#include "dft.h" +#include "filter.h" +#include "decompose.h" +#include +#include +#include "notefinder.h" +#include "outdrivers.h" +#include "parameters.h" + +short screenx, screeny; + +int set_screenx = 640; REGISTER_PARAM( set_screenx, PINT ); +int set_screeny = 480; REGISTER_PARAM( set_screeny, PINT ); +char sound_source[16]; REGISTER_PARAM( sound_source, PBUFFER ); + +#define SMARTCPU + +struct NoteFinder * nf; + +//Sound circular buffer +#define SOUNDCBSIZE 65536 +#define MAX_CHANNELS 2 + +double VisTimeEnd, VisTimeStart; +int soundhead = 0; +float sound[MAX_CHANNELS][SOUNDCBSIZE]; +int show_debug = 0; +int show_debug_basic = 1; + +int gKey = 0; + +void HandleKey( int keycode, int bDown ) +{ + char c = toupper( keycode ); + if( c == 'D' && bDown ) show_debug = !show_debug; + if( c == '9' && bDown ) { gKey--; nf->base_hz = 55 * pow( 2, gKey / 12.0 ); ChangeNFParameters( nf ); } + if( c == '-' && bDown ) { gKey++; nf->base_hz = 55 * pow( 2, gKey / 12.0 ); ChangeNFParameters( nf ); } + if( c == '0' && bDown ) { gKey = 0; nf->base_hz = 55 * pow( 2, gKey / 12.0 ); ChangeNFParameters( nf ); } + if( c == 'E' && bDown ) show_debug_basic = !show_debug_basic; + if( keycode == 65307 ) exit( 0 ); + printf( "Key: %d -> %d\n", keycode, bDown ); +} + +void HandleButton( int x, int y, int button, int bDown ) +{ + printf( "Button: %d,%d (%d) -> %d\n", x, y, button, bDown ); +} + +void HandleMotion( int x, int y, int mask ) +{ +} + +//#define HARDWAVE + +void SoundCB( float * out, float * in, int samplesr, int * samplesp, struct SoundDriver * sd ) +{ + int channelin = sd->channelsRec; +// int channelout = sd->channelsPlay; + + //Load the samples into a ring buffer. Split the channels from interleved to one per buffer. + *samplesp = 0; + + int process_channels = (MAX_CHANNELS < channelin)?MAX_CHANNELS:channelin; + +#ifdef HARDWAVE + static double sttf=0; + if( sttf > 200 ) return; +#endif + + int i; + int j; + for( i = 0; i < samplesr; i++ ) + { + for( j = 0; j < process_channels; j++ ) + { + float f = in[i*channelin+j]; + if( f < -1 || f > 1 ) continue; + sound[j][soundhead] = +#ifdef HARDWAVE + sin(sttf+=0.01); +#else + in[i*channelin+j]; +#endif + } + soundhead = (soundhead+1)%SOUNDCBSIZE; + } +} + +int main(int argc, char ** argv) +{ +// const char * OutDriver = "name=LEDOutDriver;leds=512;light_siding=1.9"; + const char * InitialFile = "default.conf"; + int i; + + if( argc > 1 ) + { + InitialFile = "default.conf"; + } + + { + char * buffer; + int r; + FILE * f = fopen( InitialFile, "rb" ); + if( !f ) + { + fprintf( stderr, "Warning: cannot open %s.\n", InitialFile ); + } + else + { + fseek( f, 0, SEEK_END ); + int size = ftell( f ); + fseek( f, 0, SEEK_SET ); + buffer = malloc( size + 1 ); + r = fread( buffer, size, 1, f ); + fclose( f ); + buffer[size] = 0; + if( r != 1 ) + { + fprintf( stderr, "Warning: %d bytes read. Expected: %d from file %s\n", r, size, InitialFile ); + } + else + { + SetParametersFromString( buffer ); + } + free( buffer ); + } + } + + if( argc > 2 ) + { + SetParametersFromString( argv[1] ); + } + + //Initialize Rawdraw + int frames = 0; + double ThisTime; + double LastFPSTime = OGGetAbsoluteTime(); + double LastFrameTime = OGGetAbsoluteTime(); + double SecToWait; + CNFGBGColor = 0x800000; + CNFGDialogColor = 0x444444; + CNFGSetup( "ColorChord Test", set_screenx, set_screeny ); + struct DriverInstances * outdriver = SetupOutDriver( ); + + //Initialize Sound + struct SoundDriver * sd = InitSound( sound_source, &SoundCB ); + + if( !sd ) + { + fprintf( stderr, "ERROR: Failed to initialize sound output device\n" ); + return -1; + } + + nf = CreateNoteFinder( sd->spsRec ); + + + while(1) + { + char stt[1024]; + //Handle Rawdraw frame swappign + CNFGHandleInput(); + CNFGClearFrame(); + CNFGColor( 0xFFFFFF ); + CNFGGetDimensions( &screenx, &screeny ); + + RunNoteFinder( nf, sound[0], (soundhead-1+SOUNDCBSIZE)%SOUNDCBSIZE, SOUNDCBSIZE ); + //Done all ColorChord work. + + VisTimeStart = OGGetAbsoluteTime(); + if( outdriver ) + outdriver->Func( outdriver->id, nf ); + VisTimeEnd = OGGetAbsoluteTime(); + + //Handle outputs. + int freqbins = nf->freqbins; + int note_peaks = freqbins/2; + int freqs = freqbins * nf->octaves; + //int maxdists = freqbins/2; + + //Do a bunch of debugging. + if( show_debug_basic ) + { + for( i = 0; i < nf->dists; i++ ) + { + CNFGPenX = (nf->dist_means[i] + 0.5) / freqbins * screenx; //Move over 0.5 for visual purposes. The means is correct. + CNFGPenY = 400-nf->dist_amps[i] * 150.0 / nf->dist_sigmas[i]; + //printf( "%f %f\n", dist_means[i], dist_amps[i] ); + sprintf( stt, "%f\n%f\n", nf->dist_means[i], nf->dist_amps[i] ); + CNFGDrawText( stt, 2 ); + } + CNFGColor( 0xffffff ); + + //Draw the folded bins + for( i = 0; i < freqbins; i++ ) + { + float x0 = i / (float)freqbins * (float)screenx; + float x1 = (i+1) / (float)freqbins * (float)screenx; + float amp = nf->folded_bins[i] * 250.0; + CNFGDialogColor = CCtoHEX( ((float)(i+0.5) / freqbins), 1.0, 1.0 ); + CNFGDrawBox( x0, 400-amp, x1, 400 ); + } + CNFGDialogColor = 0xf0f000; + + for( i = 0; i < note_peaks; i++ ) + { + //printf( "%f %f /", note_positions[i], note_amplitudes[i] ); + if( nf->note_amplitudes_out[i] < 0 ) continue; + CNFGDialogColor = CCtoHEX( (nf->note_positions[i] / freqbins), 1.0, 1.0 ); + CNFGDrawBox( ((float)i / note_peaks) * screenx, 480 - nf->note_amplitudes_out[i] * 100, ((float)(i+1) / note_peaks) * screenx, 480 ); + CNFGPenX = ((float)(i+.4) / note_peaks) * screenx; + CNFGPenY = screeny - 30; + sprintf( stt, "%d\n%0.0f", nf->enduring_note_id[i], nf->note_amplitudes2[i]*1000.0 ); + CNFGDrawText( stt, 2 ); + + } + + //Let's draw the o-scope. + int thissoundhead = soundhead; + thissoundhead = (thissoundhead-1+SOUNDCBSIZE)%SOUNDCBSIZE; + int lasty = sound[0][thissoundhead] * 128 + 128; thissoundhead = (thissoundhead-1+SOUNDCBSIZE)%SOUNDCBSIZE; + int thisy = sound[0][thissoundhead] * 128 + 128; thissoundhead = (thissoundhead-1+SOUNDCBSIZE)%SOUNDCBSIZE; + for( i = 0; i < screenx; i++ ) + { + CNFGTackSegment( i, lasty, i+1, thisy ); + lasty = thisy; + thisy = sound[0][thissoundhead] * 128 + 128; thissoundhead = (thissoundhead-1+SOUNDCBSIZE)%SOUNDCBSIZE; + } + } + + //Extra debugging? + if( show_debug ) + { + //Draw the histogram + float lasthistval; + CNFGColor( 0xffffff ); + + for( i = -1; i < screenx; i++ ) + { + float thishistval = CalcHistAt( (float)i/(float)screenx*freqbins-0.5, nf->freqbins, nf->dist_means, nf->dist_amps, nf->dist_sigmas, nf->dists ); + if( i >= 0 ) + CNFGTackSegment( i, 400-lasthistval * 250.0, i+1, 400-thishistval * 250.0 ); + lasthistval = thishistval; + } + + CNFGColor( 0xffffff ); + + //Draw the bins + for( i = 0; i < freqs; i++ ) + { + float x0 = i / (float)freqs * (float)screenx; + float x1 = (i+1) / (float)freqs * (float)screenx; + float amp = nf->outbins[i] * 250.0; + CNFGDialogColor = CCtoHEX( ((float)i / freqbins), 1.0, 1.0 ); + CNFGDrawBox( x0, 0, x1, amp ); + } + CNFGDialogColor = 0x0f0f0f; + + char stdebug[1024]; + sprintf( stdebug, "DFT:%8.2fms\nFLT:%8.2f\nDEC:%8.2f\nFNL:%8.2f\nDPY:%8.2f", + (nf->DFTTime - nf->StartTime)*1000, + (nf->FilterTime - nf->DFTTime)*1000, + (nf->DecomposeTime - nf->FilterTime)*1000, + (nf->FinalizeTime - nf->DecomposeTime)*1000, + (VisTimeEnd - VisTimeStart)*1000 ); + CNFGPenX = 50; + CNFGPenY = 50; + CNFGDrawText( stdebug, 2 ); + } + + CNFGColor( show_debug?0xffffff:0x000000 ); + CNFGPenX = 0; CNFGPenY = screeny-10; + CNFGDrawText( "Extra Debug (D)", 2 ); + + CNFGColor( show_debug_basic?0xffffff:0x000000 ); + CNFGPenX = 120; CNFGPenY = screeny-10; + CNFGDrawText( "Basic Debug (E)", 2 ); + + CNFGColor( show_debug_basic?0xffffff:0x000000 ); + CNFGPenX = 240; CNFGPenY = screeny-10; + sprintf( stt, "[9] Key: %d [0] (%3.1f) [-]", gKey, nf->base_hz ); + CNFGDrawText( stt, 2 ); + + //Finish Rawdraw with FPS counter, and a nice delay loop. + frames++; + CNFGSwapBuffers(); + ThisTime = OGGetAbsoluteTime(); + if( ThisTime > LastFPSTime + 1 ) + { + printf( "FPS: %d\n", frames ); + frames = 0; + LastFPSTime+=1; + } +#ifdef SMARTCPU + SecToWait = .016 - ( ThisTime - LastFrameTime ); + LastFrameTime += .016; + if( SecToWait > 0 ) + OGUSleep( (int)( SecToWait * 1000000 ) ); +#endif + } + +} + diff --git a/notefinder.c b/notefinder.c new file mode 100644 index 0000000..dfccf13 --- /dev/null +++ b/notefinder.c @@ -0,0 +1,350 @@ +#include "notefinder.h" +#include "parameters.h" +#include +#include +#include +#include +#include "util.h" +#include "dft.h" +#include "filter.h" +#include "decompose.h" +#include "sort.h" + +struct NoteFinder * CreateNoteFinder( int spsRec ) +{ + struct NoteFinder * ret = malloc( sizeof( struct NoteFinder ) ); + memset( ret, 0, sizeof( struct NoteFinder ) ); + ret->sps_rec = spsRec; + + //Set all default values before passing off to the parameter changer. + ret->octaves = 5; + ret->freqbins = 24; + ret->base_hz = 55; + ret->filter_strength = .5; + ret->filter_iter = 1; + ret->decompose_iterations = 1000; + ret->dft_speedup = 300; + ret->dft_q = 16; + ret->default_sigma = 1.4; + ret->note_jumpability = 2.5; + ret->note_combine_distance = 0.5; + ret->note_attach_freq_iir = 0.4; + ret->note_attach_amp_iir = 0.2; + ret->note_attach_amp_iir2 = 0.05; + ret->note_min_amplitude = 0.001; + ret->note_minimum_new_distribution_value = 0.02; + ret->note_out_chop = 0.1; + ret->dft_iir = 0.0; + ret->current_note_id = 1; + ret->amplify = 1; + + ret->ofreqs = 0; //force a re-init. + + RegisterValue( "octaves", PINT, &ret->octaves, sizeof( ret->octaves ) ); + RegisterValue( "freqbins", PINT, &ret->freqbins, sizeof( ret->freqbins ) ); + RegisterValue( "base_hz", PFLOAT, &ret->base_hz, sizeof( ret->base_hz ) ); + RegisterValue( "filter_strength", PFLOAT, &ret->filter_strength, sizeof( ret->filter_strength ) ); + RegisterValue( "filter_iter", PINT, &ret->filter_iter, sizeof( ret->filter_iter ) ); + RegisterValue( "decompose_iterations", PINT, &ret->decompose_iterations, sizeof( ret->decompose_iterations ) ); + RegisterValue( "amplify", PFLOAT, &ret->amplify, sizeof( ret->amplify ) ); + RegisterValue( "dft_speedup", PFLOAT, &ret->dft_speedup, sizeof( ret->dft_speedup ) ); + RegisterValue( "dft_q", PFLOAT, &ret->dft_q, sizeof( ret->dft_q ) ); + RegisterValue( "default_sigma", PFLOAT, &ret->default_sigma, sizeof( ret->default_sigma ) ); + RegisterValue( "note_jumpability", PFLOAT, &ret->note_jumpability, sizeof( ret->note_jumpability ) ); + RegisterValue( "note_combine_distance", PFLOAT, &ret->note_combine_distance, sizeof( ret->note_combine_distance ) ); + RegisterValue( "note_attach_freq_iir", PFLOAT, &ret->note_attach_freq_iir, sizeof( ret->note_attach_freq_iir ) ); + RegisterValue( "note_attach_amp_iir", PFLOAT, &ret->note_attach_amp_iir, sizeof( ret->note_attach_amp_iir ) ); + RegisterValue( "note_attach_amp_iir2", PFLOAT, &ret->note_attach_amp_iir2, sizeof( ret->note_attach_amp_iir2 ) ); + RegisterValue( "note_minimum_new_distribution_value", PFLOAT, &ret->note_minimum_new_distribution_value, sizeof( ret->note_minimum_new_distribution_value ) ); + RegisterValue( "note_out_chop", PFLOAT, &ret->note_out_chop, sizeof( ret->note_out_chop ) ); + RegisterValue( "dft_iir", PFLOAT, &ret->dft_iir, sizeof( ret->dft_iir ) ); + + AddCallback( "freqbins", ChangeNFParameters, ret ); + AddCallback( "octaves", ChangeNFParameters, ret ); + AddCallback( "base_hz", ChangeNFParameters, ret ); + + ChangeNFParameters( ret ); + + return ret; +} + +void ChangeNFParameters( void * v ) +{ + //int ofreqs = ret->freqbins * ret->octaves; + int i; + struct NoteFinder * ret = (struct NoteFinder*)v; +/* + char t[128]; + ret->octaves = atoi_del( GetParamStr( parameters, "octaves", setstr( ret->octaves, t ) ) ); + ret->freqbins = atoi_del( GetParamStr( parameters, "freqbins", setstr( ret->freqbins, t ) ) ); + ret->base_hz = atof_del( GetParamStr( parameters, "base_hz", setstr( ret->base_hz, t ) ) ); + ret->filter_strength = atof_del( GetParamStr( parameters, "filter_strength", setstr( ret->filter_strength, t ) ) ); + ret->filter_iter = atoi_del( GetParamStr( parameters, "filter_iter", setstr( ret->filter_iter, t ) ) ); + ret->decompose_iterations = atoi_del( GetParamStr( parameters, "decompose_iterations", setstr( ret->decompose_iterations, t ) ) ); + ret->dft_speedup = atof_del( GetParamStr( parameters, "dft_speedup", setstr( ret->dft_speedup, t ) ) ); + ret->dft_q = atof_del( GetParamStr( parameters, "dft_q", setstr( ret->dft_q, t ) ) ); + ret->default_sigma = atof_del( GetParamStr( parameters, "default_sigma", setstr( ret->default_sigma, t ) ) ); + ret->note_jumpability = atof_del( GetParamStr( parameters, "note_jumpability", setstr( ret->note_jumpability, t ) ) ); + ret->note_combine_distance = atof_del( GetParamStr( parameters, "note_combine_distance", setstr( ret->note_combine_distance, t ) ) ); + ret->note_attach_freq_iir = atof_del( GetParamStr( parameters, "note_attach_freq_iir", setstr( ret->note_attach_freq_iir, t ) ) ); + ret->note_attach_amp_iir = atof_del( GetParamStr( parameters, "note_attach_amp_iir", setstr( ret->note_attach_amp_iir, t ) ) ); + ret->note_attach_amp_iir2 = atof_del( GetParamStr( parameters, "note_attach_amp_iir2", setstr( ret->note_attach_amp_iir2, t ) ) ); + ret->note_min_amplitude = atof_del( GetParamStr( parameters, "note_min_amplitude", setstr( ret->note_min_amplitude, t ) ) ); + ret->note_minimum_new_distribution_value = atof_del( GetParamStr( parameters, "note_minimum_new_distribution_value", setstr( ret->note_minimum_new_distribution_value, t ) ) ); + ret->note_out_chop = atof_del( GetParamStr( parameters, "note_out_chop", setstr( ret->note_out_chop, t ) ) ); + ret->dft_iir = atof_del( GetParamStr( parameters, "dft_iir", setstr( ret->dft_iir, t ) ) ); + ret->sps_rec = atof_del( GetParamStr( parameters, "sps_rec", setstr( ret->sps_rec, t ) ) ); + ret->amplify = atof_del( GetParamStr( parameters, "amplify", setstr( ret->amplify, t ) ) ); +*/ + + +printf( "%d %d %f %f %f\n", ret->freqbins, ret->octaves, ret->base_hz, ret->dft_q, ret->amplify ); + + + + + int freqs = ret->freqbins * ret->octaves; + + if( freqs != ret->ofreqs ) + { + int note_peaks = ret->freqbins/2; + int maxdists = ret->freqbins/2; + ret->note_peaks = note_peaks; + + if( ret->note_positions ) free( ret->note_positions ); + ret->note_positions = calloc( 1, sizeof( float ) * note_peaks ); + + if( ret->note_amplitudes ) free( ret->note_amplitudes ); + ret->note_amplitudes = calloc( 1, sizeof( float ) * note_peaks ); + + if( ret->note_amplitudes_out ) free( ret->note_amplitudes_out ); + ret->note_amplitudes_out = calloc( 1, sizeof( float ) * note_peaks ); + + if( ret->note_amplitudes2 ) free( ret->note_amplitudes2 ); + ret->note_amplitudes2 = calloc( 1, sizeof( float ) * note_peaks ); + + if( ret->note_peaks_to_dists_mapping ) free( ret->note_peaks_to_dists_mapping ); + ret->note_peaks_to_dists_mapping = calloc( 1, sizeof( char ) * note_peaks ); + + if( ret->enduring_note_id ) free( ret->enduring_note_id ); + ret->enduring_note_id = calloc( 1, sizeof( int ) * note_peaks ); + + if( ret->note_founds ) free( ret->note_founds ); + ret->note_founds = calloc( 1, sizeof( unsigned char ) * note_peaks ); + + if( ret->frequencies ) free( ret->frequencies ); + ret->frequencies = calloc( 1, sizeof( float ) * freqs ); + if( ret->outbins ) free( ret->outbins ); + ret->outbins = calloc( 1, sizeof( float ) * freqs ); + if( ret->folded_bins ) free( ret->folded_bins ); + ret->folded_bins = calloc( 1, sizeof( float ) * ret->freqbins ); + + + if( ret->dist_amps ) free( ret->dist_amps ); + ret->dist_amps = calloc( 1, sizeof( float ) * maxdists ); + + if( ret->dist_means ) free( ret->dist_means ); + ret->dist_means = calloc( 1, sizeof( float ) * maxdists ); + + if( ret->dist_sigmas ) free( ret->dist_sigmas ); + ret->dist_sigmas = calloc( 1, sizeof( float ) * maxdists ); + + if( ret->dist_takens ) free( ret->dist_takens ); + ret->dist_takens = calloc( 1, sizeof( unsigned char ) * maxdists ); + + ret->ofreqs = freqs; + } + + for( i = 0; i < freqs; i++ ) + { + ret->frequencies[i] = ( ret->sps_rec / ret->base_hz ) / pow( 2, (float)i / ret->freqbins ); + } + +} + +void RunNoteFinder( struct NoteFinder * nf, const float * audio_stream, int head, int buffersize ) +{ + int i, j; + int freqbins = nf->freqbins; + int note_peaks = freqbins/2; + int freqs = freqbins * nf->octaves; + int maxdists = freqbins/2; + float dftbins[freqs]; + + //Now, march onto the DFT, this pulls out the bins we're after. + //This DFT function does not wavelet or anything. + nf->StartTime = OGGetAbsoluteTime(); + + DoDFTQuick( dftbins, nf->frequencies, freqs, audio_stream, head, buffersize, nf->dft_q, nf->dft_speedup ); + nf->DFTTime = OGGetAbsoluteTime(); + + for( i = 0; i < freqs; i++ ) + { + nf->outbins[i] = nf->outbins[i] * (nf->dft_iir) + (dftbins[i] * (1.-nf->dft_iir) * nf->amplify); + } + + + //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; + } + + //Combine the bins into folded bins. + for( i = 0; i < freqbins; i++ ) + { + float amp = 0; + for( j = 0; j < nf->octaves; j++ ) + { + amp += nf->outbins[i+j*freqbins]; + } + nf->folded_bins[i] = amp; + } + + FilterFoldedBinsBlob( nf->folded_bins, freqbins, nf->filter_strength, nf->filter_iter ); + + nf->FilterTime = OGGetAbsoluteTime(); + + memset( nf->dist_takens, 0, sizeof( unsigned char ) * maxdists ); + nf->dists = DecomposeHistogram( nf->folded_bins, freqbins, nf->dist_means, nf->dist_amps, nf->dist_sigmas, maxdists, nf->default_sigma, nf->decompose_iterations ); + { + int dist_sorts[nf->dists]; + SortFloats( dist_sorts, nf->dist_amps, nf->dists ); + RemapFloats( dist_sorts, nf->dist_amps, nf->dists ); + RemapFloats( dist_sorts, nf->dist_means, nf->dists ); + RemapFloats( dist_sorts, nf->dist_sigmas, nf->dists ); + } + nf->DecomposeTime = OGGetAbsoluteTime(); + + + //We now have the positions and amplitudes of the normal distributions that comprise our spectrum. IN SORTED ORDER! + //dists = # of distributions + //dist_amps[] = amplitudes of the normal distributions + //dist_means[] = positions of the normal distributions + + //We need to use this in a filtered manner to obtain the "note" peaks + //note_peaks = total number of peaks. + //note_positions[] = position of the note on the scale. + //note_amplitudes[] = amplitudes of these note peaks. + memset( nf->note_founds, 0, sizeof( unsigned char ) * note_peaks ); + + //First try to find any close peaks. + for( i = 0; i < note_peaks; i++ ) + { + for( j = 0; j < nf->dists; j++ ) + { + if( !nf->dist_takens[j] && !nf->note_founds[i] && fabsloop( nf->note_positions[i], nf->dist_means[j], freqbins ) < nf->note_jumpability ) + { + //Attach ourselves to this bin. + nf->note_peaks_to_dists_mapping[i] = j; + nf->dist_takens[j] = 1; + if( nf->enduring_note_id[i] == 0 ) + nf->enduring_note_id[i] = nf->current_note_id++; + nf->note_founds[i] = 1; + nf->note_positions[i] = avgloop( nf->note_positions[i], (1.-nf->note_attach_freq_iir), nf->dist_means[j], nf->note_attach_freq_iir, nf->freqbins); + //I guess you can't IIR this like normal. + ////note_positions[i] * (1.-note_attach_freq_iir) + dist_means[j] * note_attach_freq_iir; + + nf->note_amplitudes[i] = nf->note_amplitudes[i] * (1.-nf->note_attach_amp_iir) + nf->dist_amps[j] * nf->note_attach_amp_iir; + //XXX TODO: Consider: Always boost power, never reduce? +// if( dist_amps[i] > note_amplitudes[i] ) +// note_amplitudes[i] = dist_amps[i]; + } + } + } + + //Combine like-notes. + for( i = 0; i < note_peaks; i++ ) + { + for( j = 0; j < note_peaks; j++ ) + { + if( i == j ) continue; + if( fabsloop( nf->note_positions[i], nf->note_positions[j], nf->freqbins ) < nf->note_combine_distance && + nf->note_amplitudes[i] > 0.0 && + nf->note_amplitudes[j] > 0.0 ) + { + int a; + int b; + if( nf->note_amplitudes[i] > nf->note_amplitudes[j] ) + { + a = i; + b = j; + } + else + { + b = i; + a = j; + } + //Combine B into A. + nf->note_amplitudes[a] += nf->note_amplitudes[b]; + nf->note_positions[a] = avgloop( nf->note_positions[a], nf->note_amplitudes[a], nf->note_positions[b], nf->note_amplitudes[b], freqbins ); + nf->note_amplitudes[b] = 0; + nf->note_positions[b] = -100; + nf->enduring_note_id[b] = 0; + } + } + } + + //Assign dead or decayed notes to new peaks. + for( i = 0; i < note_peaks; i++ ) + { + if( nf->note_amplitudes[i] < nf->note_min_amplitude ) + { + nf->enduring_note_id[i] = 0; + + //Find a new peak for this note. + for( j = 0; j < nf->dists; j++ ) + { + if( !nf->dist_takens[j] && nf->dist_amps[j] > nf->note_minimum_new_distribution_value ) + { + nf->enduring_note_id[i] = nf->current_note_id++; + nf->dist_takens[j] = 1; + nf->note_amplitudes[i] = nf->dist_amps[j];//min_note_amplitude + dist_amps[j] * note_attach_amp_iir; //TODO: Should this jump? + nf->note_positions[i] = nf->dist_means[j]; + nf->note_founds[i] = 1; + nf->dist_takens[j] = 1; + } + } + } + } + + //Any remaining notes that could not find a peak good enough must be decayed to oblivion. + for( i = 0; i < note_peaks; i++ ) + { + if( !nf->note_founds[i] ) + { + nf->note_amplitudes[i] = nf->note_amplitudes[i] * (1.-nf->note_attach_amp_iir); + } + + nf->note_amplitudes2[i] = nf->note_amplitudes2[i] * (1.-nf->note_attach_amp_iir2) + nf->note_amplitudes[i] * nf->note_attach_amp_iir2; + + if( nf->note_amplitudes[i] < nf->note_min_amplitude ) + { + nf->note_amplitudes[i] = 0; + nf->note_amplitudes2[i] = 0; + } + } + + for( i = 0; i < note_peaks; i++ ) + { + nf->note_amplitudes_out[i] = nf->note_amplitudes[i] - nf->note_out_chop; + if( nf->note_amplitudes_out[i] < 0 ) + { + nf->note_amplitudes_out[i] = 0; + } + } + + //We now have our "notes" + //Notes are made of "note_amplitudes" and "note_positions" in the scale to 0..freqbins + //They are stable but in an arbitrary order. + + + nf->FinalizeTime = OGGetAbsoluteTime(); +} + + diff --git a/notefinder.h b/notefinder.h new file mode 100644 index 0000000..f42d4b4 --- /dev/null +++ b/notefinder.h @@ -0,0 +1,93 @@ +#ifndef _NOTEFINDER_H +#define _NOTEFINDER_H + +#include "os_generic.h" + + +struct NoteFinder +{ + //Setup DFT Bins + int ofreqs; + int octaves;// = 5; + int freqbins;// = 24; + int note_peaks; //Calculated from freqbins (not configurable) + float base_hz;// = 55; + float filter_strength;// = .5; //0=Disabled. + int filter_iter;// = 1; + int decompose_iterations;// = 1000; + float amplify; // =1 (amplify input across the board) + + //at 300, there is still some minimal aliasing at higher frequencies. Increase this for less low-end distortion + //NOTE: This /should/ get fixed, as we /should/ be decimating the input data intelligently with lower octaves. + float dft_speedup;// = 300; + + //The "tightness" of the curve, or how many samples back to look? + float dft_q;// = 16; + + float dft_iir; //IIR to impose the output of the IIR. + + //This controls the expected shape of the normal distributions. I am not sure how to calculate this from samplerate, Q and bins. + float default_sigma;// = 1.4;//freqbins/dft_q/1.6; //Guess? This happens to work out well? + + float note_jumpability;// = 2.5; //How far established notes are allowed to "jump" in order to attach themselves to a new "peak" + float note_combine_distance;// = 0.5; //How close established notes need to be to each other before they can be "combined" into a single note. + float note_attach_freq_iir;// = 0.2; + float note_attach_amp_iir;// = 0.2; + float note_attach_amp_iir2;// = 0.1; + float note_min_amplitude;// = 0.02; //What is considered a "dead" light? + float note_minimum_new_distribution_value;// = 0.02; //A distribution must be /this/ big otherwise, it will be discarded. + + float note_out_chop;// = .1; (How much to decimate the output notes to reduce spurious noise) + + float sps_rec; //samples per second + + //For the "note_" section, the arrays are of size (freqbins/2) + //OUTPUTS: You probably want these; the amplitude and frequency of each note the system found. + float * note_positions; //Position of note, in 0..freqbins frequency. [note_peaks] + float * note_amplitudes_out; //Amplitude of note (after "chop") [note_peaks] + float * note_amplitudes2; //Amplitude of note (after "chop") [note_peaks] (using iir2) + + //Other note informations + float * note_amplitudes; //Amplitude of note (before "chop") [note_peaks] + unsigned char * note_founds; //Array of whether or note a note is taken by a frequency normal distribution [note_peaks] + + //Utility to search from [note_peaks] as index -> nf->dists as value. + //This makes it possible to read dist_amps[note_peaks_to_dists_mapping[note]]. + char * note_peaks_to_dists_mapping; + + //Enduring note id: From frame to frame, this will keep values the same. That way you can know if a note has changed. + int * enduring_note_id; //If value is 0, it is not in use. [note_peaks] + int current_note_id; + + //What frequency each one of the unfolded bins are (in 1/sps's) + float * frequencies; + + //The unfolded spectrum. + float * outbins; + + //The folded output of the unfolded spectrum. + float * folded_bins; + + + //Dists: These things are the distributions that are found, they are very fickle and change frequently. + int dists; //# of distributions (these are the precursors to notes - it is trying to find the normal distributions.) + float * dist_amps; //Amplitude of normal distribution... [nf->dists] + float * dist_means; //Mean of normal distribution... [nf->dists] + float * dist_sigmas; //Sigma of normal distribution... [nf->dists] + unsigned char * dist_takens; //Which distributions are now associated with notes + + + //For profiling, all in absolute time in seconds. + double StartTime; + double DFTTime; + double FilterTime; + double DecomposeTime; + double FinalizeTime; +}; + +struct NoteFinder * CreateNoteFinder( int spsRec ); //spsRec = 44100, etc. +void ChangeNFParameters( void * v ); +void RunNoteFinder( struct NoteFinder * nf, const float * audio_stream, int head, int buffersize ); + +#endif + diff --git a/os_generic.c b/os_generic.c new file mode 100644 index 0000000..f268cd2 --- /dev/null +++ b/os_generic.c @@ -0,0 +1,342 @@ +#include "os_generic.h" + + +#ifdef USE_WINDOWS + +#include + +void OGSleep( int is ) +{ + Sleep( is*1000 ); +} + +void OGUSleep( int ius ) +{ + Sleep( ius/1000 ); +} + +double OGGetAbsoluteTime() +{ + static LARGE_INTEGER lpf; + LARGE_INTEGER li; + + if( !lpf.QuadPart ) + { + QueryPerformanceFrequency( &lpf ); + } + + QueryPerformanceCounter( &li ); + return (double)li.QuadPart / (double)lpf.QuadPart; +} + + +double OGGetFileTime( const char * file ) +{ + FILETIME ft; + + HANDLE h = CreateFile(file, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + + if( h==INVALID_HANDLE_VALUE ) + return -1; + + GetFileTime( h, 0, 0, &ft ); + + CloseHandle( h ); + + return ft.dwHighDateTime + ft.dwLowDateTime; +} + + +og_thread_t OGCreateThread( void * (function)( void * ), void * parameter ) +{ + return (og_thread_t)CreateThread( 0, 0, (LPTHREAD_START_ROUTINE)function, parameter, 0, 0 ); +} + +void * OGJoinThread( og_thread_t ot ) +{ + WaitForSingleObject( ot, INFINITE ); + CloseHandle( ot ); +} + +void OGCancelThread( og_thread_t ot ) +{ + CloseHandle( ot ); +} + +og_mutex_t OGCreateMutex() +{ + return CreateMutex( 0, 0, 0 ); +} + +void OGLockMutex( og_mutex_t om ) +{ + WaitForSingleObject(om, INFINITE); +} + +void OGUnlockMutex( og_mutex_t om ) +{ + ReleaseMutex(om); +} + +void OGDeleteMutex( og_mutex_t om ) +{ + CloseHandle( om ); +} + + + +og_sema_t OGCreateSema() +{ + HANDLE sem = CreateSemaphore( 0, 0, 32767, 0 ); + return (og_sema_t)sem; +} + + + +typedef LONG NTSTATUS; + +typedef NTSTATUS (NTAPI *_NtQuerySemaphore)( + HANDLE SemaphoreHandle, + DWORD SemaphoreInformationClass, /* Would be SEMAPHORE_INFORMATION_CLASS */ + PVOID SemaphoreInformation, /* but this is to much to dump here */ + ULONG SemaphoreInformationLength, + PULONG ReturnLength OPTIONAL +); + + +typedef struct _SEMAPHORE_BASIC_INFORMATION { + ULONG CurrentCount; + ULONG MaximumCount; +} SEMAPHORE_BASIC_INFORMATION; + + +int OGGetSema( og_sema_t os ) +{ + HANDLE sem = (HANDLE)os; + + static _NtQuerySemaphore NtQuerySemaphore; + SEMAPHORE_BASIC_INFORMATION BasicInfo; + NTSTATUS Status; + + if( !NtQuerySemaphore ) + { + NtQuerySemaphore = (_NtQuerySemaphore)GetProcAddress (GetModuleHandle ("ntdll.dll"), "NtQuerySemaphore"); + if( !NtQuerySemaphore ) + { + return -1; + } + } + + + Status = NtQuerySemaphore (sem, 0 /*SemaphoreBasicInformation*/, + &BasicInfo, sizeof (SEMAPHORE_BASIC_INFORMATION), NULL); + + if (Status == ERROR_SUCCESS) + { + return BasicInfo.CurrentCount; + } + + return -2; +} + +void OGLockSema( og_sema_t os ) +{ + WaitForSingleObject( (HANDLE)os, INFINITE ); +} + +void OGUnlockSema( og_sema_t os ) +{ + ReleaseSemaphore( (HANDLE)os, 1, 0 ); +} + +void OGDeleteSema( og_sema_t os ) +{ + CloseHandle( os ); +} + +#else + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include +#include +#include +#include +#include +#include + +pthread_mutex_t g_RawMutexStart = PTHREAD_MUTEX_INITIALIZER; + +void OGSleep( int is ) +{ + sleep( is ); +} + +void OGUSleep( int ius ) +{ + usleep( ius ); +} + +double OGGetAbsoluteTime() +{ + struct timeval tv; + gettimeofday( &tv, 0 ); + return ((double)tv.tv_usec)/1000000. + (tv.tv_sec); +} + +double OGGetFileTime( const char * file ) +{ + struct stat buff; + + int r = stat( file, &buff ); + + if( r < 0 ) + { + return -1; + } + + return buff.st_mtime; +} + + + +og_thread_t OGCreateThread( void * (routine)( void * ), void * parameter ) +{ + pthread_t * ret = malloc( sizeof( pthread_t ) ); + int r = pthread_create( ret, 0, routine, parameter ); + if( r ) + { + free( ret ); + return 0; + } + return (og_thread_t)ret; +} + +void * OGJoinThread( og_thread_t ot ) +{ + void * retval; + if( !ot ) + { + return 0; + } + pthread_join( *(pthread_t*)ot, &retval ); + free( ot ); + return retval; +} + +void OGCancelThread( og_thread_t ot ) +{ + if( !ot ) + { + return; + } + pthread_cancel( *(pthread_t*)ot ); + free( ot ); +} + +og_mutex_t OGCreateMutex() +{ + pthread_mutexattr_t mta; + og_mutex_t r = malloc( sizeof( pthread_mutex_t ) ); + + pthread_mutexattr_init(&mta); + pthread_mutexattr_settype(&mta, PTHREAD_MUTEX_RECURSIVE); + + pthread_mutex_init( (pthread_mutex_t *)r, &mta ); + + return r; +} + +void OGLockMutex( og_mutex_t om ) +{ + if( !om ) + { + return; + } + pthread_mutex_lock( (pthread_mutex_t*)om ); +} + +void OGUnlockMutex( og_mutex_t om ) +{ + if( !om ) + { + return; + } + pthread_mutex_unlock( (pthread_mutex_t*)om ); +} + +void OGDeleteMutex( og_mutex_t om ) +{ + if( !om ) + { + return; + } + + pthread_mutex_destroy( (pthread_mutex_t*)om ); + free( om ); +} + + + + +og_sema_t OGCreateSema() +{ + sem_t * sem = malloc( sizeof( sem_t ) ); + sem_init( sem, 0, 0 ); + return (og_sema_t)sem; +} + +int OGGetSema( og_sema_t os ) +{ + int valp; + sem_getvalue( os, &valp ); + return valp; +} + + +void OGLockSema( og_sema_t os ) +{ + sem_wait( os ); +} + +void OGUnlockSema( og_sema_t os ) +{ + sem_post( os ); +} + +void OGDeleteSema( og_sema_t os ) +{ + sem_destroy( os ); + free(os); +} + + + +#endif + +//Date Stamp: 2012-02-15 + +/* + Copyright (c) 2011-2012 <>< Charles Lohr + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of this file. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + diff --git a/os_generic.h b/os_generic.h new file mode 100644 index 0000000..8ac2239 --- /dev/null +++ b/os_generic.h @@ -0,0 +1,82 @@ +#ifndef _OS_GENERIC_H +#define _OS_GENERIC_H + +#ifdef WIN32 +#define USE_WINDOWS +#endif + + +#ifdef __cplusplus +extern "C" { +#endif + +#define EXECUTE_AT_BOOT( x, y ) \ + \ + void fn##x() __attribute__((constructor)); \ + void fn##x() \ + { y; } \ + +//Things that shouldn't be macro'd +double OGGetAbsoluteTime(); +void OGSleep( int is ); +void OGUSleep( int ius ); +double OGGetFileTime( const char * file ); + +//Threads and Mutices +typedef void* og_thread_t; +typedef void* og_mutex_t; +typedef void* og_sema_t; + +og_thread_t OGCreateThread( void * (routine)( void * ), void * parameter ); +void * OGJoinThread( og_thread_t ot ); +void OGCancelThread( og_thread_t ot ); + +//Always a recrusive mutex. +og_mutex_t OGCreateMutex(); +void OGLockMutex( og_mutex_t om ); +void OGUnlockMutex( og_mutex_t om ); +void OGDeleteMutex( og_mutex_t om ); + +//Always a semaphore +og_sema_t OGCreateSema(); //Create a semaphore, comes locked initially. NOTE: Max count is 32767 +void OGLockSema( og_sema_t os ); +int OGGetSema( og_sema_t os ); //if <0 there was a failure. +void OGUnlockSema( og_sema_t os ); +void OGDeleteSema( og_sema_t os ); + +#ifdef __cplusplus +}; +#endif + + + +#endif + + +//Date Stamp: 2014-06-12 + +/* + NOTE: Portions (namely the top section) are part of headers from other + sources. + + Copyright (c) 2011-2012 <>< Charles Lohr + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of this file. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + diff --git a/outdrivers.c b/outdrivers.c new file mode 100644 index 0000000..a14330a --- /dev/null +++ b/outdrivers.c @@ -0,0 +1,70 @@ +#include "outdrivers.h" +#include +#include "parameters.h" +#include "os_generic.h" +#include +#include + +struct OutDriverListElem ODList[MAX_OUT_DRIVERS]; +const char OutDriverParameters[MAX_OUT_DRIVER_STRING]; + +void NullUpdate(void * id, struct NoteFinder*nf) +{ +} + +void NullParams(void * id ) +{ +} + +struct DriverInstances * null( ) +{ + printf( "Null lights driver initialized.\n" ); + struct DriverInstances * ret = malloc( sizeof( struct DriverInstances ) ); + ret->id = 0; + ret->Func = NullUpdate; + ret->Params = NullParams; + return ret; +} +REGISTER_OUT_DRIVER(null); + + +struct DriverInstances * SetupOutDriver( ) +{ + int i; + const char * p = GetParameterS( "displayname", "null" ); + for( i = 0; i < MAX_OUT_DRIVERS; i++ ) + { + if( ODList[i].Name && strcmp( p, ODList[i].Name ) == 0 ) + { + printf( "Found: %s %p\n", ODList[i].Name, ODList[i].Init ); + return ODList[i].Init( ); + break; + } + } + if( i == MAX_OUT_DRIVERS ) + { + fprintf( stderr, "Error: Could not find outdriver.\n" ); + } + return 0; +} + +void RegOutDriver( const char * ron, struct DriverInstances * (*Init)( ) ) +{ + int i; + for( i = 0; i < MAX_OUT_DRIVERS; i++ ) + { + if( ODList[i].Name == 0 ) + { + ODList[i].Name = strdup( ron ); + ODList[i].Init = Init; + break; + } + } + if( i == MAX_OUT_DRIVERS ) + { + fprintf( stderr, "Error: Too many outdrivers registered.\n" ); + exit( -55 ); + } +} + + diff --git a/outdrivers.h b/outdrivers.h new file mode 100644 index 0000000..f3b0b51 --- /dev/null +++ b/outdrivers.h @@ -0,0 +1,35 @@ +#ifndef _OUTDRIVERS_H +#define _OUTDRIVERS_H + +#include "os_generic.h" + +struct NoteFinder; + + +#define MAX_OUT_DRIVERS 64 +#define MAX_OUT_DRIVER_STRING 1024 + +struct OutDriverListElem +{ + const char * Name; + struct DriverInstances * (*Init)(); +}; + +struct DriverInstances +{ + void * id; + void (*Func)(void * id, struct NoteFinder* nf ); + void (*Params)(void * id); +}; + +extern struct OutDriverListElem ODList[MAX_OUT_DRIVERS]; +extern const char OutDriverParameters[MAX_OUT_DRIVER_STRING]; + +//Pass setup "name=[driver]" +struct DriverInstances * SetupOutDriver( ); +void RegOutDriver( const char * ron, struct DriverInstances * (*Init)( ) ); + +#define REGISTER_OUT_DRIVER( name ) \ + EXECUTE_AT_BOOT( r##name, RegOutDriver( #name, name ) ); + +#endif diff --git a/parameters.c b/parameters.c new file mode 100644 index 0000000..43e67ef --- /dev/null +++ b/parameters.c @@ -0,0 +1,290 @@ +#include "parameters.h" +#include "chash.h" +#include +#include +#include + +static struct chash * parameters; + +//XXX TODO: Make this thread safe. +static char returnbuffer[32]; + +static void Init() +{ + if( !parameters ) + { + parameters = GenerateHashTable( 0 ); + } +} + + +float GetParameterF( const char * name, float defa ) +{ + struct Param * p = (struct Param*)HashGetEntry( parameters, name ); + + if( p ) + { + switch( p->t ) + { + case PFLOAT: return *((float*)p->ptr); + case PINT: return *((int*)p->ptr); + case PSTRING: + case PBUFFER: if( p->ptr ) return atof( p->ptr ); + default: break; + } + } + + return defa; +} + +int GetParameterI( const char * name, int defa ) +{ + struct Param * p = (struct Param*)HashGetEntry( parameters, name ); + + if( p ) + { + switch( p->t ) + { + case PFLOAT: return *((float*)p->ptr); + case PINT: return *((int*)p->ptr); + case PSTRING: + case PBUFFER: if( p->ptr ) return atoi( p->ptr ); + default: break; + } + } + + return defa; +} + +const char * GetParameterS( const char * name, const char * defa ) +{ + struct Param * p = (struct Param*)HashGetEntry( parameters, name ); + + if( p ) + { + switch( p->t ) + { + case PFLOAT: snprintf( returnbuffer, sizeof( returnbuffer ), "%0.4f", *((float*)p->ptr) ); return returnbuffer; + case PINT: snprintf( returnbuffer, sizeof( returnbuffer ), "%d", *((int*)p->ptr) ); return returnbuffer; + case PSTRING: + case PBUFFER: return p->ptr; + default: break; + } + } + + return defa; +} + + +static int SetParameter( struct Param * p, const char * str ) +{ + switch( p->t ) + { + case PFLOAT: + *((float*)p->ptr) = atof( str ); + break; + case PINT: + *((int*)p->ptr) = atoi( str ); + break; + case PBUFFER: + strncpy( (char*)p->ptr, str, p->size ); + if( p->size > 0 ) + ((char*)p->ptr)[p->size-1]= '\0'; + break; + case PSTRING: + default: + return -1; + } + + struct ParamCallback * cb = p->callback; + while( cb ) + { + cb->t( cb->v ); + cb = cb->next; + } + + return 0; +} + +void RegisterValue( const char * name, enum ParamType t, void * ptr, int size ) +{ + Init(); + + struct Param * p = (struct Param*)HashGetEntry( parameters, name ); + + if( p ) + { + //Entry already exists. + if( p->orphan ) + { + if( p->t != PSTRING ) + { + fprintf( stderr, "Warning: Orphan parameter %s was not a PSTRING.\n", name ); + } + char * orig = p->ptr; + p->ptr = ptr; + p->t = t; + p->size = size; + p->orphan = 0; + int r = SetParameter( p, orig ); + free( orig ); + if( r ) + { + fprintf( stderr, "Warning: Problem when setting Orphan parameter %s\n", name ); + } + } + else + { + fprintf( stderr, "Warning: Parameter %s re-registered. Cannot re-register.\n", name ); + } + } + else + { + struct Param ** n = (struct Param**)HashTableInsert( parameters, name, 1 ); + *n = malloc( sizeof( struct Param ) ); + (*n)->t = t; + (*n)->ptr = ptr; + (*n)->orphan = 0; + (*n)->size = size; + (*n)->callback = 0; + } +} + +void SetParametersFromString( const char * string ) +{ + char name[PARAM_BUFF]; + char value[PARAM_BUFF]; + char c; + + int namepos = -1; //If -1, not yet found. + int lastnamenowhite = 0; + int valpos = -1; + int lastvaluenowhite = 0; + char in_value = 0; + char in_comment = 0; + + while( 1 ) + { + c = *(string++); + char is_whitespace = ( c == ' ' || c == '\t' || c == '\r' ); + char is_break = ( c == '\n' || c == ';' || c == 0 ); + char is_comment = ( c == '#' ); + char is_equal = ( c == '=' ); + + if( is_comment ) + { + in_comment = 1; + } + + if( in_comment ) + { + if( !is_break ) + continue; + } + + if( is_break ) + { + if( namepos < 0 || valpos < 0 ) + { + //Can't do anything with this line. + } + else + { + name[lastnamenowhite] = 0; + value[lastvaluenowhite] = 0; +// printf( "Break: %s %s %d\n", name, value, lastvaluenowhite ); + + struct Param * p = (struct Param*)HashGetEntry( parameters, name ); + if( p ) + { + SetParameter( p, value ); + } + else + { + //p is an orphan. + struct Param ** n = (struct Param **)HashTableInsert( parameters, name, 0 ); + *n = malloc( sizeof ( struct Param ) ); + (*n)->orphan = 1; + (*n)->t = PSTRING; + (*n)->ptr = strdup( value ); + (*n)->size = strlen( value ) + 1; + (*n)->callback = 0; + } + } + + namepos = -1; + lastnamenowhite = 0; + valpos = -1; + lastvaluenowhite = 0; + in_value = 0; + in_comment = 0; + + if( c ) + continue; + else + break; + } + + if( is_equal ) + { + in_value = 1; + continue; + } + + if( !in_value ) + { + if( namepos == -1 ) + { + if( !is_whitespace ) + namepos = 0; + } + + if( namepos >= 0 && namepos < PARAM_BUFF ) + { + name[namepos++] = c; + if( !is_whitespace ) + lastnamenowhite = namepos; + } + } + else + { + if( valpos == -1 ) + { + if( !is_whitespace ) + valpos = 0; + } + + if( valpos >= 0 && valpos < PARAM_BUFF ) + { + value[valpos++] = c; + if( !is_whitespace ) + lastvaluenowhite = valpos; + } + } + } +} + +void AddCallback( const char * name, ParamCallbackT t, void * v ) +{ + struct Param * p = (struct Param*)HashGetEntry( parameters, name ); + if( p ) + { + struct ParamCallback ** last = &p->callback; + struct ParamCallback * cb = p->callback; + while( cb ) + { + last = &cb->next; + cb = cb->next; + } + cb = *last = malloc( sizeof( struct ParamCallback ) ); + cb->t = t; + cb->v = v; + cb->next = 0; + } + else + { + fprintf( stderr, "Warning: cannot add callback to %s\n.", name ); + } +} + + diff --git a/parameters.h b/parameters.h new file mode 100644 index 0000000..bd72f2b --- /dev/null +++ b/parameters.h @@ -0,0 +1,52 @@ +#ifndef _PARAMETERS_H +#define _PARAMETERS_H + +#define PARAM_BUFF 128 + +enum ParamType +{ + NONE, + PFLOAT, + PINT, + PSTRING, //const char *, cannot set. + PBUFFER, + NUM_PARAMS, +}; + +typedef void (*ParamCallbackT)( void * v ); + +struct ParamCallback +{ + ParamCallbackT t; + void * v; + struct ParamCallback * next; +}; + +struct Param +{ + char orphan; //If this is set, then this is something that was received from a string, but has no claimed interface. + //It will be claimed when RegisterValue is called. NOTE: When orphan is set, it must be a malloc'd string. + enum ParamType t; + void * ptr; + int size; + + struct ParamCallback * callback; +}; + +void RegisterValue( const char * name, enum ParamType, void * ptr, int size ); + +float GetParameterF( const char * name, float defa ); +int GetParameterI( const char * name, int defa ); +const char * GetParameterS( const char * name, const char * defa ); + +//Format: parameter1=value1;parameter2=value2, OR \n instead of; ... \r's treated as whitespace. Will trip whitespace. +void SetParametersFromString( const char * string ); + +void AddCallback( const char * name, ParamCallbackT t, void * v ); + +#define REGISTER_PARAM( parameter_name, type ) \ + void Register##parameter_name() __attribute__((constructor(101))); \ + void Register##parameter_name() { RegisterValue( #parameter_name, type, ¶meter_name, sizeof( parameter_name ) ); } + + +#endif diff --git a/sort.c b/sort.c new file mode 100644 index 0000000..c39844d --- /dev/null +++ b/sort.c @@ -0,0 +1,59 @@ +#include "sort.h" +#include +#include + +//Sort the indices of an array of floating point numbers +void SortFloats( int * indexouts, float * array, int count ) +{ + int i; + int j; + unsigned char selected[count]; + + memset( selected, 0, sizeof( selected ) ); + + for( i = 0; i < count; i++ ) + { + int leastindex = -1; + float leastval = -1e200; + + for( j = 0; j < count; j++ ) + { + if( !selected[j] && array[j] > leastval ) + { + leastval = array[j]; + leastindex = j; + } + } + + if( leastindex < 0 ) + { + fprintf( stderr, "ERROR: Sorting fault.\n" ); + goto fault; + } + + selected[leastindex] = 1; + indexouts[i] = leastindex; + } + + return; +fault: + for( i = 0; i < count; i++ ) + { + indexouts[i] = i; + } +} + + + +//Move the floats around according to the new index. +void RemapFloats( int * indexouts, float * array, int count ) +{ + int i; + float copyarray[count]; + memcpy( copyarray, array, sizeof( copyarray ) ); + for( i = 0; i < count; i++ ) + { + array[i] = copyarray[indexouts[i]];; + } +} + diff --git a/sort.h b/sort.h new file mode 100644 index 0000000..4ebe23a --- /dev/null +++ b/sort.h @@ -0,0 +1,16 @@ +#ifndef _SORT_H +#define _SORT_H + +//XXX WARNING: This is a TERRIBLE TERRIBLE O(N^2) SORTING ALGORITHM +//You should probably fix this!!! + +//Sort the indices of an array of floating point numbers +//Highest->Lowest +void SortFloats( int * indexouts, float * array, int count ); + +//Move the floats around according to the new index. +void RemapFloats( int * indexouts, float * array, int count ); + +#endif + + diff --git a/sound.c b/sound.c new file mode 100644 index 0000000..6497bb8 --- /dev/null +++ b/sound.c @@ -0,0 +1,106 @@ +#include "sound.h" +#include +#include +#include + +static SoundInitFn * SoundDrivers[MAX_SOUND_DRIVERS]; +static char * SoundDriverNames[MAX_SOUND_DRIVERS]; //XXX: There's a bug in my compiler, this should be 'static' +static int SoundDriverPriorities[MAX_SOUND_DRIVERS]; +/* +void CleanupSound() __attribute__((destructor)); +void CleanupSound() +{ + int i; + for( i = 0; i < MAX_SOUND_DRIVERS; i++ ) + { + if( SoundDriverNames[i] ) + { + free( SoundDriverNames[i] ); + } + } +} +*/ + +void RegSound( int priority, const char * name, SoundInitFn * fn ) +{ + int j; + + if( priority <= 0 ) + { + return; + } + + for( j = MAX_SOUND_DRIVERS-1; j >= 0; j-- ) + { + //Cruise along, find location to insert + if( j > 0 && ( !SoundDrivers[j-1] || SoundDriverPriorities[j-1] < priority ) ) + { + SoundDrivers[j] = SoundDrivers[j-1]; + SoundDriverNames[j] = SoundDriverNames[j-1]; + SoundDriverPriorities[j] = SoundDriverPriorities[j-1]; + } + else + { + SoundDrivers[j] = fn; + SoundDriverNames[j] = strdup( name ); + SoundDriverPriorities[j] = priority; + break; + } + } +} + +struct SoundDriver * InitSound( const char * driver_name, SoundCBType cb ) +{ + int i; + struct SoundDriver * ret = 0; + + if( driver_name == 0 || strlen( driver_name ) == 0 ) + { + //Search for a driver. + for( i = 0; i < MAX_SOUND_DRIVERS; i++ ) + { + if( SoundDrivers[i] == 0 ) + { + return 0; + } + ret = SoundDrivers[i]( cb ); + if( ret ) + { + return ret; + } + } + } + else + { + for( i = 0; i < MAX_SOUND_DRIVERS; i++ ) + { + if( SoundDrivers[i] == 0 ) + { + return 0; + } + if( strcmp( SoundDriverNames[i], driver_name ) == 0 ) + { + return SoundDrivers[i]( cb ); + } + } + } + return 0; +} + +int SoundState( struct SoundDriver * soundobject ) +{ + if( soundobject ) + { + return soundobject->SoundStateFn( soundobject ); + } + return -1; +} + +void CloseSound( struct SoundDriver * soundobject ) +{ + if( soundobject ) + { + soundobject->CloseFn( soundobject ); + } +} + diff --git a/sound.h b/sound.h new file mode 100644 index 0000000..25ef4d4 --- /dev/null +++ b/sound.h @@ -0,0 +1,36 @@ +#ifndef _SOUND_H +#define _SOUND_H + +#define MAX_SOUND_DRIVERS 10 + +struct SoundDriver; + +typedef void(*SoundCBType)( float * out, float * in, int samplesr, int * samplesp, struct SoundDriver * sd ); +typedef void*(SoundInitFn)( SoundCBType cb ); + +struct SoundDriver +{ + void (*CloseFn)( void * object ); + int (*SoundStateFn)( struct SoundDriver * object ); + SoundCBType callback; + int channelsPlay; + int spsPlay; + int channelsRec; + int spsRec; + + //More fields may exist on a per-sound-driver basis +}; + +//Accepts: +// samplerate=44100;channels=2;devplay=default;devrecord=default;record=1;play=1;minavailcount=4096;stopthresh=1024;startthresh=4096;buffer=1024 +// buffer is in samples +//If DriverName = 0 or empty, will try to find best driver. +struct SoundDriver * InitSound( const char * driver_name, SoundCBType cb ); +int SoundState( struct SoundDriver * soundobject ); //returns 0 if okay, negative if faulted. +void CloseSound( struct SoundDriver * soundobject ); + +//Called by various sound drivers. Notice priority must be greater than 0. Priority of 0 or less will not register. +void RegSound( int priority, const char * name, SoundInitFn * fn ); + +#endif + diff --git a/sound_alsa.c b/sound_alsa.c new file mode 100644 index 0000000..e00ff98 --- /dev/null +++ b/sound_alsa.c @@ -0,0 +1,339 @@ +#include "sound.h" +#include "os_generic.h" +#include "parameters.h" +#include + +#define BUFFERSETS 4 + +#define BLOCKING + +struct SoundDriverAlsa +{ + void (*CloseFn)( struct SoundDriverAlsa * object ); + int (*SoundStateFn)( struct SoundDriverAlsa * object ); + SoundCBType callback; + int channelsPlay; + int spsPlay; + int channelsRec; + int spsRec; + + snd_pcm_uframes_t buffer; + og_thread_t thread; + snd_pcm_t *playback_handle; + snd_pcm_t *record_handle; + + //More fields may exist on a per-sound-driver basis +}; + +static struct SoundDriverAlsa* InitASound( struct SoundDriverAlsa * r ); + +void CloseSoundAlsa( struct SoundDriverAlsa * r ); + +int SoundStateAlsa( struct SoundDriverAlsa * soundobject ) +{ + return ((soundobject->playback_handle)?1:0) | ((soundobject->record_handle)?2:0); +} + +void CloseSoundAlsa( struct SoundDriverAlsa * r ) +{ + if( r ) + { + if( r->playback_handle ) snd_pcm_close (r->playback_handle); + if( r->record_handle ) snd_pcm_close (r->record_handle); +#ifdef BLOCKING + OGUSleep(2000); + OGCancelThread( r->thread ); +#endif + free( r ); + } +} + + +static int SetHWParams( snd_pcm_t * handle, int * samplerate, int * channels, snd_pcm_uframes_t * buffer ) +{ + int err; + snd_pcm_hw_params_t *hw_params; + if ((err = snd_pcm_hw_params_malloc (&hw_params)) < 0) { + fprintf (stderr, "cannot allocate hardware parameter structure (%s)\n", + snd_strerror (err)); + return -1; + } + + if ((err = snd_pcm_hw_params_any (handle, hw_params)) < 0) { + fprintf (stderr, "cannot initialize hardware parameter structure (%s)\n", + snd_strerror (err)); + goto fail; + } + + if ((err = snd_pcm_hw_params_set_access (handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) { + fprintf (stderr, "cannot set access type (%s)\n", + snd_strerror (err)); + goto fail; + } + + if ((err = snd_pcm_hw_params_set_format (handle, hw_params, SND_PCM_FORMAT_FLOAT )) < 0) { + fprintf (stderr, "cannot set sample format (%s)\n", + snd_strerror (err)); + goto fail; + } + + if ((err = snd_pcm_hw_params_set_rate_near (handle, hw_params, (unsigned int*)samplerate, 0)) < 0) { + fprintf (stderr, "cannot set sample rate (%s)\n", + snd_strerror (err)); + goto fail; + } + + if ((err = snd_pcm_hw_params_set_channels (handle, hw_params, *channels)) < 0) { + fprintf (stderr, "cannot set channel count (%s)\n", + snd_strerror (err)); + goto fail; + } + + int dir = 0; + if( (err = snd_pcm_hw_params_set_period_size_near(handle, hw_params, buffer, &dir)) < 0 ) + { + fprintf( stderr, "cannot set period size. (%s)\n", + snd_strerror(err) ); + goto fail; + } + + + if ((err = snd_pcm_hw_params (handle, hw_params)) < 0) { + fprintf (stderr, "cannot set parameters (%s)\n", + snd_strerror (err)); + goto fail; + } + + snd_pcm_hw_params_free (hw_params); + return 0; +fail: + snd_pcm_hw_params_free (hw_params); + return -2; +} + + +static int SetSWParams( snd_pcm_t * handle, int isrec ) +{ + snd_pcm_sw_params_t *sw_params; + int err; + //Time for software parameters: + + if( !isrec ) + { + if ((err = snd_pcm_sw_params_malloc (&sw_params)) < 0) { + fprintf (stderr, "cannot allocate software parameters structure (%s)\n", + snd_strerror (err)); + goto failhard; + } + if ((err = snd_pcm_sw_params_current (handle, sw_params)) < 0) { + fprintf (stderr, "cannot initialize software parameters structure (%s) (%p)\n", + snd_strerror (err), handle); + goto fail; + } + if ((err = snd_pcm_sw_params_set_avail_min (handle, sw_params, GetParameterI( "minavailcount", 2048 ) )) < 0) { + fprintf (stderr, "cannot set minimum available count (%s)\n", + snd_strerror (err)); + goto fail; + } + if ((err = snd_pcm_sw_params_set_stop_threshold(handle, sw_params, GetParameterI( "stopthresh", 512 ))) < 0) { + fprintf (stderr, "cannot set minimum available count (%s)\n", + snd_strerror (err)); + goto fail; + } + if ((err = snd_pcm_sw_params_set_start_threshold(handle, sw_params, GetParameterI( "startthresh", 2048 ))) < 0) { + fprintf (stderr, "cannot set minimum available count (%s)\n", + snd_strerror (err)); + goto fail; + } + if ((err = snd_pcm_sw_params (handle, sw_params)) < 0) { + fprintf (stderr, "cannot set software parameters (%s)\n", + snd_strerror (err)); + goto fail; + } + + } + + + if ((err = snd_pcm_prepare (handle)) < 0) { + fprintf (stderr, "cannot prepare audio interface for use (%s)\n", + snd_strerror (err)); + goto fail; + } + + + return 0; +fail: + if( !isrec ) + { + snd_pcm_sw_params_free (sw_params); + } +failhard: + return -1; +} + +#ifdef BLOCKING +static void * SoundThread( void * v ) +{ + int i; + struct SoundDriverAlsa * a = (struct SoundDriverAlsa*)v; + float * bufr[BUFFERSETS]; + float * bufp[BUFFERSETS]; + + for(i = 0; i < BUFFERSETS; i++ ) + { + bufr[i] = malloc( a->buffer * sizeof(float) * a->channelsRec ); + bufp[i] = malloc( a->buffer * sizeof(float) * a->channelsPlay ); + } + + while( a->record_handle || a->playback_handle ) + { + int err; + + i = (i+1)%BUFFERSETS; + + if( a->record_handle ) + { + if( (err = snd_pcm_readi (a->record_handle, bufr[i], a->buffer)) != a->buffer) + { + fprintf (stderr, "read from audio interface failed (%s)\n", + snd_strerror (err)); + if( a->record_handle ) snd_pcm_close (a->record_handle); + a->record_handle = 0; + } + else + { + //has_rec = 1; + } + } + + //Do our callback. + int playbacksamples = 0; + a->callback( bufp[i], bufr[i], a->buffer, &playbacksamples, (struct SoundDriver*)a ); + //playbacksamples *= sizeof(float) * a->channelsPlay; + + if( a->playback_handle ) + { + if ((err = snd_pcm_writei (a->playback_handle, bufp[i], playbacksamples)) != playbacksamples) + { + fprintf (stderr, "write to audio interface failed (%s)\n", + snd_strerror (err)); + if( a->playback_handle ) snd_pcm_close (a->playback_handle); + a->playback_handle = 0; + } + } + } + + //Fault happened, re-initialize? + InitASound( a ); + return 0; +} +#else + +//Handle callback + +static struct SoundDriverAlsa * reccb; +static int record_callback (snd_pcm_sframes_t nframes) +{ + int err; + +// printf ("playback callback called with %u frames\n", nframes); + + /* ... fill buf with data ... */ + + if ((err = snd_pcm_writei (playback_handle, buf, nframes)) < 0) { + fprintf (stderr, "write failed (%s)\n", snd_strerror (err)); + } + + return err; +} + +#endif + +static struct SoundDriverAlsa * InitASound( struct SoundDriverAlsa * r ) +{ + int err; + if( GetParameterI( "play", 0 ) ) + { + if ((err = snd_pcm_open (&r->playback_handle, GetParameterS( "devplay", "default" ), SND_PCM_STREAM_PLAYBACK, 0)) < 0) { + fprintf (stderr, "cannot open output audio device (%s)\n", + snd_strerror (err)); + goto fail; + } + } + + if( GetParameterI( "record", 1 ) ) + { + if ((err = snd_pcm_open (&r->record_handle, GetParameterS( "devrecord", "default" ), SND_PCM_STREAM_CAPTURE, 0)) < 0) { + fprintf (stderr, "cannot open input audio device (%s)\n", + snd_strerror (err)); + goto fail; + } + } + + + + if( r->playback_handle ) + { + if( SetHWParams( r->playback_handle, &r->spsPlay, &r->channelsPlay, &r->buffer ) < 0 ) + goto fail; + if( SetSWParams( r->playback_handle, 0 ) < 0 ) + goto fail; + } + + if( r->record_handle ) + { + if( SetHWParams( r->record_handle, &r->spsRec, &r->channelsRec, &r->buffer ) < 0 ) + goto fail; + if( SetSWParams( r->record_handle, 1 ) < 0 ) + goto fail; + } + + if( r->playback_handle && r->record_handle ) + { + snd_pcm_link ( r->playback_handle, r->record_handle ); + } + +#ifdef BLOCKING + r->thread = OGCreateThread( SoundThread, r ); +#else + reccb = r; + //handle interrupt +#endif + return r; + +fail: + if( r ) + { + if( r->playback_handle ) snd_pcm_close (r->playback_handle); + if( r->record_handle ) snd_pcm_close (r->record_handle); + free( r ); + } + return 0; +} + + + +void * InitSoundAlsa( SoundCBType cb ) +{ + struct SoundDriverAlsa * r = malloc( sizeof( struct SoundDriverAlsa ) ); + + r->CloseFn = CloseSoundAlsa; + r->SoundStateFn = SoundStateAlsa; + r->callback = cb; + + r->spsPlay = GetParameterI( "samplerate", 44100 ); + r->channelsPlay = GetParameterI( "channels", 2 ); + r->spsRec = r->spsPlay; + r->channelsRec = r->channelsPlay; + + r->playback_handle = 0; + r->record_handle = 0; + r->buffer = GetParameterI( "buffer", 1024 ); + + + + return InitASound(r); +} + +EXECUTE_AT_BOOT( AlsaSoundReg, RegSound( 10, "ALSA", InitSoundAlsa ) ); + diff --git a/sound_null.c b/sound_null.c new file mode 100644 index 0000000..51117c2 --- /dev/null +++ b/sound_null.c @@ -0,0 +1,44 @@ +#include "sound.h" +#include "os_generic.h" +#include +#include "parameters.h" + +struct SoundDriverNull +{ + void (*CloseFn)( struct SoundDriverNull * object ); + int (*SoundStateFn)( struct SoundDriverNull * object ); + SoundCBType soundcb; + int channelsPlay; + int spsPlay; + int channelsRec; + int spsRec; +}; + +void CloseSoundNull( struct SoundDriverNull * object ) +{ + free( object ); +} + +int SoundStateNull( struct SoundDriverNull * object ) +{ + return 0; +} + + +void * InitSoundNull( SoundCBType cb ) +{ + struct SoundDriverNull * r = malloc( sizeof( struct SoundDriverNull ) ); + r->CloseFn = CloseSoundNull; + r->SoundStateFn = SoundStateNull; + r->soundcb = cb; + r->spsPlay = GetParameterI( "samplerate", 44100 ); + r->channelsPlay = GetParameterI( "channels", 2 ); + r->spsRec = r->spsPlay; + r->channelsRec = r->channelsRec; + return r; +} + + + +EXECUTE_AT_BOOT( NullSoundReg, RegSound( 1, "NULL", InitSoundNull ) ); + diff --git a/sound_pulse.c b/sound_pulse.c new file mode 100644 index 0000000..513b53a --- /dev/null +++ b/sound_pulse.c @@ -0,0 +1,372 @@ +//This file is really rough. Full duplex doesn't seem to work hardly at all. + + +#include "sound.h" +#include "os_generic.h" +#include "parameters.h" +#include + +#include +#include +#include +#include +#include + +#define BUFFERSETS 3 + + +//from http://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/Developer/Clients/Samples/AsyncPlayback/ +//also http://maemo.org/api_refs/5.0/5.0-final/pulseaudio/pacat_8c-example.html + + +struct SoundDriverPulse +{ + void (*CloseFn)( struct SoundDriverPulse * object ); + int (*SoundStateFn)( struct SoundDriverPulse * object ); + SoundCBType callback; + int channelsPlay; + int spsPlay; + int channelsRec; + int spsRec; + + const char * sourceName; + og_thread_t thread; + pa_stream * play; + pa_stream * rec; + pa_context * pa_ctx; + pa_mainloop *pa_ml; + int pa_ready; + int buffer; + //More fields may exist on a per-sound-driver basis +}; + + + +void CloseSoundPulse( struct SoundDriverPulse * r ); + +int SoundStatePulse( struct SoundDriverPulse * soundobject ) +{ + return ((soundobject->play)?1:0) | ((soundobject->rec)?2:0); +} + +void CloseSoundPulse( struct SoundDriverPulse * r ) +{ + if( r ) + { + if( r->play ) + { + pa_stream_unref (r->play); + r->play = 0; + } + + if( r->rec ) + { + pa_stream_unref (r->rec); + r->rec = 0; + } + OGUSleep(2000); + OGCancelThread( r->thread ); + free( r ); + } +} + +static void * SoundThread( void * v ) +{ + struct SoundDriverPulse * r = (struct SoundDriverPulse*)v; + while(1) + { + pa_mainloop_iterate( r->pa_ml, 1, NULL ); + } + return 0; +} +/* + int i; + int error; + struct SoundDriverPulse * r = (struct SoundDriverPulse*)v; + + float * bufr[BUFFERSETS]; + float * bufp[BUFFERSETS]; + + for(i = 0; i < BUFFERSETS; i++ ) + { + bufr[i] = malloc( r->buffer * sizeof(float) * r->channelsRec ); + bufp[i] = malloc( r->buffer * sizeof(float) * r->channelsPlay ); + } + + while( r->play || r->rec ) + { + i = (i+1)%BUFFERSETS; + + if( r->rec ) + { + if (pa_stream_read(r->rec, bufr[i], r->buffer * sizeof(float) * r->channelsRec, &error) < 0) { + fprintf(stderr, __FILE__": pa_stream_write() failed: %s\n", pa_strerror(error)); + pa_stream_unref( r->play ); + r->rec = 0; + } + } + + int playbacksamples = 0; + r->callback( bufp[i], bufr[i], r->buffer, &playbacksamples, (struct SoundDriver*)r ); + playbacksamples *= sizeof( float ) * r->channelsPlay; + + if( r->play ) + { + if (pa_stream_write(r->play, bufp[i], playbacksamples, NULL, 0LL, PA_SEEK_RELATIVE) < 0) { + fprintf(stderr, __FILE__": pa_stream_write() failed: %s\n", pa_strerror(error)); + pa_stream_unref( r->play ); + r->play = 0; + } + } + } + +}*/ + +static void stream_request_cb(pa_stream *s, size_t length, void *userdata) { +// pa_usec_t usec; + + struct SoundDriverPulse * r = (struct SoundDriverPulse*)userdata; + if( r->rec ) + { + return; + } + +/* + //Neat: You might want this: + + pa_stream_get_latency(s,&usec,&neg); + + if (sampleoffs*2 + length > sizeof(sampledata)) + { + sampleoffs = 0; + } + + if (length > sizeof(sampledata)) { + length = sizeof(sampledata); + } + +*/ + + +// pa_stream_write(s, &sampledata[sampleoffs], length, NULL, 0LL, PA_SEEK_RELATIVE); + int playbacksamples = 0; + float bufr[r->buffer*r->channelsRec]; + float bufp[r->buffer*r->channelsPlay]; + + r->callback( bufp, bufr, r->buffer, &playbacksamples, (struct SoundDriver*)r ); + //playbacksamples *= sizeof( float ) * r->channelsPlay; + + pa_stream_write(r->play, &bufp, playbacksamples, NULL, 0LL, PA_SEEK_RELATIVE); + +} + + +static void stream_record_cb(pa_stream *s, size_t length, void *userdata) { +// pa_usec_t usec; +// int neg; + + struct SoundDriverPulse * r = (struct SoundDriverPulse*)userdata; + +/* pa_stream_get_latency(s,&usec,&neg); + printf(" latency %8d us\n",(int)usec); + + if (sampleoffs*2 + length > sizeof(sampledata)) + { + sampleoffs = 0; + } + + if (length > sizeof(sampledata)) { + length = sizeof(sampledata); + }*/ + + int playbacksamples = 0; + float * bufr; + + if (pa_stream_peek(r->rec, (void*)&bufr, &length) < 0) { + fprintf(stderr, ("pa_stream_peek() failed: %s\n"), pa_strerror(pa_context_errno(r->pa_ctx))); + return; + } + + float * buffer; + buffer = pa_xmalloc(length); + memcpy(buffer, bufr, length); + pa_stream_drop(r->rec); + + float bufp[length*r->channelsPlay]; + r->callback( bufp, buffer, length/sizeof(float)/r->channelsRec, &playbacksamples, (struct SoundDriver*)r ); + //playbacksamples *= sizeof( float ) * r->channelsPlay; + pa_xfree( buffer ); + if( r->play ) + pa_stream_write(r->play, &bufp, playbacksamples*sizeof(float)*r->channelsPlay, NULL, 0LL, PA_SEEK_RELATIVE); +} + + + +static void stream_underflow_cb(pa_stream *s, void *userdata) { + // We increase the latency by 50% if we get 6 underflows and latency is under 2s + // This is very useful for over the network playback that can't handle low latencies + printf("underflow\n"); +// underflows++; +/* if (underflows >= 6 && latency < 2000000) { + latency = (latency*3)/2; + bufattr.maxlength = pa_usec_to_bytes(latency,&ss); + bufattr.tlength = pa_usec_to_bytes(latency,&ss); + pa_stream_set_buffer_attr(s, &bufattr, NULL, NULL); + underflows = 0; + printf("latency increased to %d\n", latency); + }*/ +} + + +void pa_state_cb(pa_context *c, void *userdata) { + pa_context_state_t state; + int *pa_ready = userdata; + state = pa_context_get_state(c); + switch (state) { + // These are just here for reference + case PA_CONTEXT_UNCONNECTED: + case PA_CONTEXT_CONNECTING: + case PA_CONTEXT_AUTHORIZING: + case PA_CONTEXT_SETTING_NAME: + default: + break; + case PA_CONTEXT_FAILED: + case PA_CONTEXT_TERMINATED: + *pa_ready = 2; + break; + case PA_CONTEXT_READY: + *pa_ready = 1; + break; + } +} + + +void * InitSoundPulse( SoundCBType cb ) +{ + static pa_buffer_attr bufattr; + static pa_sample_spec ss; + int error; + pa_mainloop_api *pa_mlapi; + struct SoundDriverPulse * r = malloc( sizeof( struct SoundDriverPulse ) ); + + r->pa_ml = pa_mainloop_new(); + pa_mlapi = pa_mainloop_get_api(r->pa_ml); + const char * title = GetParameterS( "title", "PA Test" ); + r->pa_ctx = pa_context_new(pa_mlapi, title ); + pa_context_connect(r->pa_ctx, NULL, 0, NULL); + + //TODO: pa_context_set_state_callback + + r->CloseFn = CloseSoundPulse; + r->SoundStateFn = SoundStatePulse; + r->callback = cb; + + r->spsPlay = GetParameterI( "samplerate", 44100 ); + r->channelsPlay = GetParameterI( "channels", 2 ); + r->spsRec = r->spsPlay; + r->channelsRec = r->channelsPlay; + r->sourceName = GetParameterS( "sourcename", NULL ); + + r->play = 0; + r->rec = 0; + r->buffer = GetParameterI( "buffer", 1024 ); + printf ("Pulse: from: %s (%s) / %dx%d (%d)\n", r->sourceName, title, r->spsPlay, r->channelsPlay, r->buffer ); + + memset( &ss, 0, sizeof( ss ) ); + + ss.format = PA_SAMPLE_FLOAT32NE; + ss.rate = r->spsPlay; + ss.channels = r->channelsPlay; + + r->pa_ready = 0; + pa_context_set_state_callback(r->pa_ctx, pa_state_cb, &r->pa_ready); + + while (r->pa_ready == 0) + { + pa_mainloop_iterate(r->pa_ml, 1, NULL); + } + + int bufbytes = r->buffer * sizeof(float) * r->channelsRec; + + if( GetParameterI( "play", 1 ) ) + { + if (!(r->play = pa_stream_new(r->pa_ctx, "Play", &ss, NULL))) { + error = -3; //XXX ??? TODO + fprintf(stderr, __FILE__": pa_simple_new() failed: %s\n", pa_strerror(error)); + goto fail; + } + + pa_stream_set_underflow_callback(r->play, stream_underflow_cb, NULL); + pa_stream_set_write_callback(r->play, stream_request_cb, r ); + + bufattr.fragsize = (uint32_t)-1; + bufattr.maxlength = bufbytes*3; //XXX TODO Consider making this -1 + bufattr.minreq = 0; + bufattr.prebuf = (uint32_t)-1; + bufattr.tlength = bufbytes*3; + int ret = pa_stream_connect_playback(r->play, NULL, &bufattr, + // PA_STREAM_INTERPOLATE_TIMING + // |PA_STREAM_ADJUST_LATENCY //Some servers don't like the adjust_latency flag. + // |PA_STREAM_AUTO_TIMING_UPDATE, NULL, NULL); + 0, NULL, NULL ); + printf( "Play stream.\n" ); + if( ret < 0 ) + { + fprintf(stderr, __FILE__": (PLAY) pa_stream_connect_playback() failed: %s\n", pa_strerror(ret)); + goto fail; + } + } + + if( GetParameterI( "rec", 1 ) ) + { + if (!(r->rec = pa_stream_new(r->pa_ctx, "Record", &ss, NULL))) { + error = -3; //XXX ??? TODO + fprintf(stderr, __FILE__": pa_simple_new() failed: %s\n", pa_strerror(error)); + goto fail; + } + + pa_stream_set_read_callback(r->rec, stream_record_cb, r ); + + bufattr.fragsize = bufbytes; + bufattr.maxlength = (uint32_t)-1;//(uint32_t)-1; //XXX: Todo, should this be low? + bufattr.minreq = bufbytes; + bufattr.prebuf = (uint32_t)-1; + bufattr.tlength = bufbytes*3; + printf( "Source: %s\n", r->sourceName ); + int ret = pa_stream_connect_record(r->rec, r->sourceName, &bufattr, 0 +// |PA_STREAM_INTERPOLATE_TIMING + |PA_STREAM_ADJUST_LATENCY //Some servers don't like the adjust_latency flag. +// |PA_STREAM_AUTO_TIMING_UPDATE +// 0 + ); + + printf( "Got handle: %d\n", ret ); + if( ret < 0 ) + { + fprintf(stderr, __FILE__": (REC) pa_stream_connect_playback() failed: %s\n", pa_strerror(ret)); + goto fail; + } + } + + printf( "Pulse initialized.\n" ); + + +// SoundThread( r ); + r->thread = OGCreateThread( SoundThread, r ); + return r; + +fail: + if( r ) + { + if( r->play ) pa_xfree (r->play); + if( r->rec ) pa_xfree (r->rec); + free( r ); + } + return 0; +} + + + +EXECUTE_AT_BOOT( PulseSoundReg, RegSound( 11, "PULSE", InitSoundPulse ) ); + + diff --git a/util.c b/util.c new file mode 100644 index 0000000..5177c5b --- /dev/null +++ b/util.c @@ -0,0 +1,58 @@ +#include "util.h" +#include +#include + +//Take the absolute distance between two points on a torus. +float fabsloop( float a, float b, float modl ) +{ + float fa = fabsf( a - b ); + + fa = fmodf( fa, modl ); + + if( fa > modl/2.0 ) + { + fa = modl - fa; + } + + return fa; +} + +//Get the weighted - average of two points on a torus. +float avgloop( float pta, float ampa, float ptb, float ampb, float modl ) +{ + float amptot = ampa + ampb; + + //Determine if it should go linearly, or around the edge. + if( fabsf( pta - ptb ) > modl/2.0 ) + { + //Loop around the outside. + if( pta < ptb ) + { + pta += modl; + } + else + { + ptb += modl; + } + } + + float modmid = (pta * ampa + ptb * ampb)/amptot; + + return fmodf( modmid, modl ); +} + +int atoi_del( char * data ) +{ + int ret = atoi( data ); + free( data ); + return ret; +} + +float atof_del( char * data ) +{ + float ret = atof( data ); + free( data ); + return ret; +} + + diff --git a/util.h b/util.h new file mode 100644 index 0000000..eeb85a9 --- /dev/null +++ b/util.h @@ -0,0 +1,10 @@ +#ifndef _UTIL_H +#define _UTIL_H + +float fabsloop( float a, float b, float modl ); +float avgloop( float pta, float ampa, float ptb, float ampb, float modl ); + +int atoi_del( char * data ); +float atof_del( char * data ); + +#endif