Move colorchord2 into its own folder, since colorchord embedded is its own thing.
This commit is contained in:
parent
24b606988f
commit
ed15ea49b9
56 changed files with 1 additions and 12 deletions
91
colorchord2/DisplayArray.c
Normal file
91
colorchord2/DisplayArray.c
Normal 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
125
colorchord2/DisplayDMX.c
Normal 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);
|
||||
|
||||
|
128
colorchord2/DisplayNetwork.c
Normal file
128
colorchord2/DisplayNetwork.c
Normal 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);
|
||||
|
||||
|
189
colorchord2/DisplayOUTDriver.c
Normal file
189
colorchord2/DisplayOUTDriver.c
Normal 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
79
colorchord2/DisplayPie.c
Normal 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);
|
||||
|
||||
|
166
colorchord2/DisplayUSB2812.c
Normal file
166
colorchord2/DisplayUSB2812.c
Normal 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
273
colorchord2/DrawFunctions.c
Normal file
|
@ -0,0 +1,273 @@
|
|||
/*
|
||||
Copyright (c) 2010, 2011 Charles Lohr
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included
|
||||
in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "DrawFunctions.h"
|
||||
#include <stdio.h>
|
||||
|
||||
int CNFGPenX, CNFGPenY;
|
||||
uint32_t CNFGBGColor;
|
||||
uint32_t CNFGLastColor;
|
||||
uint32_t CNFGDialogColor; //background for boxes
|
||||
|
||||
const unsigned short FontCharMap[256] = {
|
||||
65535, 0, 10, 20, 32, 44, 56, 68, 70, 65535, 65535, 80, 92, 65535, 104, 114,
|
||||
126, 132, 138, 148, 156, 166, 180, 188, 200, 206, 212, 218, 224, 228, 238, 244,
|
||||
65535, 250, 254, 258, 266, 278, 288, 302, 304, 310, 316, 324, 328, 226, 252, 330,
|
||||
332, 342, 348, 358, 366, 372, 382, 392, 400, 410, 420, 424, 428, 262, 432, 436,
|
||||
446, 460, 470, 486, 496, 508, 516, 522, 536, 542, 548, 556, 568, 572, 580, 586,
|
||||
598, 608, 622, 634, 644, 648, 654, 662, 670, 682, 692, 700, 706, 708, 492, 198,
|
||||
714, 716, 726, 734, 742, 750, 760, 768, 782, 790, 794, 802, 204, 810, 820, 384,
|
||||
828, 836, 844, 850, 860, 864, 872, 880, 890, 894, 902, 908, 916, 920, 928, 934,
|
||||
65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535,
|
||||
65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535,
|
||||
944, 948, 960, 970, 986, 996, 1000, 1016, 1020, 1026, 1034, 1042, 364, 1046, 1056, 1058,
|
||||
1066, 1072, 1080, 932, 1092, 1100, 68, 1110, 1114, 1116, 1124, 1132, 1142, 1154, 1170, 1180,
|
||||
1192, 1204, 1218, 1234, 1248, 1264, 1276, 1290, 1300, 1310, 1322, 1334, 1342, 1350, 1360, 1370,
|
||||
1384, 1396, 1406, 1416, 1428, 1442, 1454, 1458, 1472, 1480, 1488, 1498, 1508, 1520, 1530, 1544,
|
||||
1556, 1568, 1582, 1598, 1612, 1628, 1642, 1654, 1666, 1678, 1692, 1706, 1710, 1714, 1720, 1726,
|
||||
1738, 1752, 1762, 1772, 1784, 1798, 1810, 1816, 1826, 1836, 1846, 1858, 1870, 1880, 1890, 65535, };
|
||||
|
||||
const unsigned char FontCharData[1902] = {
|
||||
0x00, 0x01, 0x20, 0x21, 0x03, 0x23, 0x23, 0x14, 0x14, 0x83, 0x00, 0x01, 0x20, 0x21, 0x04, 0x24,
|
||||
0x24, 0x13, 0x13, 0x84, 0x01, 0x21, 0x21, 0x23, 0x23, 0x14, 0x14, 0x03, 0x03, 0x01, 0x11, 0x92,
|
||||
0x11, 0x22, 0x22, 0x23, 0x23, 0x14, 0x14, 0x03, 0x03, 0x02, 0x02, 0x91, 0x01, 0x21, 0x21, 0x23,
|
||||
0x23, 0x01, 0x03, 0x21, 0x03, 0x01, 0x12, 0x94, 0x03, 0x23, 0x13, 0x14, 0x23, 0x22, 0x22, 0x11,
|
||||
0x11, 0x02, 0x02, 0x83, 0x12, 0x92, 0x12, 0x12, 0x01, 0x21, 0x21, 0x23, 0x23, 0x03, 0x03, 0x81,
|
||||
0x03, 0x21, 0x21, 0x22, 0x21, 0x11, 0x03, 0x14, 0x14, 0x23, 0x23, 0x92, 0x01, 0x10, 0x10, 0x21,
|
||||
0x21, 0x12, 0x12, 0x01, 0x12, 0x14, 0x03, 0xa3, 0x02, 0x03, 0x03, 0x13, 0x02, 0x12, 0x13, 0x10,
|
||||
0x10, 0xa1, 0x01, 0x23, 0x03, 0x21, 0x02, 0x11, 0x11, 0x22, 0x22, 0x13, 0x13, 0x82, 0x00, 0x22,
|
||||
0x22, 0x04, 0x04, 0x80, 0x20, 0x02, 0x02, 0x24, 0x24, 0xa0, 0x01, 0x10, 0x10, 0x21, 0x10, 0x14,
|
||||
0x14, 0x03, 0x14, 0xa3, 0x00, 0x03, 0x04, 0x04, 0x20, 0x23, 0x24, 0xa4, 0x00, 0x20, 0x00, 0x02,
|
||||
0x02, 0x22, 0x10, 0x14, 0x20, 0xa4, 0x01, 0x21, 0x21, 0x23, 0x23, 0x03, 0x03, 0x01, 0x20, 0x10,
|
||||
0x10, 0x14, 0x14, 0x84, 0x03, 0x23, 0x23, 0x24, 0x24, 0x04, 0x04, 0x83, 0x01, 0x10, 0x10, 0x21,
|
||||
0x10, 0x14, 0x14, 0x03, 0x14, 0x23, 0x04, 0xa4, 0x01, 0x10, 0x21, 0x10, 0x10, 0x94, 0x03, 0x14,
|
||||
0x23, 0x14, 0x10, 0x94, 0x02, 0x22, 0x22, 0x11, 0x22, 0x93, 0x02, 0x22, 0x02, 0x11, 0x02, 0x93,
|
||||
0x01, 0x02, 0x02, 0xa2, 0x02, 0x22, 0x22, 0x11, 0x11, 0x02, 0x02, 0x13, 0x13, 0xa2, 0x11, 0x22,
|
||||
0x22, 0x02, 0x02, 0x91, 0x02, 0x13, 0x13, 0x22, 0x22, 0x82, 0x10, 0x13, 0x14, 0x94, 0x10, 0x01,
|
||||
0x20, 0x91, 0x10, 0x14, 0x20, 0x24, 0x01, 0x21, 0x03, 0xa3, 0x21, 0x10, 0x10, 0x01, 0x01, 0x23,
|
||||
0x23, 0x14, 0x14, 0x03, 0x10, 0x94, 0x00, 0x01, 0x23, 0x24, 0x04, 0x03, 0x03, 0x21, 0x21, 0xa0,
|
||||
0x21, 0x10, 0x10, 0x01, 0x01, 0x12, 0x12, 0x03, 0x03, 0x14, 0x14, 0x23, 0x02, 0xa4, 0x10, 0x91,
|
||||
0x10, 0x01, 0x01, 0x03, 0x03, 0x94, 0x10, 0x21, 0x21, 0x23, 0x23, 0x94, 0x01, 0x23, 0x11, 0x13,
|
||||
0x21, 0x03, 0x02, 0xa2, 0x02, 0x22, 0x11, 0x93, 0x31, 0xc0, 0x03, 0xa1, 0x00, 0x20, 0x20, 0x24,
|
||||
0x24, 0x04, 0x04, 0x00, 0x12, 0x92, 0x01, 0x10, 0x10, 0x14, 0x04, 0xa4, 0x01, 0x10, 0x10, 0x21,
|
||||
0x21, 0x22, 0x22, 0x04, 0x04, 0xa4, 0x00, 0x20, 0x20, 0x24, 0x24, 0x04, 0x12, 0xa2, 0x00, 0x02,
|
||||
0x02, 0x22, 0x20, 0xa4, 0x20, 0x00, 0x00, 0x02, 0x02, 0x22, 0x22, 0x24, 0x24, 0x84, 0x20, 0x02,
|
||||
0x02, 0x22, 0x22, 0x24, 0x24, 0x04, 0x04, 0x82, 0x00, 0x20, 0x20, 0x21, 0x21, 0x12, 0x12, 0x94,
|
||||
0x00, 0x04, 0x00, 0x20, 0x20, 0x24, 0x04, 0x24, 0x02, 0xa2, 0x00, 0x02, 0x02, 0x22, 0x22, 0x20,
|
||||
0x20, 0x00, 0x22, 0x84, 0x11, 0x11, 0x13, 0x93, 0x11, 0x11, 0x13, 0x84, 0x20, 0x02, 0x02, 0xa4,
|
||||
0x00, 0x22, 0x22, 0x84, 0x01, 0x10, 0x10, 0x21, 0x21, 0x12, 0x12, 0x13, 0x14, 0x94, 0x21, 0x01,
|
||||
0x01, 0x04, 0x04, 0x24, 0x24, 0x22, 0x22, 0x12, 0x12, 0x13, 0x13, 0xa3, 0x04, 0x01, 0x01, 0x10,
|
||||
0x10, 0x21, 0x21, 0x24, 0x02, 0xa2, 0x00, 0x04, 0x04, 0x14, 0x14, 0x23, 0x23, 0x12, 0x12, 0x02,
|
||||
0x12, 0x21, 0x21, 0x10, 0x10, 0x80, 0x23, 0x14, 0x14, 0x03, 0x03, 0x01, 0x01, 0x10, 0x10, 0xa1,
|
||||
0x00, 0x10, 0x10, 0x21, 0x21, 0x23, 0x23, 0x14, 0x14, 0x04, 0x04, 0x80, 0x00, 0x04, 0x04, 0x24,
|
||||
0x00, 0x20, 0x02, 0x92, 0x00, 0x04, 0x00, 0x20, 0x02, 0x92, 0x21, 0x10, 0x10, 0x01, 0x01, 0x03,
|
||||
0x03, 0x14, 0x14, 0x23, 0x23, 0x22, 0x22, 0x92, 0x00, 0x04, 0x20, 0x24, 0x02, 0xa2, 0x00, 0x20,
|
||||
0x10, 0x14, 0x04, 0xa4, 0x00, 0x20, 0x20, 0x23, 0x23, 0x14, 0x14, 0x83, 0x00, 0x04, 0x02, 0x12,
|
||||
0x12, 0x21, 0x21, 0x20, 0x12, 0x23, 0x23, 0xa4, 0x00, 0x04, 0x04, 0xa4, 0x04, 0x00, 0x00, 0x11,
|
||||
0x11, 0x20, 0x20, 0xa4, 0x04, 0x00, 0x00, 0x22, 0x20, 0xa4, 0x01, 0x10, 0x10, 0x21, 0x21, 0x23,
|
||||
0x23, 0x14, 0x14, 0x03, 0x03, 0x81, 0x00, 0x04, 0x00, 0x10, 0x10, 0x21, 0x21, 0x12, 0x12, 0x82,
|
||||
0x01, 0x10, 0x10, 0x21, 0x21, 0x23, 0x23, 0x14, 0x14, 0x03, 0x03, 0x01, 0x04, 0x93, 0x00, 0x04,
|
||||
0x00, 0x10, 0x10, 0x21, 0x21, 0x12, 0x12, 0x02, 0x02, 0xa4, 0x21, 0x10, 0x10, 0x01, 0x01, 0x23,
|
||||
0x23, 0x14, 0x14, 0x83, 0x00, 0x20, 0x10, 0x94, 0x00, 0x04, 0x04, 0x24, 0x24, 0xa0, 0x00, 0x03,
|
||||
0x03, 0x14, 0x14, 0x23, 0x23, 0xa0, 0x00, 0x04, 0x04, 0x24, 0x14, 0x13, 0x24, 0xa0, 0x00, 0x01,
|
||||
0x01, 0x23, 0x23, 0x24, 0x04, 0x03, 0x03, 0x21, 0x21, 0xa0, 0x00, 0x01, 0x01, 0x12, 0x12, 0x14,
|
||||
0x12, 0x21, 0x21, 0xa0, 0x00, 0x20, 0x20, 0x02, 0x02, 0x04, 0x04, 0xa4, 0x10, 0x00, 0x00, 0x04,
|
||||
0x04, 0x94, 0x01, 0xa3, 0x10, 0x20, 0x20, 0x24, 0x24, 0x94, 0x00, 0x91, 0x02, 0x04, 0x04, 0x24,
|
||||
0x24, 0x22, 0x23, 0x12, 0x12, 0x82, 0x00, 0x04, 0x04, 0x24, 0x24, 0x22, 0x22, 0x82, 0x24, 0x04,
|
||||
0x04, 0x03, 0x03, 0x12, 0x12, 0xa2, 0x20, 0x24, 0x24, 0x04, 0x04, 0x02, 0x02, 0xa2, 0x24, 0x04,
|
||||
0x04, 0x02, 0x02, 0x22, 0x22, 0x23, 0x23, 0x93, 0x04, 0x01, 0x02, 0x12, 0x01, 0x10, 0x10, 0xa1,
|
||||
0x23, 0x12, 0x12, 0x03, 0x03, 0x14, 0x14, 0x23, 0x23, 0x24, 0x24, 0x15, 0x15, 0x84, 0x00, 0x04,
|
||||
0x03, 0x12, 0x12, 0x23, 0x23, 0xa4, 0x11, 0x11, 0x12, 0x94, 0x22, 0x22, 0x23, 0x24, 0x24, 0x15,
|
||||
0x15, 0x84, 0x00, 0x04, 0x03, 0x13, 0x13, 0x22, 0x13, 0xa4, 0x02, 0x04, 0x02, 0x13, 0x12, 0x14,
|
||||
0x12, 0x23, 0x23, 0xa4, 0x02, 0x04, 0x03, 0x12, 0x12, 0x23, 0x23, 0xa4, 0x02, 0x05, 0x04, 0x24,
|
||||
0x24, 0x22, 0x22, 0x82, 0x02, 0x04, 0x04, 0x24, 0x25, 0x22, 0x22, 0x82, 0x02, 0x04, 0x03, 0x12,
|
||||
0x12, 0xa2, 0x22, 0x02, 0x02, 0x03, 0x03, 0x23, 0x23, 0x24, 0x24, 0x84, 0x11, 0x14, 0x02, 0xa2,
|
||||
0x02, 0x04, 0x04, 0x14, 0x14, 0x23, 0x24, 0xa2, 0x02, 0x03, 0x03, 0x14, 0x14, 0x23, 0x23, 0xa2,
|
||||
0x02, 0x03, 0x03, 0x14, 0x14, 0x12, 0x13, 0x24, 0x24, 0xa2, 0x02, 0x24, 0x04, 0xa2, 0x02, 0x03,
|
||||
0x03, 0x14, 0x22, 0x23, 0x23, 0x85, 0x02, 0x22, 0x22, 0x04, 0x04, 0xa4, 0x20, 0x10, 0x10, 0x14,
|
||||
0x14, 0x24, 0x12, 0x82, 0x10, 0x11, 0x13, 0x94, 0x00, 0x10, 0x10, 0x14, 0x14, 0x04, 0x12, 0xa2,
|
||||
0x01, 0x10, 0x10, 0x11, 0x11, 0xa0, 0x03, 0x04, 0x04, 0x24, 0x24, 0x23, 0x23, 0x12, 0x12, 0x83,
|
||||
0x10, 0x10, 0x11, 0x94, 0x21, 0x10, 0x10, 0x01, 0x01, 0x02, 0x02, 0x13, 0x13, 0x22, 0x10, 0x93,
|
||||
0x11, 0x00, 0x00, 0x04, 0x04, 0x24, 0x24, 0x23, 0x02, 0x92, 0x01, 0x02, 0x11, 0x21, 0x22, 0x23,
|
||||
0x03, 0x13, 0x02, 0x11, 0x11, 0x22, 0x22, 0x13, 0x13, 0x82, 0x00, 0x11, 0x11, 0x20, 0x11, 0x14,
|
||||
0x02, 0x22, 0x03, 0xa3, 0x10, 0x12, 0x13, 0x95, 0x20, 0x00, 0x00, 0x02, 0x02, 0x11, 0x11, 0x22,
|
||||
0x02, 0x13, 0x13, 0x22, 0x22, 0x24, 0x24, 0x84, 0x00, 0x00, 0x20, 0xa0, 0x20, 0x10, 0x10, 0x11,
|
||||
0x11, 0xa1, 0x10, 0x21, 0x20, 0x21, 0x21, 0x11, 0x11, 0x90, 0x11, 0x02, 0x02, 0x13, 0x21, 0x12,
|
||||
0x12, 0xa3, 0x01, 0x21, 0x21, 0xa2, 0x10, 0x20, 0x20, 0x21, 0x21, 0x11, 0x12, 0x10, 0x11, 0xa2,
|
||||
0x00, 0xa0, 0x01, 0x10, 0x10, 0x21, 0x21, 0x12, 0x12, 0x81, 0x02, 0x22, 0x11, 0x13, 0x03, 0xa3,
|
||||
0x01, 0x10, 0x10, 0x21, 0x21, 0x03, 0x03, 0xa3, 0x01, 0x10, 0x10, 0x21, 0x21, 0x12, 0x12, 0x02,
|
||||
0x12, 0x23, 0x23, 0x83, 0x02, 0x05, 0x04, 0x14, 0x14, 0x23, 0x22, 0xa4, 0x14, 0x10, 0x10, 0x01,
|
||||
0x01, 0x12, 0x10, 0x20, 0x20, 0xa4, 0x14, 0x15, 0x15, 0x85, 0x20, 0xa1, 0x10, 0x20, 0x20, 0x21,
|
||||
0x21, 0x11, 0x11, 0x90, 0x01, 0x12, 0x12, 0x03, 0x11, 0x22, 0x22, 0x93, 0x00, 0x01, 0x02, 0x20,
|
||||
0x12, 0x13, 0x13, 0x23, 0x22, 0xa4, 0x00, 0x01, 0x02, 0x20, 0x12, 0x22, 0x22, 0x13, 0x13, 0x14,
|
||||
0x14, 0xa4, 0x00, 0x10, 0x10, 0x11, 0x11, 0x01, 0x11, 0x02, 0x02, 0x20, 0x12, 0x13, 0x13, 0x23,
|
||||
0x22, 0xa4, 0x10, 0x10, 0x11, 0x12, 0x12, 0x03, 0x03, 0x14, 0x14, 0xa3, 0x04, 0x02, 0x02, 0x11,
|
||||
0x11, 0x22, 0x22, 0x24, 0x03, 0x23, 0x10, 0xa1, 0x04, 0x02, 0x02, 0x11, 0x11, 0x22, 0x22, 0x24,
|
||||
0x03, 0x23, 0x01, 0x90, 0x04, 0x02, 0x02, 0x11, 0x11, 0x22, 0x22, 0x24, 0x03, 0x23, 0x01, 0x10,
|
||||
0x10, 0xa1, 0x04, 0x02, 0x02, 0x11, 0x11, 0x22, 0x22, 0x24, 0x03, 0x23, 0x01, 0x10, 0x10, 0x11,
|
||||
0x11, 0xa0, 0x04, 0x02, 0x02, 0x11, 0x11, 0x22, 0x22, 0x24, 0x03, 0x23, 0x00, 0x00, 0x20, 0xa0,
|
||||
0x04, 0x02, 0x02, 0x11, 0x11, 0x22, 0x22, 0x24, 0x03, 0x23, 0x00, 0x20, 0x20, 0x11, 0x11, 0x80,
|
||||
0x00, 0x04, 0x02, 0x22, 0x00, 0x11, 0x10, 0x14, 0x10, 0x20, 0x14, 0xa4, 0x23, 0x14, 0x14, 0x03,
|
||||
0x03, 0x01, 0x01, 0x10, 0x10, 0x21, 0x14, 0x15, 0x15, 0x85, 0x02, 0x22, 0x02, 0x04, 0x04, 0x24,
|
||||
0x03, 0x13, 0x00, 0x91, 0x02, 0x22, 0x02, 0x04, 0x04, 0x24, 0x03, 0x13, 0x11, 0xa0, 0x02, 0x22,
|
||||
0x02, 0x04, 0x04, 0x24, 0x03, 0x13, 0x01, 0x10, 0x10, 0xa1, 0x02, 0x22, 0x02, 0x04, 0x04, 0x24,
|
||||
0x03, 0x13, 0x00, 0x00, 0x20, 0xa0, 0x02, 0x22, 0x12, 0x14, 0x04, 0x24, 0x00, 0x91, 0x02, 0x22,
|
||||
0x12, 0x14, 0x04, 0x24, 0x11, 0xa0, 0x02, 0x22, 0x12, 0x14, 0x04, 0x24, 0x01, 0x10, 0x10, 0xa1,
|
||||
0x02, 0x22, 0x12, 0x14, 0x04, 0x24, 0x20, 0x20, 0x00, 0x80, 0x00, 0x10, 0x10, 0x21, 0x21, 0x23,
|
||||
0x23, 0x14, 0x14, 0x04, 0x04, 0x00, 0x02, 0x92, 0x04, 0x02, 0x02, 0x24, 0x24, 0x22, 0x01, 0x10,
|
||||
0x10, 0x11, 0x11, 0xa0, 0x02, 0x22, 0x22, 0x24, 0x24, 0x04, 0x04, 0x02, 0x00, 0x91, 0x02, 0x22,
|
||||
0x22, 0x24, 0x24, 0x04, 0x04, 0x02, 0x11, 0xa0, 0x02, 0x22, 0x22, 0x24, 0x24, 0x04, 0x04, 0x02,
|
||||
0x11, 0x20, 0x00, 0x91, 0x02, 0x22, 0x22, 0x24, 0x24, 0x04, 0x04, 0x02, 0x01, 0x10, 0x10, 0x11,
|
||||
0x11, 0xa0, 0x01, 0x21, 0x21, 0x24, 0x24, 0x04, 0x04, 0x01, 0x00, 0x00, 0x20, 0xa0, 0x01, 0x23,
|
||||
0x03, 0xa1, 0x01, 0x10, 0x10, 0x21, 0x21, 0x23, 0x23, 0x14, 0x14, 0x03, 0x03, 0x01, 0x03, 0xa1,
|
||||
0x01, 0x04, 0x04, 0x24, 0x24, 0x21, 0x11, 0xa0, 0x01, 0x04, 0x04, 0x24, 0x24, 0x21, 0x00, 0x91,
|
||||
0x02, 0x04, 0x04, 0x24, 0x24, 0x22, 0x01, 0x10, 0x10, 0xa1, 0x01, 0x04, 0x04, 0x24, 0x24, 0x21,
|
||||
0x00, 0x00, 0x20, 0xa0, 0x01, 0x02, 0x02, 0x13, 0x13, 0x14, 0x13, 0x22, 0x22, 0x21, 0x11, 0xa0,
|
||||
0x00, 0x04, 0x01, 0x11, 0x11, 0x22, 0x22, 0x13, 0x13, 0x83, 0x00, 0x05, 0x00, 0x10, 0x10, 0x21,
|
||||
0x21, 0x12, 0x02, 0x22, 0x22, 0x24, 0x24, 0x84, 0x02, 0x04, 0x04, 0x24, 0x24, 0x22, 0x23, 0x12,
|
||||
0x12, 0x02, 0x00, 0x91, 0x02, 0x04, 0x04, 0x24, 0x24, 0x22, 0x23, 0x12, 0x12, 0x02, 0x11, 0xa0,
|
||||
0x02, 0x04, 0x04, 0x24, 0x24, 0x22, 0x23, 0x12, 0x12, 0x02, 0x01, 0x10, 0x10, 0xa1, 0x02, 0x04,
|
||||
0x04, 0x24, 0x24, 0x22, 0x23, 0x12, 0x12, 0x02, 0x01, 0x10, 0x10, 0x11, 0x11, 0xa0, 0x02, 0x04,
|
||||
0x04, 0x24, 0x24, 0x22, 0x23, 0x12, 0x12, 0x02, 0x01, 0x01, 0x21, 0xa1, 0x02, 0x04, 0x04, 0x24,
|
||||
0x24, 0x22, 0x23, 0x12, 0x12, 0x02, 0x01, 0x10, 0x10, 0x21, 0x21, 0x81, 0x02, 0x13, 0x02, 0x04,
|
||||
0x04, 0x24, 0x12, 0x14, 0x12, 0x22, 0x13, 0x23, 0x22, 0xa3, 0x03, 0x04, 0x04, 0x24, 0x03, 0x12,
|
||||
0x12, 0x22, 0x14, 0x15, 0x15, 0x85, 0x24, 0x04, 0x04, 0x02, 0x02, 0x22, 0x22, 0x23, 0x23, 0x13,
|
||||
0x00, 0x91, 0x24, 0x04, 0x04, 0x02, 0x02, 0x22, 0x22, 0x23, 0x23, 0x13, 0x11, 0xa0, 0x24, 0x04,
|
||||
0x04, 0x02, 0x02, 0x22, 0x22, 0x23, 0x23, 0x13, 0x01, 0x10, 0x10, 0xa1, 0x24, 0x04, 0x04, 0x02,
|
||||
0x02, 0x22, 0x22, 0x23, 0x23, 0x13, 0x01, 0x01, 0x21, 0xa1, 0x12, 0x14, 0x00, 0x91, 0x12, 0x14,
|
||||
0x11, 0xa0, 0x12, 0x14, 0x01, 0x10, 0x10, 0xa1, 0x12, 0x14, 0x01, 0x01, 0x21, 0xa1, 0x00, 0x22,
|
||||
0x11, 0x20, 0x02, 0x22, 0x22, 0x24, 0x24, 0x04, 0x04, 0x82, 0x02, 0x04, 0x03, 0x12, 0x12, 0x23,
|
||||
0x23, 0x24, 0x01, 0x10, 0x10, 0x11, 0x11, 0xa0, 0x02, 0x04, 0x04, 0x24, 0x24, 0x22, 0x22, 0x02,
|
||||
0x00, 0x91, 0x02, 0x04, 0x04, 0x24, 0x24, 0x22, 0x22, 0x02, 0x11, 0xa0, 0x02, 0x04, 0x04, 0x24,
|
||||
0x24, 0x22, 0x22, 0x02, 0x01, 0x10, 0x10, 0xa1, 0x02, 0x04, 0x04, 0x24, 0x24, 0x22, 0x22, 0x02,
|
||||
0x01, 0x10, 0x10, 0x11, 0x11, 0xa0, 0x02, 0x04, 0x04, 0x24, 0x24, 0x22, 0x22, 0x02, 0x01, 0x01,
|
||||
0x21, 0xa1, 0x11, 0x11, 0x02, 0x22, 0x13, 0x93, 0x02, 0x04, 0x04, 0x24, 0x24, 0x22, 0x22, 0x02,
|
||||
0x04, 0xa2, 0x02, 0x04, 0x04, 0x14, 0x14, 0x23, 0x24, 0x22, 0x00, 0x91, 0x02, 0x04, 0x04, 0x14,
|
||||
0x14, 0x23, 0x24, 0x22, 0x11, 0xa0, 0x02, 0x04, 0x04, 0x14, 0x14, 0x23, 0x24, 0x22, 0x01, 0x10,
|
||||
0x10, 0xa1, 0x02, 0x04, 0x04, 0x14, 0x14, 0x23, 0x24, 0x22, 0x01, 0x01, 0x21, 0xa1, 0x02, 0x03,
|
||||
0x03, 0x14, 0x22, 0x23, 0x23, 0x05, 0x11, 0xa0, 0x00, 0x04, 0x02, 0x11, 0x11, 0x22, 0x22, 0x13,
|
||||
0x13, 0x82, 0x02, 0x03, 0x03, 0x14, 0x22, 0x23, 0x23, 0x05, 0x01, 0x01, 0x21, 0xa1, };
|
||||
|
||||
|
||||
|
||||
void CNFGDrawText( const char * text, int scale )
|
||||
{
|
||||
const unsigned char * lmap;
|
||||
float iox = (float)CNFGPenX;
|
||||
float ioy = (float)CNFGPenY;
|
||||
|
||||
int place = 0;
|
||||
unsigned short index;
|
||||
int bQuit = 0;
|
||||
while( text[place] )
|
||||
{
|
||||
unsigned char c = text[place];
|
||||
|
||||
switch( c )
|
||||
{
|
||||
case 9:
|
||||
iox += 12 * scale;
|
||||
break;
|
||||
case 10:
|
||||
iox = (float)CNFGPenX;
|
||||
ioy += 6 * scale;
|
||||
break;
|
||||
default:
|
||||
index = FontCharMap[c];
|
||||
if( index == 65535 )
|
||||
{
|
||||
iox += 3 * scale;
|
||||
break;
|
||||
}
|
||||
|
||||
lmap = &FontCharData[index];
|
||||
do
|
||||
{
|
||||
int x1 = (int)((((*lmap) & 0x70)>>4)*scale + iox);
|
||||
int y1 = (int)(((*lmap) & 0x0f)*scale + ioy);
|
||||
int x2 = (int)((((*(lmap+1)) & 0x70)>>4)*scale + iox);
|
||||
int y2 = (int)(((*(lmap+1)) & 0x0f)*scale + ioy);
|
||||
lmap++;
|
||||
CNFGTackSegment( x1, y1, x2, y2 );
|
||||
bQuit = *lmap & 0x80;
|
||||
lmap++;
|
||||
} while( !bQuit );
|
||||
|
||||
iox += 3 * scale;
|
||||
}
|
||||
place++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CNFGDrawBox( int x1, int y1, int x2, int y2 )
|
||||
{
|
||||
unsigned lc = CNFGLastColor;
|
||||
CNFGColor( CNFGDialogColor );
|
||||
CNFGTackRectangle( x1, y1, x2, y2 );
|
||||
CNFGColor( lc );
|
||||
CNFGTackSegment( x1, y1, x2, y1 );
|
||||
CNFGTackSegment( x2, y1, x2, y2 );
|
||||
CNFGTackSegment( x2, y2, x1, y2 );
|
||||
CNFGTackSegment( x1, y2, x1, y1 );
|
||||
}
|
||||
|
||||
void CNFGGetTextExtents( const char * text, int * w, int * h, int textsize )
|
||||
{
|
||||
int charsx = 0;
|
||||
int charsy = 1;
|
||||
int charsline = 0;
|
||||
const char * s;
|
||||
|
||||
for( s = text; *s; s++ )
|
||||
{
|
||||
if( *s == '\n' )
|
||||
{
|
||||
charsline = 0;
|
||||
if( *(s+1) )
|
||||
charsy++;
|
||||
}
|
||||
else
|
||||
{
|
||||
charsline++;
|
||||
if( charsline > charsx )
|
||||
charsx = charsline;
|
||||
}
|
||||
}
|
||||
|
||||
*w = charsx * textsize * 3 + textsize;
|
||||
*h = charsy * textsize * 6;
|
||||
}
|
||||
|
||||
void CNFGDrawTextbox( int x, int y, const char * text, int textsize )
|
||||
{
|
||||
int w;
|
||||
int h;
|
||||
|
||||
CNFGGetTextExtents( text, &w, &h, textsize );
|
||||
|
||||
CNFGDrawBox( x, y, x + w, y + h );
|
||||
CNFGPenX = x + textsize;
|
||||
CNFGPenY = y + textsize;
|
||||
CNFGDrawText( text, textsize );
|
||||
}
|
54
colorchord2/DrawFunctions.h
Normal file
54
colorchord2/DrawFunctions.h
Normal file
|
@ -0,0 +1,54 @@
|
|||
//Copyright (c) 2011 <>< Charles Lohr - Under the MIT/x11 or NewBSD License you choose.
|
||||
|
||||
#ifndef _DRAWFUCNTIONS_H
|
||||
#define _DRAWFUCNTIONS_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct {
|
||||
short x, y;
|
||||
} RDPoint;
|
||||
|
||||
extern int CNFGPenX, CNFGPenY;
|
||||
extern uint32_t CNFGBGColor;
|
||||
extern uint32_t CNFGLastColor;
|
||||
extern uint32_t CNFGDialogColor; //background for boxes
|
||||
|
||||
void CNFGDrawText( const char * text, int scale );
|
||||
void CNFGDrawBox( int x1, int y1, int x2, int y2 );
|
||||
void CNFGGetTextExtents( const char * text, int * w, int * h, int textsize );
|
||||
void CNFGDrawTextbox( int x, int y, const char * text, int textsize ); //ignores pen.
|
||||
|
||||
//To be provided by driver.
|
||||
uint32_t CNFGColor( uint32_t RGB );
|
||||
void CNFGUpdateScreenWithBitmap( unsigned long * data, int w, int h );
|
||||
void CNFGTackPixel( short x1, short y1 );
|
||||
void CNFGTackSegment( short x1, short y1, short x2, short y2 );
|
||||
void CNFGTackRectangle( short x1, short y1, short x2, short y2 );
|
||||
void CNFGTackPoly( RDPoint * points, int verts );
|
||||
void CNFGClearFrame();
|
||||
void CNFGSwapBuffers();
|
||||
|
||||
void CNFGGetDimensions( short * x, short * y );
|
||||
void CNFGSetup( const char * WindowName, int w, int h );
|
||||
void CNFGSetupFullscreen( const char * WindowName, int screen_number );
|
||||
void CNFGHandleInput();
|
||||
|
||||
|
||||
//You must provide:
|
||||
void HandleKey( int keycode, int bDown );
|
||||
void HandleButton( int x, int y, int button, int bDown );
|
||||
void HandleMotion( int x, int y, int mask );
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
|
27
colorchord2/Makefile
Normal file
27
colorchord2/Makefile
Normal 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
213
colorchord2/OutputLinear.c
Normal 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);
|
||||
|
||||
|
90
colorchord2/OutputProminent.c
Normal file
90
colorchord2/OutputProminent.c
Normal 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
170
colorchord2/OutputVoronoi.c
Normal 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);
|
||||
|
||||
|
206
colorchord2/RecorderPlugin.c
Normal file
206
colorchord2/RecorderPlugin.c
Normal 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
236
colorchord2/WinDriver.c
Normal 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
258
colorchord2/XDriver.c
Normal file
|
@ -0,0 +1,258 @@
|
|||
//Copyright (c) 2011 <>< Charles Lohr - Under the MIT/x11 or NewBSD License you choose.
|
||||
//portions from
|
||||
//http://www.xmission.com/~georgeps/documentation/tutorials/Xlib_Beginner.html
|
||||
|
||||
#define HAS_XINERAMA
|
||||
|
||||
#include "DrawFunctions.h"
|
||||
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xutil.h>
|
||||
#include <X11/Xos.h>
|
||||
#include <X11/Xatom.h>
|
||||
#include <X11/keysym.h>
|
||||
#ifdef HAS_XINERAMA
|
||||
#include <X11/extensions/shape.h>
|
||||
#include <X11/extensions/Xinerama.h>
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
XWindowAttributes CNFGWinAtt;
|
||||
Display *CNFGDisplay;
|
||||
Window CNFGWindow;
|
||||
Pixmap CNFGPixmap;
|
||||
GC CNFGGC;
|
||||
GC CNFGWindowGC;
|
||||
int FullScreen = 0;
|
||||
|
||||
void CNFGGetDimensions( short * x, short * y )
|
||||
{
|
||||
*x = CNFGWinAtt.width;
|
||||
*y = CNFGWinAtt.height;
|
||||
}
|
||||
|
||||
static void InternalLinkScreenAndGo( const char * WindowName )
|
||||
{
|
||||
XGetWindowAttributes( CNFGDisplay, CNFGWindow, &CNFGWinAtt );
|
||||
|
||||
XSelectInput (CNFGDisplay, CNFGWindow, KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | ExposureMask | PointerMotionMask );
|
||||
XSetStandardProperties( CNFGDisplay, CNFGWindow, WindowName, WindowName, None, NULL, 0, NULL );
|
||||
|
||||
CNFGWindowGC = XCreateGC(CNFGDisplay, CNFGWindow, 0, 0);
|
||||
|
||||
CNFGPixmap = XCreatePixmap( CNFGDisplay, CNFGWindow, CNFGWinAtt.width, CNFGWinAtt.height, CNFGWinAtt.depth );
|
||||
CNFGGC = XCreateGC(CNFGDisplay, CNFGPixmap, 0, 0);
|
||||
}
|
||||
|
||||
void CNFGSetupFullscreen( const char * WindowName, int screen_no )
|
||||
{
|
||||
#ifdef HAS_XINERAMA
|
||||
XineramaScreenInfo *screeninfo = NULL;
|
||||
int screens;
|
||||
int event_basep, error_basep, a, b;
|
||||
CNFGDisplay = XOpenDisplay(NULL);
|
||||
int screen = XDefaultScreen(CNFGDisplay);
|
||||
int xpos, ypos;
|
||||
|
||||
if (!XShapeQueryExtension(CNFGDisplay, &event_basep, &error_basep))
|
||||
{
|
||||
fprintf( stderr, "X-Server does not support shape extension" );
|
||||
exit( 1 );
|
||||
}
|
||||
|
||||
Visual * visual = DefaultVisual(CNFGDisplay, screen);
|
||||
CNFGWinAtt.depth = DefaultDepth(CNFGDisplay, screen);
|
||||
|
||||
if (XineramaQueryExtension(CNFGDisplay, &a, &b ) &&
|
||||
(screeninfo = XineramaQueryScreens(CNFGDisplay, &screens)) &&
|
||||
XineramaIsActive(CNFGDisplay) && screen_no >= 0 &&
|
||||
screen_no < screens ) {
|
||||
|
||||
CNFGWinAtt.width = screeninfo[screen_no].width;
|
||||
CNFGWinAtt.height = screeninfo[screen_no].height;
|
||||
xpos = screeninfo[screen_no].x_org;
|
||||
ypos = screeninfo[screen_no].y_org;
|
||||
} else
|
||||
{
|
||||
CNFGWinAtt.width = XDisplayWidth(CNFGDisplay, screen);
|
||||
CNFGWinAtt.height = XDisplayHeight(CNFGDisplay, screen);
|
||||
xpos = 0;
|
||||
ypos = 0;
|
||||
}
|
||||
if (screeninfo)
|
||||
XFree(screeninfo);
|
||||
|
||||
|
||||
XSetWindowAttributes setwinattr;
|
||||
setwinattr.override_redirect = 1;
|
||||
setwinattr.save_under = 1;
|
||||
setwinattr.event_mask = StructureNotifyMask | SubstructureNotifyMask | ExposureMask | ButtonPressMask | ButtonReleaseMask | ButtonPressMask | PointerMotionMask | ButtonMotionMask | EnterWindowMask | LeaveWindowMask |KeyPressMask |KeyReleaseMask | SubstructureNotifyMask | FocusChangeMask;
|
||||
setwinattr.border_pixel = 0;
|
||||
|
||||
CNFGWindow = XCreateWindow(CNFGDisplay, XRootWindow(CNFGDisplay, screen),
|
||||
xpos, ypos, CNFGWinAtt.width, CNFGWinAtt.height,
|
||||
0, CNFGWinAtt.depth, InputOutput, visual, CWBorderPixel | CWEventMask | CWOverrideRedirect | CWSaveUnder, &setwinattr);
|
||||
|
||||
XMapWindow(CNFGDisplay, CNFGWindow);
|
||||
XSetInputFocus( CNFGDisplay, CNFGWindow, RevertToParent, CurrentTime );
|
||||
XFlush(CNFGDisplay);
|
||||
FullScreen = 1;
|
||||
//printf( "%d %d %d %d\n", xpos, ypos, CNFGWinAtt.width, CNFGWinAtt.height );
|
||||
InternalLinkScreenAndGo( WindowName );
|
||||
/*
|
||||
setwinattr.override_redirect = 1;
|
||||
XChangeWindowAttributes(
|
||||
CNFGDisplay, CNFGWindow,
|
||||
CWBorderPixel | CWEventMask | CWOverrideRedirect, &setwinattr);
|
||||
*/
|
||||
#else
|
||||
CNFGSetup( WindowName, 640, 480 );
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void CNFGSetup( const char * WindowName, int w, int h )
|
||||
{
|
||||
CNFGDisplay = XOpenDisplay(NULL);
|
||||
XGetWindowAttributes( CNFGDisplay, RootWindow(CNFGDisplay, 0), &CNFGWinAtt );
|
||||
|
||||
int depth = CNFGWinAtt.depth;
|
||||
CNFGWindow = XCreateWindow(CNFGDisplay, RootWindow(CNFGDisplay, 0), 1, 1, w, h, 0, depth, InputOutput, CopyFromParent, 0, 0 );
|
||||
XMapWindow(CNFGDisplay, CNFGWindow);
|
||||
XFlush(CNFGDisplay);
|
||||
|
||||
InternalLinkScreenAndGo( WindowName );
|
||||
}
|
||||
|
||||
void CNFGHandleInput()
|
||||
{
|
||||
static int ButtonsDown;
|
||||
XEvent report;
|
||||
|
||||
int bKeyDirection = 1;
|
||||
int r;
|
||||
while( (r=XCheckMaskEvent( CNFGDisplay, KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | ExposureMask | PointerMotionMask , &report )) )
|
||||
{
|
||||
// XEvent nev;
|
||||
// XPeekEvent(CNFGDisplay, &nev);
|
||||
|
||||
//printf( "EVENT %d\n", report.type );
|
||||
//XMaskEvent(CNFGDisplay, KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | ExposureMask, &report);
|
||||
|
||||
bKeyDirection = 1;
|
||||
switch (report.type)
|
||||
{
|
||||
case NoExpose:
|
||||
break;
|
||||
case Expose:
|
||||
XGetWindowAttributes( CNFGDisplay, CNFGWindow, &CNFGWinAtt );
|
||||
if( CNFGPixmap ) XFreePixmap( CNFGDisplay, CNFGPixmap );
|
||||
CNFGPixmap = XCreatePixmap( CNFGDisplay, CNFGWindow, CNFGWinAtt.width, CNFGWinAtt.height, CNFGWinAtt.depth );
|
||||
if( CNFGGC ) XFreeGC( CNFGDisplay, CNFGGC );
|
||||
CNFGGC = XCreateGC(CNFGDisplay, CNFGPixmap, 0, 0);
|
||||
break;
|
||||
case KeyRelease:
|
||||
bKeyDirection = 0;
|
||||
case KeyPress:
|
||||
HandleKey( XLookupKeysym(&report.xkey, 0), bKeyDirection );
|
||||
break;
|
||||
case ButtonRelease:
|
||||
bKeyDirection = 0;
|
||||
case ButtonPress:
|
||||
HandleButton( report.xbutton.x, report.xbutton.y, report.xbutton.button, bKeyDirection );
|
||||
ButtonsDown = (ButtonsDown & (~(1<<report.xbutton.button))) | ( bKeyDirection << report.xbutton.button );
|
||||
|
||||
//Intentionall fall through -- we want to send a motion in event of a button as well.
|
||||
case MotionNotify:
|
||||
HandleMotion( report.xmotion.x, report.xmotion.y, ButtonsDown>>1 );
|
||||
break;
|
||||
default:
|
||||
printf( "Event: %d\n", report.type );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CNFGUpdateScreenWithBitmap( unsigned long * data, int w, int h )
|
||||
{
|
||||
static XImage *xi;
|
||||
static int depth;
|
||||
static int lw, lh;
|
||||
|
||||
if( !xi )
|
||||
{
|
||||
int screen = DefaultScreen(CNFGDisplay);
|
||||
// Visual * visual = DefaultVisual(CNFGDisplay, screen);
|
||||
depth = DefaultDepth(CNFGDisplay, screen)/8;
|
||||
// xi = XCreateImage(CNFGDisplay, DefaultVisual( CNFGDisplay, DefaultScreen(CNFGDisplay) ), depth*8, ZPixmap, 0, (char*)data, w, h, 32, w*4 );
|
||||
// lw = w;
|
||||
// lh = h;
|
||||
}
|
||||
|
||||
if( lw != w || lh != h )
|
||||
{
|
||||
if( xi ) free( xi );
|
||||
xi = XCreateImage(CNFGDisplay, DefaultVisual( CNFGDisplay, DefaultScreen(CNFGDisplay) ), depth*8, ZPixmap, 0, (char*)data, w, h, 32, w*4 );
|
||||
lw = w;
|
||||
lh = h;
|
||||
}
|
||||
|
||||
// ls = lw * lh;
|
||||
|
||||
XPutImage(CNFGDisplay, CNFGWindow, CNFGWindowGC, xi, 0, 0, 0, 0, w, h );
|
||||
}
|
||||
|
||||
|
||||
#ifndef RASTERIZER
|
||||
|
||||
|
||||
uint32_t CNFGColor( uint32_t RGB )
|
||||
{
|
||||
unsigned char red = RGB & 0xFF;
|
||||
unsigned char grn = ( RGB >> 8 ) & 0xFF;
|
||||
unsigned char blu = ( RGB >> 16 ) & 0xFF;
|
||||
CNFGLastColor = RGB;
|
||||
unsigned long color = (red<<16)|(grn<<8)|(blu);
|
||||
XSetForeground(CNFGDisplay, CNFGGC, color);
|
||||
return color;
|
||||
}
|
||||
|
||||
void CNFGClearFrame()
|
||||
{
|
||||
XGetWindowAttributes( CNFGDisplay, CNFGWindow, &CNFGWinAtt );
|
||||
XSetForeground(CNFGDisplay, CNFGGC, CNFGColor(CNFGBGColor) );
|
||||
XFillRectangle(CNFGDisplay, CNFGPixmap, CNFGGC, 0, 0, CNFGWinAtt.width, CNFGWinAtt.height );
|
||||
}
|
||||
|
||||
void CNFGSwapBuffers()
|
||||
{
|
||||
XCopyArea(CNFGDisplay, CNFGPixmap, CNFGWindow, CNFGWindowGC, 0,0,CNFGWinAtt.width,CNFGWinAtt.height,0,0);
|
||||
XFlush(CNFGDisplay);
|
||||
if( FullScreen )
|
||||
XSetInputFocus( CNFGDisplay, CNFGWindow, RevertToParent, CurrentTime );
|
||||
}
|
||||
|
||||
void CNFGTackSegment( short x1, short y1, short x2, short y2 )
|
||||
{
|
||||
XDrawLine( CNFGDisplay, CNFGPixmap, CNFGGC, x1, y1, x2, y2 );
|
||||
XDrawPoint( CNFGDisplay, CNFGPixmap, CNFGGC, x2, y2 );
|
||||
}
|
||||
|
||||
void CNFGTackPixel( short x1, short y1 )
|
||||
{
|
||||
XDrawPoint( CNFGDisplay, CNFGPixmap, CNFGGC, x1, y1 );
|
||||
}
|
||||
|
||||
void CNFGTackRectangle( short x1, short y1, short x2, short y2 )
|
||||
{
|
||||
XFillRectangle(CNFGDisplay, CNFGPixmap, CNFGGC, x1, y1, x2-x1, y2-y1 );
|
||||
}
|
||||
|
||||
void CNFGTackPoly( RDPoint * points, int verts )
|
||||
{
|
||||
XFillPolygon(CNFGDisplay, CNFGPixmap, CNFGGC, (XPoint *)points, verts, Convex, CoordModeOrigin );
|
||||
}
|
||||
|
||||
#endif
|
||||
|
440
colorchord2/chash.c
Normal file
440
colorchord2/chash.c
Normal 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
53
colorchord2/chash.h
Normal file
|
@ -0,0 +1,53 @@
|
|||
#ifndef _CHASH_H
|
||||
#define _CHASH_H
|
||||
|
||||
|
||||
struct chashentry
|
||||
{
|
||||
const char * key;
|
||||
void * value;
|
||||
unsigned long hash;
|
||||
};
|
||||
|
||||
struct chashlist
|
||||
{
|
||||
int length;
|
||||
struct chashentry items[0];
|
||||
};
|
||||
|
||||
|
||||
struct chash
|
||||
{
|
||||
int entryCount;
|
||||
int allowMultiple;
|
||||
int bucketCountPlace;
|
||||
struct chashentry * buckets;
|
||||
};
|
||||
|
||||
struct chash * GenerateHashTable( int allowMultiple );
|
||||
|
||||
void ** HashTableInsert( struct chash * hash, const char * key, int dontDupKey );
|
||||
|
||||
//returns # of entries removed.
|
||||
int HashTableRemove( struct chash * hash, const char * key );
|
||||
|
||||
//returns 1 if entry was removed, 0 otherwise.
|
||||
int HashTableRemoveSpecific( struct chash * hash, const char * key, void * value );
|
||||
|
||||
void HashDestroy( struct chash * hash, int deleteKeys );
|
||||
|
||||
//Gets a pointer to the pointer to day, allowing you to read or modify the entry.
|
||||
//NOTE: This only returns one of the elements if multiple keys are allowed.
|
||||
void ** HashUpdateEntry( struct chash * hash, const char * key );
|
||||
|
||||
//Returns the entry itself, if none found, returns 0.
|
||||
void * HashGetEntry( struct chash * hash, const char * key );
|
||||
|
||||
//Get list of entries that match a given key.
|
||||
struct chashlist * HashGetAllEntries( struct chash * hash, const char * key );
|
||||
|
||||
//Can tolerate an empty list.
|
||||
struct chashlist * HashProduceSortedTable( struct chash * hash );
|
||||
|
||||
|
||||
#endif
|
105
colorchord2/color.c
Normal file
105
colorchord2/color.c
Normal file
|
@ -0,0 +1,105 @@
|
|||
#include "color.h"
|
||||
#include <math.h>
|
||||
|
||||
uint32_t CCtoHEX( float note, float sat, float value )
|
||||
{
|
||||
float hue = 0.0;
|
||||
note = fmodf( note, 1.0 );
|
||||
note *= 12;
|
||||
if( note < 4 )
|
||||
{
|
||||
//Needs to be YELLOW->RED
|
||||
hue = (4 - note) / 24.0;
|
||||
}
|
||||
else if( note < 8 )
|
||||
{
|
||||
// [4] [8]
|
||||
//Needs to be RED->BLUE
|
||||
hue = ( 4 - note ) / 12.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// [8] [12]
|
||||
//Needs to be BLUE->YELLOW
|
||||
hue = ( 12 - note ) / 8.0 + 1./6.;
|
||||
}
|
||||
return HSVtoHEX( hue, sat, value );
|
||||
}
|
||||
|
||||
|
||||
//0/6: RED
|
||||
//1/6: YELLOW
|
||||
//2/6: GREEN
|
||||
//3/6: CYAN
|
||||
//4/6: BLUE
|
||||
//5/6: PURPLE
|
||||
//6/6: RED
|
||||
uint32_t HSVtoHEX( float hue, float sat, float value )
|
||||
{
|
||||
|
||||
float pr = 0;
|
||||
float pg = 0;
|
||||
float pb = 0;
|
||||
|
||||
short ora = 0;
|
||||
short og = 0;
|
||||
short ob = 0;
|
||||
|
||||
float ro = fmod( hue * 6, 6. );
|
||||
|
||||
float avg = 0;
|
||||
|
||||
ro = fmod( ro + 6 + 1, 6 ); //Hue was 60* off...
|
||||
|
||||
if( ro < 1 ) //yellow->red
|
||||
{
|
||||
pr = 1;
|
||||
pg = 1. - ro;
|
||||
} else if( ro < 2 )
|
||||
{
|
||||
pr = 1;
|
||||
pb = ro - 1.;
|
||||
} else if( ro < 3 )
|
||||
{
|
||||
pr = 3. - ro;
|
||||
pb = 1;
|
||||
} else if( ro < 4 )
|
||||
{
|
||||
pb = 1;
|
||||
pg = ro - 3;
|
||||
} else if( ro < 5 )
|
||||
{
|
||||
pb = 5 - ro;
|
||||
pg = 1;
|
||||
} else
|
||||
{
|
||||
pg = 1;
|
||||
pr = ro - 5;
|
||||
}
|
||||
|
||||
//Actually, above math is backwards, oops!
|
||||
pr *= value;
|
||||
pg *= value;
|
||||
pb *= value;
|
||||
|
||||
avg += pr;
|
||||
avg += pg;
|
||||
avg += pb;
|
||||
|
||||
pr = pr * sat + avg * (1.-sat);
|
||||
pg = pg * sat + avg * (1.-sat);
|
||||
pb = pb * sat + avg * (1.-sat);
|
||||
|
||||
ora = pr * 255;
|
||||
og = pb * 255;
|
||||
ob = pg * 255;
|
||||
|
||||
if( ora < 0 ) ora = 0;
|
||||
if( ora > 255 ) ora = 255;
|
||||
if( og < 0 ) og = 0;
|
||||
if( og > 255 ) og = 255;
|
||||
if( ob < 0 ) ob = 0;
|
||||
if( ob > 255 ) ob = 255;
|
||||
|
||||
return (ob<<16) | (og<<8) | ora;
|
||||
}
|
12
colorchord2/color.h
Normal file
12
colorchord2/color.h
Normal file
|
@ -0,0 +1,12 @@
|
|||
#ifndef _COLOR_H
|
||||
#define _COLOR_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
//note = 0..1 (Root is YELLOW); Val = 0..1
|
||||
//NOTE: CC is _NOT_ HUE!!!
|
||||
uint32_t CCtoHEX( float note, float sat, float value );
|
||||
uint32_t HSVtoHEX( float hue, float sat, float value );
|
||||
|
||||
#endif
|
||||
|
215
colorchord2/decompose.c
Normal file
215
colorchord2/decompose.c
Normal file
|
@ -0,0 +1,215 @@
|
|||
#include "decompose.h"
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define SQRT2PI 2.506628253
|
||||
|
||||
#ifdef TURBO_DECOMPOSE
|
||||
|
||||
int DecomposeHistogram( float * histogram, int bins, float * out_means, float * out_amps, float * out_sigmas, int max_dists, double default_sigma, int iterations )
|
||||
{
|
||||
//Step 1: Find the actual peaks.
|
||||
|
||||
int i;
|
||||
int peak = 0;
|
||||
for( i = 0; i < bins; i++ )
|
||||
{
|
||||
float offset = 0;
|
||||
float prev = histogram[(i-1+bins)%bins];
|
||||
float this = histogram[i];
|
||||
float next = histogram[(i+1)%bins];
|
||||
|
||||
if( prev > this || next > this ) continue;
|
||||
if( prev == this && next == this ) continue;
|
||||
|
||||
//i is at a peak...
|
||||
float totaldiff = (( this - prev ) + ( this - next ));
|
||||
float porpdiffP = (this-prev)/totaldiff; //close to 0 = closer to this side... 0.5 = in the middle ... 1.0 away.
|
||||
float porpdiffN = (this-next)/totaldiff;
|
||||
|
||||
if( porpdiffP < porpdiffN )
|
||||
{
|
||||
//Closer to prev.
|
||||
offset = -(0.5 - porpdiffP);
|
||||
}
|
||||
else
|
||||
{
|
||||
offset = (0.5 - porpdiffN);
|
||||
}
|
||||
|
||||
out_means[peak] = i + offset;
|
||||
|
||||
//XXX XXX TODO Examine difference or relationship of "this" and "totaldiff"
|
||||
out_amps[peak] = this * 4;
|
||||
//powf( totaldiff, .8) * 10;//powf( totaldiff, .5 )*4; //
|
||||
out_sigmas[peak] = default_sigma;
|
||||
peak++;
|
||||
}
|
||||
|
||||
for( i = peak; i < max_dists; i++ )
|
||||
{
|
||||
out_means[i] = -1;
|
||||
out_amps[i] = 0;
|
||||
out_sigmas[i] = default_sigma;
|
||||
}
|
||||
|
||||
return peak;
|
||||
}
|
||||
|
||||
//Yick: Doesn't match.. I guess it's only for debugging, right?
|
||||
float CalcHistAt( float pt, int bins, float * out_means, float * out_amps, float * out_sigmas, int cur_dists )
|
||||
{
|
||||
int i;
|
||||
float mark = 0.0;
|
||||
for( i = 0; i < cur_dists; i++ )
|
||||
{
|
||||
float amp = out_amps[i];
|
||||
float mean = out_means[i];
|
||||
float var = out_sigmas[i];
|
||||
|
||||
float x = mean - pt;
|
||||
if( x < - bins / 2 ) x += bins;
|
||||
if( x > bins / 2 ) x -= bins;
|
||||
float nrm = amp / (var * SQRT2PI ) * expf( - ( x * x ) / ( 2 * var * var ) );
|
||||
mark += nrm;
|
||||
}
|
||||
return mark;
|
||||
}
|
||||
|
||||
|
||||
#else
|
||||
|
||||
float AttemptDecomposition( float * histogram, int bins, float * out_means, float * out_amps, float * out_sigmas, int cur_dists );
|
||||
float CalcHistAt( float pt, int bins, float * out_means, float * out_amps, float * out_sigmas, int cur_dists );
|
||||
|
||||
|
||||
int DecomposeHistogram( float * histogram, int bins, float * out_means, float * out_amps, float * out_sigmas, int max_dists, double default_sigma, int iterations )
|
||||
{
|
||||
//NOTE: The sigma may change depending on all sorts of things, maybe?
|
||||
|
||||
int sigs = 0;
|
||||
int i;
|
||||
|
||||
int went_up = 0;
|
||||
|
||||
float vhist = histogram[bins-1];
|
||||
|
||||
for( i = 0; i <= bins; i++ )
|
||||
{
|
||||
float thishist = histogram[i%bins];
|
||||
if( thishist > vhist )
|
||||
{
|
||||
went_up = 1;
|
||||
}
|
||||
else if( went_up)
|
||||
{
|
||||
went_up = 0;
|
||||
out_amps[sigs] = thishist / (default_sigma + 1);
|
||||
out_sigmas[sigs] = default_sigma;
|
||||
out_means[sigs] = i-0.5;
|
||||
sigs++;
|
||||
}
|
||||
vhist = thishist;
|
||||
}
|
||||
if( sigs == 0 )
|
||||
return 0;
|
||||
|
||||
int iteration;
|
||||
float errbest = AttemptDecomposition( histogram, bins, out_means, out_amps, out_sigmas, sigs );
|
||||
int dropped[bins];
|
||||
for( i = 0; i < bins; i++ )
|
||||
{
|
||||
dropped[i] = 0;
|
||||
}
|
||||
|
||||
for( iteration = 0; iteration < iterations; iteration++ )
|
||||
{
|
||||
if( dropped[iteration%sigs] ) continue;
|
||||
//Tweak with the values until they are closer to what we want.
|
||||
float backup_mean = out_means[iteration%sigs];
|
||||
float backup_amps = out_amps[iteration%sigs];
|
||||
float backup_sigmas = out_sigmas[iteration%sigs];
|
||||
|
||||
float mute = 20. / (iteration+20.);
|
||||
#define RANDFN ((rand()%2)-0.5)*mute
|
||||
//#define RANDFN ((rand()%10000)-5000.0) / 5000.0 * mute
|
||||
// out_sigmas[iteration%sigs] += RANDFN;
|
||||
out_means[iteration%sigs] += RANDFN;
|
||||
out_amps[iteration%sigs] += RANDFN;
|
||||
float err = AttemptDecomposition( histogram, bins, out_means, out_amps, out_sigmas, sigs );
|
||||
if( err > errbest )
|
||||
{
|
||||
out_means[iteration%sigs] = backup_mean;
|
||||
out_amps[iteration%sigs] = backup_amps;
|
||||
out_sigmas[iteration%sigs] = backup_sigmas;
|
||||
}
|
||||
else
|
||||
{
|
||||
if( out_amps[iteration%sigs] < 0.01 )
|
||||
{
|
||||
dropped[iteration%sigs] = 1;
|
||||
out_amps[iteration%sigs] = 0.0;
|
||||
}
|
||||
errbest = err;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// printf( "%f / %f = %f\n", origerr, errbest, origerr/errbest );
|
||||
|
||||
return sigs;
|
||||
}
|
||||
|
||||
float CalcHistAt( float pt, int bins, float * out_means, float * out_amps, float * out_sigmas, int cur_dists )
|
||||
{
|
||||
int i;
|
||||
float mark = 0.0;
|
||||
for( i = 0; i < cur_dists; i++ )
|
||||
{
|
||||
float amp = out_amps[i];
|
||||
float mean = out_means[i];
|
||||
float var = out_sigmas[i];
|
||||
|
||||
float x = mean - pt;
|
||||
if( x < - bins / 2 ) x += bins;
|
||||
if( x > bins / 2 ) x -= bins;
|
||||
float nrm = amp / (var * SQRT2PI ) * expf( - ( x * x ) / ( 2 * var * var ) );
|
||||
mark += nrm;
|
||||
}
|
||||
return mark;
|
||||
}
|
||||
|
||||
float AttemptDecomposition( float * histogram, int bins, float * out_means, float * out_amps, float * out_sigmas, int cur_dists )
|
||||
{
|
||||
int i, j;
|
||||
float hist[bins];
|
||||
memcpy( hist, histogram, sizeof(hist) );
|
||||
for( i = 0; i < cur_dists; i++ )
|
||||
{
|
||||
float amp = out_amps[i];
|
||||
float mean = out_means[i];
|
||||
float var = out_sigmas[i];
|
||||
|
||||
for( j = 0; j < bins; j++ )
|
||||
{
|
||||
float x = mean - j;
|
||||
if( x < - bins / 2 ) x += bins;
|
||||
if( x > bins / 2 ) x -= bins;
|
||||
float nrm = amp / (var * SQRT2PI ) * expf( - ( x * x ) / ( 2 * var * var ) );
|
||||
hist[j] -= nrm;
|
||||
}
|
||||
}
|
||||
|
||||
float remain = 0;
|
||||
for( j = 0; j < bins; j++ )
|
||||
{
|
||||
remain += hist[j] * hist[j];
|
||||
}
|
||||
return remain;
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
12
colorchord2/decompose.h
Normal file
12
colorchord2/decompose.h
Normal file
|
@ -0,0 +1,12 @@
|
|||
#ifndef _DECOMPOSE_H
|
||||
#define _DECOMPOSE_H
|
||||
|
||||
//Decompose a histogram into a series of normal distributions.
|
||||
int DecomposeHistogram( float * histogram, int bins, float * out_means, float * out_amps, float * out_sigmas, int max_dists, double default_sigma, int iterations );
|
||||
float CalcHistAt( float pt, int bins, float * out_means, float * out_amps, float * out_sigmas, int cur_dists );
|
||||
|
||||
|
||||
#define TURBO_DECOMPOSE
|
||||
|
||||
#endif
|
||||
|
94
colorchord2/default.conf
Normal file
94
colorchord2/default.conf
Normal 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
690
colorchord2/dft.c
Normal 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
76
colorchord2/dft.h
Normal 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
29
colorchord2/dmx.conf
Normal 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
58
colorchord2/filter.c
Normal 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
11
colorchord2/filter.h
Normal file
|
@ -0,0 +1,11 @@
|
|||
#ifndef _FILTER_H
|
||||
#define _FILTER_H
|
||||
|
||||
|
||||
//Perform a two-pass filter on the data, circularly. Once right, then left.
|
||||
//void FilterFoldedBinsIIRTWOPASS( float * folded, int bins, float strength );
|
||||
|
||||
void FilterFoldedBinsBlob( float * folded, int bins, float strength, int iter );
|
||||
|
||||
|
||||
#endif
|
102
colorchord2/hook.c
Normal file
102
colorchord2/hook.c
Normal 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
18
colorchord2/hook.h
Normal 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
|
||||
|
5
colorchord2/integerprog.conf
Normal file
5
colorchord2/integerprog.conf
Normal file
|
@ -0,0 +1,5 @@
|
|||
do_progressive_dft = 3
|
||||
samplerate = 8000
|
||||
buffer = 64
|
||||
sourcename = alsa_output.pci-0000_00_1b.0.analog-stereo.monitor
|
||||
|
18
colorchord2/linearpie.conf
Normal file
18
colorchord2/linearpie.conf
Normal 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
504
colorchord2/main.c
Normal 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
26
colorchord2/netlight.conf
Normal 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
380
colorchord2/notefinder.c
Normal 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
95
colorchord2/notefinder.h
Normal 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
342
colorchord2/os_generic.c
Normal file
|
@ -0,0 +1,342 @@
|
|||
#include "os_generic.h"
|
||||
|
||||
|
||||
#ifdef USE_WINDOWS
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
void OGSleep( int is )
|
||||
{
|
||||
Sleep( is*1000 );
|
||||
}
|
||||
|
||||
void OGUSleep( int ius )
|
||||
{
|
||||
Sleep( ius/1000 );
|
||||
}
|
||||
|
||||
double OGGetAbsoluteTime()
|
||||
{
|
||||
static LARGE_INTEGER lpf;
|
||||
LARGE_INTEGER li;
|
||||
|
||||
if( !lpf.QuadPart )
|
||||
{
|
||||
QueryPerformanceFrequency( &lpf );
|
||||
}
|
||||
|
||||
QueryPerformanceCounter( &li );
|
||||
return (double)li.QuadPart / (double)lpf.QuadPart;
|
||||
}
|
||||
|
||||
|
||||
double OGGetFileTime( const char * file )
|
||||
{
|
||||
FILETIME ft;
|
||||
|
||||
HANDLE h = CreateFile(file, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
|
||||
|
||||
if( h==INVALID_HANDLE_VALUE )
|
||||
return -1;
|
||||
|
||||
GetFileTime( h, 0, 0, &ft );
|
||||
|
||||
CloseHandle( h );
|
||||
|
||||
return ft.dwHighDateTime + ft.dwLowDateTime;
|
||||
}
|
||||
|
||||
|
||||
og_thread_t OGCreateThread( void * (function)( void * ), void * parameter )
|
||||
{
|
||||
return (og_thread_t)CreateThread( 0, 0, (LPTHREAD_START_ROUTINE)function, parameter, 0, 0 );
|
||||
}
|
||||
|
||||
void * OGJoinThread( og_thread_t ot )
|
||||
{
|
||||
WaitForSingleObject( ot, INFINITE );
|
||||
CloseHandle( ot );
|
||||
}
|
||||
|
||||
void OGCancelThread( og_thread_t ot )
|
||||
{
|
||||
CloseHandle( ot );
|
||||
}
|
||||
|
||||
og_mutex_t OGCreateMutex()
|
||||
{
|
||||
return CreateMutex( 0, 0, 0 );
|
||||
}
|
||||
|
||||
void OGLockMutex( og_mutex_t om )
|
||||
{
|
||||
WaitForSingleObject(om, INFINITE);
|
||||
}
|
||||
|
||||
void OGUnlockMutex( og_mutex_t om )
|
||||
{
|
||||
ReleaseMutex(om);
|
||||
}
|
||||
|
||||
void OGDeleteMutex( og_mutex_t om )
|
||||
{
|
||||
CloseHandle( om );
|
||||
}
|
||||
|
||||
|
||||
|
||||
og_sema_t OGCreateSema()
|
||||
{
|
||||
HANDLE sem = CreateSemaphore( 0, 0, 32767, 0 );
|
||||
return (og_sema_t)sem;
|
||||
}
|
||||
|
||||
|
||||
|
||||
typedef LONG NTSTATUS;
|
||||
|
||||
typedef NTSTATUS (NTAPI *_NtQuerySemaphore)(
|
||||
HANDLE SemaphoreHandle,
|
||||
DWORD SemaphoreInformationClass, /* Would be SEMAPHORE_INFORMATION_CLASS */
|
||||
PVOID SemaphoreInformation, /* but this is to much to dump here */
|
||||
ULONG SemaphoreInformationLength,
|
||||
PULONG ReturnLength OPTIONAL
|
||||
);
|
||||
|
||||
|
||||
typedef struct _SEMAPHORE_BASIC_INFORMATION {
|
||||
ULONG CurrentCount;
|
||||
ULONG MaximumCount;
|
||||
} SEMAPHORE_BASIC_INFORMATION;
|
||||
|
||||
|
||||
int OGGetSema( og_sema_t os )
|
||||
{
|
||||
HANDLE sem = (HANDLE)os;
|
||||
|
||||
static _NtQuerySemaphore NtQuerySemaphore;
|
||||
SEMAPHORE_BASIC_INFORMATION BasicInfo;
|
||||
NTSTATUS Status;
|
||||
|
||||
if( !NtQuerySemaphore )
|
||||
{
|
||||
NtQuerySemaphore = (_NtQuerySemaphore)GetProcAddress (GetModuleHandle ("ntdll.dll"), "NtQuerySemaphore");
|
||||
if( !NtQuerySemaphore )
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Status = NtQuerySemaphore (sem, 0 /*SemaphoreBasicInformation*/,
|
||||
&BasicInfo, sizeof (SEMAPHORE_BASIC_INFORMATION), NULL);
|
||||
|
||||
if (Status == ERROR_SUCCESS)
|
||||
{
|
||||
return BasicInfo.CurrentCount;
|
||||
}
|
||||
|
||||
return -2;
|
||||
}
|
||||
|
||||
void OGLockSema( og_sema_t os )
|
||||
{
|
||||
WaitForSingleObject( (HANDLE)os, INFINITE );
|
||||
}
|
||||
|
||||
void OGUnlockSema( og_sema_t os )
|
||||
{
|
||||
ReleaseSemaphore( (HANDLE)os, 1, 0 );
|
||||
}
|
||||
|
||||
void OGDeleteSema( og_sema_t os )
|
||||
{
|
||||
CloseHandle( os );
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#ifndef _GNU_SOURCE
|
||||
#define _GNU_SOURCE
|
||||
#endif
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <stdlib.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/time.h>
|
||||
#include <semaphore.h>
|
||||
#include <unistd.h>
|
||||
|
||||
pthread_mutex_t g_RawMutexStart = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
void OGSleep( int is )
|
||||
{
|
||||
sleep( is );
|
||||
}
|
||||
|
||||
void OGUSleep( int ius )
|
||||
{
|
||||
usleep( ius );
|
||||
}
|
||||
|
||||
double OGGetAbsoluteTime()
|
||||
{
|
||||
struct timeval tv;
|
||||
gettimeofday( &tv, 0 );
|
||||
return ((double)tv.tv_usec)/1000000. + (tv.tv_sec);
|
||||
}
|
||||
|
||||
double OGGetFileTime( const char * file )
|
||||
{
|
||||
struct stat buff;
|
||||
|
||||
int r = stat( file, &buff );
|
||||
|
||||
if( r < 0 )
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
return buff.st_mtime;
|
||||
}
|
||||
|
||||
|
||||
|
||||
og_thread_t OGCreateThread( void * (routine)( void * ), void * parameter )
|
||||
{
|
||||
pthread_t * ret = malloc( sizeof( pthread_t ) );
|
||||
int r = pthread_create( ret, 0, routine, parameter );
|
||||
if( r )
|
||||
{
|
||||
free( ret );
|
||||
return 0;
|
||||
}
|
||||
return (og_thread_t)ret;
|
||||
}
|
||||
|
||||
void * OGJoinThread( og_thread_t ot )
|
||||
{
|
||||
void * retval;
|
||||
if( !ot )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
pthread_join( *(pthread_t*)ot, &retval );
|
||||
free( ot );
|
||||
return retval;
|
||||
}
|
||||
|
||||
void OGCancelThread( og_thread_t ot )
|
||||
{
|
||||
if( !ot )
|
||||
{
|
||||
return;
|
||||
}
|
||||
pthread_cancel( *(pthread_t*)ot );
|
||||
free( ot );
|
||||
}
|
||||
|
||||
og_mutex_t OGCreateMutex()
|
||||
{
|
||||
pthread_mutexattr_t mta;
|
||||
og_mutex_t r = malloc( sizeof( pthread_mutex_t ) );
|
||||
|
||||
pthread_mutexattr_init(&mta);
|
||||
pthread_mutexattr_settype(&mta, PTHREAD_MUTEX_RECURSIVE);
|
||||
|
||||
pthread_mutex_init( (pthread_mutex_t *)r, &mta );
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
void OGLockMutex( og_mutex_t om )
|
||||
{
|
||||
if( !om )
|
||||
{
|
||||
return;
|
||||
}
|
||||
pthread_mutex_lock( (pthread_mutex_t*)om );
|
||||
}
|
||||
|
||||
void OGUnlockMutex( og_mutex_t om )
|
||||
{
|
||||
if( !om )
|
||||
{
|
||||
return;
|
||||
}
|
||||
pthread_mutex_unlock( (pthread_mutex_t*)om );
|
||||
}
|
||||
|
||||
void OGDeleteMutex( og_mutex_t om )
|
||||
{
|
||||
if( !om )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
pthread_mutex_destroy( (pthread_mutex_t*)om );
|
||||
free( om );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
og_sema_t OGCreateSema()
|
||||
{
|
||||
sem_t * sem = malloc( sizeof( sem_t ) );
|
||||
sem_init( sem, 0, 0 );
|
||||
return (og_sema_t)sem;
|
||||
}
|
||||
|
||||
int OGGetSema( og_sema_t os )
|
||||
{
|
||||
int valp;
|
||||
sem_getvalue( os, &valp );
|
||||
return valp;
|
||||
}
|
||||
|
||||
|
||||
void OGLockSema( og_sema_t os )
|
||||
{
|
||||
sem_wait( os );
|
||||
}
|
||||
|
||||
void OGUnlockSema( og_sema_t os )
|
||||
{
|
||||
sem_post( os );
|
||||
}
|
||||
|
||||
void OGDeleteSema( og_sema_t os )
|
||||
{
|
||||
sem_destroy( os );
|
||||
free(os);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
//Date Stamp: 2012-02-15
|
||||
|
||||
/*
|
||||
Copyright (c) 2011-2012 <>< Charles Lohr
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of this file.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
IN THE SOFTWARE.
|
||||
*/
|
||||
|
82
colorchord2/os_generic.h
Normal file
82
colorchord2/os_generic.h
Normal file
|
@ -0,0 +1,82 @@
|
|||
#ifndef _OS_GENERIC_H
|
||||
#define _OS_GENERIC_H
|
||||
|
||||
#ifdef WIN32
|
||||
#define USE_WINDOWS
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define EXECUTE_AT_BOOT( x, y ) \
|
||||
\
|
||||
void fn##x() __attribute__((constructor)); \
|
||||
void fn##x() \
|
||||
{ y; } \
|
||||
|
||||
//Things that shouldn't be macro'd
|
||||
double OGGetAbsoluteTime();
|
||||
void OGSleep( int is );
|
||||
void OGUSleep( int ius );
|
||||
double OGGetFileTime( const char * file );
|
||||
|
||||
//Threads and Mutices
|
||||
typedef void* og_thread_t;
|
||||
typedef void* og_mutex_t;
|
||||
typedef void* og_sema_t;
|
||||
|
||||
og_thread_t OGCreateThread( void * (routine)( void * ), void * parameter );
|
||||
void * OGJoinThread( og_thread_t ot );
|
||||
void OGCancelThread( og_thread_t ot );
|
||||
|
||||
//Always a recrusive mutex.
|
||||
og_mutex_t OGCreateMutex();
|
||||
void OGLockMutex( og_mutex_t om );
|
||||
void OGUnlockMutex( og_mutex_t om );
|
||||
void OGDeleteMutex( og_mutex_t om );
|
||||
|
||||
//Always a semaphore
|
||||
og_sema_t OGCreateSema(); //Create a semaphore, comes locked initially. NOTE: Max count is 32767
|
||||
void OGLockSema( og_sema_t os );
|
||||
int OGGetSema( og_sema_t os ); //if <0 there was a failure.
|
||||
void OGUnlockSema( og_sema_t os );
|
||||
void OGDeleteSema( og_sema_t os );
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
//Date Stamp: 2014-06-12
|
||||
|
||||
/*
|
||||
NOTE: Portions (namely the top section) are part of headers from other
|
||||
sources.
|
||||
|
||||
Copyright (c) 2011-2012 <>< Charles Lohr
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of this file.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
IN THE SOFTWARE.
|
||||
*/
|
||||
|
73
colorchord2/outdrivers.c
Normal file
73
colorchord2/outdrivers.c
Normal 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
44
colorchord2/outdrivers.h
Normal 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
349
colorchord2/parameters.c
Normal 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
63
colorchord2/parameters.h
Normal 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, ¶meter_name, sizeof( parameter_name ) ); }
|
||||
|
||||
|
||||
#endif
|
26
colorchord2/quickwash.conf
Normal file
26
colorchord2/quickwash.conf
Normal 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
|
||||
|
7
colorchord2/recordtest.conf
Normal file
7
colorchord2/recordtest.conf
Normal 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
59
colorchord2/sort.c
Normal file
|
@ -0,0 +1,59 @@
|
|||
#include "sort.h"
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
//Sort the indices of an array of floating point numbers
|
||||
void SortFloats( int * indexouts, float * array, int count )
|
||||
{
|
||||
int i;
|
||||
int j;
|
||||
unsigned char selected[count];
|
||||
|
||||
memset( selected, 0, sizeof( selected ) );
|
||||
|
||||
for( i = 0; i < count; i++ )
|
||||
{
|
||||
int leastindex = -1;
|
||||
float leastval = -1e200;
|
||||
|
||||
for( j = 0; j < count; j++ )
|
||||
{
|
||||
if( !selected[j] && array[j] > leastval )
|
||||
{
|
||||
leastval = array[j];
|
||||
leastindex = j;
|
||||
}
|
||||
}
|
||||
|
||||
if( leastindex < 0 )
|
||||
{
|
||||
fprintf( stderr, "ERROR: Sorting fault.\n" );
|
||||
goto fault;
|
||||
}
|
||||
|
||||
selected[leastindex] = 1;
|
||||
indexouts[i] = leastindex;
|
||||
}
|
||||
|
||||
return;
|
||||
fault:
|
||||
for( i = 0; i < count; i++ )
|
||||
{
|
||||
indexouts[i] = i;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//Move the floats around according to the new index.
|
||||
void RemapFloats( int * indexouts, float * array, int count )
|
||||
{
|
||||
int i;
|
||||
float copyarray[count];
|
||||
memcpy( copyarray, array, sizeof( copyarray ) );
|
||||
for( i = 0; i < count; i++ )
|
||||
{
|
||||
array[i] = copyarray[indexouts[i]];;
|
||||
}
|
||||
}
|
||||
|
16
colorchord2/sort.h
Normal file
16
colorchord2/sort.h
Normal file
|
@ -0,0 +1,16 @@
|
|||
#ifndef _SORT_H
|
||||
#define _SORT_H
|
||||
|
||||
//XXX WARNING: This is a TERRIBLE TERRIBLE O(N^2) SORTING ALGORITHM
|
||||
//You should probably fix this!!!
|
||||
|
||||
//Sort the indices of an array of floating point numbers
|
||||
//Highest->Lowest
|
||||
void SortFloats( int * indexouts, float * array, int count );
|
||||
|
||||
//Move the floats around according to the new index.
|
||||
void RemapFloats( int * indexouts, float * array, int count );
|
||||
|
||||
#endif
|
||||
|
||||
|
106
colorchord2/sound.c
Normal file
106
colorchord2/sound.c
Normal file
|
@ -0,0 +1,106 @@
|
|||
#include "sound.h"
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static SoundInitFn * SoundDrivers[MAX_SOUND_DRIVERS];
|
||||
static char * SoundDriverNames[MAX_SOUND_DRIVERS]; //XXX: There's a bug in my compiler, this should be 'static'
|
||||
static int SoundDriverPriorities[MAX_SOUND_DRIVERS];
|
||||
/*
|
||||
void CleanupSound() __attribute__((destructor));
|
||||
void CleanupSound()
|
||||
{
|
||||
int i;
|
||||
for( i = 0; i < MAX_SOUND_DRIVERS; i++ )
|
||||
{
|
||||
if( SoundDriverNames[i] )
|
||||
{
|
||||
free( SoundDriverNames[i] );
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
void RegSound( int priority, const char * name, SoundInitFn * fn )
|
||||
{
|
||||
int j;
|
||||
|
||||
if( priority <= 0 )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for( j = MAX_SOUND_DRIVERS-1; j >= 0; j-- )
|
||||
{
|
||||
//Cruise along, find location to insert
|
||||
if( j > 0 && ( !SoundDrivers[j-1] || SoundDriverPriorities[j-1] < priority ) )
|
||||
{
|
||||
SoundDrivers[j] = SoundDrivers[j-1];
|
||||
SoundDriverNames[j] = SoundDriverNames[j-1];
|
||||
SoundDriverPriorities[j] = SoundDriverPriorities[j-1];
|
||||
}
|
||||
else
|
||||
{
|
||||
SoundDrivers[j] = fn;
|
||||
SoundDriverNames[j] = strdup( name );
|
||||
SoundDriverPriorities[j] = priority;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct SoundDriver * InitSound( const char * driver_name, SoundCBType cb )
|
||||
{
|
||||
int i;
|
||||
struct SoundDriver * ret = 0;
|
||||
|
||||
if( driver_name == 0 || strlen( driver_name ) == 0 )
|
||||
{
|
||||
//Search for a driver.
|
||||
for( i = 0; i < MAX_SOUND_DRIVERS; i++ )
|
||||
{
|
||||
if( SoundDrivers[i] == 0 )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
ret = SoundDrivers[i]( cb );
|
||||
if( ret )
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for( i = 0; i < MAX_SOUND_DRIVERS; i++ )
|
||||
{
|
||||
if( SoundDrivers[i] == 0 )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if( strcmp( SoundDriverNames[i], driver_name ) == 0 )
|
||||
{
|
||||
return SoundDrivers[i]( cb );
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int SoundState( struct SoundDriver * soundobject )
|
||||
{
|
||||
if( soundobject )
|
||||
{
|
||||
return soundobject->SoundStateFn( soundobject );
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void CloseSound( struct SoundDriver * soundobject )
|
||||
{
|
||||
if( soundobject )
|
||||
{
|
||||
soundobject->CloseFn( soundobject );
|
||||
}
|
||||
}
|
||||
|
36
colorchord2/sound.h
Normal file
36
colorchord2/sound.h
Normal file
|
@ -0,0 +1,36 @@
|
|||
#ifndef _SOUND_H
|
||||
#define _SOUND_H
|
||||
|
||||
#define MAX_SOUND_DRIVERS 10
|
||||
|
||||
struct SoundDriver;
|
||||
|
||||
typedef void(*SoundCBType)( float * out, float * in, int samplesr, int * samplesp, struct SoundDriver * sd );
|
||||
typedef void*(SoundInitFn)( SoundCBType cb );
|
||||
|
||||
struct SoundDriver
|
||||
{
|
||||
void (*CloseFn)( void * object );
|
||||
int (*SoundStateFn)( struct SoundDriver * object );
|
||||
SoundCBType callback;
|
||||
int channelsPlay;
|
||||
int spsPlay;
|
||||
int channelsRec;
|
||||
int spsRec;
|
||||
|
||||
//More fields may exist on a per-sound-driver basis
|
||||
};
|
||||
|
||||
//Accepts:
|
||||
// samplerate=44100;channels=2;devplay=default;devrecord=default;record=1;play=1;minavailcount=4096;stopthresh=1024;startthresh=4096;buffer=1024
|
||||
// buffer is in samples
|
||||
//If DriverName = 0 or empty, will try to find best driver.
|
||||
struct SoundDriver * InitSound( const char * driver_name, SoundCBType cb );
|
||||
int SoundState( struct SoundDriver * soundobject ); //returns 0 if okay, negative if faulted.
|
||||
void CloseSound( struct SoundDriver * soundobject );
|
||||
|
||||
//Called by various sound drivers. Notice priority must be greater than 0. Priority of 0 or less will not register.
|
||||
void RegSound( int priority, const char * name, SoundInitFn * fn );
|
||||
|
||||
#endif
|
||||
|
339
colorchord2/sound_alsa.c
Normal file
339
colorchord2/sound_alsa.c
Normal file
|
@ -0,0 +1,339 @@
|
|||
#include "sound.h"
|
||||
#include "os_generic.h"
|
||||
#include "parameters.h"
|
||||
#include <alsa/asoundlib.h>
|
||||
|
||||
#define BUFFERSETS 4
|
||||
|
||||
#define BLOCKING
|
||||
|
||||
struct SoundDriverAlsa
|
||||
{
|
||||
void (*CloseFn)( struct SoundDriverAlsa * object );
|
||||
int (*SoundStateFn)( struct SoundDriverAlsa * object );
|
||||
SoundCBType callback;
|
||||
int channelsPlay;
|
||||
int spsPlay;
|
||||
int channelsRec;
|
||||
int spsRec;
|
||||
|
||||
snd_pcm_uframes_t buffer;
|
||||
og_thread_t thread;
|
||||
snd_pcm_t *playback_handle;
|
||||
snd_pcm_t *record_handle;
|
||||
|
||||
//More fields may exist on a per-sound-driver basis
|
||||
};
|
||||
|
||||
static struct SoundDriverAlsa* InitASound( struct SoundDriverAlsa * r );
|
||||
|
||||
void CloseSoundAlsa( struct SoundDriverAlsa * r );
|
||||
|
||||
int SoundStateAlsa( struct SoundDriverAlsa * soundobject )
|
||||
{
|
||||
return ((soundobject->playback_handle)?1:0) | ((soundobject->record_handle)?2:0);
|
||||
}
|
||||
|
||||
void CloseSoundAlsa( struct SoundDriverAlsa * r )
|
||||
{
|
||||
if( r )
|
||||
{
|
||||
if( r->playback_handle ) snd_pcm_close (r->playback_handle);
|
||||
if( r->record_handle ) snd_pcm_close (r->record_handle);
|
||||
#ifdef BLOCKING
|
||||
OGUSleep(2000);
|
||||
OGCancelThread( r->thread );
|
||||
#endif
|
||||
free( r );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int SetHWParams( snd_pcm_t * handle, int * samplerate, int * channels, snd_pcm_uframes_t * buffer )
|
||||
{
|
||||
int err;
|
||||
snd_pcm_hw_params_t *hw_params;
|
||||
if ((err = snd_pcm_hw_params_malloc (&hw_params)) < 0) {
|
||||
fprintf (stderr, "cannot allocate hardware parameter structure (%s)\n",
|
||||
snd_strerror (err));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((err = snd_pcm_hw_params_any (handle, hw_params)) < 0) {
|
||||
fprintf (stderr, "cannot initialize hardware parameter structure (%s)\n",
|
||||
snd_strerror (err));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((err = snd_pcm_hw_params_set_access (handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
|
||||
fprintf (stderr, "cannot set access type (%s)\n",
|
||||
snd_strerror (err));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((err = snd_pcm_hw_params_set_format (handle, hw_params, SND_PCM_FORMAT_FLOAT )) < 0) {
|
||||
fprintf (stderr, "cannot set sample format (%s)\n",
|
||||
snd_strerror (err));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((err = snd_pcm_hw_params_set_rate_near (handle, hw_params, (unsigned int*)samplerate, 0)) < 0) {
|
||||
fprintf (stderr, "cannot set sample rate (%s)\n",
|
||||
snd_strerror (err));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((err = snd_pcm_hw_params_set_channels (handle, hw_params, *channels)) < 0) {
|
||||
fprintf (stderr, "cannot set channel count (%s)\n",
|
||||
snd_strerror (err));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
int dir = 0;
|
||||
if( (err = snd_pcm_hw_params_set_period_size_near(handle, hw_params, buffer, &dir)) < 0 )
|
||||
{
|
||||
fprintf( stderr, "cannot set period size. (%s)\n",
|
||||
snd_strerror(err) );
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
||||
if ((err = snd_pcm_hw_params (handle, hw_params)) < 0) {
|
||||
fprintf (stderr, "cannot set parameters (%s)\n",
|
||||
snd_strerror (err));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
snd_pcm_hw_params_free (hw_params);
|
||||
return 0;
|
||||
fail:
|
||||
snd_pcm_hw_params_free (hw_params);
|
||||
return -2;
|
||||
}
|
||||
|
||||
|
||||
static int SetSWParams( snd_pcm_t * handle, int isrec )
|
||||
{
|
||||
snd_pcm_sw_params_t *sw_params;
|
||||
int err;
|
||||
//Time for software parameters:
|
||||
|
||||
if( !isrec )
|
||||
{
|
||||
if ((err = snd_pcm_sw_params_malloc (&sw_params)) < 0) {
|
||||
fprintf (stderr, "cannot allocate software parameters structure (%s)\n",
|
||||
snd_strerror (err));
|
||||
goto failhard;
|
||||
}
|
||||
if ((err = snd_pcm_sw_params_current (handle, sw_params)) < 0) {
|
||||
fprintf (stderr, "cannot initialize software parameters structure (%s) (%p)\n",
|
||||
snd_strerror (err), handle);
|
||||
goto fail;
|
||||
}
|
||||
if ((err = snd_pcm_sw_params_set_avail_min (handle, sw_params, GetParameterI( "minavailcount", 2048 ) )) < 0) {
|
||||
fprintf (stderr, "cannot set minimum available count (%s)\n",
|
||||
snd_strerror (err));
|
||||
goto fail;
|
||||
}
|
||||
if ((err = snd_pcm_sw_params_set_stop_threshold(handle, sw_params, GetParameterI( "stopthresh", 512 ))) < 0) {
|
||||
fprintf (stderr, "cannot set minimum available count (%s)\n",
|
||||
snd_strerror (err));
|
||||
goto fail;
|
||||
}
|
||||
if ((err = snd_pcm_sw_params_set_start_threshold(handle, sw_params, GetParameterI( "startthresh", 2048 ))) < 0) {
|
||||
fprintf (stderr, "cannot set minimum available count (%s)\n",
|
||||
snd_strerror (err));
|
||||
goto fail;
|
||||
}
|
||||
if ((err = snd_pcm_sw_params (handle, sw_params)) < 0) {
|
||||
fprintf (stderr, "cannot set software parameters (%s)\n",
|
||||
snd_strerror (err));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
if ((err = snd_pcm_prepare (handle)) < 0) {
|
||||
fprintf (stderr, "cannot prepare audio interface for use (%s)\n",
|
||||
snd_strerror (err));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
fail:
|
||||
if( !isrec )
|
||||
{
|
||||
snd_pcm_sw_params_free (sw_params);
|
||||
}
|
||||
failhard:
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef BLOCKING
|
||||
static void * SoundThread( void * v )
|
||||
{
|
||||
int i;
|
||||
struct SoundDriverAlsa * a = (struct SoundDriverAlsa*)v;
|
||||
float * bufr[BUFFERSETS];
|
||||
float * bufp[BUFFERSETS];
|
||||
|
||||
for(i = 0; i < BUFFERSETS; i++ )
|
||||
{
|
||||
bufr[i] = malloc( a->buffer * sizeof(float) * a->channelsRec );
|
||||
bufp[i] = malloc( a->buffer * sizeof(float) * a->channelsPlay );
|
||||
}
|
||||
|
||||
while( a->record_handle || a->playback_handle )
|
||||
{
|
||||
int err;
|
||||
|
||||
i = (i+1)%BUFFERSETS;
|
||||
|
||||
if( a->record_handle )
|
||||
{
|
||||
if( (err = snd_pcm_readi (a->record_handle, bufr[i], a->buffer)) != a->buffer)
|
||||
{
|
||||
fprintf (stderr, "read from audio interface failed (%s)\n",
|
||||
snd_strerror (err));
|
||||
if( a->record_handle ) snd_pcm_close (a->record_handle);
|
||||
a->record_handle = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
//has_rec = 1;
|
||||
}
|
||||
}
|
||||
|
||||
//Do our callback.
|
||||
int playbacksamples = 0;
|
||||
a->callback( bufp[i], bufr[i], a->buffer, &playbacksamples, (struct SoundDriver*)a );
|
||||
//playbacksamples *= sizeof(float) * a->channelsPlay;
|
||||
|
||||
if( a->playback_handle )
|
||||
{
|
||||
if ((err = snd_pcm_writei (a->playback_handle, bufp[i], playbacksamples)) != playbacksamples)
|
||||
{
|
||||
fprintf (stderr, "write to audio interface failed (%s)\n",
|
||||
snd_strerror (err));
|
||||
if( a->playback_handle ) snd_pcm_close (a->playback_handle);
|
||||
a->playback_handle = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Fault happened, re-initialize?
|
||||
InitASound( a );
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
|
||||
//Handle callback
|
||||
|
||||
static struct SoundDriverAlsa * reccb;
|
||||
static int record_callback (snd_pcm_sframes_t nframes)
|
||||
{
|
||||
int err;
|
||||
|
||||
// printf ("playback callback called with %u frames\n", nframes);
|
||||
|
||||
/* ... fill buf with data ... */
|
||||
|
||||
if ((err = snd_pcm_writei (playback_handle, buf, nframes)) < 0) {
|
||||
fprintf (stderr, "write failed (%s)\n", snd_strerror (err));
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static struct SoundDriverAlsa * InitASound( struct SoundDriverAlsa * r )
|
||||
{
|
||||
int err;
|
||||
if( GetParameterI( "play", 0 ) )
|
||||
{
|
||||
if ((err = snd_pcm_open (&r->playback_handle, GetParameterS( "devplay", "default" ), SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
|
||||
fprintf (stderr, "cannot open output audio device (%s)\n",
|
||||
snd_strerror (err));
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
if( GetParameterI( "record", 1 ) )
|
||||
{
|
||||
if ((err = snd_pcm_open (&r->record_handle, GetParameterS( "devrecord", "default" ), SND_PCM_STREAM_CAPTURE, 0)) < 0) {
|
||||
fprintf (stderr, "cannot open input audio device (%s)\n",
|
||||
snd_strerror (err));
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
if( r->playback_handle )
|
||||
{
|
||||
if( SetHWParams( r->playback_handle, &r->spsPlay, &r->channelsPlay, &r->buffer ) < 0 )
|
||||
goto fail;
|
||||
if( SetSWParams( r->playback_handle, 0 ) < 0 )
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if( r->record_handle )
|
||||
{
|
||||
if( SetHWParams( r->record_handle, &r->spsRec, &r->channelsRec, &r->buffer ) < 0 )
|
||||
goto fail;
|
||||
if( SetSWParams( r->record_handle, 1 ) < 0 )
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if( r->playback_handle && r->record_handle )
|
||||
{
|
||||
snd_pcm_link ( r->playback_handle, r->record_handle );
|
||||
}
|
||||
|
||||
#ifdef BLOCKING
|
||||
r->thread = OGCreateThread( SoundThread, r );
|
||||
#else
|
||||
reccb = r;
|
||||
//handle interrupt
|
||||
#endif
|
||||
return r;
|
||||
|
||||
fail:
|
||||
if( r )
|
||||
{
|
||||
if( r->playback_handle ) snd_pcm_close (r->playback_handle);
|
||||
if( r->record_handle ) snd_pcm_close (r->record_handle);
|
||||
free( r );
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void * InitSoundAlsa( SoundCBType cb )
|
||||
{
|
||||
struct SoundDriverAlsa * r = malloc( sizeof( struct SoundDriverAlsa ) );
|
||||
|
||||
r->CloseFn = CloseSoundAlsa;
|
||||
r->SoundStateFn = SoundStateAlsa;
|
||||
r->callback = cb;
|
||||
|
||||
r->spsPlay = GetParameterI( "samplerate", 44100 );
|
||||
r->channelsPlay = GetParameterI( "channels", 2 );
|
||||
r->spsRec = r->spsPlay;
|
||||
r->channelsRec = r->channelsPlay;
|
||||
|
||||
r->playback_handle = 0;
|
||||
r->record_handle = 0;
|
||||
r->buffer = GetParameterI( "buffer", 1024 );
|
||||
|
||||
|
||||
|
||||
return InitASound(r);
|
||||
}
|
||||
|
||||
EXECUTE_AT_BOOT( AlsaSoundReg, RegSound( 10, "ALSA", InitSoundAlsa ) );
|
||||
|
44
colorchord2/sound_null.c
Normal file
44
colorchord2/sound_null.c
Normal file
|
@ -0,0 +1,44 @@
|
|||
#include "sound.h"
|
||||
#include "os_generic.h"
|
||||
#include <stdlib.h>
|
||||
#include "parameters.h"
|
||||
|
||||
struct SoundDriverNull
|
||||
{
|
||||
void (*CloseFn)( struct SoundDriverNull * object );
|
||||
int (*SoundStateFn)( struct SoundDriverNull * object );
|
||||
SoundCBType soundcb;
|
||||
int channelsPlay;
|
||||
int spsPlay;
|
||||
int channelsRec;
|
||||
int spsRec;
|
||||
};
|
||||
|
||||
void CloseSoundNull( struct SoundDriverNull * object )
|
||||
{
|
||||
free( object );
|
||||
}
|
||||
|
||||
int SoundStateNull( struct SoundDriverNull * object )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void * InitSoundNull( SoundCBType cb )
|
||||
{
|
||||
struct SoundDriverNull * r = malloc( sizeof( struct SoundDriverNull ) );
|
||||
r->CloseFn = CloseSoundNull;
|
||||
r->SoundStateFn = SoundStateNull;
|
||||
r->soundcb = cb;
|
||||
r->spsPlay = GetParameterI( "samplerate", 44100 );
|
||||
r->channelsPlay = GetParameterI( "channels", 2 );
|
||||
r->spsRec = r->spsPlay;
|
||||
r->channelsRec = r->channelsRec;
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
|
||||
EXECUTE_AT_BOOT( NullSoundReg, RegSound( 1, "NULL", InitSoundNull ) );
|
||||
|
377
colorchord2/sound_pulse.c
Normal file
377
colorchord2/sound_pulse.c
Normal 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
179
colorchord2/sound_win.c
Normal 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
24
colorchord2/usb.conf
Normal 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
58
colorchord2/util.c
Normal file
|
@ -0,0 +1,58 @@
|
|||
#include "util.h"
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
//Take the absolute distance between two points on a torus.
|
||||
float fabsloop( float a, float b, float modl )
|
||||
{
|
||||
float fa = fabsf( a - b );
|
||||
|
||||
fa = fmodf( fa, modl );
|
||||
|
||||
if( fa > modl/2.0 )
|
||||
{
|
||||
fa = modl - fa;
|
||||
}
|
||||
|
||||
return fa;
|
||||
}
|
||||
|
||||
//Get the weighted - average of two points on a torus.
|
||||
float avgloop( float pta, float ampa, float ptb, float ampb, float modl )
|
||||
{
|
||||
float amptot = ampa + ampb;
|
||||
|
||||
//Determine if it should go linearly, or around the edge.
|
||||
if( fabsf( pta - ptb ) > modl/2.0 )
|
||||
{
|
||||
//Loop around the outside.
|
||||
if( pta < ptb )
|
||||
{
|
||||
pta += modl;
|
||||
}
|
||||
else
|
||||
{
|
||||
ptb += modl;
|
||||
}
|
||||
}
|
||||
|
||||
float modmid = (pta * ampa + ptb * ampb)/amptot;
|
||||
|
||||
return fmodf( modmid, modl );
|
||||
}
|
||||
|
||||
int atoi_del( char * data )
|
||||
{
|
||||
int ret = atoi( data );
|
||||
free( data );
|
||||
return ret;
|
||||
}
|
||||
|
||||
float atof_del( char * data )
|
||||
{
|
||||
float ret = atof( data );
|
||||
free( data );
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
10
colorchord2/util.h
Normal file
10
colorchord2/util.h
Normal 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
|
Loading…
Add table
Add a link
Reference in a new issue