Move colorchord2 into its own folder, since colorchord embedded is its own thing.

This commit is contained in:
cnlohr 2015-07-27 03:39:16 -04:00
parent 24b606988f
commit ed15ea49b9
56 changed files with 1 additions and 12 deletions

View file

@ -0,0 +1,91 @@
#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 DPODriver
{
int xn;
int yn;
int rot90;
int zigzag;
};
static void DPOUpdate(void * id, struct NoteFinder*nf)
{
int x,y;
struct DPODriver * d = (struct DPODriver*)id;
float cw = ((float)(d->rot90?screeny:screenx)) / d->xn;
float ch = ((float)(d->rot90?screenx:screeny)) / d->yn;
for( y = 0; y < d->yn; y++ )
for( x = 0; x < d->xn; x++ )
{
int index = 0;
if( d->zigzag )
{
if( y & 1 )
{
index = (d->xn-x-1)+y*d->xn;
}
else
{
index = x+y*d->xn;
}
}
else
{
index = x+y*d->xn;
}
CNFGColor( OutLEDs[index*3+0] | (OutLEDs[index*3+1] <<8)|(OutLEDs[index*3+2] <<16) );
float dx = (x) * cw;
float dy = (y) * ch;
if( d->rot90 )
CNFGTackRectangle( dy, dx, dy+ch+.5, dx+cw+.5 );
else
CNFGTackRectangle( dx, dy, dx+cw+.5, dy+ch+.5 );
}
CNFGColor( 0xffffff );
}
static void DPOParams(void * id )
{
struct DPODriver * d = (struct DPODriver*)id;
d->xn = 16; RegisterValue( "lightx", PAINT, &d->xn, sizeof( d->xn ) );
d->yn = 9; RegisterValue( "lighty", PAINT, &d->yn, sizeof( d->yn ) );
d->zigzag = 0; RegisterValue( "zigzag", PAINT, &d->zigzag, sizeof( d->zigzag ) );
d->rot90 = 0; RegisterValue( "rot90", PAINT, &d->rot90, sizeof( d->rot90 ) );
}
static struct DriverInstances * DisplayArray(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(DisplayArray);

125
colorchord2/DisplayDMX.c Normal file
View file

@ -0,0 +1,125 @@
#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 DMXOutDriver
{
struct libusb_device_handle *devh;
int did_init;
int byte_offset;
int total_leds;
float outamp;
uint8_t * last_leds;
volatile int readyFlag;
};
static void * DMXOutThread( void * v )
{
struct DMXOutDriver * led = (struct DMXOutDriver*)v;
while(1)
{
if( led->readyFlag && led->devh )
{
/* int r = libusb_control_transfer( led->devh,
0x40, //reqtype
0xA5, //request
0x0100, //wValue
0x0000, //wIndex
led->last_leds,
(led->total_leds*3)+1,
1000 );*/
int r = libusb_control_transfer( led->devh, 0, 0xc6, 0x100, 0,
led->last_leds,
(led->total_leds*3)+led->byte_offset+1,
2000 );
if( r < 0 )
{
led->did_init = 0;
printf( "Fault sending DMXs.\n" );
}
}
led->readyFlag = 0;
usleep(100);
}
return 0;
}
static void DMXUpdate(void * id, struct NoteFinder*nf)
{
int i;
struct DMXOutDriver * led = (struct DMXOutDriver*)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, 0x16c0, 0x27dd );
// led->devh = libusb_open_device_with_vid_pid( NULL, 0x1d6b, 0x0002 );
if( !led->devh )
{
led->did_init = 0;
fprintf( stderr, "Error: Cannot find device.\n" );
// exit( -98 );
}
}
while( led->readyFlag ) usleep(100);
//Advance the DMXs to this position when outputting the values.
for( i = 0; i < led->total_leds; i++ )
{
int source = i;
led->last_leds[i*3+0 + led->byte_offset] = OutLEDs[source*3+0] * led->outamp;
led->last_leds[i*3+1 + led->byte_offset] = OutLEDs[source*3+1] * led->outamp;
led->last_leds[i*3+2 + led->byte_offset] = OutLEDs[source*3+2] * led->outamp;
}
led->readyFlag = 1;
}
static void DMXParams(void * id )
{
struct DMXOutDriver * led = (struct DMXOutDriver*)id;
led->total_leds = GetParameterI( "leds", 10 );
led->byte_offset = 32; RegisterValue( "byte_offset", PAINT, &led->byte_offset, sizeof( led->byte_offset ) );
led->last_leds = malloc( 1026 );
led->outamp = .1; RegisterValue( "ledoutamp", PAFLOAT, &led->outamp, sizeof( led->outamp ) );
led->did_init = 0;
}
static struct DriverInstances * DisplayDMX()
{
struct DriverInstances * ret = malloc( sizeof( struct DriverInstances ) );
memset( ret, 0, sizeof( struct DriverInstances ) );
struct DMXOutDriver * led = ret->id = malloc( sizeof( struct DMXOutDriver ) );
ret->Func = DMXUpdate;
ret->Params = DMXParams;
OGCreateThread( DMXOutThread, led );
led->readyFlag = 0;
DMXParams( led );
return ret;
}
REGISTER_OUT_DRIVER(DisplayDMX);

View file

@ -0,0 +1,128 @@
#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"
#include <unistd.h>
#ifdef WIN32
#include <windows.h>
#define MSG_NOSIGNAL 0
#else
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#endif
#define MAX_BUFFER 1487
struct DPODriver
{
int leds;
int skipfirst;
int firstval;
int port;
int oldport;
char address[PARAM_BUFF];
char oldaddress[PARAM_BUFF];
struct sockaddr_in servaddr;
int socket;
};
static void DPOUpdate(void * id, struct NoteFinder*nf)
{
struct DPODriver * d = (struct DPODriver*)id;
int i, j;
if( strcmp( d->oldaddress, d->address ) != 0 || d->socket == -1 || d->oldport != d->port )
{
d->socket = socket(AF_INET,SOCK_DGRAM,0);
struct hostent *hname;
hname = gethostbyname(d->address);
if( hname )
{
memset(&d->servaddr, 0, sizeof(d->servaddr));
d->servaddr.sin_family = hname->h_addrtype;
d->servaddr.sin_port = htons( d->port );
d->servaddr.sin_addr.s_addr = *(long*)hname->h_addr;
if( d->socket >= 0 )
{
d->oldport = d->port;
memcpy( d->oldaddress, d->address, PARAM_BUFF );
}
else
{
fprintf( stderr, "Socket Error.\n" );
}
}
else
{
fprintf( stderr, "Error: Cannot find host \"%s\":%d\n", d->address, d->port );
}
}
if( d->socket > 0 )
{
char buffer[MAX_BUFFER];
i = 0;
while( i < d->skipfirst )
buffer[i++] = d->firstval;
if( d->leds * 3 + i >= MAX_BUFFER )
d->leds = (MAX_BUFFER-1)/3;
for( j = 0; j < d->leds; j++ )
{
buffer[i++] = OutLEDs[j*3+0]; //RED
buffer[i++] = OutLEDs[j*3+2]; //BLUE
buffer[i++] = OutLEDs[j*3+1]; //GREEN
}
int r = sendto( d->socket, buffer, i, MSG_NOSIGNAL,(const struct sockaddr *) &d->servaddr, sizeof( d->servaddr ) );
if( r < 0 )
{
fprintf( stderr, "Send fault.\n" );
close( d->socket );
d->socket = -1;
}
}
}
static void DPOParams(void * id )
{
struct DPODriver * d = (struct DPODriver*)id;
strcpy( d->address, "localhost" );
d->leds = 10; RegisterValue( "leds", PAINT, &d->leds, sizeof( d->leds ) );
d->skipfirst = 1; RegisterValue( "skipfirst", PAINT, &d->skipfirst, sizeof( d->skipfirst ) );
d->port = 7777; RegisterValue( "port", PAINT, &d->port, sizeof( d->port ) );
d->firstval = 0; RegisterValue( "firstval", PAINT, &d->firstval, sizeof( d->firstval ) );
RegisterValue( "address", PABUFFER, d->address, sizeof( d->address ) );
d->socket = -1;
d->oldaddress[0] = 0;
}
static struct DriverInstances * DisplayNetwork(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(DisplayNetwork);

View file

@ -0,0 +1,189 @@
//XXX This needs to be re-worked to only output LEDs so DisplayArray can take it.
//XXX CONSIDER DUMPING
#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 1024
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 );
}
}*/
int led = 0;
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;
uint32_t color = CCtoHEX( nf->note_positions[i] / nf->freqbins, 1.0, sat );
OutLEDs[led*3+0] = color & 0xff;
OutLEDs[led*3+1] = ( color >> 8 ) & 0xff;
OutLEDs[led*3+2] = ( color >> 16 ) & 0xff;
led++;
}
}
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);

79
colorchord2/DisplayPie.c Normal file
View file

@ -0,0 +1,79 @@
#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 DPODriver
{
int leds;
float pie_min;
float pie_max;
};
static void DPOUpdate(void * id, struct NoteFinder*nf)
{
struct DPODriver * d = (struct DPODriver*)id;
int i;
float cw = ((float)(screenx)) / 2.0;
float ch = ((float)(screeny)) / 2.0;
RDPoint pts[4];
float sizeA = sqrtf( screenx * screenx + screeny * screeny ) * d->pie_min;
float sizeB = sqrtf( screenx * screenx + screeny * screeny ) * d->pie_max;
for( i = 0; i < d->leds; i++ )
{
float angA = 6.28318 * (float)i / (float)d->leds;
float angB = 6.28318 * (float)(i+1) / (float)d->leds + .002;
pts[0].x = cw + cos(angA) * sizeA;
pts[0].y = ch + sin(angA) * sizeA;
pts[1].x = cw + cos(angA) * sizeB;
pts[1].y = ch + sin(angA) * sizeB;
pts[2].x = cw + cos(angB) * sizeB;
pts[2].y = ch + sin(angB) * sizeB;
pts[3].x = cw + cos(angB) * sizeA;
pts[3].y = ch + sin(angB) * sizeA;
CNFGColor( OutLEDs[i*3+0] | (OutLEDs[i*3+1] <<8)|(OutLEDs[i*3+2] <<16) );
CNFGTackPoly( pts, 4 );
}
CNFGColor( 0xffffff );
}
static void DPOParams(void * id )
{
struct DPODriver * d = (struct DPODriver*)id;
d->leds = 9; RegisterValue( "leds", PAINT, &d->leds, sizeof( d->leds ) );
d->pie_min = .18; RegisterValue( "pie_min", PAFLOAT, &d->pie_min, sizeof( d->pie_min ) );
d->pie_max = .3; RegisterValue( "pie_max", PAFLOAT, &d->pie_max, sizeof( d->pie_max ) );
}
static struct DriverInstances * DisplayPie(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(DisplayPie);

View file

@ -0,0 +1,166 @@
#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 zigzag;
int total_leds;
int array;
float outamp;
uint8_t * last_leds;
volatile int readyFlag;
int xn;
int yn;
int rot90;
};
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;
printf( "Fault sending LEDs.\n" );
}
led->readyFlag = 0;
}
usleep(100);
}
return 0;
}
static void LEDUpdate(void * id, struct NoteFinder*nf)
{
int i;
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 );
}
}
while( led->readyFlag ) usleep(100);
//Advance the LEDs to this position when outputting the values.
for( i = 0; i < led->total_leds; i++ )
{
int source = i;
if( !led->array )
{
int sx, sy;
if( led->rot90 )
{
sy = i % led->yn;
sx = i / led->yn;
}
else
{
sx = i % led->xn;
sy = i / led->xn;
}
if( led->zigzag )
{
if( led->rot90 )
{
if( sx & 1 )
{
sy = led->yn - sy - 1;
}
}
else
{
if( sy & 1 )
{
sx = led->xn - sx - 1;
}
}
}
if( led->rot90 )
{
source = sx + sy * led->xn;
}
else
{
source = sx + sy * led->yn;
}
}
led->last_leds[i*3+0] = OutLEDs[source*3+1] * led->outamp;
led->last_leds[i*3+1] = OutLEDs[source*3+0] * led->outamp;
led->last_leds[i*3+2] = OutLEDs[source*3+2] * led->outamp;
}
led->readyFlag = 1;
}
static void LEDParams(void * id )
{
struct LEDOutDriver * led = (struct LEDOutDriver*)id;
led->total_leds = GetParameterI( "leds", 300 );
led->last_leds = malloc( led->total_leds * 3 + 1 );
led->outamp = .1; RegisterValue( "ledoutamp", PAFLOAT, &led->outamp, sizeof( led->outamp ) );
led->zigzag = 0; RegisterValue( "zigzag", PAINT, &led->zigzag, sizeof( led->zigzag ) );
led->xn = 16; RegisterValue( "lightx", PAINT, &led->xn, sizeof( led->xn ) );
led->yn = 9; RegisterValue( "lighty", PAINT, &led->yn, sizeof( led->yn ) );
led->rot90 = 0; RegisterValue( "rot90", PAINT, &led->rot90, sizeof( led->rot90 ) );
led->array = 0; RegisterValue( "ledarray", PAINT, &led->array, sizeof( led->array ) );
led->did_init = 0;
}
static struct DriverInstances * DisplayUSB2812()
{
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(DisplayUSB2812);

273
colorchord2/DrawFunctions.c Normal file
View 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 );
}

