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