add a bunch of files
This commit is contained in:
parent
27b65f10dc
commit
13d0f17b11
169
DisplayOUTDriver.c
Normal file
169
DisplayOUTDriver.c
Normal file
|
@ -0,0 +1,169 @@
|
|||
#include "outdrivers.h"
|
||||
#include "notefinder.h"
|
||||
#include <stdio.h>
|
||||
#include "parameters.h"
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
#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);
|
||||
|
||||
|
171
DisplayShapeDriver.c
Normal file
171
DisplayShapeDriver.c
Normal file
|
@ -0,0 +1,171 @@
|
|||
#include "outdrivers.h"
|
||||
#include "notefinder.h"
|
||||
#include <stdio.h>
|
||||
#include "parameters.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "color.h"
|
||||
#include <math.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 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);
|
||||
|
||||
|
273
DrawFunctions.c
Normal file
273
DrawFunctions.c
Normal file
|
@ -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 <stdio.h>
|
||||
|
||||
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 );
|
||||
}
|
54
DrawFunctions.h
Normal file
54
DrawFunctions.h
Normal file
|
@ -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 <stdint.h>
|
||||
|
||||
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
|
||||
|
197
LEDOUTDriver.c
Normal file
197
LEDOUTDriver.c
Normal file
|
@ -0,0 +1,197 @@
|
|||
#include "outdrivers.h"
|
||||
#include "notefinder.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "parameters.h"
|
||||
#include <stdlib.h>
|
||||
#include <libusb-1.0/libusb.h>
|
||||
#include "color.h"
|
||||
#include <math.h>
|
||||
#include <unistd.h>
|
||||
|
||||
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);
|
||||
|
||||
|
16
Makefile
Normal file
16
Makefile
Normal file
|
@ -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
|
7
TODO
Normal file
7
TODO
Normal file
|
@ -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.
|
226
WinDriver.c
Normal file
226
WinDriver.c
Normal file
|
@ -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 <windows.h>
|
||||
#include <stdlib.h>
|
||||
#include <malloc.h> //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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
258
XDriver.c
Normal file
258
XDriver.c
Normal file
|
@ -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 <X11/Xlib.h>
|
||||
#include <X11/Xutil.h>
|
||||
#include <X11/Xos.h>
|
||||
#include <X11/Xatom.h>
|
||||
#include <X11/keysym.h>
|
||||
#ifdef HAS_XINERAMA
|
||||
#include <X11/extensions/shape.h>
|
||||
#include <X11/extensions/Xinerama.h>
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
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<<report.xbutton.button))) | ( bKeyDirection << report.xbutton.button );
|
||||
|
||||
//Intentionall fall through -- we want to send a motion in event of a button as well.
|
||||
case MotionNotify:
|
||||
HandleMotion( report.xmotion.x, report.xmotion.y, ButtonsDown>>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
|
||||
|
439
chash.c
Normal file
439
chash.c
Normal file
|
@ -0,0 +1,439 @@
|
|||
#include "chash.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
|
53
chash.h
Normal file
53
chash.h
Normal file
|
@ -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
|
105
color.c
Normal file
105
color.c
Normal file
|
@ -0,0 +1,105 @@
|
|||
#include "color.h"
|
||||
#include <math.h>
|
||||
|
||||
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;
|
||||
}
|
12
color.h
Normal file
12
color.h
Normal file
|
@ -0,0 +1,12 @@
|
|||
#ifndef _COLOR_H
|
||||
#define _COLOR_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
//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
|
||||
|
215
decompose.c
Normal file
215
decompose.c
Normal file
|
@ -0,0 +1,215 @@
|
|||
#include "decompose.h"
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#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
|
||||
|
12
decompose.h
Normal file
12
decompose.h
Normal file
|
@ -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
|
||||
|
19
default.conf
Normal file
19
default.conf
Normal file
|
@ -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
|
||||
|
75
dft.c
Normal file
75
dft.c
Normal file
|
@ -0,0 +1,75 @@
|
|||
#include "dft.h"
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
16
dft.h
Normal file
16
dft.h
Normal file
|
@ -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
|
||||
|
57
filter.c
Normal file
57
filter.c
Normal file
|
@ -0,0 +1,57 @@
|
|||
#include "filter.h"
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
11
filter.h
Normal file
11
filter.h
Normal file
|
@ -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
|
309
main.c
Normal file
309
main.c
Normal file
|
@ -0,0 +1,309 @@
|
|||
#include <ctype.h>
|
||||
#include "color.h"
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include "sound.h"
|
||||
#include "os_generic.h"
|
||||
#include "DrawFunctions.h"
|
||||
#include "dft.h"
|
||||
#include "filter.h"
|
||||
#include "decompose.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#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
|
||||
}
|
||||
|
||||
}
|
||||
|
350
notefinder.c
Normal file
350
notefinder.c
Normal file
|
@ -0,0 +1,350 @@
|
|||
#include "notefinder.h"
|
||||
#include "parameters.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#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();
|
||||
}
|
||||
|
||||
|
93
notefinder.h
Normal file
93
notefinder.h
Normal file
|
@ -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
|
||||
|
342
os_generic.c
Normal file
342
os_generic.c
Normal file
|
@ -0,0 +1,342 @@
|
|||
#include "os_generic.h"
|
||||
|
||||
|
||||
#ifdef USE_WINDOWS
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
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 <sys/stat.h>
|
||||
#include <stdlib.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/time.h>
|
||||
#include <semaphore.h>
|
||||
#include <unistd.h>
|
||||
|
||||
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.
|
||||
*/
|
||||
|
82
os_generic.h
Normal file
82
os_generic.h
Normal file
|
@ -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.
|
||||
*/
|
||||
|
70
outdrivers.c
Normal file
70
outdrivers.c
Normal file
|
@ -0,0 +1,70 @@
|
|||
#include "outdrivers.h"
|
||||
#include <string.h>
|
||||
#include "parameters.h"
|
||||
#include "os_generic.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
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 );
|
||||
}
|
||||
}
|
||||
|
||||
|
35
outdrivers.h
Normal file
35
outdrivers.h
Normal file
|
@ -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
|
290
parameters.c
Normal file
290
parameters.c
Normal file
|
@ -0,0 +1,290 @@
|
|||
#include "parameters.h"
|
||||
#include "chash.h"
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
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 );
|
||||
}
|
||||
}
|
||||
|
||||
|
52
parameters.h
Normal file
52
parameters.h
Normal file
|
@ -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
|
59
sort.c
Normal file
59
sort.c
Normal file
|
@ -0,0 +1,59 @@
|
|||
#include "sort.h"
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
//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]];;
|
||||
}
|
||||
}
|
||||
|
16
sort.h
Normal file
16
sort.h
Normal file
|
@ -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
|
||||
|
||||
|
106
sound.c
Normal file
106
sound.c
Normal file
|
@ -0,0 +1,106 @@
|
|||
#include "sound.h"
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
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 );
|
||||
}
|
||||
}
|
||||
|
36
sound.h
Normal file
36
sound.h
Normal file
|
@ -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
|
||||
|
339
sound_alsa.c
Normal file
339
sound_alsa.c
Normal file
|
@ -0,0 +1,339 @@
|
|||
#include "sound.h"
|
||||
#include "os_generic.h"
|
||||
#include "parameters.h"
|
||||
#include <alsa/asoundlib.h>
|
||||
|
||||
#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 ) );
|
||||
|
44
sound_null.c
Normal file
44
sound_null.c
Normal file
|
@ -0,0 +1,44 @@
|
|||
#include "sound.h"
|
||||
#include "os_generic.h"
|
||||
#include <stdlib.h>
|
||||
#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 ) );
|
||||
|
372
sound_pulse.c
Normal file
372
sound_pulse.c
Normal file
|
@ -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 <stdlib.h>
|
||||
|
||||
#include <pulse/simple.h>
|
||||
#include <pulse/pulseaudio.h>
|
||||
#include <pulse/error.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#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 ) );
|
||||
|
||||
|
58
util.c
Normal file
58
util.c
Normal file
|
@ -0,0 +1,58 @@
|
|||
#include "util.h"
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
//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;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in a new issue