View 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

27
colorchord2/Makefile Normal file
View file

@ -0,0 +1,27 @@
all : colorchord
RAWDRAW:=DrawFunctions.o XDriver.o
SOUND:=sound.o sound_alsa.o sound_pulse.o sound_null.o
OUTS := OutputVoronoi.o DisplayArray.o OutputLinear.o DisplayPie.o DisplayNetwork.o DisplayUSB2812.o DisplayDMX.o OutputProminent.o RecorderPlugin.o
WINGCC:=i586-mingw32msvc-gcc
WINGCCFLAGS:= -O2 -Wl,--relax -Wl,--gc-sections -ffunction-sections -fdata-sections -s
WINLDFLAGS:=-lwinmm -lgdi32 -lws2_32
RAWDRAWLIBS:=-lX11 -lm -lpthread -lXinerama -lXext
LDLIBS:=-lpthread -lasound -lm -lpulse-simple -lpulse
CFLAGS:=-g -Os -flto -Wall -ffast-math -Iembeddedcommon
EXTRALIBS:=-lusb-1.0
colorchord : os_generic.o main.o dft.o decompose.o filter.o color.o sort.o notefinder.o util.o outdrivers.o $(RAWDRAW) $(SOUND) $(OUTS) parameters.o chash.o hook.o embeddedcommon/DFT32.o
gcc -o $@ $^ $(CFLAGS) $(LDLIBS) $(EXTRALIBS) $(RAWDRAWLIBS)
colorchord.exe : os_generic.c main.c dft.c decompose.c filter.c color.c sort.c notefinder.c util.c outdrivers.c DrawFunctions.c parameters.c chash.c WinDriver.c sound.c sound_null.c sound_win.c OutputVoronoi.c DisplayArray.c OutputLinear.c DisplayPie.c DisplayNetwork.c hook.c RecorderPlugin.c embeddedcommon/DFT32.c
$(WINGCC) $(WINGCCFLAGS) -o $@ $^ $(WINLDFLAGS)
clean :
rm -rf *.o *~ colorchord colorchord.exe embeddedcc

213
colorchord2/OutputLinear.c Normal file
View file

@ -0,0 +1,213 @@
//XXX TODO Figure out why it STILL fails when going around a loop
#include "outdrivers.h"
#include "notefinder.h"
#include <stdio.h>
#include <string.h>
#include "parameters.h"
#include <stdlib.h>
#include "color.h"
#include <stdlib.h>
#include <math.h>
#include <unistd.h>
struct LEDOutDriver
{
int did_init;
int total_leds;
int is_loop;
float light_siding;
float last_led_pos[MAX_LEDS];
float last_led_pos_filter[MAX_LEDS];
float last_led_amp[MAX_LEDS];
int steady_bright;
float led_floor;
float satamp;
int lastadvance;
};
static float lindiff( float a, float b ) //Find the minimum change around a wheel.
{
float diff = a - b;
if( diff < 0 ) diff *= -1;
float otherdiff = (a<b)?(a+1):(a-1);
otherdiff -=b;
if( otherdiff < 0 ) otherdiff *= -1;
if( diff < otherdiff )
return diff;
else
return otherdiff;
}
static void LEDUpdate(void * id, struct NoteFinder*nf)
{
struct LEDOutDriver * led = (struct LEDOutDriver*)id;
//Step 1: Calculate the quantity of all the LEDs we'll want.
int totbins = nf->note_peaks;//nf->dists;
int i, j;
float binvals[totbins];
float binvalsQ[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_amplitudes2[i], led->light_siding );
// binvals[i] = (binvals[i]<led->led_floor)?0:binvals[i];
// if( nf->note_positions[i] < 0 ) { binvals[i] = 0; binvalsQ[i] = 0; }
binvalsQ[i] =pow( nf->note_amplitudes[i], led->light_siding );
// nf->note_amplitudes[i];//
totalbinval += binvals[i];
}
float newtotal = 0;
for( i = 0; i < totbins; i++ )
{
#define SMOOTHZERO
#ifdef SMOOTHZERO
binvals[i] -= led->led_floor*totalbinval;
if( binvals[i] / totalbinval < 0 )
binvals[i] = binvalsQ[i] = 0;
#else
if( binvals[i] / totalbinval < led->led_floor )
binvals[i] = binvalsQ[i] = 0;
#endif
newtotal += binvals[i];
}
totalbinval = newtotal;
float rledpos[led->total_leds];
float rledamp[led->total_leds];
float rledampQ[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];
rledampQ[rbinout] = binvalsQ[i];
rbinout++;
}
}
for( ; rbinout < led->total_leds; rbinout++ )
{
rledpos[rbinout] = rledpos[rbinout-1];
rledamp[rbinout] = rledamp[rbinout-1];
rledampQ[rbinout] = rledampQ[rbinout-1];
}
//Now we have to minimize "advance".
int minadvance = 0;
if( led->is_loop )
{
float mindiff = 1e20;
//Uncomment this for a rotationally continuous surface.
for( i = 0; i < led->total_leds; i++ )
{
float diff = 0;
diff = 0;
for( j = 0; j < led->total_leds; j++ )
{
int r = (j + i) % led->total_leds;
float rd = lindiff( led->last_led_pos_filter[j], rledpos[r]);
diff += rd;//*rd;
}
int advancediff = ( led->lastadvance - i );
if( advancediff < 0 ) advancediff *= -1;
if( advancediff > led->total_leds/2 ) advancediff = led->total_leds - advancediff;
float ad = (float)advancediff/(float)led->total_leds;
diff += ad * ad;// * led->total_leds;
if( diff < mindiff )
{
mindiff = diff;
minadvance = i;
}
}
}
led->lastadvance = minadvance;
// printf( "MA: %d %f\n", minadvance, mindiff );
//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;
float sat = rledamp[ia] * led->satamp;
float satQ = rledampQ[ia] * led->satamp;
if( satQ > 1 ) satQ = 1;
led->last_led_pos[i] = rledpos[ia];
led->last_led_amp[i] = sat;
float sendsat = (led->steady_bright?sat:satQ);
if( sendsat > 1 ) sendsat = 1;
int r = CCtoHEX( led->last_led_pos[i], 1.0, sendsat );
OutLEDs[i*3+0] = r & 0xff;
OutLEDs[i*3+1] = (r>>8) & 0xff;
OutLEDs[i*3+2] = (r>>16) & 0xff;
}
if( led->is_loop )
{
for( i = 0; i < led->total_leds; i++ )
{
led->last_led_pos_filter[i] = led->last_led_pos_filter[i] * .9 + led->last_led_pos[i] * .1;
}
}
}
static void LEDParams(void * id )
{
struct LEDOutDriver * led = (struct LEDOutDriver*)id;
led->satamp = 2; RegisterValue( "satamp", PAFLOAT, &led->satamp, sizeof( led->satamp ) );
led->total_leds = 300; RegisterValue( "leds", PAINT, &led->total_leds, sizeof( led->total_leds ) );
led->led_floor = .1; RegisterValue( "led_floor", PAFLOAT, &led->led_floor, sizeof( led->led_floor ) );
led->light_siding = 1.4;RegisterValue( "light_siding", PAFLOAT, &led->light_siding, sizeof( led->light_siding ) );
led->is_loop = 0; RegisterValue( "is_loop", PAINT, &led->is_loop, sizeof( led->is_loop ) );
led->steady_bright = 1; RegisterValue( "steady_bright", PAINT, &led->steady_bright, sizeof( led->steady_bright ) );
printf( "Found LEDs for output. leds=%d\n", led->total_leds );
}
static struct DriverInstances * OutputLinear()
{
struct DriverInstances * ret = malloc( sizeof( struct DriverInstances ) );
memset( ret, 0, sizeof( struct DriverInstances ) );
struct LEDOutDriver * led = ret->id = malloc( sizeof( struct LEDOutDriver ) );
memset( led, 0, sizeof( struct LEDOutDriver ) );
ret->Func = LEDUpdate;
ret->Params = LEDParams;
LEDParams( led );
return ret;
}
REGISTER_OUT_DRIVER(OutputLinear);

View file

@ -0,0 +1,90 @@
//Really basic driver, that just selects the most prominent color and washes all the LEDs with that.
#include "outdrivers.h"
#include "notefinder.h"
#include <stdio.h>
#include <string.h>
#include "parameters.h"
#include <stdlib.h>
#include "color.h"
#include <stdlib.h>
#include <math.h>
#include <unistd.h>
struct ProminentDriver
{
int did_init;
int total_leds;
float satamp;
};
static void LEDUpdate(void * id, struct NoteFinder*nf)
{
struct ProminentDriver * led = (struct ProminentDriver*)id;
//Step 1: Calculate the quantity of all the LEDs we'll want.
int totbins = nf->note_peaks;//nf->dists;
int i;
float selected_amp = 0;
float selected_note = 0;
// if( totbins > led_bins ) totbins = led_bins;
for( i = 0; i < totbins; i++ )
{
float freq = nf->note_positions[i] / nf->freqbins;
float amp = nf->note_amplitudes2[i] * led->satamp;
if( amp > selected_amp )
{
selected_amp = amp;
selected_note = freq;
}
}
//Advance the LEDs to this position when outputting the values.
for( i = 0; i < led->total_leds; i++ )
{
float sendsat = selected_amp;
if( sendsat > 1 ) sendsat = 1;
int r = CCtoHEX( selected_note, 1.0, sendsat );
OutLEDs[i*3+0] = r & 0xff;
OutLEDs[i*3+1] = (r>>8) & 0xff;
OutLEDs[i*3+2] = (r>>16) & 0xff;
}
}
static void LEDParams(void * id )
{
struct ProminentDriver * led = (struct ProminentDriver*)id;
led->satamp = 2; RegisterValue( "satamp", PAFLOAT, &led->satamp, sizeof( led->satamp ) );
led->total_leds = 4; RegisterValue( "leds", PAINT, &led->total_leds, sizeof( led->total_leds ) );
printf( "Found Prominent for output. leds=%d\n", led->total_leds );
}
static struct DriverInstances * OutputProminent()
{
struct DriverInstances * ret = malloc( sizeof( struct DriverInstances ) );
memset( ret, 0, sizeof( struct DriverInstances ) );
struct ProminentDriver * led = ret->id = malloc( sizeof( struct ProminentDriver ) );
memset( led, 0, sizeof( struct ProminentDriver ) );
ret->Func = LEDUpdate;
ret->Params = LEDParams;
LEDParams( led );
return ret;
}
REGISTER_OUT_DRIVER(OutputProminent);

170
colorchord2/OutputVoronoi.c Normal file
View file

@ -0,0 +1,170 @@
#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
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 = d->xn/2.0;
float cy = d->yn/2.0;
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()%d->xn;
l->y = rand()%d->yn;
}
}
for( i = 0; i < d->note_peaks; i++ )
{
struct LINote * l = &d->notes[i];
l->lednrm = l->ledexp * tleds / totalexp;
}
int x, y;
int led = 0;
for( y = 0; y < d->yn; y++ )
for( x = 0; x < d->xn; x++ )
{
float lx = (x+.5);
float ly = (y+.5);
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;
}
}
uint32_t color = 0;
if( bestmatch != -1 )
{
float sat = nf->note_amplitudes_out[bestmatch] * d->satamp;
if( sat > 1.0 ) sat = 1.0;
color = CCtoHEX( nf->note_positions[bestmatch] / nf->freqbins, 1.0, sat );
}
OutLEDs[led*3+0] = color & 0xff;
OutLEDs[led*3+1] = ( color >> 8 ) & 0xff;
OutLEDs[led*3+2] = ( color >> 16 ) & 0xff;
led++;
}
CNFGColor ( 0xffffff );
}
static void DPOParams(void * id )
{
struct DPODriver * d = (struct DPODriver*)id;
//XXX WRONG
d->xn = 160; RegisterValue( "lightx", PAINT, &d->xn, sizeof( d->xn ) );
d->yn = 90; RegisterValue( "lighty", PAINT, &d->yn, sizeof( d->yn ) );
d->cutoff = .01; RegisterValue( "Voronoi_cutoff", PAFLOAT, &d->cutoff, sizeof( d->cutoff ) );
d->satamp = 5; RegisterValue( "satamp", PAFLOAT, &d->satamp, sizeof( d->satamp ) );
d->amppow = 2.51; RegisterValue( "amppow", PAFLOAT, &d->amppow, sizeof( d->amppow ) );
d->distpow = 1.5; RegisterValue( "distpow", PAFLOAT, &d->distpow, sizeof( d->distpow ) );
d->from_sides = 1; RegisterValue( "fromsides", PAINT, &d->from_sides, sizeof( d->from_sides ) );
d->note_peaks = 0;
}
static struct DriverInstances * OutputVoronoi(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(OutputVoronoi);

View file

@ -0,0 +1,206 @@
#include "outdrivers.h"
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <ctype.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "parameters.h"
#include "hook.h"
extern unsigned char OutLEDs[MAX_LEDS*3];
extern int UsedLEDs;
struct RecorderPlugin
{
int is_recording;
int sps;
char In_Filename[PARAM_BUFF];
char Out_Filename[PARAM_BUFF];
int DunBoop;
int BypassLength;
int TimeSinceStart;
FILE * fRec;
FILE * fPlay;
};
void StopRecording( struct RecorderPlugin * rp )
{
if( !rp->is_recording ) return;
rp->TimeSinceStart = 0;
rp->DunBoop = 0;
if( rp->fRec ) fclose( rp->fRec );
if( rp->fPlay) fclose( rp->fPlay );
rp->is_recording = 0;
rp->fRec = 0;
rp->fPlay = 0;
printf( "Stopped.\n" );
}
void StartRecording( struct RecorderPlugin * rp )
{
if( rp->is_recording ) return;
rp->TimeSinceStart = 0;
rp->DunBoop = 0;
rp->is_recording = 1;
printf( "Starting Recording: /%s/%s/\n", rp->In_Filename, rp->Out_Filename );
if( rp->In_Filename[0] == 0 )
{
//Nothing
rp->fPlay = 0;
}
else
{
rp->fPlay = fopen( rp->In_Filename, "rb" );
if( !rp->fPlay )
{
fprintf( stderr, "Warning: Could not play filename: %s\n", rp->In_Filename );
}
printf( "Play file opened.\n" );
}
if( rp->Out_Filename[0] == 0 )
{
//Nothing
rp->fRec = 0;
}
else
{
struct stat buf;
char cts[1024];
int i;
for( i = 0; i < 999; i++ )
{
if( i == 0 )
{
snprintf( cts, 1023, "%s", rp->Out_Filename );
}
else
{
snprintf( cts, 1023, "%s.%03d", rp->Out_Filename, i );
}
if( stat( cts, &buf ) != 0 )
break;
}
printf( "Found rec file %s\n", cts );
rp->fRec = fopen( cts, "wb" );
if( !rp->fRec )
{
fprintf( stderr, "Error: cannot start recording file \"%s\"\n", cts );
return;
}
printf( "Recording...\n" );
}
}
static void RecordEvent(void * v, int samples, float * samps, int channel_ct)
{
struct RecorderPlugin * rp = (struct RecorderPlugin*)v;
if( !rp->fRec || !rp->is_recording ) return;
if( rp->DunBoop || !rp->fPlay )
{
int r = fwrite( samps, channel_ct * sizeof( float ), samples, rp->fRec );
if( r != samples )
{
StopRecording( rp );
}
}
}
static void PlaybackEvent(void * v, int samples, float * samps, int channel_ct)
{
struct RecorderPlugin * rp = (struct RecorderPlugin*)v;
if( !rp->fPlay ) return;
int r = fread( samps, channel_ct * sizeof( float ), samples, rp->fPlay );
if( r != samples )
{
StopRecording( rp );
return;
}
rp->TimeSinceStart += samples;
if( rp->TimeSinceStart < rp->BypassLength )
{
if( rp->is_recording )
force_white = 1;
else
force_white = 0;
int r = fwrite( samps, channel_ct * sizeof( float ), samples, rp->fRec );
if( r != samples )
{
StopRecording( rp );
}
}
else
{
force_white = 0;
rp->DunBoop = 1;
}
}
static void MKeyEvent( void * v, int keycode, int down )
{
struct RecorderPlugin * rp = (struct RecorderPlugin*)v;
char c = toupper( keycode );
if( c == 'R' && down && !rp->is_recording ) StartRecording( rp );
if( c == 'S' && down && rp->is_recording ) StopRecording( rp );
}
static void DPOUpdate(void * id, struct NoteFinder*nf)
{
//Do nothing, happens every frame.
}
static void DPOParams(void * id )
{
struct RecorderPlugin * d = (struct RecorderPlugin*)id;
d->is_recording = 0;
d->fRec = 0;
d->fPlay = 0;
d->DunBoop = 0;
d->TimeSinceStart = 0;
memset( d->In_Filename, 0, PARAM_BUFF ); RegisterValue( "player_filename", PABUFFER, d->In_Filename, PARAM_BUFF );
memset( d->Out_Filename, 0, PARAM_BUFF ); RegisterValue( "recorder_filename", PABUFFER, d->Out_Filename, PARAM_BUFF );
d->sps = 0; RegisterValue( "samplerate", PAINT, &d->sps, sizeof( d->sps ) );
d->BypassLength = 0; RegisterValue( "recorder_bypass", PAINT, &d->BypassLength, sizeof( d->BypassLength ) );
}
static struct DriverInstances * RecorderPlugin(const char * parameters)
{
struct DriverInstances * ret = malloc( sizeof( struct DriverInstances ) );
struct RecorderPlugin * rp = ret->id = malloc( sizeof( struct RecorderPlugin ) );
memset( rp, 0, sizeof( struct RecorderPlugin ) );
ret->Func = DPOUpdate;
ret->Params = DPOParams;
DPOParams( rp );
HookKeyEvent( MKeyEvent, rp );
HookSoundInEvent( RecordEvent, rp, 0 );
HookSoundInEvent( PlaybackEvent, rp, 1 );
return ret;
}
REGISTER_OUT_DRIVER(RecorderPlugin);

236
colorchord2/WinDriver.c Normal file
View file

@ -0,0 +1,236 @@
//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 <stdio.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 WindowsTerm();
void CNFGHandleInput()
{
int ldown = 0;
MSG msg;
while( PeekMessage( &msg, lsHWND, 0, 0xFFFF, 1 ) )
{
TranslateMessage(&msg);
switch( msg.message )
{
case WM_QUIT:
case WM_DESTROY:
case WM_CLOSE:
printf( "Close\n" );
WindowsTerm();
TerminateProcess( 0, 0 );
break;
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
colorchord2/XDriver.c Normal file
View 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, verts, Convex, CoordModeOrigin );
}
#endif

440
colorchord2/chash.c Normal file
View file

@ -0,0 +1,440 @@
#include "chash.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <malloc.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
colorchord2/chash.h Normal file
View 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
colorchord2/color.c Normal file
View 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
colorchord2/color.h Normal file
View 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
colorchord2/decompose.c Normal file
View 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
colorchord2/decompose.h Normal file
View 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

94
colorchord2/default.conf Normal file
View file

@ -0,0 +1,94 @@
# This is the configuration file for colorchord.
# Most values are already defaulted in the software.
# This file is constantly checked for new versions.
# \r, and ; are used as terminators, so you can put
# multiple entries on the same line.
#Whether to limit the control loop to ~60ish FPS.
cpu_autolimit = 1
#General GUI properties.
title = PA Test
set_screenx = 720
set_screeny = 480
#Sound properties.
buffer = 128
play = 0
rec = 1
channels = 2
samplerate = 44100
wininput = 0
#Compiled version will default this.
#sound_source = PULSE
#-1 indicates left and right, 0 left, 1 right.
sample_channel = 1
sourcename = default
alsa_output.pci-0000_00_1b.0.analog-stereo.monitor
##################################
# General ColorChord properties. #
##################################
# How much to amplify the incoming signal.
amplify = 2.0
# What is the base note? I.e. the lowest note.
# Note that it won't have very much impact until an octave up though!
base_hz = 55.0000
# This is only used when dealing with the slow decompose (now defunct)
# decompose_iterations = 1000
# default_sigma = 1.4000
# DFT properties for the DFT up top.
dft_iir = 0.6
dft_q = 20.0000
dft_speedup = 1000.0000
octaves = 5
# Should we use a progressive DFT?
# 0 = DFT Quick
# 1 = DFT Progressive
# 2 = DFT Progressive Integer
# 3 = DFT Progressive Integer Skippy
# 4 = Integer, 32-Bit, Progressive, Skippy.
do_progressive_dft = 4
filter_iter = 2
filter_strength = .5
# How many bins per octave to use?
freqbins = 24
# For the final note information... How much to slack everything?
note_attach_amp_iir = 0.2000
note_attach_amp_iir2 = 0.150
note_attach_freq_iir = 0.3000
#How many bins a note can jump from frame to frame to be considered a slide.
#this is used to prevent notes from popping in and out a lot.
note_combine_distance = 0.5000
note_jumpability = 1.8000
note_minimum_new_distribution_value = 0.0200
note_out_chop = 0.05000
#=======================================================================
#Outputs
This is a vornoi thing:
outdrivers = OutputVoronoi, DisplayArray
lightx = 64
lighty = 32
fromsides = 1
shape_cutoff = 0.03
satamp = 5.000
amppow = 2.510
distpow = 1.500

690
colorchord2/dft.c Normal file
View file

@ -0,0 +1,690 @@
#include "dft.h"
#include <math.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#ifndef CCEMBEDDED
void DoDFT( float * outbins, float * frequencies, int bins, float * databuffer, int place_in_data_buffer, int size_of_data_buffer, float q )
{
int i, j;
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++ )
{
int flirts = 0;
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;
flirts++;
}
float amp = sqrtf( binqtys * binqtys + binqtyc * binqtyc );
outbins[i] = amp / freq / q * skip;
}
}
////////////////////////////DFT Progressive is for embedded systems, primarily.
static float * gbinqtys;
static float * gbinqtyc;
static float * phis;
static float * gfrequencies;
static float * lastbins;
static float * advances;
static float * goutbins;
static int gbins;
static float gq;
static float gspeedup;
#define PROGIIR .005
void HandleProgressive( float sample )
{
int i;
for( i = 0; i < gbins; i++ )
{
float thiss = sinf( phis[i] ) * sample;
float thisc = cosf( phis[i] ) * sample;
float s = gbinqtys[i] = gbinqtys[i] * (1.-PROGIIR) + thiss * PROGIIR;
float c = gbinqtyc[i] = gbinqtyc[i] * (1.-PROGIIR) + thisc * PROGIIR;
phis[i] += advances[i];
if( phis[i] > 6.283 ) phis[i]-=6.283;
goutbins[i] = sqrtf( s * s + c * c );
}
}
void DoDFTProgressive( 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;
static int last_place;
if( gbins != bins )
{
if( gbinqtys ) free( gbinqtys );
if( gbinqtyc ) free( gbinqtyc );
if( phis ) free( phis );
if( lastbins ) free( lastbins );
if( advances ) free( advances );
gbinqtys = malloc( sizeof(float)*bins );
gbinqtyc = malloc( sizeof(float)*bins );
phis = malloc( sizeof(float)*bins );
lastbins = malloc( sizeof(float)*bins );
advances = malloc( sizeof(float)*bins );
memset( gbinqtys, 0, sizeof(float)*bins );
memset( gbinqtyc, 0, sizeof(float)*bins );
memset( phis, 0, sizeof(float)*bins );
memset( lastbins, 0, sizeof(float)*bins );
}
memcpy( outbins, lastbins, sizeof(float)*bins );
for( i = 0; i < bins; i++ )
{
float freq = frequencies[i];
advances[i] = 3.14159*2.0/freq;
}
gbins = bins;
gfrequencies = frequencies;
goutbins = outbins;
gspeedup = speedup;
gq = q;
place_in_data_buffer = (place_in_data_buffer+1)%size_of_data_buffer;
int didrun = 0;
for( i = last_place; i != place_in_data_buffer; i = (i+1)%size_of_data_buffer )
{
float fin = ((float)((int)(databuffer[i] * 127))) / 127.0; //simulate 8-bit input (it looks FINE!)
HandleProgressive( fin );
didrun = 1;
}
last_place = place_in_data_buffer;
if( didrun )
{
memcpy( lastbins, outbins, sizeof(float)*bins );
}
/* for( i = 0; i < bins; i++ )
{
printf( "%0.2f ", outbins[i]*100 );
}
printf( "\n" );*/
}
/////////////////////////////INTEGER DFT
//NOTES to self:
//
// Let's say we want to try this on an AVR.
// 24 bins, 5 octaves = 120 bins.
// 20 MHz clock / 4.8k sps = 4096 IPS = 34 clocks per bin = :(
// We can do two at the same time, this frees us up some
static uint8_t donefirstrun;
static int8_t sintable[512]; //Actually [sin][cos] pairs.
//LDD instruction on AVR can read with constant offset. We can set Y to be the place in the buffer, and read with offset.
static uint16_t * datspace; //(advances,places,isses,icses)
void HandleProgressiveInt( int8_t sample1, int8_t sample2 )
{
int i;
int16_t ts, tc;
int16_t tmp1;
int8_t s1, c1;
uint16_t ipl, localipl, adv;
uint16_t * ds = &datspace[0];
int8_t * st;
//Clocks are listed for AVR.
//Estimated 78 minimum instructions... So for two pairs each... just over 4ksps, theoretical.
//Running overall at ~2kHz. With GCC: YUCK! 102 cycles!!!
for( i = 0; i < gbins; i++ ) //Loop, fixed size = 3 + 2 cycles N/A
{
//12 cycles MIN
adv = *(ds++); //Read, indirect from RAM (and increment) 2+2 cycles 4
ipl = *(ds++); //Read, indirect from RAM (and increment) 2+2 cycles 4
//13 cycles MIN
ipl += adv; //Advance, 16bit += 16bit, 1 + 1 cycles 2
localipl = (ipl>>8)<<1; //Select upper 8 bits 1 cycles 1 *** AS/IS: 4
st = &sintable[localipl];
s1 = *(st++); //Read s1 component out of table. 2+2 cycles 2
c1 = *st; //Read c1 component out of table. 2 cycles 2 *** AS/IS: 4
ts = (s1 * sample1); // 8 x 8 multiply signed + copy R1 out. zero MSB ts 2 ->Deferred
tc = (c1 * sample1); // 8 x 8 multiply signed + copy R1 out. zero MSB tc 2 ->Deferred
//15 cycles MIN
ipl += adv; //Advance, 16bit += 16bit, 1 + 1 cycles 2
localipl = (ipl>>8)<<1; //Select upper 8 bits 1 cycles 1 *** AS/IS: 4
// need to load Z with 'sintable' and add localipl 2
st = &sintable[localipl];
s1 = *(st++); //Read s1 component out of table. 2 cycles 2
c1 = *st; //Read c1 component out of table. 2 cycles 2 *** AS/IS: 4
ts += (s1 * sample2); // 8 x 8 multiply signed + add R1 out. 3 ->Deferred
tc += (c1 * sample2); // 8 x 8 multiply signed + add R1 out. 3 ->Deferred
//Add TS and TC to the datspace stuff. (24 instructions)
tmp1 = (*ds); //Read out, sin component. 4 Accurate.
tmp1 -= tmp1>>7; //Subtract from the MSB (with carry) 2 -> 6 AS/IS: 7+7 = 14
tmp1 += ts>>7; //Add MSBs with carry 2 -> 6 AS/IS: 6
*(ds++) = tmp1; //Store values back 4
tmp1 = *ds; //Read out, sin component. 4
tmp1 -= tmp1>>7; //Subtract from the MSB (with carry) 2 -> 6 AS/IS: 7+7 = 14
tmp1 += tc>>7; //Add MSBs with carry 2 -> 6 AS/IS: 6
*ds++ = tmp1; //Store values back 4
*(ds-3) = ipl; //Store values back 4 AS/IS: 6
//AS-IS: 8 loop overhead.
}
}
void DoDFTProgressiveInteger( float * outbins, float * frequencies, int bins, const float * databuffer, int place_in_data_buffer, int size_of_data_buffer, float q, float speedup )
{
int i;
static int last_place;
if( !donefirstrun )
{
donefirstrun = 1;
for( i = 0; i < 256; i++ )
{
sintable[i*2+0] = (int8_t)((sinf( i / 256.0 * 6.283 ) * 127.0));
sintable[i*2+1] = (int8_t)((cosf( i / 256.0 * 6.283 ) * 127.0));
}
}
if( gbins != bins )
{
gbins = bins;
if( datspace ) free( datspace );
datspace = malloc( bins * 2 * 4 );
}
for( i = 0; i < bins; i++ )
{
float freq = frequencies[i];
datspace[i*4] = 65536.0/freq;
}
for( i = last_place; i != ( place_in_data_buffer&0xffffe ); i = (i+2)%size_of_data_buffer )
{
int8_t ifr1 = (int8_t)( ((databuffer[i+0]) ) * 127 );
int8_t ifr2 = (int8_t)( ((databuffer[i+1]) ) * 127 );
// printf( "%d %d\n", i, place_in_data_buffer&0xffffe );
HandleProgressiveInt( ifr1, ifr2 );
}
last_place = place_in_data_buffer&0xfffe;
//Extract bins.
for( i = 0; i < bins; i++ )
{
int16_t isps = datspace[i*4+2];
int16_t ispc = datspace[i*4+3];
int16_t mux = ( (isps/256) * (isps/256)) + ((ispc/256) * (ispc/256));
// printf( "%d (%d %d)\n", mux, isps, ispc );
outbins[i] = sqrt( mux )/100.0;
}
// printf( "\n");
}
#endif
////////////////////////SKIPPY DFT
//NOTES to self:
//
// Let's say we want to try this on an AVR.
// 24 bins, 5 octaves = 120 bins.
// 20 MHz clock / 4.8k sps = 4096 IPS = 34 clocks per bin = :(
// We can do two at the same time, this frees us up some
static uint8_t Sdonefirstrun;
//int8_t Ssintable[512]; //Actually [sin][cos] pairs.
static const int8_t Ssintable[512] = {
0, 127, 3, 126, 6, 126, 9, 126, 12, 126, 15, 126, 18, 125, 21, 125,
24, 124, 27, 123, 30, 123, 33, 122, 36, 121, 39, 120, 42, 119, 45, 118,
48, 117, 51, 116, 54, 114, 57, 113, 59, 112, 62, 110, 65, 108, 67, 107,
70, 105, 73, 103, 75, 102, 78, 100, 80, 98, 82, 96, 85, 94, 87, 91,
89, 89, 91, 87, 94, 85, 96, 82, 98, 80, 100, 78, 102, 75, 103, 73,
105, 70, 107, 67, 108, 65, 110, 62, 112, 59, 113, 57, 114, 54, 116, 51,
117, 48, 118, 45, 119, 42, 120, 39, 121, 36, 122, 33, 123, 30, 123, 27,
124, 24, 125, 21, 125, 18, 126, 15, 126, 12, 126, 9, 126, 6, 126, 3,
127, 0, 126, -3, 126, -6, 126, -9, 126, -12, 126, -15, 125, -18, 125, -21,
124, -24, 123, -27, 123, -30, 122, -33, 121, -36, 120, -39, 119, -42, 118, -45,
117, -48, 116, -51, 114, -54, 113, -57, 112, -59, 110, -62, 108, -65, 107, -67,
105, -70, 103, -73, 102, -75, 100, -78, 98, -80, 96, -82, 94, -85, 91, -87,
89, -89, 87, -91, 85, -94, 82, -96, 80, -98, 78,-100, 75,-102, 73,-103,
70,-105, 67,-107, 65,-108, 62,-110, 59,-111, 57,-113, 54,-114, 51,-116,
48,-117, 45,-118, 42,-119, 39,-120, 36,-121, 33,-122, 30,-123, 27,-123,
24,-124, 21,-125, 18,-125, 15,-126, 12,-126, 9,-126, 6,-126, 3,-126,
0,-127, -3,-126, -6,-126, -9,-126, -12,-126, -15,-126, -18,-125, -21,-125,
-24,-124, -27,-123, -30,-123, -33,-122, -36,-121, -39,-120, -42,-119, -45,-118,
-48,-117, -51,-116, -54,-114, -57,-113, -59,-112, -62,-110, -65,-108, -67,-107,
-70,-105, -73,-103, -75,-102, -78,-100, -80, -98, -82, -96, -85, -94, -87, -91,
-89, -89, -91, -87, -94, -85, -96, -82, -98, -80,-100, -78,-101, -75,-103, -73,
-105, -70,-107, -67,-108, -65,-110, -62,-111, -59,-113, -57,-114, -54,-116, -51,
-117, -48,-118, -45,-119, -42,-120, -39,-121, -36,-122, -33,-123, -30,-123, -27,
-124, -24,-125, -21,-125, -18,-126, -15,-126, -12,-126, -9,-126, -6,-126, -3,
-127, 0,-126, 3,-126, 6,-126, 9,-126, 12,-126, 15,-125, 18,-125, 21,
-124, 24,-123, 27,-123, 30,-122, 33,-121, 36,-120, 39,-119, 42,-118, 45,
-117, 48,-116, 51,-114, 54,-113, 57,-112, 59,-110, 62,-108, 65,-107, 67,
-105, 70,-103, 73,-102, 75,-100, 78, -98, 80, -96, 82, -94, 85, -91, 87,
-89, 89, -87, 91, -85, 94, -82, 96, -80, 98, -78, 100, -75, 101, -73, 103,
-70, 105, -67, 107, -65, 108, -62, 110, -59, 111, -57, 113, -54, 114, -51, 116,
-48, 117, -45, 118, -42, 119, -39, 120, -36, 121, -33, 122, -30, 123, -27, 123,
-24, 124, -21, 125, -18, 125, -15, 126, -12, 126, -9, 126, -6, 126, -3, 126,};
/** The above table was created using the following code:
#include <math.h>
#include <stdio.h>
#include <stdint.h>
int8_t Ssintable[512]; //Actually [sin][cos] pairs.
int main()
{
int i;
for( i = 0; i < 256; i++ )
{
Ssintable[i*2+0] = (int8_t)((sinf( i / 256.0 * 6.283 ) * 127.0));
Ssintable[i*2+1] = (int8_t)((cosf( i / 256.0 * 6.283 ) * 127.0));
}
printf( "static const int8_t Ssintable[512] = {" );
for( i = 0; i < 512; i++ )
{
if( !(i & 0xf ) )
{
printf( "\n\t" );
}
printf( "%4d," ,Ssintable[i] );
}
printf( "};\n" );
}
*/
uint16_t Sdatspace[FIXBINS*4]; //(advances,places,isses,icses)
//For
static uint8_t Sdo_this_octave[BINCYCLE];
static int16_t Saccum_octavebins[OCTAVES];
static uint8_t Swhichoctaveplace;
uint16_t embeddedbins[FIXBINS]; //This is updated every time the DFT hits the octavecount, or 1/32 updates.
//From: http://stackoverflow.com/questions/1100090/looking-for-an-efficient-integer-square-root-algorithm-for-arm-thumb2
/**
* \brief Fast Square root algorithm, with rounding
*
* This does arithmetic rounding of the result. That is, if the real answer
* would have a fractional part of 0.5 or greater, the result is rounded up to
* the next integer.
* - SquareRootRounded(2) --> 1
* - SquareRootRounded(3) --> 2
* - SquareRootRounded(4) --> 2
* - SquareRootRounded(6) --> 2
* - SquareRootRounded(7) --> 3
* - SquareRootRounded(8) --> 3
* - SquareRootRounded(9) --> 3
*
* \param[in] a_nInput - unsigned integer for which to find the square root
*
* \return Integer square root of the input value.
*/
static uint16_t SquareRootRounded(uint32_t a_nInput)
{
uint32_t op = a_nInput;
uint32_t res = 0;
uint32_t one = 1uL << 30; // The second-to-top bit is set: use 1u << 14 for uint16_t type; use 1uL<<30 for uint32_t type
// "one" starts at the highest power of four <= than the argument.
while (one > op)
{
one >>= 2;
}
while (one != 0)
{
if (op >= res + one)
{
op = op - (res + one);
res = res + 2 * one;
}
res >>= 1;
one >>= 2;
}
/* Do arithmetic rounding to nearest integer */
if (op > res)
{
res++;
}
return res;
}
void HandleProgressiveIntSkippy( int8_t sample1 )
{
int i;
int16_t ts, tc;
int16_t tmp1;
int8_t s1, c1;
uint16_t ipl, localipl, adv;
uint8_t oct = Sdo_this_octave[Swhichoctaveplace];
Swhichoctaveplace ++;
Swhichoctaveplace &= BINCYCLE-1;
if( oct > 128 )
{
//Special: This is when we can update everything.
/* if( (rand()%100) == 0 )
{
for( i = 0; i < FIXBINS; i++ )
// printf( "%0.2f ",goutbins[i]*100 );
printf( "(%d %d)",Sdatspace[i*4+2], Sdatspace[i*4+3] );
printf( "\n" );
} */
for( i = 0; i < FIXBINS; i++ )
{
int16_t isps = Sdatspace[i*4+2];
int16_t ispc = Sdatspace[i*4+3];
// printf( "%d (%d %d)\n", mux, isps, ispc );
int octave = i / FIXBPERO;
// mux >>= octave;
#ifndef CCEMBEDDED
uint32_t mux = ( (isps/256) * (isps/256)) + ((ispc/256) * (ispc/256));
goutbins[i] = sqrt( mux );
goutbins[i]/=25*(1<<octave);
#endif
uint32_t rmux = ( (isps) * (isps)) + ((ispc) * (ispc));
embeddedbins[i] = SquareRootRounded( rmux );
embeddedbins[i] >>= octave;
Sdatspace[i*4+2] -= isps>>4; //XXX 4 is more responsive AND doesn't overflow as easily.
Sdatspace[i*4+3] -= ispc>>4; //XXX 4 is more responsive AND doesn't overflow as easily.
//TRICKY: It is possible for the sin and cos accumulators to overflow,
//I DO NOT INTEND TO FIX THIS NOW! It could be easily fixed by using 32-bit integers, or
//by decreasing the quality a little bit, but it is an extreme case with a pure, full-volume sinewave.
}
return;
}
for( i = 0; i < OCTAVES;i++ )
{
Saccum_octavebins[i] += sample1;
}
uint16_t * ds = &Sdatspace[oct*FIXBPERO*4];
const int8_t * st;
sample1 = Saccum_octavebins[oct]>>(OCTAVES-oct);
Saccum_octavebins[oct] = 0;
for( i = 0; i < FIXBPERO; i++ ) //Loop, fixed size = 3 + 2 cycles N/A
{
//12 cycles MIN
adv = *(ds++); //Read, indirect from RAM (and increment) 2+2 cycles 4
ipl = *(ds++); //Read, indirect from RAM (and increment) 2+2 cycles 4
//13 cycles MIN
ipl += adv; //Advance, 16bit += 16bit, 1 + 1 cycles 2
localipl = (ipl>>8)<<1; //Select upper 8 bits 1 cycles 1 *** AS/IS: 4
st = &Ssintable[localipl];
s1 = *(st++); //Read s1 component out of table. 2+2 cycles 2
c1 = *st; //Read c1 component out of table. 2 cycles 2 *** AS/IS: 4
ts = (s1 * sample1); // 8 x 8 multiply signed + copy R1 out. zero MSB ts 2 ->Deferred
tc = (c1 * sample1); // 8 x 8 multiply signed + copy R1 out. zero MSB tc 2 ->Deferred
//Add TS and TC to the datspace stuff. (24 instructions)
tmp1 = (*ds); //Read out, sin component. 4 Accurate.
// tmp1 -= tmp1>>4; //Subtract from the MSB (with carry) 2 -> 6 AS/IS: 7+7 = 14
tmp1 += ts>>SHIFT_ADD_DETAIL; //Add MSBs with carry 2 -> 6 AS/IS: 6
*(ds++) = tmp1; //Store values back 4
tmp1 = *ds; //Read out, sin component. 4
// tmp1 -= tmp1>>4; //Subtract from the MSB (with carry) 2 -> 6 AS/IS: 7+7 = 14
tmp1 += tc>>SHIFT_ADD_DETAIL; //Add MSBs with carry 2 -> 6 AS/IS: 6
*ds++ = tmp1; //Store values back 4
*(ds-3) = ipl; //Store values back 4 AS/IS: 6
//AS-IS: 8 loop overhead.
}
}
void SetupDFTProgressiveIntegerSkippy()
{
int i;
int j;
//Sdatspace = malloc(FIXBPERO*OCTAVES*8);
//memset(Sdatspace,0,FIXBPERO*OCTAVES*8);
//printf( "MS: %d\n", FIXBPERO*OCTAVES*8);
Sdonefirstrun = 1;
/*
for( i = 0; i < 256; i++ )
{
Ssintable[i*2+0] = (int8_t)((sinf( i / 256.0 * 6.283 ) * 127.0));
Ssintable[i*2+1] = (int8_t)((cosf( i / 256.0 * 6.283 ) * 127.0));
}
*/
for( i = 0; i < BINCYCLE; i++ )
{
// Sdo_this_octave =
// 4 3 4 2 4 3 4 ...
//search for "first" zero
for( j = 0; j <= OCTAVES; j++ )
{
if( ((1<<j) & i) == 0 ) break;
}
if( j > OCTAVES )
{
fprintf( stderr, "Error: algorithm fault.\n" );
exit( -1 );
}
Sdo_this_octave[i] = OCTAVES-j-1;
}
}
#ifndef CCEMBEDDED
void UpdateBinsForProgressiveIntegerSkippy( const float * frequencies )
{
int i;
for( i = 0; i < FIXBINS; i++ )
{
float freq = frequencies[(i%FIXBPERO) + (FIXBPERO*(OCTAVES-1))];
Sdatspace[i*4] = (65536.0/freq);// / oneoveroctave;
}
}
#endif
void UpdateBinsForProgressiveIntegerSkippyInt( const uint16_t * frequencies )
{
int i;
for( i = 0; i < FIXBINS; i++ )
{
uint16_t freq = frequencies[i%FIXBPERO];
Sdatspace[i*4] = freq;// / oneoveroctave;
}
}
void Push8BitIntegerSkippy( int8_t dat )
{
HandleProgressiveIntSkippy( dat );
HandleProgressiveIntSkippy( dat );
}
#ifndef CCEMBEDDED
void DoDFTProgressiveIntegerSkippy( float * outbins, float * frequencies, int bins, const float * databuffer, int place_in_data_buffer, int size_of_data_buffer, float q, float speedup )
{
static float backupbins[FIXBINS];
int i;
static int last_place;
memset( outbins, 0, bins * sizeof( float ) );
goutbins = outbins;
memcpy( outbins, backupbins, FIXBINS*4 );
if( FIXBINS != bins )
{
fprintf( stderr, "Error: Bins was reconfigured. skippy requires a constant number of bins.\n" );
return;
}
//printf( "SKIPPY\n" );
if( !Sdonefirstrun )
{
SetupDFTProgressiveIntegerSkippy();
Sdonefirstrun = 1;
}
UpdateBinsForProgressiveIntegerSkippy( frequencies );
for( i = last_place; i != place_in_data_buffer; i = (i+1)%size_of_data_buffer )
{
int8_t ifr1 = (int8_t)( ((databuffer[i]) ) * 127 );
HandleProgressiveIntSkippy( ifr1 );
HandleProgressiveIntSkippy( ifr1 );
}
last_place = place_in_data_buffer;
memcpy( backupbins, outbins, FIXBINS*4 );
}
#endif

76
colorchord2/dft.h Normal file
View file

@ -0,0 +1,76 @@
#ifndef _DFT_H
#define _DFT_H
#include <stdint.h>
//Warning: Most ColorChords are not available for ColorChord Embedded.
#ifndef CCEMBEDDED
//There are several options here, the last few are selectable by modifying the do_progressive_dft flag.
//Do a DFT on a live audio ring buffer. It assumes new samples are added on in the + direction, older samples go negative.
//Frequencies are as a function of the samplerate, for example, a frequency of 22050 is actually 2 Hz @ 44100 SPS
//bins = number of frequencies to check against.
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.
//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 );
//An unusual tool to do a "progressive" DFT, using data from previous rounds.
void DoDFTProgressive( float * outbins, float * frequencies, int bins, const float * databuffer, int place_in_data_buffer, int size_of_data_buffer, float q, float speedup );
//A progressive DFT that's done using only low-bit integer math.
//This is almost fast enough to work on an AVR, with two AVRs, it's likely that it could be powerful enough.
//This is fast enough to run on an ESP8266
void DoDFTProgressiveInteger( float * outbins, float * frequencies, int bins, const float * databuffer, int place_in_data_buffer, int size_of_data_buffer, float q, float speedup );
#endif
//Everything the integer one buys, except it only calculates 2 octaves worth of notes per audio frame.
//This is sort of working, but still have some quality issues.
//It would theoretically be fast enough to work on an AVR.
//NOTE: This is the only DFT available to the embedded port of ColorChord
void DoDFTProgressiveIntegerSkippy( float * outbins, float * frequencies, int bins, const float * databuffer, int place_in_data_buffer, int size_of_data_buffer, float q, float speedup );
//It's actually split into a few functions, which you can call on your own:
void SetupDFTProgressiveIntegerSkippy(); //Call at start.
#ifndef CCEMBEDDED
void UpdateBinsForProgressiveIntegerSkippy( const float * frequencies ); //Update the frequencies
#endif
void UpdateBinsForProgressiveIntegerSkippyInt( const uint16_t * frequencies );
void Push8BitIntegerSkippy( int8_t dat ); //Call this to push on new frames of sound.
//You can # define these to be other things.
#ifndef OCTAVES
#define OCTAVES 5
#endif
#ifndef FIXBPERO
#define FIXBPERO 24
#endif
#ifndef FIXBINS
#define FIXBINS (FIXBPERO*OCTAVES)
#endif
#ifndef BINCYCLE
#define BINCYCLE (1<<OCTAVES)
#endif
//This variable determins how much to nerf the current sample of the DFT.
//I've found issues when this is smaller, but bigger values do have a negative
//impact on quality. We should strongly consider using 32-bit accumulators.
#ifndef SHIFT_ADD_DETAIL
#define SHIFT_ADD_DETAIL 5
#endif
//Whenever you need to read the bins, you can do it from here.
extern uint16_t Sdatspace[]; //(advances,places,isses,icses)
extern uint16_t embeddedbins[]; //This is updated every time the DFT hits the octavecount, or every BINCYCLE updates.
#endif

29
colorchord2/dmx.conf Normal file
View file

@ -0,0 +1,29 @@
outdrivers = DisplayDMX, OutputLinear, DisplayArray, RecorderPlugin
play = 1
buffer = 512
player_filename = lora1.raw
recorder_filename = recfile.raw
recorder_bypass = 44100
byte_offset = 32
ledoutamp = 1.0
leds = 4
lightx = 2
lighty = 2
light_siding = 1.6
note_attach_amp_iir = .3000
note_attach_amp_iir2 = .1500
note_attach_freq_iir = 0.3000
steady_bright = 0
skipfirst = 1
firstval = 0
port = 7777
address = 192.168.0.245

58
colorchord2/filter.c Normal file
View file

@ -0,0 +1,58 @@
#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];
float right = tmp[(i+bins+1)%bins];
float left = tmp[(i+bins-1)%bins];
folded[i] = folded[i] * (1.-strength) + (left + right) * strength * 0.5;
}
}
}

11
colorchord2/filter.h Normal file
View 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

102
colorchord2/hook.c Normal file
View file

@ -0,0 +1,102 @@
#include "hook.h"
struct KeyEvent
{
void (*KeyE)( void * v, int key, int down );
void * v;
} KeyEvents[MAX_KEY_EVENTS];
void KeyHappened( int key, int down )
{
int i;
for( i = 0; i < MAX_KEY_EVENTS; i++ )
{
if( KeyEvents[i].KeyE )
KeyEvents[i].KeyE( KeyEvents[i].v, key, down );
}
}
void HookKeyEvent( void (*KeyE)( void * v, int key, int down ), void * v )
{
int i;
for( i = 0; i < MAX_KEY_EVENTS; i++ )
{
if( !KeyEvents[i].KeyE )
{
KeyEvents[i].KeyE = KeyE;
KeyEvents[i].v = v;
break;
}
}
}
void UnhookKeyEvent( void (*KeyE)( void * v, int key, int down ), void * v )
{
int i;
for( i = 0; i < MAX_KEY_EVENTS; i++ )
{
if( KeyEvents[i].KeyE == KeyE && KeyEvents[i].v == v )
{
KeyEvents[i].KeyE = 0;
KeyEvents[i].v = 0;
}
}
}
struct SoundEvent
{
void (*SoundE)( void * v, int samples, float * samps, int channel_ct );
void * v;
};
struct SoundEvent SoundEvents[2][MAX_SOUND_EVENTS];
void SoundEventHappened( int samples, float * samps, int is_out, int channel_ct )
{
int i;
for( i = 0; i < MAX_SOUND_EVENTS; i++ )
{
if( SoundEvents[is_out][i].SoundE )
{
SoundEvents[is_out][i].SoundE( SoundEvents[is_out][i].v, samples, samps, channel_ct );
}
}
}
void HookSoundInEvent( void (*SoundE)( void * v, int samples, float * samps, int channel_ct ), void * v, int is_out )
{
int i;
for( i = 0; i < MAX_SOUND_EVENTS; i++ )
{
if( !SoundEvents[is_out][i].SoundE )
{
SoundEvents[is_out][i].SoundE = SoundE;
SoundEvents[is_out][i].v = v;
break;
}
}
}
void UnhookSoundInEvent( void (*SoundE)( void * v, int samples, float * samps, int channel_ct ), void * v, int is_out )
{
int i;
for( i = 0; i < MAX_SOUND_EVENTS; i++ )
{
if( SoundEvents[is_out][i].SoundE == SoundE && SoundEvents[is_out][i].v == v )
{
SoundEvents[is_out][i].SoundE = 0;
SoundEvents[is_out][i].v = 0;
}
}
}

18
colorchord2/hook.h Normal file
View file

@ -0,0 +1,18 @@
#ifndef _KEYHOOK_H
#define _KEYHOOK_H
#define MAX_KEY_EVENTS 16
#define MAX_SOUND_EVENTS 16
void KeyHappened( int key, int down );
void HookKeyEvent( void (*KeyEvent)( void * v, int key, int down ), void * v );
void UnhookKeyEvent( void (*KeyEvent)( void * v, int key, int down ), void * v );
void SoundEventHappened( int samples, float * samps, int channel_ct, int is_out );
void HookSoundInEvent( void (*SoundE)( void * v, int samples, float * samps, int channel_ct ), void * v, int is_out );
void UnhookSoundInEvent( void (*SoundE)( void * v, int samples, float * samps, int channel_ct ), void * v, int is_out );
#endif

View file

@ -0,0 +1,5 @@
do_progressive_dft = 3
samplerate = 8000
buffer = 64
sourcename = alsa_output.pci-0000_00_1b.0.analog-stereo.monitor

View file

@ -0,0 +1,18 @@
#What display output driver should be used?
outdrivers = DisplayPie, OutputLinear
leds = 100
light_siding = 2.2
satamp = 6.000
is_loop=1
led_floor = .1
note_attach_amp_iir2 = .0500
note_attach_amp_iir2 = .1500
note_attach_freq_iir = 0.3000
steady_bright = 0
#dft_iir = 0.0
#dft_q = 20.0000
#dft_speedup = 1000.0000
pie_min=.15
pie_max=.25

504
colorchord2/main.c Normal file
View file

@ -0,0 +1,504 @@
#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"
#include "hook.h"
#define NRDEFFILES 10
struct SoundDriver * sd;
#ifdef WIN32
#include <windows.h>
void WindowsTerm()
{
CloseSound( sd );
}
#endif
int lastfps;
short screenx, screeny;
int gargc;
char ** gargv;
struct DriverInstances * outdriver[MAX_OUT_DRIVERS];
int headless = 0; REGISTER_PARAM( headless, PAINT );
int set_screenx = 640; REGISTER_PARAM( set_screenx, PAINT );
int set_screeny = 480; REGISTER_PARAM( set_screeny, PAINT );
char sound_source[16]; REGISTER_PARAM( sound_source, PABUFFER );
int cpu_autolimit = 1; REGISTER_PARAM( cpu_autolimit, PAINT );
int sample_channel = -1;REGISTER_PARAM( sample_channel, PAINT );
struct NoteFinder * nf;
//Sound circular buffer
#define SOUNDCBSIZE 8096
#define MAX_CHANNELS 2
double VisTimeEnd, VisTimeStart;
int soundhead = 0;
float sound[SOUNDCBSIZE];
int show_debug = 0;
int show_debug_basic = 1;
int gKey = 0;
extern int force_white;
void HandleKey( int keycode, int bDown )
{
char c = toupper( keycode );
if( c == 'D' && bDown ) show_debug = !show_debug;
if( c == 'W' ) force_white = bDown;
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( c == 'K' && bDown ) DumpParameters();
if( keycode == 65307 ) exit( 0 );
printf( "Key: %d -> %d\n", keycode, bDown );
KeyHappened( 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 )
{
}
void SoundCB( float * out, float * in, int samplesr, int * samplesp, struct SoundDriver * sd )
{
int channelin = sd->channelsRec;
// int channelout = sd->channelsPlay;
//*samplesp = 0;
// int process_channels = (MAX_CHANNELS < channelin)?MAX_CHANNELS:channelin;
//Load the samples into a ring buffer. Split the channels from interleved to one per buffer.
int i;
int j;
for( i = 0; i < samplesr; i++ )
{
for( j = 0; j < channelin; j++ )
{
out[i*channelin+j] = 0;
}
if( sample_channel < 0 )
{
float fo = 0;
for( j = 0; j < channelin; j++ )
{
float f = in[i*channelin+j];
if( f >= -1 && f <= 1 )
{
fo += f;
}
else
{
fo += (f>0)?1:-1;
// printf( "Sound fault A %d/%d %d/%d %f\n", j, channelin, i, samplesr, f );
}
}
fo /= channelin;
sound[soundhead] = fo;
soundhead = (soundhead+1)%SOUNDCBSIZE;
}
else
{
float f = in[i*channelin+sample_channel];
if( f > 1 || f < -1 )
{
f = (f>0)?1:-1;
}
//printf( "Sound fault B %d/%d\n", i, samplesr );
sound[soundhead] = f;
soundhead = (soundhead+1)%SOUNDCBSIZE;
}
}
SoundEventHappened( samplesr, in, 0, channelin );
SoundEventHappened( samplesr, out, 1, sd->channelsPlay );
*samplesp = samplesr;
}
void LoadFile( const char * filename )
{
char * buffer;
int r;
FILE * f = fopen( filename, "rb" );
if( !f )
{
fprintf( stderr, "Warning: cannot open %s.\n", filename );
}
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, filename );
}
else
{
SetParametersFromString( buffer );
}
free( buffer );
}
}
const char * InitialFile[NRDEFFILES];
double FileTimes[NRDEFFILES];
int InitialFileCount = 1;
void SetEnvValues()
{
int i;
int hits = 0;
for( i = 0; i < InitialFileCount; i++ )
{
double ft = OGGetFileTime( InitialFile[i] );
if( FileTimes[i] != ft )
{
FileTimes[i] = ft;
hits++;
}
}
if( !hits ) return;
//Otherwise, something changed.
LoadFile( InitialFile[0] );
for( i = 1; i < gargc; i++ )
{
if( strchr( gargv[i], '=' ) != 0 )
{
printf( "AP: %s\n", gargv[i] );
SetParametersFromString( gargv[i] );
}
else
{
printf( "LF: %s\n", gargv[i] );
LoadFile( gargv[i] );
}
}
}
void ProcessArgs()
{
int i;
for( i = 1; i < gargc; i++ )
{
if( strchr( gargv[i], '=' ) != 0 )
{
//A value setting operation
}
else
{
InitialFile[InitialFileCount++] = gargv[i];
}
}
SetEnvValues();
}
int main(int argc, char ** argv)
{
int i;
#ifdef WIN32
WSADATA wsaData;
WSAStartup(0x202, &wsaData);
strcpy( sound_source, "WIN" );
#else
strcpy( sound_source, "PULSE" );
#endif
gargc = argc;
gargv = argv;
InitialFile[0] = "default.conf";
ProcessArgs();
//Initialize Rawdraw
int frames = 0;
double ThisTime;
double LastFPSTime = OGGetAbsoluteTime();
double LastFrameTime = OGGetAbsoluteTime();
double SecToWait;
CNFGBGColor = 0x800000;
CNFGDialogColor = 0x444444;
char title[1024];
char * tp = title;
memcpy( tp, "ColorChord ", strlen( "ColorChord " ) );
tp += strlen( "ColorChord " );
for( i = 1; i < argc; i++ )
{
memcpy( tp, argv[i], strlen( argv[i] ) );
tp += strlen( argv[i] );
*tp = ' ';
tp++;
}
*tp = 0;
if( !headless )
CNFGSetup( title, set_screenx, set_screeny );
char * OutDriverNames = strdup( GetParameterS( "outdrivers", "null" ) );
char * ThisDriver = OutDriverNames;
char * TDStart;
for( i = 0; i < MAX_OUT_DRIVERS; i++ )
{
while( *ThisDriver == ' ' || *ThisDriver == '\t' ) ThisDriver++;
if( !*ThisDriver ) break;
TDStart = ThisDriver;
while( *ThisDriver != 0 && *ThisDriver != ',' )
{
if( *ThisDriver == '\t' || *ThisDriver == ' ' ) *ThisDriver = 0;
ThisDriver++;
}
if( *ThisDriver )
{
*ThisDriver = 0;
ThisDriver++;
}
printf( "Loading: %s\n", TDStart );
outdriver[i] = SetupOutDriver( TDStart );
}
free(OutDriverNames);
//Initialize Sound
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
if( !headless )
{
CNFGHandleInput();
CNFGClearFrame();
CNFGColor( 0xFFFFFF );
CNFGGetDimensions( &screenx, &screeny );
}
RunNoteFinder( nf, sound, (soundhead-1+SOUNDCBSIZE)%SOUNDCBSIZE, SOUNDCBSIZE );
//Done all ColorChord work.
VisTimeStart = OGGetAbsoluteTime();
for( i = 0; i < MAX_OUT_DRIVERS; i++ )
{
if( force_white )
{
memset( OutLEDs, 0x7f, MAX_LEDS*3 );
}
if( outdriver[i] )
outdriver[i]->Func( outdriver[i]->id, nf );
}
VisTimeEnd = OGGetAbsoluteTime();
if( !headless )
{
//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[thissoundhead] * 128 + 128; thissoundhead = (thissoundhead-1+SOUNDCBSIZE)%SOUNDCBSIZE;
int thisy = sound[thissoundhead] * 128 + 128; thissoundhead = (thissoundhead-1+SOUNDCBSIZE)%SOUNDCBSIZE;
for( i = 0; i < screenx; i++ )
{
if( thisy < 0 || thisy > 256 ) printf( "%d/%d\n", thisy,thissoundhead );
CNFGTackSegment( i, lasty, i+1, thisy );
lasty = thisy;
thisy = sound[thissoundhead] * 128 + 128; thissoundhead = (thissoundhead-1+SOUNDCBSIZE)%SOUNDCBSIZE;
}
}
//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 );
CNFGColor( 0xffffff );
CNFGPenX = 440; CNFGPenY = screeny-10;
sprintf( stt, "FPS: %d", lastfps );
CNFGDrawText( stt, 2 );
CNFGSwapBuffers();
}
//Finish Rawdraw with FPS counter, and a nice delay loop.
frames++;
ThisTime = OGGetAbsoluteTime();
if( ThisTime > LastFPSTime + 1 )
{
// printf( "FPS: %d\n", frames );
lastfps = frames;
frames = 0;
LastFPSTime+=1;
}
if( cpu_autolimit )
{
SecToWait = .016 - ( ThisTime - LastFrameTime );
LastFrameTime += .016;
if( SecToWait < -.1 ) LastFrameTime = ThisTime - .1;
if( SecToWait > 0 )
OGUSleep( (int)( SecToWait * 1000000 ) );
}
SetEnvValues();
}
}

26
colorchord2/netlight.conf Normal file
View file

@ -0,0 +1,26 @@
outdrivers = DisplayPie,DisplayNetwork, OutputLinear
leds = 296
light_siding = 1.0 #Turn this to ~1.9 for more uniformity, ~1.0 for less.
satamp = 1.600
is_loop=0
led_floor = .1 #Turn to .25 for more uniformity, .1 for less.
#note_attach_amp_iir = .3 #.3000
#note_attach_amp_iir2 = .15 #.1500
#note_attach_freq_iir = .3 #0.3000
steady_bright = 0
#dft_iir = 0.0
#dft_q = 20.0000
#dft_speedup = 1000.0000
skipfirst = 1
firstval = 0
port = 7777
address = 192.168.0.245
slope=.10
amplify=.3
lightx = 20
lighty = 20

380
colorchord2/notefinder.c Normal file
View file

@ -0,0 +1,380 @@
#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"
#include "DFT32.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->slope = 0.0;
ret->do_progressive_dft = 0;
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", PAINT, &ret->octaves, sizeof( ret->octaves ) );
RegisterValue( "freqbins", PAINT, &ret->freqbins, sizeof( ret->freqbins ) );
RegisterValue( "base_hz", PAFLOAT, &ret->base_hz, sizeof( ret->base_hz ) );
RegisterValue( "filter_strength", PAFLOAT, &ret->filter_strength, sizeof( ret->filter_strength ) );
RegisterValue( "filter_iter", PAINT, &ret->filter_iter, sizeof( ret->filter_iter ) );
RegisterValue( "decompose_iterations", PAINT, &ret->decompose_iterations, sizeof( ret->decompose_iterations ) );
RegisterValue( "amplify", PAFLOAT, &ret->amplify, sizeof( ret->amplify ) );
RegisterValue( "dft_speedup", PAFLOAT, &ret->dft_speedup, sizeof( ret->dft_speedup ) );
RegisterValue( "dft_q", PAFLOAT, &ret->dft_q, sizeof( ret->dft_q ) );
RegisterValue( "default_sigma", PAFLOAT, &ret->default_sigma, sizeof( ret->default_sigma ) );
RegisterValue( "note_jumpability", PAFLOAT, &ret->note_jumpability, sizeof( ret->note_jumpability ) );
RegisterValue( "note_combine_distance", PAFLOAT, &ret->note_combine_distance, sizeof( ret->note_combine_distance ) );
RegisterValue( "slope", PAFLOAT, &ret->slope, sizeof( ret->slope ) );
RegisterValue( "note_attach_freq_iir", PAFLOAT, &ret->note_attach_freq_iir, sizeof( ret->note_attach_freq_iir ) );
RegisterValue( "note_attach_amp_iir", PAFLOAT, &ret->note_attach_amp_iir, sizeof( ret->note_attach_amp_iir ) );
RegisterValue( "note_attach_amp_iir2", PAFLOAT, &ret->note_attach_amp_iir2, sizeof( ret->note_attach_amp_iir2 ) );
RegisterValue( "note_minimum_new_distribution_value", PAFLOAT, &ret->note_minimum_new_distribution_value, sizeof( ret->note_minimum_new_distribution_value ) );
RegisterValue( "note_out_chop", PAFLOAT, &ret->note_out_chop, sizeof( ret->note_out_chop ) );
RegisterValue( "dft_iir", PAFLOAT, &ret->dft_iir, sizeof( ret->dft_iir ) );
RegisterValue( "do_progressive_dft", PAINT, &ret->do_progressive_dft, sizeof( ret->do_progressive_dft ) );
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();
switch( nf->do_progressive_dft )
{
case 0:
DoDFTQuick( dftbins, nf->frequencies, freqs, audio_stream, head, buffersize, nf->dft_q, nf->dft_speedup );
break;
case 1:
DoDFTProgressive( dftbins, nf->frequencies, freqs, audio_stream, head, buffersize, nf->dft_q, nf->dft_speedup );
break;
case 2:
DoDFTProgressiveInteger( dftbins, nf->frequencies, freqs, audio_stream, head, buffersize, nf->dft_q, nf->dft_speedup );
break;
case 3:
DoDFTProgressiveIntegerSkippy( dftbins, nf->frequencies, freqs, audio_stream, head, buffersize, nf->dft_q, nf->dft_speedup );
break;
case 4:
DoDFTProgressive32( dftbins, nf->frequencies, freqs, audio_stream, head, buffersize, nf->dft_q, nf->dft_speedup );
break;
default:
fprintf( stderr, "Error: No DFT Seleced\n" );
}
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 * ( 1. + nf->slope * i )));
}
//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;
}
//This is here to reduce the number of false-positive hits. It helps remove peaks that are meaningless.
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 && nf->dist_amps[j] > 0.00001 ) //0.00001 for stability.
{
//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++ )
{
// printf( "%f %f %d\n", nf->note_amplitudes[i], nf->note_positions[i], nf->enduring_note_id[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;
}
float newp = avgloop( nf->note_positions[a], nf->note_amplitudes[a], nf->note_positions[b], nf->note_amplitudes[b], freqbins );
//Combine B into A.
nf->note_amplitudes[a] += nf->note_amplitudes[b];
nf->note_positions[a] = newp;
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_amplitudes2[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();
}

95
colorchord2/notefinder.h Normal file
View file

@ -0,0 +1,95 @@
#ifndef _NOTEFINDER_H
#define _NOTEFINDER_H
#include "os_generic.h"
struct NoteFinder
{
//Setup DFT Bins
int ofreqs;
float slope;// = 0
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)
int do_progressive_dft; //= 1
//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
colorchord2/os_generic.c Normal file
View 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
colorchord2/os_generic.h Normal file
View 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.
*/

73
colorchord2/outdrivers.c Normal file
View file

@ -0,0 +1,73 @@
#include "outdrivers.h"
#include <string.h>
#include "parameters.h"
#include "os_generic.h"
#include <stdio.h>
#include <stdlib.h>
int force_white = 0;
unsigned char OutLEDs[MAX_LEDS*3];
int UsedLEDs;
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( const char * drivername )
{
int i;
for( i = 0; i < MAX_OUT_DRIVERS; i++ )
{
if( ODList[i].Name && strcmp( drivername, 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 );
}
}

44
colorchord2/outdrivers.h Normal file
View file

@ -0,0 +1,44 @@
//Output drivers, used for outputting lights, etc... However, this technique
//may be used for unrelated-to-output plugins.
#ifndef _OUTDRIVERS_H
#define _OUTDRIVERS_H
#include "os_generic.h"
struct NoteFinder;
#define MAX_LEDS 32678
extern int force_white;
extern unsigned char OutLEDs[MAX_LEDS*3];
extern int UsedLEDs;
#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

349
colorchord2/parameters.c Normal file
View file

@ -0,0 +1,349 @@
#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 PAFLOAT: return *((float*)p->lp->ptr);
case PAINT: return *((int*)p->lp->ptr);
case PASTRING:
case PABUFFER: if( p->lp->ptr ) return atof( p->lp->ptr );
default: break;
}
}
printf( "U: %s = %f\n", name, defa );
return defa;
}
int GetParameterI( const char * name, int defa )
{
struct Param * p = (struct Param*)HashGetEntry( parameters, name );
if( p )
{
switch( p->t )
{
case PAFLOAT: return *((float*)p->lp->ptr);
case PAINT: return *((int*)p->lp->ptr);
case PASTRING:
case PABUFFER: if( p->lp->ptr ) return atoi( p->lp->ptr );
default: break;
}
}
printf( "U: %s = %d\n", name, defa );
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 PAFLOAT: snprintf( returnbuffer, sizeof( returnbuffer ), "%0.4f", *((float*)p->lp->ptr) ); return returnbuffer;
case PAINT: snprintf( returnbuffer, sizeof( returnbuffer ), "%d", *((int*)p->lp->ptr) ); return returnbuffer;
case PASTRING:
case PABUFFER: return p->lp->ptr;
default: break;
}
}
printf( "U: %s = %s\n", name, defa );
return defa;
}
static int SetParameter( struct Param * p, const char * str )
{
struct LinkedParameter * lp;
lp = p->lp;
switch( p->t )
{
case PAFLOAT:
while( lp )
{
*((float*)lp->ptr) = atof( str );
lp = lp->lp;
}
break;
case PAINT:
while( lp )
{
*((int*)lp->ptr) = atoi( str );
lp = lp->lp;
}
break;
case PABUFFER:
while( lp )
{
strncpy( (char*)lp->ptr, str, p->size );
if( p->size > 0 )
((char*)lp->ptr)[p->size-1]= '\0';
lp = lp->lp;
}
break;
case PASTRING:
while( lp )
{
free( lp->ptr );
lp->ptr = strdup( str );
lp = lp->lp;
}
break;
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 != PASTRING )
{
fprintf( stderr, "Warning: Orphan parameter %s was not a PSTRING.\n", name );
}
char * orig = p->lp->ptr;
p->lp->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
{
struct LinkedParameter * lp = p->lp;
if( size != p->size )
{
fprintf( stderr, "Size mismatch: Parameter %s.\n", name );
}
else
{
p->lp = malloc( sizeof( struct LinkedParameter ) );
p->lp->lp = lp;
p->lp->ptr = ptr;
memcpy( p->lp->ptr, p->lp->lp->ptr, size );
}
}
}
else
{
struct Param ** n = (struct Param**)HashTableInsert( parameters, name, 1 );
*n = malloc( sizeof( struct Param ) );
(*n)->t = t;
(*n)->lp = malloc( sizeof( struct LinkedParameter ) );
(*n)->lp->lp = 0;
(*n)->lp->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;
struct Param * p = (struct Param*)HashGetEntry( parameters, name );
if( p )
{
printf( "Set: %s %s\n", name, value );
SetParameter( p, value );
}
else
{
//p is an orphan.
// printf( "Orp: %s %s\n", name, value );
struct Param ** n = (struct Param **)HashTableInsert( parameters, name, 0 );
*n = malloc( sizeof ( struct Param ) );
(*n)->orphan = 1;
(*n)->t = PASTRING;
(*n)->lp = malloc( sizeof( struct LinkedParameter ) );
(*n)->lp->lp = 0;
(*n)->lp->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 );
}
}
void DumpParameters()
{
int i;
struct chashlist * l = HashProduceSortedTable( parameters );
for( i = 0; i < l->length; i++ )
{
struct chashentry * e = &l->items[i];
printf( "%s = %s\n", e->key, GetParameterS( e->key, "" ) );
}
printf( "\n" );
free( l );
}

63
colorchord2/parameters.h Normal file
View file

@ -0,0 +1,63 @@
#ifndef _PARAMETERS_H
#define _PARAMETERS_H
#define PARAM_BUFF 128
enum ParamType
{
NONE,
PAFLOAT,
PAINT,
PASTRING, //const char *, cannot set.
PABUFFER,
NUM_PARAMS,
};
typedef void (*ParamCallbackT)( void * v );
struct ParamCallback
{
ParamCallbackT t;
void * v;
struct ParamCallback * next;
};
struct LinkedParameter
{
void * ptr;
struct LinkedParameter * lp;
};
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;
int size;
struct LinkedParameter * lp;
struct ParamCallback * callback;
};
//This is the preferred method for getting settings, that way changes will be propogated
void RegisterValue( const char * name, enum ParamType, void * ptr, int size );
void DumpParameters();
//Use these only if you really can't register your value.
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)); \
void Register##parameter_name() { RegisterValue( #parameter_name, type, &parameter_name, sizeof( parameter_name ) ); }
#endif

View file

@ -0,0 +1,26 @@
This is a vornoi thing:
outdrivers = DisplayArray, OutputProminent
lightx = 2
lighty = 2
leds = 4
fromsides = 1
shape_cutoff = 0.03
satamp = 5.000
amppow = 2.510
distpow = 1.500
samplerate = 11025
buffer = 64
sourcename = default
amplify = 2.5
note_attach_amp_iir = 0.9000
note_attach_amp_iir2 = 0.550
note_attach_freq_iir = 0.9000
dft_iir = .6
dft_q = 20.0000
dft_speedup = 1000.0000
note_jumpability = 1.0000

View file

@ -0,0 +1,7 @@
outdrivers = DisplayArray, OutputVoronoi, RecorderPlugin
play = 1
buffer = 512
player_filename = inchristalone.raw
recorder_filename = recfile.raw
recorder_bypass = 44100

59
colorchord2/sort.c Normal file
View 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
colorchord2/sort.h Normal file
View 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
colorchord2/sound.c Normal file
View 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
colorchord2/sound.h Normal file
View 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
colorchord2/sound_alsa.c Normal file
View 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
colorchord2/sound_null.c Normal file
View 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 ) );

377
colorchord2/sound_pulse.c Normal file
View file

@ -0,0 +1,377 @@
//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 );
if( strcmp( r->sourceName, "default" ) == 0 )
{
r->sourceName = 0;
}
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 ) );

179
colorchord2/sound_win.c Normal file
View file

@ -0,0 +1,179 @@
#include <windows.h>
#include "parameters.h"
#include "sound.h"
#include "os_generic.h"
#include <mmsystem.h>
#include <stdio.h>
#include <stdint.h>
#if defined(WIN32)
#pragma comment(lib,"winmm.lib")
#endif
#define BUFFS 2
struct SoundDriverWin
{
void (*CloseFn)( struct SoundDriverWin * object );
int (*SoundStateFn)( struct SoundDriverWin * object );
SoundCBType callback;
int channelsPlay;
int spsPlay;
int channelsRec;
int spsRec;
int buffer;
int isEnding;
int GOBUFF;
int recording;
HWAVEIN hMyWave;
WAVEHDR WavBuff[BUFFS];
};
static struct SoundDriverWin * w;
void CloseSoundWin( struct SoundDriverWin * r )
{
int i;
if( r )
{
waveInStop(r->hMyWave);
waveInReset(r->hMyWave);
for ( i=0;i<BUFFS;i++)
{
waveInUnprepareHeader(r->hMyWave,&(r->WavBuff[i]),sizeof(WAVEHDR));
free ((r->WavBuff[i]).lpData);
}
waveInClose(r->hMyWave);
free( r );
}
}
int SoundStateWin( struct SoundDriverWin * soundobject )
{
return soundobject->recording;
}
void CALLBACK HANDLEMIC(HWAVEIN hwi,UINT umsg, DWORD dwi, DWORD hdr, DWORD dwparm)
{
int ctr;
int ob;
long cValue;
unsigned int maxWave=0;
float buffer[w->buffer*w->channelsRec];
if (w->isEnding) return;
switch (umsg)
{
case MM_WIM_OPEN:
printf( "Mic Open.\n" );
w->recording = 1;
break;
case MM_WIM_DATA:
// printf( "Mic Data.\n");
ob = (w->GOBUFF+(BUFFS))%BUFFS;
// waveInPrepareHeader(w->hMyWave,&(w->WavBuff[w->Cbuff]),sizeof(WAVEHDR));
for (ctr=0;ctr<w->buffer * w->channelsRec;ctr++) {
float cv = (uint16_t)(((uint8_t)w->WavBuff[ob].lpData[ctr*2+1])*256+((uint8_t)w->WavBuff[ob].lpData[ctr*2])+32768)-32768;
cv /= 32768;
// if( ctr < 3 ) cv = -1;
// buffer[(w->buffer * w->channelsRec)-ctr-1] = cv;
buffer[ctr] = cv;
}
waveInAddBuffer(w->hMyWave,&(w->WavBuff[w->GOBUFF]),sizeof(WAVEHDR));
w->GOBUFF = ( w->GOBUFF + 1 ) % BUFFS;
int playbacksamples; //Unused
w->callback( 0, buffer, w->buffer, &playbacksamples, (struct SoundDriver*)w );
}
}
static struct SoundDriverWin * InitWinSound( struct SoundDriverWin * r )
{
int i;
WAVEFORMATEX wfmt;
if( GetParameterI( "play", 0 ) )
{
fprintf( stderr, "Error: This Windows Sound Driver does not support playback.\n" );
exit( -1 );
}
w = r;
printf( "WFMT: %d %d %d\n", r->channelsRec, r->spsRec,
r->spsRec * r->channelsRec );
wfmt.wFormatTag = WAVE_FORMAT_PCM;
wfmt.nChannels = r->channelsRec;
wfmt.nSamplesPerSec = r->spsRec;
wfmt.nAvgBytesPerSec = r->spsRec * r->channelsRec;
wfmt.nBlockAlign = r->channelsRec * 2;
wfmt.wBitsPerSample = 16;
wfmt.cbSize = 0;
long dwdevice;
dwdevice = GetParameterI( "wininput", WAVE_MAPPER );
printf( "Wave Devs: %d; WAVE_MAPPER: %d; Selected Input: %d\n", waveInGetNumDevs(), WAVE_MAPPER, dwdevice );
printf( "waveInOpen: %p, %p\n", &r->hMyWave, &wfmt );
int p = waveInOpen(&r->hMyWave, dwdevice, &wfmt,(DWORD)(void*)(&HANDLEMIC) , 0, CALLBACK_FUNCTION);
printf( "WIO: %d\n", p ); //On real windows, returns 11
for ( i=0;i<BUFFS;i++)
{
memset( &(r->WavBuff[i]), 0, sizeof(r->WavBuff[i]) );
(r->WavBuff[i]).dwBufferLength = r->buffer*2*r->channelsRec;
(r->WavBuff[i]).dwLoops = 1;
(r->WavBuff[i]).lpData=(char*) malloc(r->buffer*r->channelsRec*2);
waveInPrepareHeader(r->hMyWave,&(r->WavBuff[i]),sizeof(WAVEHDR));
waveInAddBuffer(r->hMyWave,&(r->WavBuff[i]),sizeof(WAVEHDR));
}
p = waveInStart(r->hMyWave);
printf( "WIS: %d\n", p ); //On real windows returns 5.
return r;
}
void * InitSoundWin( SoundCBType cb )
{
struct SoundDriverWin * r = malloc( sizeof( struct SoundDriverWin ) );
r->CloseFn = CloseSoundWin;
r->SoundStateFn = SoundStateWin;
r->callback = cb;
r->spsRec = GetParameterI( "samplerate", 44100 );
r->channelsRec = GetParameterI( "channels", 2 );
r->buffer = GetParameterI( "buffer", 384 );
r->recording = 0;
r->isEnding = 0;
printf( "Buffer: %d\n", r->buffer );
r->GOBUFF=0;
return InitWinSound(r);
}
EXECUTE_AT_BOOT( WinSoundReg, RegSound( 10, "WIN", InitSoundWin ) );

24
colorchord2/usb.conf Normal file
View file

@ -0,0 +1,24 @@
outdrivers = DisplayUSB2812, OutputVoronoi
leds = 512
lightx = 32
lighty = 16
fromsides = 1
shape_cutoff = 0.03
satamp = 2.000
amppow = 2.5
distpow = 1.500
zigzag = 1
rot90 = 1
ledoutamp = .4
note_attach_amp_iir = .3000
note_attach_amp_iir2 = .1500
note_attach_freq_iir = 0.3000
steady_bright = 0
skipfirst = 1
firstval = 0
port = 7777
address = 192.168.0.245

58
colorchord2/util.c Normal file
View 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;
}

10
colorchord2/util.h Normal file
View file

@ -0,0 +1,10 @@
#ifndef _UTIL_H
#define _UTIL_H
float fabsloop( float a, float b, float modl );
float avgloop( float pta, float ampa, float ptb, float ampb, float modl );
int atoi_del( char * data );
float atof_del( char * data );
#endif