Compare commits
No commits in common. "isochronous" and "master" have entirely different histories.
isochronou
...
master
|
@ -1,44 +0,0 @@
|
|||
# Basic style rules by Charles Lohr
|
||||
BasedOnStyle: Microsoft
|
||||
AccessModifierOffset: -4
|
||||
AlwaysBreakTemplateDeclarations: 'Yes'
|
||||
AlignAfterOpenBracket: DontAlign
|
||||
AlignTrailingComments: 'true'
|
||||
AllowAllParametersOfDeclarationOnNextLine: 'false'
|
||||
BreakBeforeBinaryOperators: None
|
||||
BreakBeforeBraces: Allman
|
||||
BreakConstructorInitializers: BeforeComma
|
||||
ColumnLimit: '120'
|
||||
ExperimentalAutoDetectBinPacking: 'false'
|
||||
NamespaceIndentation: None
|
||||
MaxEmptyLinesToKeep: 2
|
||||
PointerAlignment: Right
|
||||
SpaceInEmptyParentheses: 'false'
|
||||
SpacesInAngles: 'true'
|
||||
# this is just atrocious
|
||||
#SpacesInCStyleCastParentheses: 'true'
|
||||
SpacesInContainerLiterals: 'true'
|
||||
SpacesInParentheses: 'true'
|
||||
SpacesInSquareBrackets: 'true'
|
||||
TabWidth: '4'
|
||||
UseTab: AlignWithSpaces
|
||||
FixNamespaceComments: 'true'
|
||||
IndentCaseLabels: 'true'
|
||||
|
||||
# Additions by Sam Ellicott
|
||||
ColumnLimit: 100
|
||||
# From general dislike from Charles and Cai, I am turning these off
|
||||
#AlignConsecutiveAssignments: true
|
||||
#AlignConsecutiveMacros: true
|
||||
#AlignTrailingComments: true
|
||||
#AlignOperands: AlignAfterOperator
|
||||
# Turn off alignment
|
||||
AlignConsecutiveAssignments: false
|
||||
AlignConsecutiveMacros: false
|
||||
AlignTrailingComments: false
|
||||
AlignEscapedNewlines: Left
|
||||
AlignOperands: AlignAfterOperator
|
||||
AllowShortBlocksOnASingleLine: Always
|
||||
AllowShortIfStatementsOnASingleLine: WithoutElse
|
||||
AllowShortCaseLabelsOnASingleLine: true
|
||||
AllowShortLoopsOnASingleLine: true
|
7
.gitignore
vendored
7
.gitignore
vendored
|
@ -1,7 +1,2 @@
|
|||
colorchord2/windows/colorchord.def
|
||||
colorchord2/colorchord.def
|
||||
*.o
|
||||
**/*.exp
|
||||
**/*.ilk
|
||||
**/*.pdb
|
||||
colorchord2/colorchord.lib
|
||||
colorchord2/colorchord.def
|
9
.gitmodules
vendored
9
.gitmodules
vendored
|
@ -1,12 +1,3 @@
|
|||
[submodule "embedded8266/esp82xx"]
|
||||
path = embedded8266/esp82xx
|
||||
url = https://github.com/cnlohr/esp82xx.git
|
||||
[submodule "colorchord2/rawdraw"]
|
||||
path = colorchord2/rawdraw
|
||||
url = https://github.com/cntools/rawdraw
|
||||
[submodule "colorchord2/android/rawdrawandroid"]
|
||||
path = colorchord2/android/rawdrawandroid
|
||||
url = https://github.com/cnlohr/rawdrawandroid
|
||||
[submodule "colorchord2/cnfa"]
|
||||
path = colorchord2/cnfa
|
||||
url = https://github.com/cntools/cnfa.git
|
||||
|
|
12
LICENSE
12
LICENSE
|
@ -1,14 +1,4 @@
|
|||
Copyright (c) 2012-2022, Charles Lohr (CNLohr), et. al.
|
||||
|
||||
This license covers the core of ColorChord, but not the ESP8266 or STM32
|
||||
embedded builds, for them, they have a separate license.
|
||||
|
||||
All major contributors have agreed to allowing ColorChord to be distributed
|
||||
in either CC0, MIT-x11 or NewBSD license terms as of September, 2022.
|
||||
Depending on the user's preferences and needs. You may feel free to copy
|
||||
and use ColorChord in your project under ther terms listed below (NewBSD
|
||||
license) or any of the afore-mentioned licenses.
|
||||
|
||||
Copyright (c) 2015, Charles Lohr (CNLohr)
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
|
|
73
README.md
73
README.md
|
@ -1,25 +1,6 @@
|
|||
ColorChord
|
||||
==========
|
||||
|
||||
Table of Contents
|
||||
-----------------
|
||||
|
||||
- [ColorChord](#colorchord)
|
||||
- [Table of Contents](#table-of-contents)
|
||||
- [What is ColorChord?](#what-is-colorchord)
|
||||
- [Background](#background)
|
||||
- [Current State of Affairs](#current-state-of-affairs)
|
||||
- [ColorChord: Embedded](#colorchord-embedded)
|
||||
- [Build Instructions](#build-instructions)
|
||||
- [Building ColorChord Embedded](#building-colorchord-embedded)
|
||||
- [Building with Linux](#building-with-linux)
|
||||
- [Building with Windows](#building-with-windows)
|
||||
- [clang](#clang)
|
||||
- [TCC](#tcc)
|
||||
- [MSYS2](#msys2)
|
||||
- [Using](#using)
|
||||
- [Additional Videos](#additional-videos)
|
||||
|
||||
What is ColorChord?
|
||||
-------------------
|
||||
|
||||
|
@ -61,36 +42,14 @@ ColorChord: Embedded
|
|||
|
||||
There is work on an embedded version of ColorChord, which avoids floating point operations anywhere in the output pipeline. Though I have made efforts to port it to AVRs, it doesn't seem feasable to operate on AVRs without some shifty tricks which I'd like to avoid, so I have retargeted my efforts to 32-bit systems, such as the STM32F303, STM32F407, and (somehow) the ESP8266. ColorChord Embedded uses a different codebase, located in the [embeddedcommon](/embeddedcommon) and distributed among the various embedded* folders.
|
||||
|
||||
Build Instructions
|
||||
==================
|
||||
|
||||
Building ColorChord Embedded
|
||||
----------------------------
|
||||
|
||||
The embedded version of Colorchord has a different build system than the desktop versions of Colorchord. See the build instructions for each of the supported embedded architectures in their respective folders
|
||||
|
||||
- [esp8266](embedded8266/README.md#build-instructions)
|
||||
- [stm32f303](embeddedstm32f303/README.md)
|
||||
- [stm32f407](embeddedstm32f407/) - TODO Add readme
|
||||
|
||||
Building with Linux
|
||||
-----------------
|
||||
From the linux terminal use `apt-get` to install the following packages for Debian/Ubuntu/Mint:
|
||||
Use `apt-get` to install the following packages for Debian/Ubuntu/Mint:
|
||||
```
|
||||
apt-get install build-essential libpulse-dev libasound2-dev libx11-dev libxext-dev libxinerama-dev libusb-1.0-0-dev libudev-dev
|
||||
apt-get install libpulse-dev libasound2-dev libx11-dev libxext-dev libxinerama-dev libusb-1.0-0-dev libudev-dev
|
||||
```
|
||||
|
||||
To get colorchord, type:
|
||||
|
||||
```
|
||||
git clone --recurse-submodules https://github.com/cnlohr/colorchord
|
||||
```
|
||||
This will download the colorchord source code into the colorchord directory
|
||||
|
||||
You really want to be using colorchord2, so we need to move to it's build directory to compile the source code.
|
||||
```
|
||||
cd colorchord/colorchord2
|
||||
```
|
||||
To make colorchord, type:
|
||||
|
||||
```
|
||||
|
@ -99,33 +58,6 @@ make
|
|||
Building with Windows
|
||||
-------------------
|
||||
|
||||
There are 3 options available for building on Windows, clang, or TCC, MSYS2.
|
||||
### clang
|
||||
Start by [downloading](https://clang.llvm.org/) the clang compiler, and installing it.
|
||||
You can also install using `winget` using the following command in PowerShell
|
||||
```
|
||||
winget install -e --id LLVM.LLVM
|
||||
```
|
||||
|
||||
If you have the Windows SDK installed, you should not need to do any additional work.
|
||||
If you do not, you'll want to either [install it](https://developer.microsoft.com/en-US/windows/downloads/windows-11-sdk/) to get the official headers. Realistically, it's easyist to get the headers by installing Visual Studio Communitiy and selecting the C/C++ Desktop Application devlopment package and installing it from there [Visual Studio](https://visualstudio.microsoft.com/).
|
||||
|
||||
Once the Windows headers are installed run the clang batch script, and it should output to `colorchord2/colorchord.exe`.
|
||||
```
|
||||
./compile-clang.bat
|
||||
```
|
||||
|
||||
### TCC
|
||||
Start by [downloading TCC](http://savannah.nongnu.org/projects/tinycc/), and extracting it to `C:\tcc`.
|
||||
You can put it anywhere you like, but the compile script defaults to this location. If you install
|
||||
to the default location, you can skip the next step.
|
||||
|
||||
Edit the batch script at `colorchord2/windows/compile.bat`:
|
||||
- Edit line 17 (`CC`) to be the location where you put TCC. If there are spaces in the path, wrap the entire path in quotes.
|
||||
|
||||
Note that TCC is not able to use the Windows SDK, and as such using the unofficial headers is required, and automatically enabled when compiling with TCC. If you encounter issues, try the clang method above instead. TCC does not support open-gl rendering and is limited to software rendering.
|
||||
|
||||
### MSYS2
|
||||
With either 64bit or 32bit [MSYS2](https://msys2.github.io/) installed, run the _MSYS2 MSYS_ launcher and use `pacman` to set up a MinGW32 toolchain, if you don't have one already:
|
||||
```
|
||||
pacman -S mingw-w64-i686-toolchain
|
||||
|
@ -150,7 +82,6 @@ To run colorchord, use the following syntax:
|
|||
|
||||
If you edit default.conf while the program is running and resave it, it will use the settings in the newly saved file.
|
||||
|
||||
Note that the colorchord executable file is located in the colorchord2 directory in the colorchord repository.
|
||||
|
||||
Additional Videos
|
||||
-----------------
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
PROJS:=basic_oldstyle_test
|
||||
|
||||
all : $(PROJS)
|
||||
|
||||
CFLAGS:=-I../../colorchord2/rawdraw -g
|
||||
LDFLAGS:=-lGL -lm -lpthread -lX11
|
||||
|
||||
$(PROJS): %: %.c
|
||||
gcc -o $@ $^ $(CFLAGS) $(LDFLAGS)
|
||||
|
||||
clean :
|
||||
rm -rf $(PROJS)
|
||||
|
||||
|
|
@ -1,452 +0,0 @@
|
|||
/*
|
||||
An experiment in very, very low-spec ColorChord. This technique foregoes
|
||||
multiplies.
|
||||
|
||||
Approach 1:
|
||||
|
||||
This approach uses a table, like several colorchord algorithms to only
|
||||
process one octave each cycle. It then uses a table to decide how often
|
||||
to process each bin. It performs that bin at 4x the bin's sampling
|
||||
frequency, in quadrature. So that it performs the +real, +imag, -real,
|
||||
-imag operations over each cycle.
|
||||
|
||||
You can observe an overtone at the current bin - 1.5 octaves! This is
|
||||
expected, since, it's the inverse of what the DFT of a square wave would
|
||||
be.
|
||||
|
||||
That is a minor drawback, but the **major** drawback is that any DC
|
||||
offset, OR lower frequencies present when computing higher frequencies will
|
||||
induce a significant ground flutter, and make results really inaccurate.
|
||||
|
||||
This will need to be addressed before the algorithm is ready for prime-time.
|
||||
|
||||
NOTE: To explore:
|
||||
1) Consider SAMPLE_Q to possibly use all 4 cycles - though this will
|
||||
add latency, it will be more "accurate" --> YES! This helps!
|
||||
2) Use the cursor to move left and right to sweep tone and up and down
|
||||
to change DC-bias. This exhibits this algo's shortcoming.
|
||||
TODO: Can we somehow zero out the DC offset?
|
||||
|
||||
|
||||
DISCOVERY: This approach (octave-step in octave) is very sensitive FSPS
|
||||
|
||||
Theories:
|
||||
* If we force all 4 quadrature value to be the same number of
|
||||
integration cycles, does that solve it? --> NO! IT GETS MESSY (see Approach 1B)
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
|
||||
#define CNFG_IMPLEMENTATION
|
||||
#define CNFGOGL
|
||||
#include "rawdraw_sf.h"
|
||||
|
||||
#include "os_generic.h"
|
||||
|
||||
|
||||
uint32_t EHSVtoHEX( uint8_t hue, uint8_t sat, uint8_t val );
|
||||
int binmode = 0;
|
||||
int additionaltone = 0;
|
||||
int lastx = 200;
|
||||
int lasty = 1000;
|
||||
|
||||
// Keep notimes as small as possible
|
||||
static int timul( int adder, int notimes )
|
||||
{
|
||||
int sum = 0;
|
||||
while( notimes )
|
||||
{
|
||||
if ( notimes & 1) sum += adder;
|
||||
adder <<= 1;
|
||||
notimes >>= 1;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
#define FSPS 22100 // Careful, this matters! It is selected to avoid periodic peaks encountered with DC offsets.
|
||||
#define OCTAVES 6
|
||||
#define BPERO 32
|
||||
#define BASE_FREQ 22.5
|
||||
#define QUADRATURE_STEP_DENOMINATOR 16384
|
||||
|
||||
// This ultimately determines smoothness + q.
|
||||
#define COMPLEX_IIR 3
|
||||
|
||||
#define TEST_SAMPLES 1024
|
||||
|
||||
|
||||
int16_t samples[TEST_SAMPLES];
|
||||
int i;
|
||||
|
||||
|
||||
CNFGSetup( "Example App", 1024, 768 );
|
||||
|
||||
// Precomputed Tables
|
||||
int8_t whichoctave[2<<OCTAVES];
|
||||
for( i = 0; i < (2<<OCTAVES); i++ )
|
||||
{
|
||||
int j;
|
||||
for( j = 0; j < OCTAVES; j++ )
|
||||
{
|
||||
if( i & (1<<j) ) break;
|
||||
}
|
||||
if( j == OCTAVES )
|
||||
whichoctave[i] = -1;
|
||||
else
|
||||
whichoctave[i] = OCTAVES - j - 1;
|
||||
}
|
||||
// Make a running counter to count up by this amount every cycle.
|
||||
// If the new number > 2048, then perform a quadrature step.
|
||||
int32_t flipdistance[BPERO];
|
||||
for( i = 0; i < BPERO; i++ )
|
||||
{
|
||||
double freq = pow( 2, (double)i / (double)BPERO ) * (BASE_FREQ/2.0);
|
||||
double pfreq = pow( 2, OCTAVES ) * freq;
|
||||
double spacing = (FSPS / 2) / pfreq / 4;
|
||||
|
||||
flipdistance[i] = QUADRATURE_STEP_DENOMINATOR * spacing;
|
||||
// Spacing = "quadrature every X samples"
|
||||
//printf( "%f %d\n", spacing, flipdistance[i] );
|
||||
}
|
||||
|
||||
|
||||
struct bindata
|
||||
{
|
||||
int32_t quadrature_timing_last;
|
||||
uint32_t last_accumulated_value[2];
|
||||
int32_t real_imaginary_running[2];
|
||||
uint32_t magsum;
|
||||
uint8_t quadrature_state;
|
||||
uint8_t reserved1, reserved2, reserved3;
|
||||
} bins[BPERO*OCTAVES] = { 0 };
|
||||
// This is for timing. Not accumulated data.
|
||||
|
||||
uint32_t octave_timing[OCTAVES] = { 0 };
|
||||
|
||||
uint32_t sample_accumulator = 0;
|
||||
|
||||
int frameno = 0;
|
||||
double dLT = OGGetAbsoluteTime();
|
||||
int samplenoIn = 0;
|
||||
int sampleno = 0;
|
||||
double ToneOmega = 0;
|
||||
double ToneOmega2 = 0;
|
||||
int ops;
|
||||
while( CNFGHandleInput() )
|
||||
{
|
||||
short w, h;
|
||||
CNFGGetDimensions( &w, &h );
|
||||
CNFGClearFrame();
|
||||
|
||||
frameno++;
|
||||
float freq =
|
||||
//pow( 2, (frameno%600)/100.0 ) * 25;
|
||||
pow( 2, (lastx)/100.0 ) * 22.5;
|
||||
//101;
|
||||
float freq2 =
|
||||
//pow( 2, (frameno%600)/100.0 ) * 25;
|
||||
pow( 2, (500)/100.0 ) * 22.5;
|
||||
//101;
|
||||
|
||||
for( i = 0; i < TEST_SAMPLES; i++ )
|
||||
{
|
||||
samples[i] = (lasty/5 + sin( ToneOmega ) * 127 + (additionaltone?(sin(ToneOmega2)*128):0))/2;// + (rand()%128)-64;
|
||||
ToneOmega += 1 / (double)FSPS * (double)freq * 3.14159 * 2.0;
|
||||
ToneOmega2 += 1 / (double)FSPS * (double)freq2 * 3.14159 * 2.0;
|
||||
}
|
||||
char cts[1024];
|
||||
sprintf( cts, "%f %d %f binmode: %d", freq, sampleno, ops/(double)sampleno, binmode );
|
||||
CNFGColor( 0xffffffff );
|
||||
CNFGPenX = 2;
|
||||
CNFGPenY = 2;
|
||||
CNFGDrawText( cts, 2 );
|
||||
|
||||
while( OGGetAbsoluteTime() < dLT + TEST_SAMPLES / (double)FSPS );
|
||||
dLT += TEST_SAMPLES / (double)FSPS;
|
||||
|
||||
|
||||
for( i = 0; i < TEST_SAMPLES; i++ )
|
||||
{
|
||||
sample_accumulator += samples[i];
|
||||
int octave = whichoctave[(sampleno)&((2<<OCTAVES)-1)];
|
||||
sampleno++;
|
||||
if( octave < 0 )
|
||||
{
|
||||
// A "free cycle" this happens every 1/(2^octaves)
|
||||
continue;
|
||||
}
|
||||
|
||||
#define WATCHBIN 1
|
||||
int b;
|
||||
int binno = octave * BPERO;
|
||||
int ocative_time = octave_timing[octave] += QUADRATURE_STEP_DENOMINATOR;
|
||||
for( b = 0; b < BPERO; b++, binno++ )
|
||||
{
|
||||
ops+=5;
|
||||
struct bindata * thisbin = &bins[binno];
|
||||
if( ocative_time - thisbin->quadrature_timing_last > 0 )
|
||||
{
|
||||
ops+=20;
|
||||
thisbin->quadrature_timing_last += flipdistance[b];
|
||||
|
||||
// This code will get appropriately executed every quadrature update.
|
||||
|
||||
int qstate = thisbin->quadrature_state = ( thisbin->quadrature_state + 1 ) % 4;
|
||||
|
||||
int last_q_bin = ( qstate & 1 );
|
||||
|
||||
int delta;
|
||||
if( binmode )
|
||||
{
|
||||
delta = sample_accumulator - thisbin->last_accumulated_value[0];
|
||||
thisbin->last_accumulated_value[0] = sample_accumulator;
|
||||
delta *= 1.5;
|
||||
}
|
||||
else
|
||||
{
|
||||
delta = sample_accumulator - thisbin->last_accumulated_value[last_q_bin];
|
||||
thisbin->last_accumulated_value[last_q_bin] = sample_accumulator;
|
||||
}
|
||||
|
||||
|
||||
// Qstate =
|
||||
// (0) = +Cos, (1) = +Sin, (2) = -Cos, (3) = -Sin
|
||||
if( qstate & 2 ) delta *= -1;
|
||||
|
||||
// Update real and imaginary components with delta.
|
||||
int running = thisbin->real_imaginary_running[last_q_bin];
|
||||
running = running + delta;
|
||||
thisbin->real_imaginary_running[last_q_bin] = running;
|
||||
|
||||
// Only perform on full quadrature completions.
|
||||
if( qstate == 0 )
|
||||
{
|
||||
ops+=20;
|
||||
int newmagR = thisbin->real_imaginary_running[0];
|
||||
int newmagI = thisbin->real_imaginary_running[1];
|
||||
|
||||
thisbin->real_imaginary_running[0] = newmagR - (newmagR>>COMPLEX_IIR);
|
||||
thisbin->real_imaginary_running[1] = newmagI - (newmagI>>COMPLEX_IIR);
|
||||
|
||||
// Super-cheap, non-multiply, approximate complex vector magnitude calculation.
|
||||
newmagR = (newmagR<0)?-newmagR:newmagR;
|
||||
newmagI = (newmagI<0)?-newmagI:newmagI;
|
||||
int newmag =
|
||||
//sqrt(newmagR*newmagR + newmagI*newmagI );
|
||||
newmagR > newmagI ? newmagR + (newmagI>>1) : newmagI + (newmagR>>1);
|
||||
thisbin->magsum = newmag >> (OCTAVES-octave);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int folded_bins[BPERO];
|
||||
|
||||
// Taper and fold (happens when we want to update lights)
|
||||
for( i = 0; i < BPERO; i++ )
|
||||
{
|
||||
int b = i;
|
||||
int running;
|
||||
running = bins[b].magsum * i / BPERO;
|
||||
b+=BPERO;
|
||||
int j;
|
||||
for( j = 1; j < OCTAVES-1; j++ )
|
||||
{
|
||||
running += bins[b].magsum;
|
||||
b+=BPERO;
|
||||
}
|
||||
running += bins[b].magsum * ( BPERO - i ) / BPERO;
|
||||
folded_bins[i] = running * pow( 2, (i%BPERO) / (double)BPERO ); //XXX TODO: Make this fixed.
|
||||
}
|
||||
|
||||
int lx, ly;
|
||||
#if 0
|
||||
// Boxcar filter
|
||||
int fuzzed_bins[BPERO];
|
||||
int fuzzed_bins2[BPERO];
|
||||
|
||||
// Taper and fold (happens when we want to update lights)
|
||||
for( i = 0; i < BPERO; i++ )
|
||||
{
|
||||
fuzzed_bins[i] = (folded_bins[i] + (folded_bins[(i+1)%BPERO]>>0) + (folded_bins[(i+BPERO-1)%BPERO]>>0))>>1;
|
||||
}
|
||||
|
||||
// Taper and fold (happens when we want to update lights)
|
||||
for( i = 0; i < BPERO; i++ )
|
||||
{
|
||||
fuzzed_bins2[i] = (fuzzed_bins[i] + (folded_bins[(i+1)%BPERO]>>0) + (fuzzed_bins[(i+BPERO-1)%BPERO]>>0))>>1;
|
||||
}
|
||||
|
||||
// Filter agian.
|
||||
for( i = 0; i < BPERO; i++ )
|
||||
{
|
||||
fuzzed_bins[i] = (fuzzed_bins2[i] + (fuzzed_bins2[(i+1)%BPERO]>>0) + (fuzzed_bins2[(i+BPERO-1)%BPERO]>>0))>>1;
|
||||
}
|
||||
|
||||
// Taper and fold (happens when we want to update lights)
|
||||
for( i = 0; i < BPERO; i++ )
|
||||
{
|
||||
fuzzed_bins2[i] = (fuzzed_bins[i] + (folded_bins[(i+1)%BPERO]>>0) + (fuzzed_bins[(i+BPERO-1)%BPERO]>>0))>>1;
|
||||
|
||||
fuzzed_bins2[i] = fuzzed_bins2[i]>>1;
|
||||
}
|
||||
|
||||
#else
|
||||
int fuzzed_bins2[BPERO];
|
||||
int fziir = folded_bins[i];
|
||||
// Forward IIR
|
||||
int j;
|
||||
|
||||
#define FUZZ_IIR 2
|
||||
for( j = 0; j < 4; j++ )
|
||||
for( i = 0; i < BPERO; i++ )
|
||||
{
|
||||
// Initiate IIR
|
||||
fziir = fziir - (fziir>>FUZZ_IIR) + folded_bins[i];
|
||||
}
|
||||
for( i = 0; i < BPERO; i++ )
|
||||
{
|
||||
fziir = fziir - (fziir>>FUZZ_IIR) + folded_bins[i];;
|
||||
fuzzed_bins2[i] = fziir;
|
||||
}
|
||||
|
||||
// reverse IIR.
|
||||
for( j = 0; j < 4; j++ )
|
||||
for( i = BPERO-1; i >= 0; i-- )
|
||||
{
|
||||
// Initiate IIR
|
||||
fziir = fziir - (fziir>>FUZZ_IIR) + fuzzed_bins2[i];
|
||||
}
|
||||
for( i = BPERO-1; i >= 0; i-- )
|
||||
{
|
||||
fziir = fziir - (fziir>>FUZZ_IIR) + fuzzed_bins2[i];
|
||||
fuzzed_bins2[i] = fziir;
|
||||
}
|
||||
|
||||
int minfuzz = fziir;
|
||||
for( i = 0; i < BPERO; i++ )
|
||||
{
|
||||
if( minfuzz > fuzzed_bins2[i] ) minfuzz = fuzzed_bins2[i];
|
||||
}
|
||||
for( i = 0; i < BPERO; i++ )
|
||||
{
|
||||
fuzzed_bins2[i] = ( fuzzed_bins2[i] - minfuzz ) >> FUZZ_IIR;
|
||||
}
|
||||
#endif
|
||||
for( i = 0; i < BPERO*OCTAVES; i++ )
|
||||
{
|
||||
CNFGColor( (EHSVtoHEX( (i * 256 / BPERO)&0xff, 255, 255 ) << 8) | 0xff );
|
||||
float mag = (float)bins[i].magsum * pow( 2, (i%BPERO) / (double)BPERO );
|
||||
int y = 768 - mag/20;
|
||||
if( i ) CNFGTackSegment( i*4, y, lx*4, ly );
|
||||
lx = i; ly= y;
|
||||
}
|
||||
|
||||
|
||||
for( i = 0; i < BPERO*2; i++ )
|
||||
{
|
||||
CNFGColor( (EHSVtoHEX( (i * 256 / BPERO)&0xff, 255, 255 ) << 8) | 0xff );
|
||||
float mag = (float)folded_bins[i%BPERO];
|
||||
int y = 600 - mag/100;
|
||||
if( i ) CNFGTackSegment( i*8, y, lx*8, ly );
|
||||
lx = i; ly= y;
|
||||
}
|
||||
|
||||
for( i = 0; i < BPERO*2; i++ )
|
||||
{
|
||||
CNFGColor( (EHSVtoHEX( (i * 256 / BPERO)&0xff, 255, 255 ) << 8) | 0xff );
|
||||
float mag = (float)fuzzed_bins2[i%BPERO];
|
||||
int y = 500 - mag/100;
|
||||
if( i ) CNFGTackSegment( i*8, y, lx*8, ly );
|
||||
lx = i; ly= y;
|
||||
}
|
||||
|
||||
|
||||
CNFGSwapBuffers();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void HandleKey( int keycode, int bDown ) { if( bDown ) { if( keycode == 'a' ) binmode =!binmode; if( keycode == 'b' ) additionaltone = ! additionaltone; } }
|
||||
void HandleButton( int x, int y, int button, int bDown ) { }
|
||||
void HandleMotion( int x, int y, int mask ) { lastx = x; lasty = y; }
|
||||
void HandleDestroy() { }
|
||||
|
||||
|
||||
uint32_t EHSVtoHEX( uint8_t hue, uint8_t sat, uint8_t val )
|
||||
{
|
||||
#define SIXTH1 43
|
||||
#define SIXTH2 85
|
||||
#define SIXTH3 128
|
||||
#define SIXTH4 171
|
||||
#define SIXTH5 213
|
||||
|
||||
uint16_t or = 0, og = 0, ob = 0;
|
||||
|
||||
hue -= SIXTH1; //Off by 60 degrees.
|
||||
|
||||
//TODO: There are colors that overlap here, consider
|
||||
//tweaking this to make the best use of the colorspace.
|
||||
|
||||
if( hue < SIXTH1 ) //Ok: Yellow->Red.
|
||||
{
|
||||
or = 255;
|
||||
og = 255 - ((uint16_t)hue * 255) / (SIXTH1);
|
||||
}
|
||||
else if( hue < SIXTH2 ) //Ok: Red->Purple
|
||||
{
|
||||
or = 255;
|
||||
ob = (uint16_t)hue*255 / SIXTH1 - 255;
|
||||
}
|
||||
else if( hue < SIXTH3 ) //Ok: Purple->Blue
|
||||
{
|
||||
ob = 255;
|
||||
or = ((SIXTH3-hue) * 255) / (SIXTH1);
|
||||
}
|
||||
else if( hue < SIXTH4 ) //Ok: Blue->Cyan
|
||||
{
|
||||
ob = 255;
|
||||
og = (hue - SIXTH3)*255 / SIXTH1;
|
||||
}
|
||||
else if( hue < SIXTH5 ) //Ok: Cyan->Green.
|
||||
{
|
||||
og = 255;
|
||||
ob = ((SIXTH5-hue)*255) / SIXTH1;
|
||||
}
|
||||
else //Green->Yellow
|
||||
{
|
||||
og = 255;
|
||||
or = (hue - SIXTH5) * 255 / SIXTH1;
|
||||
}
|
||||
|
||||
uint16_t rv = val;
|
||||
if( rv > 128 ) rv++;
|
||||
uint16_t rs = sat;
|
||||
if( rs > 128 ) rs++;
|
||||
|
||||
//or, og, ob range from 0...255 now.
|
||||
//Need to apply saturation and value.
|
||||
|
||||
or = (or * val)>>8;
|
||||
og = (og * val)>>8;
|
||||
ob = (ob * val)>>8;
|
||||
|
||||
//OR..OB == 0..65025
|
||||
or = or * rs + 255 * (256-rs);
|
||||
og = og * rs + 255 * (256-rs);
|
||||
ob = ob * rs + 255 * (256-rs);
|
||||
//printf( "__%d %d %d =-> %d\n", or, og, ob, rs );
|
||||
|
||||
or >>= 8;
|
||||
og >>= 8;
|
||||
ob >>= 8;
|
||||
|
||||
return or | (og<<8) | ((uint32_t)ob<<16);
|
||||
}
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
PROJS:=test_using_square_wave_octave_approach test_using_work_selection_heap test_using_work_selection_table test_using_square_wave_octave_approach_stipple
|
||||
|
||||
all : $(PROJS)
|
||||
|
||||
CFLAGS:=-I../colorchord2/rawdraw -g
|
||||
LDFLAGS:=-lGL -lm -lpthread -lX11
|
||||
|
||||
$(PROJS): %: %.c
|
||||
gcc -o $@ $^ $(CFLAGS) $(LDFLAGS)
|
||||
|
||||
clean :
|
||||
rm -rf $(PROJS)
|
||||
|
||||
|
|
@ -1,301 +0,0 @@
|
|||
/*
|
||||
An experiment in very, very low-spec ColorChord. This technique foregoes
|
||||
multiplies.
|
||||
|
||||
Approach 1B --- This variant locks the number of updates for any given
|
||||
integration to exactly the quadrature size. It was to test theory 1.
|
||||
|
||||
Approach 1 is square_wave_octave_approac_stipple.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
|
||||
#define CNFG_IMPLEMENTATION
|
||||
#define CNFGOGL
|
||||
#include "rawdraw_sf.h"
|
||||
|
||||
#include "os_generic.h"
|
||||
|
||||
|
||||
uint32_t EHSVtoHEX( uint8_t hue, uint8_t sat, uint8_t val );
|
||||
|
||||
int lastx = 200;
|
||||
int lasty = 1000;
|
||||
|
||||
int main()
|
||||
{
|
||||
#define FSPS 16000
|
||||
#define OCTAVES 6
|
||||
#define BPERO 24
|
||||
#define BASE_FREQ 22.5
|
||||
#define QUADRATURE_STEP_DENOMINATOR 16384
|
||||
|
||||
// Careful: It makes a lot of sense to explore these relationships.
|
||||
#define SAMPLE_Q 4
|
||||
#define MAG_IIR 0
|
||||
#define RUNNING_IIR 31
|
||||
#define COMPLEX_IIR 2
|
||||
|
||||
#define TEST_SAMPLES 256
|
||||
|
||||
|
||||
int16_t samples[TEST_SAMPLES];
|
||||
int i;
|
||||
|
||||
|
||||
CNFGSetup( "Example App", 1024, 768 );
|
||||
|
||||
// Precomputed Tables
|
||||
int8_t whichoctave[2<<OCTAVES];
|
||||
for( i = 0; i < (2<<OCTAVES); i++ )
|
||||
{
|
||||
int j;
|
||||
for( j = 0; j < OCTAVES; j++ )
|
||||
{
|
||||
if( i & (1<<j) ) break;
|
||||
}
|
||||
if( j == OCTAVES )
|
||||
whichoctave[i] = -1;
|
||||
else
|
||||
whichoctave[i] = OCTAVES - j - 1;
|
||||
}
|
||||
// Make a running counter to count up by this amount every cycle.
|
||||
// If the new number > 2048, then perform a quadrature step.
|
||||
int32_t flipdistance[BPERO];
|
||||
int binstothis[BPERO*OCTAVES] = { 0 };
|
||||
int nextloopbins[BPERO*OCTAVES] = { 0 };
|
||||
for( i = 0; i < BPERO; i++ )
|
||||
{
|
||||
double freq = pow( 2, (double)i / (double)BPERO ) * (BASE_FREQ/2.0);
|
||||
double pfreq = pow( 2, OCTAVES ) * freq;
|
||||
double spacing = (FSPS / 2) / pfreq / 4;
|
||||
|
||||
flipdistance[i] = QUADRATURE_STEP_DENOMINATOR * spacing;
|
||||
// Spacing = "quadrature every X samples"
|
||||
//printf( "%f %d\n", spacing, flipdistance[i] );
|
||||
//flipdistance[i] = QUADRATURE_STEP_DENOMINATOR * (int)(spacing+0.5);
|
||||
binstothis[i] = (int)(spacing+0.5);
|
||||
nextloopbins[i] = binstothis[i];
|
||||
}
|
||||
|
||||
// This is for timing. Not accumulated data.
|
||||
int32_t quadrature_timing_last[BPERO*OCTAVES] = { 0 };
|
||||
|
||||
|
||||
uint8_t quadrature_state[BPERO*OCTAVES] = { 0 };
|
||||
|
||||
uint32_t last_accumulated_value[BPERO*OCTAVES*2] = { 0 };
|
||||
|
||||
uint32_t octave_timing[OCTAVES] = { 0 };
|
||||
|
||||
int32_t real_imaginary_running[BPERO*OCTAVES*2] = { 0 };
|
||||
|
||||
uint32_t sample_accumulator = 0;
|
||||
|
||||
int32_t qcount[BPERO*OCTAVES] = { 0 };
|
||||
int32_t magsum[BPERO*OCTAVES] = { 0 };
|
||||
|
||||
int frameno = 0;
|
||||
double dLT = OGGetAbsoluteTime();
|
||||
int samplenoIn = 0;
|
||||
int sampleno = 0;
|
||||
double ToneOmega = 0;
|
||||
while( CNFGHandleInput() )
|
||||
{
|
||||
CNFGClearFrame();
|
||||
|
||||
frameno++;
|
||||
float freq =
|
||||
//pow( 2, (frameno%600)/100.0 ) * 25;
|
||||
pow( 2, (lastx)/100.0 ) * lastx;
|
||||
//101;
|
||||
|
||||
for( i = 0; i < TEST_SAMPLES; i++ )
|
||||
{
|
||||
samples[i] = lasty/5 + sin( ToneOmega ) * 127;// + (rand()%128)-64;
|
||||
ToneOmega += 1 / (double)FSPS * (double)freq * 3.14159 * 2.0;
|
||||
}
|
||||
char cts[1024];
|
||||
sprintf( cts, "%f %d", freq, sampleno );
|
||||
CNFGColor( 0xffffffff );
|
||||
CNFGPenX = 2;
|
||||
CNFGPenY = 2;
|
||||
CNFGDrawText( cts, 2 );
|
||||
|
||||
while( OGGetAbsoluteTime() < dLT + TEST_SAMPLES / (double)FSPS );
|
||||
dLT += TEST_SAMPLES / (double)FSPS;
|
||||
|
||||
for( i = 0; i < TEST_SAMPLES; i++ )
|
||||
{
|
||||
sample_accumulator += samples[i];
|
||||
int octave = whichoctave[(sampleno)&((2<<OCTAVES)-1)];
|
||||
sampleno++;
|
||||
if( octave < 0 )
|
||||
{
|
||||
// A "free cycle" this happens every 1/2^octaves
|
||||
continue;
|
||||
}
|
||||
|
||||
#define WATCHBIN -1
|
||||
int b;
|
||||
int binno = octave * BPERO;
|
||||
int ocative_time = octave_timing[octave] += QUADRATURE_STEP_DENOMINATOR;
|
||||
for( b = 0; b < BPERO; b++, binno++ )
|
||||
{
|
||||
if( binno == WATCHBIN )
|
||||
{
|
||||
printf( "%d %d %d %6d %6d %6d\n", ocative_time, quadrature_timing_last[binno], quadrature_state[0], real_imaginary_running[0], real_imaginary_running[1], magsum[0] );
|
||||
}
|
||||
if( --nextloopbins[binno] <= 0 )
|
||||
{
|
||||
// This code will get appropriately executed every quadrature update.
|
||||
|
||||
int qstate = quadrature_state[binno] = ( quadrature_state[binno] + 1 ) % 4;
|
||||
|
||||
int last_q_bin = (binno * 2) + ( qstate & 1 );
|
||||
int delta = sample_accumulator - last_accumulated_value[last_q_bin];
|
||||
last_accumulated_value[last_q_bin] = sample_accumulator;
|
||||
|
||||
if( binno == WATCHBIN )
|
||||
printf( "Delta: %d\n", delta );
|
||||
|
||||
// Qstate =
|
||||
// (0) = +Cos, (1) = +Sin, (2) = -Cos, (3) = -Sin
|
||||
if( qstate & 2 ) delta *= -1;
|
||||
|
||||
// Update real and imaginary components with delta.
|
||||
int running = real_imaginary_running[last_q_bin];
|
||||
running = running - (running>>RUNNING_IIR) + delta;
|
||||
real_imaginary_running[last_q_bin] = running;
|
||||
|
||||
int q = ++qcount[binno];
|
||||
if( q == SAMPLE_Q ) // Effective Q factor.
|
||||
{
|
||||
qcount[binno] = 0;
|
||||
int newmagR = real_imaginary_running[(binno * 2)];
|
||||
int newmagI = real_imaginary_running[(binno * 2)+1];
|
||||
|
||||
real_imaginary_running[(binno * 2)] = newmagR - (newmagR>>COMPLEX_IIR);
|
||||
real_imaginary_running[(binno * 2)+1] = newmagI - (newmagI>>COMPLEX_IIR);
|
||||
|
||||
// Super-cheap, non-multiply, approximate complex vector magnitude calculation.
|
||||
newmagR = (newmagR<0)?-newmagR:newmagR;
|
||||
newmagI = (newmagI<0)?-newmagI:newmagI;
|
||||
int newmag =
|
||||
//sqrt(newmagR*newmagR + newmagI*newmagI );
|
||||
newmagR > newmagI ? newmagR + (newmagI>>1) : newmagI + (newmagR>>1);
|
||||
|
||||
int lastmag = magsum[binno];
|
||||
magsum[binno] = lastmag - (lastmag>>MAG_IIR) + newmag;
|
||||
|
||||
|
||||
quadrature_timing_last[binno] += flipdistance[b]*4;
|
||||
int nextsteps = -(ocative_time - quadrature_timing_last[binno])/QUADRATURE_STEP_DENOMINATOR;
|
||||
binstothis[binno] = nextsteps/4;
|
||||
}
|
||||
nextloopbins[binno] = binstothis[binno];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int lx, ly;
|
||||
for( i = 0; i < BPERO*OCTAVES; i++ )
|
||||
{
|
||||
CNFGColor( (EHSVtoHEX( (i * 256 / BPERO)&0xff, 255, 255 ) << 8) | 0xff );
|
||||
float real = real_imaginary_running[i*2+0];
|
||||
float imag = real_imaginary_running[i*2+1];
|
||||
float mag = sqrt( real * real + imag * imag );
|
||||
|
||||
mag = (float)magsum[i] * pow( 2, i / (double)BPERO );
|
||||
int y = 768 - ((int)(mag / FSPS * 10) >> MAG_IIR);
|
||||
if( i ) CNFGTackSegment( i*4, y, lx*4, ly );
|
||||
lx = i; ly= y;
|
||||
//printf( "%d %d\n", real_imaginary_running[i*2+0], real_imaginary_running[i*2+1] );
|
||||
}
|
||||
|
||||
CNFGSwapBuffers();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void HandleKey( int keycode, int bDown ) { }
|
||||
void HandleButton( int x, int y, int button, int bDown ) { }
|
||||
void HandleMotion( int x, int y, int mask ) { lastx = x; lasty = y; }
|
||||
void HandleDestroy() { }
|
||||
|
||||
|
||||
uint32_t EHSVtoHEX( uint8_t hue, uint8_t sat, uint8_t val )
|
||||
{
|
||||
#define SIXTH1 43
|
||||
#define SIXTH2 85
|
||||
#define SIXTH3 128
|
||||
#define SIXTH4 171
|
||||
#define SIXTH5 213
|
||||
|
||||
uint16_t or = 0, og = 0, ob = 0;
|
||||
|
||||
hue -= SIXTH1; //Off by 60 degrees.
|
||||
|
||||
//TODO: There are colors that overlap here, consider
|
||||
//tweaking this to make the best use of the colorspace.
|
||||
|
||||
if( hue < SIXTH1 ) //Ok: Yellow->Red.
|
||||
{
|
||||
or = 255;
|
||||
og = 255 - ((uint16_t)hue * 255) / (SIXTH1);
|
||||
}
|
||||
else if( hue < SIXTH2 ) //Ok: Red->Purple
|
||||
{
|
||||
or = 255;
|
||||
ob = (uint16_t)hue*255 / SIXTH1 - 255;
|
||||
}
|
||||
else if( hue < SIXTH3 ) //Ok: Purple->Blue
|
||||
{
|
||||
ob = 255;
|
||||
or = ((SIXTH3-hue) * 255) / (SIXTH1);
|
||||
}
|
||||
else if( hue < SIXTH4 ) //Ok: Blue->Cyan
|
||||
{
|
||||
ob = 255;
|
||||
og = (hue - SIXTH3)*255 / SIXTH1;
|
||||
}
|
||||
else if( hue < SIXTH5 ) //Ok: Cyan->Green.
|
||||
{
|
||||
og = 255;
|
||||
ob = ((SIXTH5-hue)*255) / SIXTH1;
|
||||
}
|
||||
else //Green->Yellow
|
||||
{
|
||||
og = 255;
|
||||
or = (hue - SIXTH5) * 255 / SIXTH1;
|
||||
}
|
||||
|
||||
uint16_t rv = val;
|
||||
if( rv > 128 ) rv++;
|
||||
uint16_t rs = sat;
|
||||
if( rs > 128 ) rs++;
|
||||
|
||||
//or, og, ob range from 0...255 now.
|
||||
//Need to apply saturation and value.
|
||||
|
||||
or = (or * val)>>8;
|
||||
og = (og * val)>>8;
|
||||
ob = (ob * val)>>8;
|
||||
|
||||
//OR..OB == 0..65025
|
||||
or = or * rs + 255 * (256-rs);
|
||||
og = og * rs + 255 * (256-rs);
|
||||
ob = ob * rs + 255 * (256-rs);
|
||||
//printf( "__%d %d %d =-> %d\n", or, og, ob, rs );
|
||||
|
||||
or >>= 8;
|
||||
og >>= 8;
|
||||
ob >>= 8;
|
||||
|
||||
return or | (og<<8) | ((uint32_t)ob<<16);
|
||||
}
|
||||
|
|
@ -1,323 +0,0 @@
|
|||
/*
|
||||
An experiment in very, very low-spec ColorChord. This technique foregoes
|
||||
multiplies.
|
||||
|
||||
Approach 1:
|
||||
|
||||
This approach uses a table, like several colorchord algorithms to only
|
||||
process one octave each cycle. It then uses a table to decide how often
|
||||
to process each bin. It performs that bin at 4x the bin's sampling
|
||||
frequency, in quadrature. So that it performs the +real, +imag, -real,
|
||||
-imag operations over each cycle.
|
||||
|
||||
You can observe an overtone at the current bin - 1.5 octaves! This is
|
||||
expected, since, it's the inverse of what the DFT of a square wave would
|
||||
be.
|
||||
|
||||
That is a minor drawback, but the **major** drawback is that any DC
|
||||
offset, OR lower frequencies present when computing higher frequencies will
|
||||
induce a significant ground flutter, and make results really inaccurate.
|
||||
|
||||
This will need to be addressed before the algorithm is ready for prime-time.
|
||||
|
||||
NOTE: To explore:
|
||||
1) Consider SAMPLE_Q to possibly use all 4 cycles - though this will
|
||||
add latency, it will be more "accurate" --> YES! This helps!
|
||||
2) Use the cursor to move left and right to sweep tone and up and down
|
||||
to change DC-bias. This exhibits this algo's shortcoming.
|
||||
TODO: Can we somehow zero out the DC offset?
|
||||
|
||||
|
||||
DISCOVERY: This approach (octave-step in octave) is very sensitive FSPS
|
||||
|
||||
Theories:
|
||||
* If we force all 4 quadrature value to be the same number of
|
||||
integration cycles, does that solve it? --> NO! IT GETS MESSY (see Approach 1B)
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
|
||||
#define CNFG_IMPLEMENTATION
|
||||
#define CNFGOGL
|
||||
#include "rawdraw_sf.h"
|
||||
|
||||
#include "os_generic.h"
|
||||
|
||||
|
||||
uint32_t EHSVtoHEX( uint8_t hue, uint8_t sat, uint8_t val );
|
||||
|
||||
int lastx = 200;
|
||||
int lasty = 1000;
|
||||
|
||||
int main()
|
||||
{
|
||||
#define FSPS 22100 // Careful, this matters! It is selected to avoid periodic peaks encountered with DC offsets.
|
||||
#define OCTAVES 6
|
||||
#define BPERO 24
|
||||
#define BASE_FREQ 22.5
|
||||
#define QUADRATURE_STEP_DENOMINATOR 16384
|
||||
|
||||
// Careful: It makes a lot of sense to explore these relationships.
|
||||
#define SAMPLE_Q 4
|
||||
#define MAG_IIR 0
|
||||
#define RUNNING_IIR 31
|
||||
#define COMPLEX_IIR 2
|
||||
|
||||
#define TEST_SAMPLES 256
|
||||
|
||||
|
||||
int16_t samples[TEST_SAMPLES];
|
||||
int i;
|
||||
|
||||
|
||||
CNFGSetup( "Example App", 1024, 768 );
|
||||
|
||||
// Precomputed Tables
|
||||
int8_t whichoctave[2<<OCTAVES];
|
||||
for( i = 0; i < (2<<OCTAVES); i++ )
|
||||
{
|
||||
int j;
|
||||
for( j = 0; j < OCTAVES; j++ )
|
||||
{
|
||||
if( i & (1<<j) ) break;
|
||||
}
|
||||
if( j == OCTAVES )
|
||||
whichoctave[i] = -1;
|
||||
else
|
||||
whichoctave[i] = OCTAVES - j - 1;
|
||||
}
|
||||
// Make a running counter to count up by this amount every cycle.
|
||||
// If the new number > 2048, then perform a quadrature step.
|
||||
int32_t flipdistance[BPERO];
|
||||
for( i = 0; i < BPERO; i++ )
|
||||
{
|
||||
double freq = pow( 2, (double)i / (double)BPERO ) * (BASE_FREQ/2.0);
|
||||
double pfreq = pow( 2, OCTAVES ) * freq;
|
||||
double spacing = (FSPS / 2) / pfreq / 4;
|
||||
|
||||
flipdistance[i] = QUADRATURE_STEP_DENOMINATOR * spacing;
|
||||
// Spacing = "quadrature every X samples"
|
||||
//printf( "%f %d\n", spacing, flipdistance[i] );
|
||||
}
|
||||
|
||||
// This is for timing. Not accumulated data.
|
||||
int32_t quadrature_timing_last[BPERO*OCTAVES] = { 0 };
|
||||
uint8_t quadrature_state[BPERO*OCTAVES] = { 0 };
|
||||
|
||||
uint32_t last_accumulated_value[BPERO*OCTAVES*2] = { 0 };
|
||||
|
||||
uint32_t octave_timing[OCTAVES] = { 0 };
|
||||
|
||||
int32_t real_imaginary_running[BPERO*OCTAVES*2] = { 0 };
|
||||
|
||||
uint32_t sample_accumulator = 0;
|
||||
|
||||
int32_t qcount[BPERO*OCTAVES] = { 0 };
|
||||
int32_t magsum[BPERO*OCTAVES] = { 0 };
|
||||
|
||||
int frameno = 0;
|
||||
double dLT = OGGetAbsoluteTime();
|
||||
int samplenoIn = 0;
|
||||
int sampleno = 0;
|
||||
double ToneOmega = 0;
|
||||
int ops;
|
||||
while( CNFGHandleInput() )
|
||||
{
|
||||
CNFGClearFrame();
|
||||
|
||||
frameno++;
|
||||
float freq =
|
||||
//pow( 2, (frameno%600)/100.0 ) * 25;
|
||||
pow( 2, (lastx)/100.0 ) * lastx;
|
||||
//101;
|
||||
|
||||
for( i = 0; i < TEST_SAMPLES; i++ )
|
||||
{
|
||||
samples[i] = lasty/5 + sin( ToneOmega ) * 127;// + (rand()%128)-64;
|
||||
ToneOmega += 1 / (double)FSPS * (double)freq * 3.14159 * 2.0;
|
||||
}
|
||||
char cts[1024];
|
||||
sprintf( cts, "%f %d %f", freq, sampleno, ops/(double)sampleno );
|
||||
CNFGColor( 0xffffffff );
|
||||
CNFGPenX = 2;
|
||||
CNFGPenY = 2;
|
||||
CNFGDrawText( cts, 2 );
|
||||
|
||||
while( OGGetAbsoluteTime() < dLT + TEST_SAMPLES / (double)FSPS );
|
||||
dLT += TEST_SAMPLES / (double)FSPS;
|
||||
|
||||
if( 0 )
|
||||
{
|
||||
memset( real_imaginary_running, 0, sizeof( real_imaginary_running ) );
|
||||
memset( last_accumulated_value, 0, sizeof( last_accumulated_value ) );
|
||||
memset( quadrature_timing_last, 0, sizeof( quadrature_timing_last ) );
|
||||
memset( quadrature_state, 0, sizeof( quadrature_state ) );
|
||||
memset( octave_timing, 0, sizeof( octave_timing ) );
|
||||
sample_accumulator = 0;
|
||||
}
|
||||
|
||||
for( i = 0; i < TEST_SAMPLES; i++ )
|
||||
{
|
||||
sample_accumulator += samples[i];
|
||||
int octave = whichoctave[(sampleno)&((2<<OCTAVES)-1)];
|
||||
sampleno++;
|
||||
if( octave < 0 )
|
||||
{
|
||||
// A "free cycle" this happens every 1/(2^octaves)
|
||||
continue;
|
||||
}
|
||||
|
||||
#define WATCHBIN 1
|
||||
int b;
|
||||
int binno = octave * BPERO;
|
||||
int ocative_time = octave_timing[octave] += QUADRATURE_STEP_DENOMINATOR;
|
||||
for( b = 0; b < BPERO; b++, binno++ )
|
||||
{
|
||||
ops+=5;
|
||||
if( ocative_time - quadrature_timing_last[binno] > 0 )
|
||||
{
|
||||
ops+=20;
|
||||
quadrature_timing_last[binno] += flipdistance[b];
|
||||
// This code will get appropriately executed every quadrature update.
|
||||
|
||||
int qstate = quadrature_state[binno] = ( quadrature_state[binno] + 1 ) % 4;
|
||||
|
||||
int last_q_bin = (binno * 2) + ( qstate & 1 );
|
||||
int delta = sample_accumulator - last_accumulated_value[last_q_bin];
|
||||
last_accumulated_value[last_q_bin] = sample_accumulator;
|
||||
|
||||
// Qstate =
|
||||
// (0) = +Cos, (1) = +Sin, (2) = -Cos, (3) = -Sin
|
||||
if( qstate & 2 ) delta *= -1;
|
||||
|
||||
// Update real and imaginary components with delta.
|
||||
int running = real_imaginary_running[last_q_bin];
|
||||
running = running - (running>>RUNNING_IIR) + delta;
|
||||
real_imaginary_running[last_q_bin] = running;
|
||||
|
||||
int q = ++qcount[binno];
|
||||
if( q == SAMPLE_Q ) // Effective Q factor.
|
||||
{
|
||||
ops+=20;
|
||||
qcount[binno] = 0;
|
||||
int newmagR = real_imaginary_running[(binno * 2)];
|
||||
int newmagI = real_imaginary_running[(binno * 2)+1];
|
||||
|
||||
real_imaginary_running[(binno * 2)] = newmagR - (newmagR>>COMPLEX_IIR);
|
||||
real_imaginary_running[(binno * 2)+1] = newmagI - (newmagI>>COMPLEX_IIR);
|
||||
|
||||
// Super-cheap, non-multiply, approximate complex vector magnitude calculation.
|
||||
newmagR = (newmagR<0)?-newmagR:newmagR;
|
||||
newmagI = (newmagI<0)?-newmagI:newmagI;
|
||||
int newmag =
|
||||
//sqrt(newmagR*newmagR + newmagI*newmagI );
|
||||
newmagR > newmagI ? newmagR + (newmagI>>1) : newmagI + (newmagR>>1);
|
||||
|
||||
int lastmag = magsum[binno];
|
||||
magsum[binno] = lastmag - (lastmag>>MAG_IIR) + newmag;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int lx, ly;
|
||||
for( i = 0; i < BPERO*OCTAVES; i++ )
|
||||
{
|
||||
CNFGColor( (EHSVtoHEX( (i * 256 / BPERO)&0xff, 255, 255 ) << 8) | 0xff );
|
||||
float real = real_imaginary_running[i*2+0];
|
||||
float imag = real_imaginary_running[i*2+1];
|
||||
float mag = sqrt( real * real + imag * imag );
|
||||
|
||||
mag = (float)magsum[i] * pow( 2, i / (double)BPERO );
|
||||
int y = 768 - ((int)(mag / FSPS * 10) >> MAG_IIR);
|
||||
if( i ) CNFGTackSegment( i*4, y, lx*4, ly );
|
||||
lx = i; ly= y;
|
||||
//printf( "%d %d\n", real_imaginary_running[i*2+0], real_imaginary_running[i*2+1] );
|
||||
}
|
||||
|
||||
CNFGSwapBuffers();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void HandleKey( int keycode, int bDown ) { }
|
||||
void HandleButton( int x, int y, int button, int bDown ) { }
|
||||
void HandleMotion( int x, int y, int mask ) { lastx = x; lasty = y; }
|
||||
void HandleDestroy() { }
|
||||
|
||||
|
||||
uint32_t EHSVtoHEX( uint8_t hue, uint8_t sat, uint8_t val )
|
||||
{
|
||||
#define SIXTH1 43
|
||||
#define SIXTH2 85
|
||||
#define SIXTH3 128
|
||||
#define SIXTH4 171
|
||||
#define SIXTH5 213
|
||||
|
||||
uint16_t or = 0, og = 0, ob = 0;
|
||||
|
||||
hue -= SIXTH1; //Off by 60 degrees.
|
||||
|
||||
//TODO: There are colors that overlap here, consider
|
||||
//tweaking this to make the best use of the colorspace.
|
||||
|
||||
if( hue < SIXTH1 ) //Ok: Yellow->Red.
|
||||
{
|
||||
or = 255;
|
||||
og = 255 - ((uint16_t)hue * 255) / (SIXTH1);
|
||||
}
|
||||
else if( hue < SIXTH2 ) //Ok: Red->Purple
|
||||
{
|
||||
or = 255;
|
||||
ob = (uint16_t)hue*255 / SIXTH1 - 255;
|
||||
}
|
||||
else if( hue < SIXTH3 ) //Ok: Purple->Blue
|
||||
{
|
||||
ob = 255;
|
||||
or = ((SIXTH3-hue) * 255) / (SIXTH1);
|
||||
}
|
||||
else if( hue < SIXTH4 ) //Ok: Blue->Cyan
|
||||
{
|
||||
ob = 255;
|
||||
og = (hue - SIXTH3)*255 / SIXTH1;
|
||||
}
|
||||
else if( hue < SIXTH5 ) //Ok: Cyan->Green.
|
||||
{
|
||||
og = 255;
|
||||
ob = ((SIXTH5-hue)*255) / SIXTH1;
|
||||
}
|
||||
else //Green->Yellow
|
||||
{
|
||||
og = 255;
|
||||
or = (hue - SIXTH5) * 255 / SIXTH1;
|
||||
}
|
||||
|
||||
uint16_t rv = val;
|
||||
if( rv > 128 ) rv++;
|
||||
uint16_t rs = sat;
|
||||
if( rs > 128 ) rs++;
|
||||
|
||||
//or, og, ob range from 0...255 now.
|
||||
//Need to apply saturation and value.
|
||||
|
||||
or = (or * val)>>8;
|
||||
og = (og * val)>>8;
|
||||
ob = (ob * val)>>8;
|
||||
|
||||
//OR..OB == 0..65025
|
||||
or = or * rs + 255 * (256-rs);
|
||||
og = og * rs + 255 * (256-rs);
|
||||
ob = ob * rs + 255 * (256-rs);
|
||||
//printf( "__%d %d %d =-> %d\n", or, og, ob, rs );
|
||||
|
||||
or >>= 8;
|
||||
og >>= 8;
|
||||
ob >>= 8;
|
||||
|
||||
return or | (og<<8) | ((uint32_t)ob<<16);
|
||||
}
|
||||
|
|
@ -1,367 +0,0 @@
|
|||
/*
|
||||
An experiment in very, very low-spec ColorChord. This technique foregoes
|
||||
multiplies.
|
||||
|
||||
Approach 2:
|
||||
|
||||
Similar approach to Approach 1, in that this uses square waves and quarter
|
||||
wavelength segments to quadrature encode, but instead of using an octave
|
||||
at a time, it instead creates a heap to work through every sample.
|
||||
|
||||
That way, the error induced by sample stutter is minimized and the square
|
||||
waves are as accurate as possible.
|
||||
|
||||
WARNING: With this approach, operations can 'bunch up' so that you may
|
||||
need to clear many, many ops in a single cycle, so it is not at all
|
||||
appropirate for being run in an interrupt.
|
||||
|
||||
Another benefit: If sample rate is large, no time is spent working on
|
||||
samples that don't need work. This is better for a sparse set of ops.
|
||||
|
||||
|
||||
TODO: Can we do this approach, but with a fixed table to instruct when to
|
||||
perform every bin?
|
||||
|
||||
GENERAL OBSERVATION FOR ALL VERSIONS: (applicableto all) If we integrate
|
||||
only bumps for sin/cos, it seems to have different noise properties.
|
||||
May be beneficial!
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
|
||||
#define CNFG_IMPLEMENTATION
|
||||
#define CNFGOGL
|
||||
#include "rawdraw_sf.h"
|
||||
|
||||
#include "os_generic.h"
|
||||
|
||||
|
||||
uint32_t EHSVtoHEX( uint8_t hue, uint8_t sat, uint8_t val );
|
||||
|
||||
int lastx = 200;
|
||||
int lasty = 1000;
|
||||
|
||||
#define FSPS 12000
|
||||
#define OCTAVES 6
|
||||
#define BPERO 24
|
||||
#define BASE_FREQ 22.5
|
||||
#define QUADRATURE_STEP_DENOMINATOR 16384
|
||||
|
||||
// Careful: It makes a lot of sense to explore these relationships.
|
||||
#define SAMPLE_Q 4
|
||||
#define MAG_IIR 0
|
||||
#define COMPLEX_IIR 2
|
||||
#define RUNNING_IIR 31
|
||||
|
||||
#define TEST_SAMPLES 256
|
||||
|
||||
int32_t next_heap_events[OCTAVES*BPERO*2] = { 0 };
|
||||
|
||||
int sineshape;
|
||||
// This will sort the head node back down into the heap, so the heap will
|
||||
// remain a min-heap. This is done in log(n) time. But, with our data, it
|
||||
// experimentally only needs to run for 6.47 iterations per call on average
|
||||
// assuming 24 BPERO and 6 OCTAVES.
|
||||
|
||||
int heapsteps = 0;
|
||||
int reheaps = 0;
|
||||
void PercolateHeap( int32_t current_time )
|
||||
{
|
||||
reheaps++;
|
||||
int this_index = 0;
|
||||
int this_val = next_heap_events[0];
|
||||
do
|
||||
{
|
||||
heapsteps++;
|
||||
int left = (this_index * 2 + 1);
|
||||
int right = (this_index * 2 + 2);
|
||||
|
||||
// At end. WARNING: This heap algorithm is only useful if it's an even number of things.
|
||||
if( right >= OCTAVES*BPERO ) return;
|
||||
|
||||
int leftval = next_heap_events[left*2];
|
||||
int rightval = next_heap_events[right*2];
|
||||
int diffleft = leftval - this_val;
|
||||
int diffright = rightval - this_val;
|
||||
//printf( "RESORT ID %d / INDEX %d / [%d %d] %d %d %d %d\n", next_heap_events[this_index*2+1], this_index, diffleft, diffright, leftval, rightval, left, right );
|
||||
if( diffleft > 0 && diffright > 0 )
|
||||
{
|
||||
// The heap is sorted. We're done.
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise we have to pick an edge to sort on.
|
||||
if( diffleft <= diffright )
|
||||
{
|
||||
//printf( "LEFT %d / %d\n", left, this_val );
|
||||
int swapevent = next_heap_events[left*2+1];
|
||||
next_heap_events[left*2+1] = next_heap_events[this_index*2+1];
|
||||
next_heap_events[this_index*2+1] = swapevent;
|
||||
|
||||
next_heap_events[this_index*2+0] = leftval;
|
||||
next_heap_events[left*2+0] = this_val;
|
||||
|
||||
this_index = left;
|
||||
}
|
||||
else
|
||||
{
|
||||
//printf( "RIGHT %d\n", right );
|
||||
|
||||
int swapevent = next_heap_events[right*2+1];
|
||||
next_heap_events[right*2+1] = next_heap_events[this_index*2+1];
|
||||
next_heap_events[this_index*2+1] = swapevent;
|
||||
|
||||
next_heap_events[this_index*2+0] = rightval;
|
||||
next_heap_events[right*2+0] = this_val;
|
||||
|
||||
this_index = right;
|
||||
}
|
||||
} while( 1 );
|
||||
}
|
||||
|
||||
|
||||
int main()
|
||||
{
|
||||
|
||||
|
||||
int16_t samples[TEST_SAMPLES];
|
||||
int i;
|
||||
|
||||
|
||||
CNFGSetup( "Example App", 1024, 768 );
|
||||
|
||||
// Make a running counter to count up by this amount every cycle.
|
||||
// If the new number > 2048, then perform a quadrature step.
|
||||
int32_t flipdistance[BPERO*OCTAVES];
|
||||
for( i = 0; i < BPERO*OCTAVES; i++ )
|
||||
{
|
||||
double freq = pow( 2, (double)i / (double)BPERO ) * (BASE_FREQ/2.0);
|
||||
double pfreq = freq;
|
||||
double spacing = (FSPS / 2) / pfreq / 4;
|
||||
|
||||
flipdistance[i] = QUADRATURE_STEP_DENOMINATOR * spacing;
|
||||
// Spacing = "quadrature every X samples"
|
||||
|
||||
next_heap_events[i*2+1] = i;
|
||||
}
|
||||
|
||||
// This is for timing. Not accumulated data.
|
||||
uint8_t quadrature_state[BPERO*OCTAVES] = { 0 };
|
||||
|
||||
uint32_t last_accumulated_value[BPERO*OCTAVES*2] = { 0 };
|
||||
|
||||
int32_t real_imaginary_running[BPERO*OCTAVES*2] = { 0 };
|
||||
|
||||
uint32_t sample_accumulator = 0;
|
||||
int32_t time_accumulator = 0;
|
||||
|
||||
int32_t qcount[BPERO*OCTAVES] = { 0 };
|
||||
int32_t magsum[BPERO*OCTAVES] = { 0 };
|
||||
|
||||
int frameno = 0;
|
||||
double dLT = OGGetAbsoluteTime();
|
||||
|
||||
double ToneOmega = 0;
|
||||
while( CNFGHandleInput() )
|
||||
{
|
||||
CNFGClearFrame();
|
||||
|
||||
frameno++;
|
||||
float freq =
|
||||
//pow( 2, (frameno%600)/100.0 ) * 25;
|
||||
pow( 2, (lastx)/100.0 ) * lastx;
|
||||
//101;
|
||||
|
||||
for( i = 0; i < TEST_SAMPLES; i++ )
|
||||
{
|
||||
samples[i] = lasty/5 + sin( ToneOmega ) * 127;// + (rand()%128)-64;
|
||||
ToneOmega += 1 / (double)FSPS * (double)freq * 3.14159 * 2.0;
|
||||
}
|
||||
char cts[1024];
|
||||
sprintf( cts, "%f %d %f %f SINESHAPE: %d", freq, time_accumulator, (double)heapsteps / (double)reheaps, (double)reheaps/(time_accumulator/QUADRATURE_STEP_DENOMINATOR), sineshape );
|
||||
CNFGColor( 0xffffffff );
|
||||
CNFGPenX = 2;
|
||||
CNFGPenY = 2;
|
||||
CNFGDrawText( cts, 2 );
|
||||
|
||||
while( OGGetAbsoluteTime() < dLT + TEST_SAMPLES / (double)FSPS );
|
||||
dLT += TEST_SAMPLES / (double)FSPS;
|
||||
|
||||
|
||||
#define WATCHBIN -1
|
||||
|
||||
for( i = 0; i < TEST_SAMPLES; i++ )
|
||||
{
|
||||
sample_accumulator += samples[i];
|
||||
|
||||
time_accumulator += QUADRATURE_STEP_DENOMINATOR;
|
||||
|
||||
while( (time_accumulator - next_heap_events[0]) > 0 )
|
||||
{
|
||||
// Event has occurred.
|
||||
int binno = next_heap_events[1];
|
||||
//printf( "%d %d\n", binno, next_heap_events[0] );
|
||||
next_heap_events[0] += flipdistance[binno];
|
||||
PercolateHeap( time_accumulator );
|
||||
|
||||
//int j;
|
||||
//for( j = 0; j < OCTAVES*BPERO; j++ ) printf( "[%d %d]", next_heap_events[j*2+0], next_heap_events[j*2+1] );
|
||||
//printf( "\n" );
|
||||
|
||||
int qstate = quadrature_state[binno] = ( quadrature_state[binno] + 1 ) % 4;
|
||||
|
||||
int last_q_bin = (binno * 2) + ( qstate & 1 );
|
||||
int delta;
|
||||
if( !sineshape )
|
||||
{
|
||||
delta = sample_accumulator - last_accumulated_value[last_q_bin];
|
||||
last_accumulated_value[last_q_bin] = sample_accumulator;
|
||||
}
|
||||
else
|
||||
{
|
||||
// TESTING: Sine Shape - this only integrates bumps for sin/cos
|
||||
// instead of full triangle waves.
|
||||
// Observation: For higher frequency bins, this seems to help
|
||||
// a lot.
|
||||
// side-benefit, this takes less RAM.
|
||||
// BUT BUT! It messes with lower frequencies, making them uglier.
|
||||
delta = sample_accumulator - last_accumulated_value[binno];
|
||||
last_accumulated_value[binno] = sample_accumulator;
|
||||
delta *= 1.4; // Just to normalize the results of the test (not for production)
|
||||
}
|
||||
if( binno == WATCHBIN )
|
||||
printf( "Delta: %d\n", delta );
|
||||
|
||||
// Qstate =
|
||||
// (0) = +Cos, (1) = +Sin, (2) = -Cos, (3) = -Sin
|
||||
if( qstate & 2 ) delta *= -1;
|
||||
|
||||
// Update real and imaginary components with delta.
|
||||
int running = real_imaginary_running[last_q_bin];
|
||||
running = running - (running>>RUNNING_IIR) + delta;
|
||||
real_imaginary_running[last_q_bin] = running;
|
||||
|
||||
int q = ++qcount[binno];
|
||||
if( q == SAMPLE_Q ) // Effective Q factor.
|
||||
{
|
||||
qcount[binno] = 0;
|
||||
int newmagR = real_imaginary_running[(binno * 2)];
|
||||
int newmagI = real_imaginary_running[(binno * 2)+1];
|
||||
|
||||
real_imaginary_running[(binno * 2)] = newmagR - (newmagR>>COMPLEX_IIR);
|
||||
real_imaginary_running[(binno * 2)+1] = newmagI - (newmagI>>COMPLEX_IIR);
|
||||
|
||||
// Super-cheap, non-multiply, approximate complex vector magnitude calculation.
|
||||
newmagR = (newmagR<0)?-newmagR:newmagR;
|
||||
newmagI = (newmagI<0)?-newmagI:newmagI;
|
||||
int newmag =
|
||||
//sqrt(newmagR*newmagR + newmagI*newmagI );
|
||||
newmagR > newmagI ? newmagR + (newmagI>>1) : newmagI + (newmagR>>1);
|
||||
|
||||
int lastmag = magsum[binno];
|
||||
magsum[binno] = lastmag - (lastmag>>MAG_IIR) + newmag;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int lx, ly;
|
||||
for( i = 0; i < BPERO*OCTAVES; i++ )
|
||||
{
|
||||
CNFGColor( (EHSVtoHEX( (i * 256 / BPERO)&0xff, 255, 255 ) << 8) | 0xff );
|
||||
float real = real_imaginary_running[i*2+0];
|
||||
float imag = real_imaginary_running[i*2+1];
|
||||
float mag = sqrt( real * real + imag * imag );
|
||||
|
||||
mag = (float)magsum[i] * pow( 2, i / (double)BPERO );
|
||||
int y = 768 - ((int)(mag / FSPS * 10) >> MAG_IIR);
|
||||
if( i ) CNFGTackSegment( i*4, y, lx*4, ly );
|
||||
lx = i; ly= y;
|
||||
//printf( "%d %d\n", real_imaginary_running[i*2+0], real_imaginary_running[i*2+1] );
|
||||
}
|
||||
|
||||
CNFGSwapBuffers();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void HandleKey( int keycode, int bDown ) { if( keycode == 'a' && bDown ) sineshape = !sineshape; }
|
||||
void HandleButton( int x, int y, int button, int bDown ) { }
|
||||
void HandleMotion( int x, int y, int mask ) { lastx = x; lasty = y; }
|
||||
void HandleDestroy() { }
|
||||
|
||||
|
||||
uint32_t EHSVtoHEX( uint8_t hue, uint8_t sat, uint8_t val )
|
||||
{
|
||||
#define SIXTH1 43
|
||||
#define SIXTH2 85
|
||||
#define SIXTH3 128
|
||||
#define SIXTH4 171
|
||||
#define SIXTH5 213
|
||||
|
||||
uint16_t or = 0, og = 0, ob = 0;
|
||||
|
||||
hue -= SIXTH1; //Off by 60 degrees.
|
||||
|
||||
//TODO: There are colors that overlap here, consider
|
||||
//tweaking this to make the best use of the colorspace.
|
||||
|
||||
if( hue < SIXTH1 ) //Ok: Yellow->Red.
|
||||
{
|
||||
or = 255;
|
||||
og = 255 - ((uint16_t)hue * 255) / (SIXTH1);
|
||||
}
|
||||
else if( hue < SIXTH2 ) //Ok: Red->Purple
|
||||
{
|
||||
or = 255;
|
||||
ob = (uint16_t)hue*255 / SIXTH1 - 255;
|
||||
}
|
||||
else if( hue < SIXTH3 ) //Ok: Purple->Blue
|
||||
{
|
||||
ob = 255;
|
||||
or = ((SIXTH3-hue) * 255) / (SIXTH1);
|
||||
}
|
||||
else if( hue < SIXTH4 ) //Ok: Blue->Cyan
|
||||
{
|
||||
ob = 255;
|
||||
og = (hue - SIXTH3)*255 / SIXTH1;
|
||||
}
|
||||
else if( hue < SIXTH5 ) //Ok: Cyan->Green.
|
||||
{
|
||||
og = 255;
|
||||
ob = ((SIXTH5-hue)*255) / SIXTH1;
|
||||
}
|
||||
else //Green->Yellow
|
||||
{
|
||||
og = 255;
|
||||
or = (hue - SIXTH5) * 255 / SIXTH1;
|
||||
}
|
||||
|
||||
uint16_t rv = val;
|
||||
if( rv > 128 ) rv++;
|
||||
uint16_t rs = sat;
|
||||
if( rs > 128 ) rs++;
|
||||
|
||||
//or, og, ob range from 0...255 now.
|
||||
//Need to apply saturation and value.
|
||||
|
||||
or = (or * val)>>8;
|
||||
og = (og * val)>>8;
|
||||
ob = (ob * val)>>8;
|
||||
|
||||
//OR..OB == 0..65025
|
||||
or = or * rs + 255 * (256-rs);
|
||||
og = og * rs + 255 * (256-rs);
|
||||
ob = ob * rs + 255 * (256-rs);
|
||||
//printf( "__%d %d %d =-> %d\n", or, og, ob, rs );
|
||||
|
||||
or >>= 8;
|
||||
og >>= 8;
|
||||
ob >>= 8;
|
||||
|
||||
return or | (og<<8) | ((uint32_t)ob<<16);
|
||||
}
|
||||
|
|
@ -1,321 +0,0 @@
|
|||
/*
|
||||
An experiment in very, very low-spec ColorChord. This technique foregoes
|
||||
multiplies.
|
||||
|
||||
Approach 3: Based on Approach 2, but using a work selection table.
|
||||
|
||||
This won't work for the ch32v003, since the minimum practical table for 6
|
||||
octaves at 24BPERO with 12kSPS and bottom freq of 22.5 is about 80kB.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
|
||||
#define CNFG_IMPLEMENTATION
|
||||
#define CNFGOGL
|
||||
#include "rawdraw_sf.h"
|
||||
|
||||
#include "os_generic.h"
|
||||
|
||||
|
||||
uint32_t EHSVtoHEX( uint8_t hue, uint8_t sat, uint8_t val );
|
||||
|
||||
int lastx = 200;
|
||||
int lasty = 1000;
|
||||
|
||||
#define FSPS 16000
|
||||
#define OCTAVES 6
|
||||
#define BPERO 24
|
||||
#define BASE_FREQ 22.5
|
||||
#define QUADRATURE_STEP_DENOMINATOR 16384
|
||||
|
||||
// Careful: It makes a lot of sense to explore these relationships.
|
||||
#define SAMPLE_Q 4
|
||||
#define MAG_IIR 0
|
||||
#define COMPLEX_IIR 2
|
||||
#define RUNNING_IIR 31
|
||||
|
||||
#define TEST_SAMPLES 256
|
||||
|
||||
int sineshape;
|
||||
// This will sort the head node back down into the heap, so the heap will
|
||||
// remain a min-heap. This is done in log(n) time. But, with our data, it
|
||||
// experimentally only needs to run for 6.47 iterations per call on average
|
||||
// assuming 24 BPERO and 6 OCTAVES.
|
||||
|
||||
|
||||
int main()
|
||||
{
|
||||
|
||||
|
||||
int16_t samples[TEST_SAMPLES];
|
||||
int i;
|
||||
|
||||
|
||||
CNFGSetup( "Example App", 1024, 768 );
|
||||
|
||||
// Not size of table (that's usually larger) but # of samples
|
||||
// to record the work instructions for.
|
||||
#define WORKLOOP 6144
|
||||
|
||||
// Make a running counter to count up by this amount every cycle.
|
||||
// If the new number > 2048, then perform a quadrature step.
|
||||
double spacings[BPERO*OCTAVES];
|
||||
double runningspace[BPERO*OCTAVES];
|
||||
for( i = 0; i < BPERO*OCTAVES; i++ )
|
||||
{
|
||||
double freq = pow( 2, (double)i / (double)BPERO ) * (BASE_FREQ/2.0);
|
||||
double pfreq = freq;
|
||||
double spacing = (FSPS / 2) / pfreq / 4;
|
||||
|
||||
// make spacings line up to a denominator of workloop, this makes it
|
||||
// so you don't get a werid jump at the end of the work loop.
|
||||
double wdt = WORKLOOP / spacing;
|
||||
printf( "%f %f %f\n", wdt, spacing, WORKLOOP / (double)((int)wdt+0.5) );
|
||||
wdt = (int)(wdt+0.5);
|
||||
spacing = WORKLOOP / wdt;
|
||||
spacings[i] = spacing;
|
||||
}
|
||||
|
||||
|
||||
|
||||
uint8_t worktable[WORKLOOP*BPERO*OCTAVES];
|
||||
int worktablelen = 0;
|
||||
for( i = 0; i < WORKLOOP; i++ )
|
||||
{
|
||||
int j;
|
||||
for( j = 0; j < BPERO*OCTAVES; j++ )
|
||||
{
|
||||
if( i >= runningspace[j] )
|
||||
{
|
||||
runningspace[j] += spacings[j];
|
||||
worktable[worktablelen++] = j;
|
||||
}
|
||||
}
|
||||
worktable[worktablelen++] = 255;
|
||||
}
|
||||
|
||||
|
||||
// This is for timing. Not accumulated data.
|
||||
uint8_t quadrature_state[BPERO*OCTAVES] = { 0 };
|
||||
|
||||
uint32_t last_accumulated_value[BPERO*OCTAVES*2] = { 0 };
|
||||
|
||||
int32_t real_imaginary_running[BPERO*OCTAVES*2] = { 0 };
|
||||
|
||||
uint32_t sample_accumulator = 0;
|
||||
int32_t time_accumulator = 0;
|
||||
|
||||
int32_t qcount[BPERO*OCTAVES] = { 0 };
|
||||
int32_t magsum[BPERO*OCTAVES] = { 0 };
|
||||
|
||||
int frameno = 0;
|
||||
double dLT = OGGetAbsoluteTime();
|
||||
|
||||
double ToneOmega = 0;
|
||||
|
||||
int worktableplace = 0;
|
||||
while( CNFGHandleInput() )
|
||||
{
|
||||
CNFGClearFrame();
|
||||
|
||||
frameno++;
|
||||
float freq =
|
||||
//pow( 2, (frameno%600)/100.0 ) * 25;
|
||||
pow( 2, (lastx)/100.0 ) * lastx;
|
||||
//101;
|
||||
|
||||
for( i = 0; i < TEST_SAMPLES; i++ )
|
||||
{
|
||||
samples[i] = lasty/5 + sin( ToneOmega ) * 127;// + (rand()%128)-64;
|
||||
ToneOmega += 1 / (double)FSPS * (double)freq * 3.14159 * 2.0;
|
||||
}
|
||||
char cts[1024];
|
||||
sprintf( cts, "%f %d SINESHAPE: %d WT %d", freq, time_accumulator, sineshape , worktablelen);
|
||||
CNFGColor( 0xffffffff );
|
||||
CNFGPenX = 2;
|
||||
CNFGPenY = 2;
|
||||
CNFGDrawText( cts, 2 );
|
||||
|
||||
while( OGGetAbsoluteTime() < dLT + TEST_SAMPLES / (double)FSPS );
|
||||
dLT += TEST_SAMPLES / (double)FSPS;
|
||||
|
||||
|
||||
#define WATCHBIN -1
|
||||
|
||||
for( i = 0; i < TEST_SAMPLES; i++ )
|
||||
{
|
||||
sample_accumulator += samples[i];
|
||||
|
||||
time_accumulator += QUADRATURE_STEP_DENOMINATOR;
|
||||
|
||||
while( 1 )
|
||||
{
|
||||
int wtp = worktable[worktableplace];
|
||||
worktableplace = worktableplace + 1;
|
||||
if( worktableplace >= worktablelen ) worktableplace = 0;
|
||||
if( wtp == 255 ) break;
|
||||
|
||||
// Event has occurred.
|
||||
int binno = wtp;
|
||||
|
||||
//int j;
|
||||
//for( j = 0; j < OCTAVES*BPERO; j++ ) printf( "[%d %d]", next_heap_events[j*2+0], next_heap_events[j*2+1] );
|
||||
//printf( "\n" );
|
||||
|
||||
int qstate = quadrature_state[binno] = ( quadrature_state[binno] + 1 ) % 4;
|
||||
|
||||
int last_q_bin = (binno * 2) + ( qstate & 1 );
|
||||
int delta;
|
||||
if( !sineshape )
|
||||
{
|
||||
delta = sample_accumulator - last_accumulated_value[last_q_bin];
|
||||
last_accumulated_value[last_q_bin] = sample_accumulator;
|
||||
}
|
||||
else
|
||||
{
|
||||
// TESTING: Sine Shape - this only integrates bumps for sin/cos
|
||||
// instead of full triangle waves.
|
||||
// Observation: For higher frequency bins, this seems to help
|
||||
// a lot.
|
||||
// side-benefit, this takes less RAM.
|
||||
// BUT BUT! It messes with lower frequencies, making them uglier.
|
||||
delta = sample_accumulator - last_accumulated_value[binno];
|
||||
last_accumulated_value[binno] = sample_accumulator;
|
||||
delta *= 1.4; // Just to normalize the results of the test (not for production)
|
||||
}
|
||||
if( binno == WATCHBIN )
|
||||
printf( "Delta: %d\n", delta );
|
||||
|
||||
// Qstate =
|
||||
// (0) = +Cos, (1) = +Sin, (2) = -Cos, (3) = -Sin
|
||||
if( qstate & 2 ) delta *= -1;
|
||||
|
||||
// Update real and imaginary components with delta.
|
||||
int running = real_imaginary_running[last_q_bin];
|
||||
running = running - (running>>RUNNING_IIR) + delta;
|
||||
real_imaginary_running[last_q_bin] = running;
|
||||
|
||||
int q = ++qcount[binno];
|
||||
if( q == SAMPLE_Q ) // Effective Q factor.
|
||||
{
|
||||
qcount[binno] = 0;
|
||||
int newmagR = real_imaginary_running[(binno * 2)];
|
||||
int newmagI = real_imaginary_running[(binno * 2)+1];
|
||||
|
||||
real_imaginary_running[(binno * 2)] = newmagR - (newmagR>>COMPLEX_IIR);
|
||||
real_imaginary_running[(binno * 2)+1] = newmagI - (newmagI>>COMPLEX_IIR);
|
||||
|
||||
// Super-cheap, non-multiply, approximate complex vector magnitude calculation.
|
||||
newmagR = (newmagR<0)?-newmagR:newmagR;
|
||||
newmagI = (newmagI<0)?-newmagI:newmagI;
|
||||
int newmag =
|
||||
//sqrt(newmagR*newmagR + newmagI*newmagI );
|
||||
newmagR > newmagI ? newmagR + (newmagI>>1) : newmagI + (newmagR>>1);
|
||||
|
||||
int lastmag = magsum[binno];
|
||||
magsum[binno] = lastmag - (lastmag>>MAG_IIR) + newmag;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int lx, ly;
|
||||
for( i = 0; i < BPERO*OCTAVES; i++ )
|
||||
{
|
||||
CNFGColor( (EHSVtoHEX( (i * 256 / BPERO)&0xff, 255, 255 ) << 8) | 0xff );
|
||||
float real = real_imaginary_running[i*2+0];
|
||||
float imag = real_imaginary_running[i*2+1];
|
||||
float mag = sqrt( real * real + imag * imag );
|
||||
|
||||
mag = (float)magsum[i] * pow( 2, i / (double)BPERO );
|
||||
int y = 768 - ((int)(mag / FSPS * 10) >> MAG_IIR);
|
||||
if( i ) CNFGTackSegment( i*4, y, lx*4, ly );
|
||||
lx = i; ly= y;
|
||||
//printf( "%d %d\n", real_imaginary_running[i*2+0], real_imaginary_running[i*2+1] );
|
||||
}
|
||||
|
||||
CNFGSwapBuffers();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void HandleKey( int keycode, int bDown ) { if( keycode == 'a' && bDown ) sineshape = !sineshape; }
|
||||
void HandleButton( int x, int y, int button, int bDown ) { }
|
||||
void HandleMotion( int x, int y, int mask ) { lastx = x; lasty = y; }
|
||||
void HandleDestroy() { }
|
||||
|
||||
|
||||
uint32_t EHSVtoHEX( uint8_t hue, uint8_t sat, uint8_t val )
|
||||
{
|
||||
#define SIXTH1 43
|
||||
#define SIXTH2 85
|
||||
#define SIXTH3 128
|
||||
#define SIXTH4 171
|
||||
#define SIXTH5 213
|
||||
|
||||
uint16_t or = 0, og = 0, ob = 0;
|
||||
|
||||
hue -= SIXTH1; //Off by 60 degrees.
|
||||
|
||||
//TODO: There are colors that overlap here, consider
|
||||
//tweaking this to make the best use of the colorspace.
|
||||
|
||||
if( hue < SIXTH1 ) //Ok: Yellow->Red.
|
||||
{
|
||||
or = 255;
|
||||
og = 255 - ((uint16_t)hue * 255) / (SIXTH1);
|
||||
}
|
||||
else if( hue < SIXTH2 ) //Ok: Red->Purple
|
||||
{
|
||||
or = 255;
|
||||
ob = (uint16_t)hue*255 / SIXTH1 - 255;
|
||||
}
|
||||
else if( hue < SIXTH3 ) //Ok: Purple->Blue
|
||||
{
|
||||
ob = 255;
|
||||
or = ((SIXTH3-hue) * 255) / (SIXTH1);
|
||||
}
|
||||
else if( hue < SIXTH4 ) //Ok: Blue->Cyan
|
||||
{
|
||||
ob = 255;
|
||||
og = (hue - SIXTH3)*255 / SIXTH1;
|
||||
}
|
||||
else if( hue < SIXTH5 ) //Ok: Cyan->Green.
|
||||
{
|
||||
og = 255;
|
||||
ob = ((SIXTH5-hue)*255) / SIXTH1;
|
||||
}
|
||||
else //Green->Yellow
|
||||
{
|
||||
og = 255;
|
||||
or = (hue - SIXTH5) * 255 / SIXTH1;
|
||||
}
|
||||
|
||||
uint16_t rv = val;
|
||||
if( rv > 128 ) rv++;
|
||||
uint16_t rs = sat;
|
||||
if( rs > 128 ) rs++;
|
||||
|
||||
//or, og, ob range from 0...255 now.
|
||||
//Need to apply saturation and value.
|
||||
|
||||
or = (or * val)>>8;
|
||||
og = (og * val)>>8;
|
||||
ob = (ob * val)>>8;
|
||||
|
||||
//OR..OB == 0..65025
|
||||
or = or * rs + 255 * (256-rs);
|
||||
og = og * rs + 255 * (256-rs);
|
||||
ob = ob * rs + 255 * (256-rs);
|
||||
//printf( "__%d %d %d =-> %d\n", or, og, ob, rs );
|
||||
|
||||
or >>= 8;
|
||||
og >>= 8;
|
||||
ob >>= 8;
|
||||
|
||||
return or | (og<<8) | ((uint32_t)ob<<16);
|
||||
}
|
||||
|
|
@ -8,7 +8,7 @@
|
|||
#include <math.h>
|
||||
#include <string.h>
|
||||
#include "color.h"
|
||||
#include "CNFG.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.
|
||||
|
@ -54,7 +54,7 @@ static void DPOUpdate(void * id, struct NoteFinder*nf)
|
|||
{
|
||||
index = x+y*d->xn;
|
||||
}
|
||||
CNFGColor( (OutLEDs[index*3+0] <<24) | (OutLEDs[index*3+1] <<16) | (OutLEDs[index*3+2] <<8) | 0xff );
|
||||
CNFGColor( OutLEDs[index*3+0] | (OutLEDs[index*3+1] <<8)|(OutLEDs[index*3+2] <<16) );
|
||||
float dx = (x) * cw;
|
||||
float dy = (y) * ch;
|
||||
|
||||
|
@ -63,7 +63,7 @@ static void DPOUpdate(void * id, struct NoteFinder*nf)
|
|||
else
|
||||
CNFGTackRectangle( dx, dy, dx+cw+.5, dy+ch+.5 );
|
||||
}
|
||||
CNFGColor( 0xffffffff );
|
||||
CNFGColor( 0xffffff );
|
||||
}
|
||||
|
||||
static void DPOParams(void * id )
|
||||
|
|
|
@ -1,10 +1,5 @@
|
|||
//Copyright 2015 <>< Charles Lohr under the ColorChord License.
|
||||
|
||||
// only compile file if we are one a unix system
|
||||
#if defined( WINDOWS ) || defined( USE_WINDOWS ) || defined( WIN32 ) || defined( WIN64 ) || \
|
||||
defined( _WIN32 ) || defined( _WIN64 )
|
||||
#warning Windows does not support the DisplayDMX module
|
||||
#else
|
||||
#include "outdrivers.h"
|
||||
#include "notefinder.h"
|
||||
#include <stdio.h>
|
||||
|
@ -128,4 +123,5 @@ static struct DriverInstances * DisplayDMX()
|
|||
}
|
||||
|
||||
REGISTER_OUT_DRIVER(DisplayDMX);
|
||||
#endif
|
||||
|
||||
|
||||
|
|
|
@ -1,10 +1,5 @@
|
|||
//Copyright 2015 <>< Charles Lohr under the ColorChord License.
|
||||
|
||||
// only compile file if we are one a unix system
|
||||
#if defined( WINDOWS ) || defined( USE_WINDOWS ) || defined( WIN32 ) || defined( WIN64 ) || \
|
||||
defined( _WIN32 ) || defined( _WIN64 )
|
||||
#warning Windows does not support the DisplayFileWrite module
|
||||
#else
|
||||
#include "outdrivers.h"
|
||||
#include "notefinder.h"
|
||||
#include <stdio.h>
|
||||
|
@ -13,7 +8,7 @@
|
|||
#include <math.h>
|
||||
#include <string.h>
|
||||
#include "color.h"
|
||||
#include "CNFG.h"
|
||||
#include "DrawFunctions.h"
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h> /* For mode constants */
|
||||
#include <fcntl.h> /* For O_* constants */
|
||||
|
@ -121,4 +116,5 @@ static struct DriverInstances * DisplayFileWrite(const char * parameters)
|
|||
}
|
||||
|
||||
REGISTER_OUT_DRIVER(DisplayFileWrite);
|
||||
#endif
|
||||
|
||||
|
||||
|
|
|
@ -117,7 +117,7 @@ static void * LEDOutThread( void * v )
|
|||
}
|
||||
}
|
||||
led->readyFlag = 0;
|
||||
//printf( "." ); fflush( stdout );
|
||||
printf( "." ); fflush( stdout );
|
||||
}
|
||||
OGUSleep(100);
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include <math.h>
|
||||
#include <string.h>
|
||||
#include "color.h"
|
||||
#include "DrawFunctions.h"
|
||||
|
||||
#if defined(WIN32) || defined(WINDOWS)
|
||||
#include <windows.h>
|
||||
|
@ -16,7 +17,6 @@
|
|||
#endif
|
||||
#define MSG_NOSIGNAL 0
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#define closesocket( x ) close( x )
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
|
@ -30,10 +30,7 @@ struct DPODriver
|
|||
{
|
||||
int leds;
|
||||
int skipfirst;
|
||||
int wled_realtime;
|
||||
int wled_timeout;
|
||||
int fliprg;
|
||||
int flipgb;
|
||||
int firstval;
|
||||
int port;
|
||||
int is_rgby;
|
||||
|
@ -102,17 +99,6 @@ static void DPOUpdate(void * id, struct NoteFinder*nf)
|
|||
|
||||
d->firstval = 0;
|
||||
i = 0;
|
||||
|
||||
if(d->wled_realtime)
|
||||
{
|
||||
//Packet byte 0 is 2 (DRGB mode) or 3 (DRGBW mode) if is_rgby
|
||||
buffer[i] = 2 + d->is_rgby;
|
||||
lbuff[i++] = 2 + d->is_rgby;
|
||||
//Packet byte 1 is the time in seconds the controller will wait to take control after it stops receiving data
|
||||
buffer[i] = d->wled_timeout;
|
||||
lbuff[i++] = d->wled_timeout;
|
||||
}
|
||||
|
||||
while( i < d->skipfirst )
|
||||
{
|
||||
lbuff[i] = d->firstval;
|
||||
|
@ -162,15 +148,6 @@ static void DPOUpdate(void * id, struct NoteFinder*nf)
|
|||
lbuff[i++] = OutLEDs[j*3+2]; //BLUE
|
||||
}
|
||||
}
|
||||
else if( d->flipgb )
|
||||
{
|
||||
for( j = 0; j < d->leds; j++ )
|
||||
{
|
||||
lbuff[i++] = OutLEDs[j*3+0]; //RED
|
||||
lbuff[i++] = OutLEDs[j*3+1]; //GREEN
|
||||
lbuff[i++] = OutLEDs[j*3+2]; //BLUE
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for( j = 0; j < d->leds; j++ )
|
||||
|
@ -211,7 +188,7 @@ static void DPOUpdate(void * id, struct NoteFinder*nf)
|
|||
}
|
||||
}
|
||||
|
||||
int r = sendto( d->socket, (const char*) buffer, i, MSG_NOSIGNAL,(const struct sockaddr *) &d->servaddr, sizeof( d->servaddr ) );
|
||||
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" );
|
||||
|
@ -229,13 +206,10 @@ static void DPOParams(void * id )
|
|||
|
||||
d->leds = 10; RegisterValue( "leds", PAINT, &d->leds, sizeof( d->leds ) );
|
||||
d->skipfirst = 1; RegisterValue( "skipfirst", PAINT, &d->skipfirst, sizeof( d->skipfirst ) );
|
||||
d->wled_realtime = 0;RegisterValue( "wled_realtime", PAINT, &d->wled_realtime, sizeof( d->wled_realtime ) );
|
||||
d->wled_timeout = 2;RegisterValue( "wled_timeout", PAINT, &d->wled_timeout, sizeof( d->wled_timeout ) );
|
||||
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->fliprg = 0; RegisterValue( "fliprg", PAINT, &d->fliprg, sizeof( d->fliprg ) );
|
||||
d->flipgb = 0; RegisterValue( "flipgb", PAINT, &d->flipgb, sizeof( d->flipgb ) );
|
||||
d->is_rgby = 0; RegisterValue( "rgby", PAINT, &d->is_rgby, sizeof( d->is_rgby ) );
|
||||
d->skittlequantity=0;RegisterValue( "skittlequantity", PAINT, &d->skittlequantity, sizeof( d->skittlequantity ) );
|
||||
d->socket = -1;
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
#include <math.h>
|
||||
#include <string.h>
|
||||
#include "color.h"
|
||||
#include "CNFG.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.
|
||||
|
@ -121,6 +121,26 @@ static void DPOUpdate(void * id, struct NoteFinder*nf)
|
|||
}
|
||||
}
|
||||
|
||||
/* 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++ )
|
||||
{
|
||||
|
@ -130,9 +150,9 @@ static void DPOUpdate(void * id, struct NoteFinder*nf)
|
|||
if( sat > 1 ) sat = 1;
|
||||
uint32_t color = CCtoHEX( nf->note_positions[i] / nf->freqbins, 1.0, sat );
|
||||
|
||||
OutLEDs[led*3+0] = ( color >> 24 ) & 0xff;
|
||||
OutLEDs[led*3+1] = ( color >> 16 ) & 0xff;
|
||||
OutLEDs[led*3+2] = ( color >> 8 ) & 0xff;
|
||||
OutLEDs[led*3+0] = color & 0xff;
|
||||
OutLEDs[led*3+1] = ( color >> 8 ) & 0xff;
|
||||
OutLEDs[led*3+2] = ( color >> 16 ) & 0xff;
|
||||
led++;
|
||||
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
#include <math.h>
|
||||
#include <string.h>
|
||||
#include "color.h"
|
||||
#include "CNFG.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.
|
||||
|
@ -31,8 +31,7 @@ static void DPOUpdate(void * id, struct NoteFinder*nf)
|
|||
int i;
|
||||
float cw = ((float)(screenx)) / 2.0;
|
||||
float ch = ((float)(screeny)) / 2.0;
|
||||
|
||||
RDPoint pts[6];
|
||||
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++ )
|
||||
|
@ -45,21 +44,15 @@ static void DPOUpdate(void * id, struct NoteFinder*nf)
|
|||
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;
|
||||
|
||||
pts[3].x = cw + cos(angA) * sizeA;
|
||||
pts[3].y = ch + sin(angA) * sizeA;
|
||||
pts[4].x = cw + cos(angB) * sizeB;
|
||||
pts[4].y = ch + sin(angB) * sizeB;
|
||||
pts[5].x = cw + cos(angB) * sizeA;
|
||||
pts[5].y = ch + sin(angB) * sizeA;
|
||||
|
||||
CNFGColor( ( OutLEDs[i*3+0] <<24) | (OutLEDs[i*3+1] <<16) | (OutLEDs[i*3+2] <<8) | 0xff );
|
||||
CNFGTackPoly( pts, 3 );
|
||||
CNFGTackPoly( pts+3, 3 );
|
||||
CNFGColor( OutLEDs[i*3+0] | (OutLEDs[i*3+1] <<8)|(OutLEDs[i*3+2] <<16) );
|
||||
CNFGTackPoly( pts, 4 );
|
||||
}
|
||||
|
||||
|
||||
CNFGColor( 0xffffffff );
|
||||
CNFGColor( 0xffffff );
|
||||
}
|
||||
|
||||
static void DPOParams(void * id )
|
||||
|
|
|
@ -1,153 +0,0 @@
|
|||
//Copyright 2015 <>< Charles Lohr under the ColorChord License.
|
||||
|
||||
#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 "CNFG.h"
|
||||
|
||||
//Uses: note_amplitudes2[note] for how many lights to use.
|
||||
//Uses: note_amplitudes_out[note] for how bright it should be.
|
||||
|
||||
extern short screenx, screeny;
|
||||
|
||||
struct DPRDriver
|
||||
{
|
||||
float centeroffset;
|
||||
float radialscale;
|
||||
float polewidth;
|
||||
int radialmode;
|
||||
};
|
||||
|
||||
|
||||
static void DPRUpdate(void * id, struct NoteFinder*nf)
|
||||
{
|
||||
struct DPRDriver * d = (struct DPRDriver*)id;
|
||||
if( d->radialmode == 0 )
|
||||
{
|
||||
int pole = 0;
|
||||
int freqbins = nf->freqbins;
|
||||
for( pole = 0; pole < freqbins; pole++ )
|
||||
{
|
||||
float angle = pole / (float)freqbins * 3.14159 * 2;
|
||||
float cx = screenx/2;
|
||||
float cy = screeny/2;
|
||||
cx += cos( angle ) * d->centeroffset;
|
||||
cy += sin( angle ) * d->centeroffset;
|
||||
float extentx = cx + cos(angle) * d->radialscale * nf->folded_bins[pole];
|
||||
float extenty = cy + sin(angle) * d->radialscale * nf->folded_bins[pole];
|
||||
float orthox = sin(angle) * d->polewidth;
|
||||
float orthoy = -cos(angle) * d->polewidth;
|
||||
|
||||
float p1x = cx + orthox;
|
||||
float p1y = cy + orthoy;
|
||||
float p2x = cx - orthox;
|
||||
float p2y = cy - orthoy;
|
||||
float p3x = extentx + orthox;
|
||||
float p3y = extenty + orthoy;
|
||||
float p4x = extentx - orthox;
|
||||
float p4y = extenty - orthoy;
|
||||
|
||||
CNFGColor( CCtoHEX( (float)pole / (float)freqbins, 1.0, 1.0 ) );
|
||||
RDPoint pts[6] = {
|
||||
{ p1x, p1y },
|
||||
{ p2x, p2y },
|
||||
{ p3x, p3y },
|
||||
{ p4x, p4y },
|
||||
{ p3x, p3y },
|
||||
{ p2x, p2y },
|
||||
};
|
||||
CNFGTackPoly( pts, 3 );
|
||||
CNFGTackPoly( pts+3, 3 );
|
||||
|
||||
|
||||
CNFGColor( 0x000000ff );
|
||||
CNFGTackSegment( p1x, p1y, p2x, p2y);
|
||||
CNFGTackSegment( p2x, p2y, p4x, p4y);
|
||||
CNFGTackSegment( p3x, p3y, p1x, p1y);
|
||||
CNFGTackSegment( p4x, p4y, p3x, p3y);
|
||||
|
||||
}
|
||||
}
|
||||
else if( d->radialmode == 1 )
|
||||
{
|
||||
int pole = 0;
|
||||
int polen = 0;
|
||||
int freqbins = nf->freqbins;
|
||||
for( pole = 0; pole < freqbins; pole++ )
|
||||
{
|
||||
polen = (pole+1)%freqbins;
|
||||
|
||||
float angleT = pole / (float)freqbins * 3.14159 * 2;
|
||||
float angleN = polen / (float)freqbins * 3.14159 * 2;
|
||||
float cx = screenx/2;
|
||||
float cy = screeny/2;
|
||||
|
||||
float p1x = cx + cos( angleT ) * d->centeroffset;
|
||||
float p1y = cy + sin( angleT ) * d->centeroffset;
|
||||
|
||||
float p2x = cx + cos( angleN ) * d->centeroffset;
|
||||
float p2y = cy + sin( angleN ) * d->centeroffset;
|
||||
|
||||
float binval = nf->folded_bins[pole];
|
||||
float binvalN = nf->folded_bins[polen];
|
||||
|
||||
float p3x = cx + cos( angleT ) * (d->radialscale * binval + d->centeroffset);
|
||||
float p3y = cy + sin( angleT ) * (d->radialscale * binval + d->centeroffset);
|
||||
|
||||
float p4x = cx + cos( angleN ) * (d->radialscale * binvalN + d->centeroffset);
|
||||
float p4y = cy + sin( angleN ) * (d->radialscale * binvalN + d->centeroffset);
|
||||
|
||||
CNFGColor( CCtoHEX( (float)pole / (float)freqbins, 1.0, 1.0 ) );
|
||||
RDPoint pts[6] = {
|
||||
{ p1x, p1y },
|
||||
{ p2x, p2y },
|
||||
{ p3x, p3y },
|
||||
{ p4x, p4y },
|
||||
{ p3x, p3y },
|
||||
{ p2x, p2y },
|
||||
};
|
||||
CNFGTackPoly( pts, 3 );
|
||||
CNFGTackPoly( pts+3, 3 );
|
||||
|
||||
CNFGColor( 0x000000ff );
|
||||
CNFGTackSegment( p1x, p1y, p2x, p2y);
|
||||
CNFGTackSegment( p2x, p2y, p4x, p4y);
|
||||
CNFGTackSegment( p3x, p3y, p1x, p1y);
|
||||
CNFGTackSegment( p4x, p4y, p3x, p3y);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
CNFGColor( 0xffffffff );
|
||||
}
|
||||
|
||||
static void DPRParams(void * id )
|
||||
{
|
||||
struct DPRDriver * d = (struct DPRDriver*)id;
|
||||
|
||||
d->centeroffset = 160; RegisterValue( "centeroffset", PAFLOAT, &d->centeroffset, sizeof( d->centeroffset ) );
|
||||
d->radialscale = 1000; RegisterValue( "radialscale", PAFLOAT, &d->radialscale, sizeof( d->radialscale ) );
|
||||
d->polewidth = 20; RegisterValue( "polewidth", PAFLOAT, &d->polewidth, sizeof( d->polewidth ) );
|
||||
d->radialmode = 0; RegisterValue( "radialmode", PAINT, &d->radialmode, sizeof( d->radialmode ) );
|
||||
}
|
||||
|
||||
static struct DriverInstances * DisplayRadialPoles(const char * parameters)
|
||||
{
|
||||
struct DriverInstances * ret = malloc( sizeof( struct DriverInstances ) );
|
||||
struct DPRDriver * d = ret->id = malloc( sizeof( struct DPRDriver ) );
|
||||
memset( d, 0, sizeof( struct DPRDriver ) );
|
||||
ret->Func = DPRUpdate;
|
||||
ret->Params = DPRParams;
|
||||
DPRParams( d );
|
||||
return ret;
|
||||
}
|
||||
|
||||
REGISTER_OUT_DRIVER(DisplayRadialPoles);
|
||||
|
||||
|
|
@ -1,10 +1,5 @@
|
|||
//Copyright 2015 <>< Charles Lohr under the ColorChord License.
|
||||
|
||||
// only compile file if we are one a unix system
|
||||
#if defined( WINDOWS ) || defined( USE_WINDOWS ) || defined( WIN32 ) || defined( WIN64 ) || \
|
||||
defined( _WIN32 ) || defined( _WIN64 )
|
||||
#warning Windows does not support the DisplaySHM module
|
||||
#else
|
||||
#include "outdrivers.h"
|
||||
#include "notefinder.h"
|
||||
#include <stdio.h>
|
||||
|
@ -13,7 +8,7 @@
|
|||
#include <math.h>
|
||||
#include <string.h>
|
||||
#include "color.h"
|
||||
#include "CNFG.h"
|
||||
#include "DrawFunctions.h"
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h> /* For mode constants */
|
||||
#include <fcntl.h> /* For O_* constants */
|
||||
|
@ -122,4 +117,5 @@ static struct DriverInstances * DisplaySHM(const char * parameters)
|
|||
}
|
||||
|
||||
REGISTER_OUT_DRIVER(DisplaySHM);
|
||||
#endif
|
||||
|
||||
|
||||
|
|
|
@ -1,10 +1,5 @@
|
|||
//Copyright 2015 <>< Charles Lohr under the ColorChord License.
|
||||
|
||||
// only compile file if we are one a unix system
|
||||
#if defined( WINDOWS ) || defined( USE_WINDOWS ) || defined( WIN32 ) || defined( WIN64 ) || \
|
||||
defined( _WIN32 ) || defined( _WIN64 )
|
||||
#warning Windows does not support the DisplayUSB2812 module
|
||||
#else
|
||||
#include "outdrivers.h"
|
||||
#include "notefinder.h"
|
||||
#include <stdio.h>
|
||||
|
@ -171,4 +166,5 @@ static struct DriverInstances * DisplayUSB2812()
|
|||
}
|
||||
|
||||
REGISTER_OUT_DRIVER(DisplayUSB2812);
|
||||
#endif
|
||||
|
||||
|
||||
|
|
|
@ -1,197 +0,0 @@
|
|||
//Copyright 2015 <>< Charles Lohr under the ColorChord License.
|
||||
|
||||
#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;
|
||||
volatile int transferring;
|
||||
int xn;
|
||||
int yn;
|
||||
int rot90;
|
||||
};
|
||||
|
||||
static void isocb(struct libusb_transfer *tf)
|
||||
{
|
||||
struct LEDOutDriver *led = (struct LEDOutDriver*)tf->user_data;
|
||||
led->transferring = 0;
|
||||
}
|
||||
|
||||
static void * LEDOutThread( void * v )
|
||||
{
|
||||
struct LEDOutDriver * led = (struct LEDOutDriver*)v;
|
||||
|
||||
while(1)
|
||||
{
|
||||
while(!led->readyFlag || !led->did_init || !led->devh) usleep(100);
|
||||
|
||||
int transfers = ((led->total_leds + 19) / 20);
|
||||
int totallength = transfers * 64;
|
||||
|
||||
struct libusb_transfer *tf = libusb_alloc_transfer(transfers);
|
||||
|
||||
tf->dev_handle = led->devh;
|
||||
tf->flags = LIBUSB_TRANSFER_FREE_TRANSFER;
|
||||
tf->type = LIBUSB_TRANSFER_TYPE_ISOCHRONOUS;
|
||||
tf->num_iso_packets = transfers;
|
||||
libusb_set_iso_packet_lengths(tf, 64);
|
||||
tf->endpoint = 1;
|
||||
tf->flags = 0;
|
||||
tf->buffer = led->last_leds;
|
||||
tf->callback = isocb;
|
||||
tf->length = totallength;
|
||||
tf->user_data = led;
|
||||
|
||||
if(libusb_submit_transfer(tf))
|
||||
{
|
||||
led->did_init = 0;
|
||||
printf( "Fault sending LEDs.\n" );
|
||||
}
|
||||
led->transferring = 1;
|
||||
|
||||
while(led->transferring)
|
||||
{
|
||||
libusb_handle_events(NULL);
|
||||
}
|
||||
led->readyFlag = 0;
|
||||
}
|
||||
|
||||
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, 0x16c0, 0x05dc );
|
||||
|
||||
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 + (i / 20 + 1) * 4] = OutLEDs[source*3+1] * led->outamp;
|
||||
led->last_leds[i*3+1 + (i / 20 + 1) * 4] = OutLEDs[source*3+0] * led->outamp;
|
||||
led->last_leds[i*3+2 + (i / 20 + 1) * 4] = OutLEDs[source*3+2] * led->outamp;
|
||||
if(i % 20 == 0)
|
||||
{
|
||||
led->last_leds[(i / 20) * 64 + 0] = i;
|
||||
led->last_leds[(i / 20) * 64 + 1] = i >> 8;
|
||||
led->last_leds[(i / 20) * 64 + 2] = i >> 16;
|
||||
led->last_leds[(i / 20) * 64 + 3] = i >> 24;
|
||||
}
|
||||
}
|
||||
|
||||
led->readyFlag = 1;
|
||||
}
|
||||
|
||||
static void LEDParams(void * id )
|
||||
{
|
||||
struct LEDOutDriver * led = (struct LEDOutDriver*)id;
|
||||
|
||||
led->total_leds = GetParameterI( "leds", 300 );
|
||||
led->last_leds = (uint8_t*)malloc( led->total_leds * 3 + 1 + ((led->total_leds + 19) / 20) * 4 + 64);
|
||||
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 * DisplayUSBIsochronous()
|
||||
{
|
||||
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(DisplayUSBIsochronous);
|
||||
|
||||
|
276
colorchord2/DrawFunctions.c
Normal file
276
colorchord2/DrawFunctions.c
Normal file
|
@ -0,0 +1,276 @@
|
|||
/*
|
||||
Copyright (c) 2010, 2011, 2015 <>< Charles Lohr
|
||||
|
||||
This file may be licensed under the NewBSD License OR The license found
|
||||
here:
|
||||
|
||||
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
|
||||
|
|
@ -1,42 +1,29 @@
|
|||
all : colorchord colorchord-opengl
|
||||
all : colorchord
|
||||
|
||||
OUTS := OutputVoronoi.o DisplayArray.o OutputLinear.o DisplayPie.o DisplayNetwork.o DisplayUSB2812.o DisplayUSBIsochronous.o DisplayDMX.o OutputProminent.o RecorderPlugin.o DisplayHIDAPI.o hidapi.o OutputCells.o DisplaySHM.o DisplayFileWrite.o DisplayRadialPoles.o
|
||||
RAWDRAW:=DrawFunctions.o XDriver.o
|
||||
SOUND:=sound.o sound_alsa.o sound_pulse.o sound_null.o
|
||||
|
||||
WINGCC?=i686-w64-mingw32-gcc
|
||||
OUTS := OutputVoronoi.o DisplayArray.o OutputLinear.o DisplayPie.o DisplayNetwork.o DisplayUSB2812.o DisplayDMX.o OutputProminent.o RecorderPlugin.o DisplayHIDAPI.o hidapi.o OutputCells.o DisplaySHM.o DisplayFileWrite.o
|
||||
|
||||
WINGCCFLAGS:= -g -DICACHE_FLASH_ATTR= -I../embeddedcommon -Icnfa -Irawdraw -I. -O1 #-O2 -Wl,--relax -Wl,--gc-sections -ffunction-sections -fdata-sections
|
||||
WINLDFLAGS:=-lwinmm -lgdi32 -lws2_32 -lsetupapi -lole32 -lavrt
|
||||
WINGCC:= i686-w64-mingw32-gcc
|
||||
|
||||
#CFLAGS:=-g -O1 -flto -Wall -ffast-math -I../embeddedcommon -I. -Icnfa -Irawdraw -DICACHE_FLASH_ATTR=
|
||||
CFLAGS:=-g -O2 -Wall -ffast-math -I../embeddedcommon -I. -Icnfa -Irawdraw -DICACHE_FLASH_ATTR=
|
||||
WINGCCFLAGS:= -g -DICACHE_FLASH_ATTR= -I../embeddedcommon -I. -O1 #-O2 -Wl,--relax -Wl,--gc-sections -ffunction-sections -fdata-sections
|
||||
WINLDFLAGS:=-lwinmm -lgdi32 -lws2_32 -lsetupapi
|
||||
|
||||
ifneq ($(OS),Windows_NT)
|
||||
ifneq ($(shell uname),Darwin)
|
||||
RAWDRAWCFLAGS:=$(shell pkg-config --cflags x11 xinerama xext libusb-1.0)
|
||||
RAWDRAWLIBS:=-lm -lrt -pthread $(shell pkg-config --libs x11 xinerama xext libusb-1.0)
|
||||
CFLAGS+=$(RAWDRAWCFLAGS)
|
||||
endif
|
||||
endif
|
||||
RAWDRAWLIBS:=-lX11 -lm -lpthread -lXinerama -lXext
|
||||
LDLIBS:=-lpthread -lasound -lm -lpulse-simple -lpulse -ludev -lrt
|
||||
|
||||
ifeq ($(shell uname),Linux)
|
||||
EXTRALIBS+=-ludev
|
||||
EXTRALIBS+=$(shell pkg-config --libs alsa)
|
||||
EXTRALIBS+=$(shell pkg-config --libs libpulse)
|
||||
EXTRALIBS+=$(shell pkg-config --libs libpulse-simple)
|
||||
OPENGLLIBS=$(shell pkg-config --cflags --libs glut)
|
||||
endif
|
||||
|
||||
OBJS:=main.o dft.o decompose.o filter.o color.o notefinder.o util.o outdrivers.o $(OUTS) parameters.o chash.o hook.o ../embeddedcommon/DFT32.o configs.o
|
||||
CFLAGS:=-g -O0 -flto -Wall -ffast-math -I../embeddedcommon -I. -DICACHE_FLASH_ATTR=
|
||||
EXTRALIBS:=-lusb-1.0
|
||||
|
||||
colorchord : $(OBJS)
|
||||
$(CC) -o $@ $^ $(EXTRALIBS) $(RAWDRAWLIBS)
|
||||
colorchord : os_generic.o main.o dft.o decompose.o filter.o color.o notefinder.o util.o outdrivers.o $(RAWDRAW) $(SOUND) $(OUTS) parameters.o chash.o hook.o ../embeddedcommon/DFT32.o configs.o
|
||||
gcc -o $@ $^ $(CFLAGS) $(LDLIBS) $(EXTRALIBS) $(RAWDRAWLIBS)
|
||||
|
||||
colorchord-opengl : $(OBJS)
|
||||
$(CC) -o $@ $^ -DCNFGOGL $(EXTRALIBS) $(OPENGLLIBS) $(RAWDRAWLIBS)
|
||||
|
||||
colorchord.exe : main.c dft.c decompose.c filter.c color.c notefinder.c util.c outdrivers.c parameters.c chash.c OutputVoronoi.c OutputProminent.c DisplayArray.c OutputLinear.c DisplayPie.c DisplayNetwork.c hook.c RecorderPlugin.c ../embeddedcommon/DFT32.c OutputCells.c configs.c hidapi.c DisplayHIDAPI.c
|
||||
$(WINGCC) $(WINGCCFLAGS) -o $@ $^ $(o) $(WINLDFLAGS)
|
||||
colorchord.exe : os_generic.c main.c dft.c decompose.c filter.c color.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 OutputProminent.c DisplayArray.c OutputLinear.c DisplayPie.c DisplayNetwork.c hook.c RecorderPlugin.c ../embeddedcommon/DFT32.c OutputCells.c configs.c hidapi.c DisplayHIDAPI.c
|
||||
$(WINGCC) $(WINGCCFLAGS) -o $@ $^ $(WINLDFLAGS)
|
||||
|
||||
|
||||
clean :
|
||||
rm -rf *.o *~ colorchord colorchord-opengl colorchord.exe embeddedcc $(OBJS)
|
||||
rm -rf *.o *~ colorchord colorchord.exe embeddedcc
|
||||
|
|
|
@ -30,7 +30,6 @@ struct CellsOutDriver
|
|||
float light_siding;
|
||||
float satamp;
|
||||
float qtyamp;
|
||||
float outgamma;
|
||||
int steady_bright;
|
||||
int timebased; //Useful for pies, turn off for linear systems.
|
||||
int snakey; //Advance head for where to get LEDs around.
|
||||
|
@ -185,11 +184,11 @@ static void LEDUpdate(void * id, struct NoteFinder*nf)
|
|||
if( satQ > 1 ) satQ = 1;
|
||||
float sendsat = (led->steady_bright?sat:satQ);
|
||||
if( sendsat > 1 ) sendsat = 1;
|
||||
int r = CCtoHEX( binpos[ia], 1.0, pow( sendsat, led->outgamma ) );
|
||||
int r = CCtoHEX( binpos[ia], 1.0, sendsat );
|
||||
|
||||
OutLEDs[i*3+0] = (r>>24) & 0xff;
|
||||
OutLEDs[i*3+1] = (r>>16) & 0xff;
|
||||
OutLEDs[i*3+2] = (r>>8) & 0xff;
|
||||
OutLEDs[i*3+0] = r & 0xff;
|
||||
OutLEDs[i*3+1] = (r>>8) & 0xff;
|
||||
OutLEDs[i*3+2] = (r>>16) & 0xff;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -200,12 +199,11 @@ static void LEDParams(void * 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->steady_bright = 1; RegisterValue( "steady_bright", PAINT, &led->steady_bright, sizeof( led->steady_bright ) );
|
||||
led->steady_bright = 1; RegisterValue( "seady_bright", PAINT, &led->steady_bright, sizeof( led->steady_bright ) );
|
||||
led->led_floor = .1; RegisterValue( "led_floor", PAFLOAT, &led->led_floor, sizeof( led->led_floor ) );
|
||||
led->light_siding = 1.9;RegisterValue( "light_siding", PAFLOAT, &led->light_siding, sizeof( led->light_siding ) );
|
||||
led->qtyamp = 20; RegisterValue( "qtyamp", PAFLOAT, &led->qtyamp, sizeof( led->qtyamp ) );
|
||||
led->timebased = 1; RegisterValue( "timebased", PAINT, &led->timebased, sizeof( led->timebased ) );
|
||||
led->outgamma = 1.0; RegisterValue( "outgamma", PAFLOAT, &led->outgamma, sizeof( led->outgamma ) );
|
||||
|
||||
led->snakey = 0; RegisterValue( "snakey", PAINT, &led->snakey, sizeof( led->snakey ) );
|
||||
|
||||
|
|
|
@ -24,7 +24,6 @@ struct LEDOutDriver
|
|||
float last_led_amp[MAX_LEDS];
|
||||
int steady_bright;
|
||||
float led_floor;
|
||||
float outgamma;
|
||||
float led_limit; //Maximum brightness
|
||||
float satamp;
|
||||
int lastadvance;
|
||||
|
@ -178,13 +177,14 @@ static void LEDUpdate(void * id, struct NoteFinder*nf)
|
|||
|
||||
if( sendsat > led->led_limit ) sendsat = led->led_limit;
|
||||
|
||||
int r = CCtoHEX( led->last_led_pos[i], 1.0, pow( sendsat, led->outgamma ) );
|
||||
int r = CCtoHEX( led->last_led_pos[i], 1.0, sendsat );
|
||||
|
||||
OutLEDs[i*3+0] = (r>>24) & 0xff;
|
||||
OutLEDs[i*3+1] = (r>>16) & 0xff;
|
||||
OutLEDs[i*3+2] = (r>>8) & 0xff;
|
||||
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++ )
|
||||
|
@ -206,7 +206,6 @@ static void LEDParams(void * id )
|
|||
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 ) );
|
||||
led->led_limit = 1; RegisterValue( "led_limit", PAFLOAT, &led->led_limit, sizeof( led->led_limit ) );
|
||||
led->outgamma = 1.0; RegisterValue( "outgamma", PAFLOAT, &led->outgamma, sizeof( led->outgamma ) );
|
||||
|
||||
printf( "Found LEDs for output. leds=%d\n", led->total_leds );
|
||||
|
||||
|
|
|
@ -43,16 +43,18 @@ static void LEDUpdate(void * id, struct NoteFinder*nf)
|
|||
}
|
||||
}
|
||||
|
||||
float sendsat = selected_amp;
|
||||
if( sendsat > 1 ) sendsat = 1;
|
||||
int r = CCtoHEX( selected_note, 1.0, sendsat );
|
||||
|
||||
|
||||
//Advance the LEDs to this position when outputting the values.
|
||||
for( i = 0; i < led->total_leds; i++ )
|
||||
{
|
||||
OutLEDs[i*3+0] = (r>>24) & 0xff;
|
||||
OutLEDs[i*3+1] = (r>>16) & 0xff;
|
||||
OutLEDs[i*3+2] = (r>>8) & 0xff;
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -25,7 +25,6 @@ struct DPODriver
|
|||
{
|
||||
int xn;
|
||||
int yn;
|
||||
float outgamma;
|
||||
float cutoff;
|
||||
float satamp;
|
||||
float amppow; //For amplitudes
|
||||
|
@ -127,13 +126,12 @@ static void DPOUpdate(void * id, struct NoteFinder*nf)
|
|||
{
|
||||
float sat = nf->note_amplitudes_out[bestmatch] * d->satamp;
|
||||
if( sat > 1.0 ) sat = 1.0;
|
||||
float note_color = nf->note_positions[bestmatch] / nf->freqbins;
|
||||
color = CCtoHEX( note_color, 1.0, pow( sat, d->outgamma ) );
|
||||
color = CCtoHEX( nf->note_positions[bestmatch] / nf->freqbins, 1.0, sat );
|
||||
}
|
||||
|
||||
OutLEDs[led*3+0] = ( color >> 24 ) & 0xff;
|
||||
OutLEDs[led*3+1] = ( color >> 16 ) & 0xff;
|
||||
OutLEDs[led*3+2] = ( color >> 8 ) & 0xff;
|
||||
OutLEDs[led*3+0] = color & 0xff;
|
||||
OutLEDs[led*3+1] = ( color >> 8 ) & 0xff;
|
||||
OutLEDs[led*3+2] = ( color >> 16 ) & 0xff;
|
||||
led++;
|
||||
}
|
||||
}
|
||||
|
@ -147,7 +145,7 @@ static void DPOParams(void * id )
|
|||
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->outgamma = 1.0; RegisterValue( "outgamma", PAFLOAT, &d->outgamma, sizeof( d->outgamma ) );
|
||||
|
||||
d->amppow = 2.51; RegisterValue( "amppow", PAFLOAT, &d->amppow, sizeof( d->amppow ) );
|
||||
d->distpow = 1.5; RegisterValue( "distpow", PAFLOAT, &d->distpow, sizeof( d->distpow ) );
|
||||
|
||||
|
|
|
@ -107,7 +107,7 @@ void StartRecording( struct RecorderPlugin * rp )
|
|||
}
|
||||
|
||||
|
||||
static void RecordEvent(void * v, int samples, short * samps, int channel_ct)
|
||||
static void RecordEvent(void * v, int samples, float * samps, int channel_ct)
|
||||
{
|
||||
struct RecorderPlugin * rp = (struct RecorderPlugin*)v;
|
||||
|
||||
|
@ -115,7 +115,7 @@ static void RecordEvent(void * v, int samples, short * samps, int channel_ct)
|
|||
|
||||
if( rp->DunBoop || !rp->fPlay )
|
||||
{
|
||||
int r = fwrite( samps, channel_ct * sizeof( short ), samples, rp->fRec );
|
||||
int r = fwrite( samps, channel_ct * sizeof( float ), samples, rp->fRec );
|
||||
if( r != samples )
|
||||
{
|
||||
StopRecording( rp );
|
||||
|
@ -123,12 +123,12 @@ static void RecordEvent(void * v, int samples, short * samps, int channel_ct)
|
|||
}
|
||||
}
|
||||
|
||||
static void PlaybackEvent(void * v, int samples, short * samps, int channel_ct)
|
||||
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( short ), samples, rp->fPlay );
|
||||
int r = fread( samps, channel_ct * sizeof( float ), samples, rp->fPlay );
|
||||
if( r != samples )
|
||||
{
|
||||
StopRecording( rp );
|
||||
|
@ -143,7 +143,7 @@ static void PlaybackEvent(void * v, int samples, short * samps, int channel_ct)
|
|||
else
|
||||
force_white = 0;
|
||||
|
||||
int r = fwrite( samps, channel_ct * sizeof( short ), samples, rp->fRec );
|
||||
int r = fwrite( samps, channel_ct * sizeof( float ), samples, rp->fRec );
|
||||
if( r != samples )
|
||||
{
|
||||
StopRecording( rp );
|
||||
|
|
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
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
||||
<manifest xmlns:tools="http://schemas.android.com/tools" xmlns:android="http://schemas.android.com/apk/res/android" package="org.cnlohr.colorchord"
|
||||
android:versionCode="8">
|
||||
|
||||
<uses-sdk android:minSdkVersion="22"
|
||||
android:targetSdkVersion="28" />
|
||||
|
||||
<uses-permission android:name="android.permission.SET_RELEASE_APP"/>
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
|
||||
<application android:debuggable="false" android:hasCode="false" android:label="colorchord" tools:replace="android:icon,android:theme,android:allowBackup,label" android:icon="@mipmap/icon" > <!--android:requestLegacyExternalStorage="true" Not needed til Android 29 -->
|
||||
<activity android:configChanges="keyboardHidden|orientation" android:label="colorchord" android:name="android.app.NativeActivity">
|
||||
|
||||
<!-- This device filter seems to do nothing at all! If you figure out how to use it or what it does, let me know!! -->
|
||||
<intent-filter>
|
||||
<action android:name="android.hardware.usb.action.ACTION_USB_DEVICE_ATTACHED" />
|
||||
</intent-filter>
|
||||
<meta-data android:name="android.hardware.usb.action.ACTION_USB_DEVICE_ATTACHED" android:resource="@xml/device_filter" />
|
||||
|
||||
<meta-data android:name="android.app.lib_name" android:value="colorchord"/>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
</manifest>
|
|
@ -1,129 +0,0 @@
|
|||
#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 "CNFG.h"
|
||||
#include "rawdrawandroid/android_usb_devices.h"
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <linux/usbdevice_fs.h>
|
||||
#include <asm/byteorder.h>
|
||||
|
||||
extern int is_suspended;
|
||||
|
||||
#define MAX_LEDS_PER_NOTE 512
|
||||
|
||||
extern short screenx, screeny;
|
||||
|
||||
struct DTADriver
|
||||
{
|
||||
int leds;
|
||||
};
|
||||
|
||||
char TensigralDebugStatus[8192];
|
||||
|
||||
int RequestPermissionOrGetConnectionFD( char * debug_status, uint16_t vid, uint16_t pid );
|
||||
void DisconnectUSB(); //Disconnect from USB
|
||||
|
||||
extern jobject deviceConnection;
|
||||
extern int deviceConnectionFD;
|
||||
|
||||
|
||||
|
||||
static void DTAUpdate(void * id, struct NoteFinder*nf)
|
||||
{
|
||||
struct DTADriver * d = (struct DTADriver*)id;
|
||||
int i;
|
||||
|
||||
if( deviceConnectionFD == 0 )
|
||||
{
|
||||
RequestPermissionOrGetConnectionFD( TensigralDebugStatus, 0xabcd, 0xf410 );
|
||||
}
|
||||
|
||||
if( !is_suspended )
|
||||
{
|
||||
CNFGPenX = 800;
|
||||
CNFGPenY = 800;
|
||||
CNFGColor( 0xffffff );
|
||||
CNFGDrawText( TensigralDebugStatus, 2 );
|
||||
}
|
||||
|
||||
if( !deviceConnectionFD ) return;
|
||||
|
||||
|
||||
uint8_t packet[64];
|
||||
|
||||
if( deviceConnectionFD )
|
||||
{
|
||||
//This section does the crazy wacky stuff to actually split the LEDs into HID Packets and get them out the door... Carefully.
|
||||
int byrem = d->leds*4; //OutLEDs[i*3+1]
|
||||
int offset = 0;
|
||||
int marker = 0;
|
||||
for( i = 0; i < 2; i++ )
|
||||
{
|
||||
uint8_t sendbuf[64];
|
||||
sendbuf[0] = (byrem > 60)?15:(byrem/4);
|
||||
sendbuf[1] = offset;
|
||||
|
||||
// memcpy( sendbuf + 2, Colorbuf + offset*4, sendbuf[0]*4 );
|
||||
int j;
|
||||
for( j = 0; j < sendbuf[0]; j++ )
|
||||
{
|
||||
sendbuf[j*4+3] = OutLEDs[marker++];
|
||||
sendbuf[j*4+2] = OutLEDs[marker++];
|
||||
sendbuf[j*4+4] = OutLEDs[marker++];
|
||||
sendbuf[j*4+5] = 0;
|
||||
}
|
||||
|
||||
offset += sendbuf[0];
|
||||
byrem -= sendbuf[0]*4;
|
||||
|
||||
|
||||
if( byrem == 0 ) sendbuf[0] |= 0x80;
|
||||
int tsend = 65; //Size of payload (must be 64+1 always)
|
||||
|
||||
//Ok this looks weird, because Android has a bulkTransfer function, but that has a TON of layers of misdirection before it just calls the ioctl.
|
||||
struct usbdevfs_bulktransfer ctrl;
|
||||
memset(&ctrl, 0, sizeof(ctrl));
|
||||
ctrl.ep = 0x02; //Endpoint 0x02 is output endpoint.
|
||||
ctrl.len = 64;
|
||||
ctrl.data = sendbuf;
|
||||
ctrl.timeout = 100;
|
||||
int lastFDWrite = ioctl(deviceConnectionFD, USBDEVFS_BULK, &ctrl);
|
||||
if( lastFDWrite < 0 )
|
||||
{
|
||||
DisconnectUSB();
|
||||
break;
|
||||
}
|
||||
usleep(1000);
|
||||
}
|
||||
}
|
||||
|
||||
CNFGColor( 0xffffff );
|
||||
}
|
||||
|
||||
static void DTAParams(void * id )
|
||||
{
|
||||
struct DTADriver * d = (struct DTADriver*)id;
|
||||
|
||||
d->leds = 9; RegisterValue( "leds", PAINT, &d->leds, sizeof( d->leds ) );
|
||||
}
|
||||
|
||||
static struct DriverInstances * DisplayTensigralAndroid(const char * parameters)
|
||||
{
|
||||
struct DriverInstances * ret = malloc( sizeof( struct DriverInstances ) );
|
||||
struct DTADriver * d = ret->id = malloc( sizeof( struct DTADriver ) );
|
||||
memset( d, 0, sizeof( struct DTADriver ) );
|
||||
ret->Func = DTAUpdate;
|
||||
ret->Params = DTAParams;
|
||||
DTAParams( d );
|
||||
return ret;
|
||||
}
|
||||
|
||||
REGISTER_OUT_DRIVER(DisplayTensigralAndroid);
|
||||
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
|
||||
APPNAME=colorchord
|
||||
RAWDRAWANDROID=rawdrawandroid
|
||||
PACKAGENAME?=org.cnlohr.$(APPNAME)
|
||||
|
||||
CFLAGS:=-I. -I.. -Irawdrawandroid/rawdraw -I../cnfa -I../../embeddedcommon \
|
||||
-ffunction-sections -Os -s -DPRINTF_NO_OVERRIDDE -fvisibility=hidden \
|
||||
-DRDALOGFNCB=example_log_function
|
||||
|
||||
ANDROIDVERSION=22
|
||||
ANDROIDTARGET=28
|
||||
|
||||
LDFLAGS:=-s -lOpenSLES
|
||||
|
||||
CC_C:= ../main.c ../dft.c ../decompose.c ../filter.c ../color.c ../notefinder.c ../util.c ../hook.c ../outdrivers.c ../parameters.c ../chash.c ../OutputVoronoi.c ../OutputProminent.c ../DisplayArray.c ../OutputLinear.c ../DisplayPie.c ../DisplayNetwork.c ../../embeddedcommon/DFT32.c ../OutputCells.c ../configs.c
|
||||
|
||||
SRC:=rawdrawandroid/android_usb_devices.c DisplayTensigralAndroid.c $(CC_C)
|
||||
|
||||
|
||||
#Uncomment to make all targets.
|
||||
TARGETS:=makecapk/lib/armeabi-v7a/lib$(APPNAME).so #makecapk/lib/arm64-v8a/lib$(APPNAME).so makecapk/lib/x86/lib$(APPNAME).so makecapk/lib/x86_64/lib$(APPNAME).so
|
||||
|
||||
|
||||
include rawdrawandroid/Makefile
|
||||
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
This part of the project is baed on https://github.com/cnlohr/androidusbtest
|
||||
|
||||
You should be able to build this. If you give your app full permissions, and you
|
||||
copy a copy of `colorchord-android.conf` into the external storage folder on your
|
||||
Android device at the root level, it should pick it up and switch over to a pie
|
||||
style display and output to the tensigral lamp.
|
Binary file not shown.
Before Width: | Height: | Size: 81 KiB |
|
@ -1,8 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<!-- This file seems to do NOTHING AT ALL! If you have any idea how to use the device filters file, please let me know! -->
|
||||
<resources>
|
||||
<!-- <usb-device vendor-id="abcd" product-id="f410" /> --> <!-- class="3" subclass="255" protocol="255" -->
|
||||
<usb-device vendor-id="43981" product-id="62480" />
|
||||
</resources>
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
# Put configs here. Note when you reload the file, a lot of variables can be updated dynamically, but some like drivers cannot be changed at the time.
|
||||
|
||||
#Play with OutputCells and OutputLinear
|
||||
|
||||
|
||||
amplify=4.0
|
||||
outdrivers = DisplayPie,DisplayTensigralAndroid,OutputCells
|
||||
leds = 20
|
||||
light_siding = 2.2 #Turn this to ~1.9 for more uniformity, ~1.0 for less. (Linear driver looks good at 1, cells look good at 1.9)
|
||||
|
||||
timebased = 1
|
||||
snakey = 1
|
||||
|
||||
|
|
@ -1 +0,0 @@
|
|||
Subproject commit c9700f455e17ff3db9bb509c3f4cb54edfff81b0
|
|
@ -21,6 +21,8 @@ amplify=.4
|
|||
compress_coefficient = 4.0
|
||||
compress_exponent = .5
|
||||
|
||||
sourcename = alsa_output.pci-0000_00_1f.3.analog-stereo.monitor
|
||||
|
||||
fliprg = 1
|
||||
skittlequantity = 24
|
||||
timebased = 1
|
|
@ -10,13 +10,6 @@
|
|||
#include <stdio.h>
|
||||
#include <malloc.h>
|
||||
|
||||
#if defined(WINDOWS) || defined(WIN32) || defined(WIN64) \
|
||||
|| defined(_WIN32) || defined(_WIN64)
|
||||
#ifndef strdup
|
||||
#define strdup _strdup
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define I_AM_LITTLE (((union { unsigned x; unsigned char c; }){1}).c)
|
||||
|
||||
static unsigned long GetStrHash( const char * c )
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
Subproject commit d271e0196d81412032eeffa634a94a1aaf0060a7
|
|
@ -9,23 +9,23 @@ uint32_t CCtoHEX( float note, float sat, float value )
|
|||
{
|
||||
float hue = 0.0;
|
||||
note = fmodf( note, 1.0 );
|
||||
note *= 12.0;
|
||||
if( note < 4.0 )
|
||||
note *= 12;
|
||||
if( note < 4 )
|
||||
{
|
||||
//Needs to be YELLOW->RED
|
||||
hue = (4.0 - note) / 24.0;
|
||||
hue = (4 - note) / 24.0;
|
||||
}
|
||||
else if( note < 8.0 )
|
||||
else if( note < 8 )
|
||||
{
|
||||
// [4] [8]
|
||||
//Needs to be RED->BLUE
|
||||
hue = ( 4.0 - note ) / 12.0;
|
||||
hue = ( 4 - note ) / 12.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// [8] [12]
|
||||
//Needs to be BLUE->YELLOW
|
||||
hue = ( 12.0 - note ) / 8.0 + 1.0/6.0;
|
||||
hue = ( 12 - note ) / 8.0 + 1./6.;
|
||||
}
|
||||
return HSVtoHEX( hue, sat, value );
|
||||
}
|
||||
|
@ -41,44 +41,44 @@ uint32_t CCtoHEX( float note, float sat, float value )
|
|||
uint32_t HSVtoHEX( float hue, float sat, float value )
|
||||
{
|
||||
|
||||
float pr = 0.0;
|
||||
float pg = 0.0;
|
||||
float pb = 0.0;
|
||||
float pr = 0;
|
||||
float pg = 0;
|
||||
float pb = 0;
|
||||
|
||||
short ora = 0.0;
|
||||
short og = 0.0;
|
||||
short ob = 0.0;
|
||||
short ora = 0;
|
||||
short og = 0;
|
||||
short ob = 0;
|
||||
|
||||
float ro = fmod( hue * 6.0, 6.0 );
|
||||
float ro = fmod( hue * 6, 6. );
|
||||
|
||||
float avg = 0.0;
|
||||
float avg = 0;
|
||||
|
||||
ro = fmod( ro + 6.0 + 1.0, 6.0 ); //Hue was 60* off...
|
||||
ro = fmod( ro + 6 + 1, 6 ); //Hue was 60* off...
|
||||
|
||||
if( ro < 1.0 ) //yellow->red
|
||||
if( ro < 1 ) //yellow->red
|
||||
{
|
||||
pr = 1.0;
|
||||
pg = 1.0 - ro;
|
||||
} else if( ro < 2.0 )
|
||||
pr = 1;
|
||||
pg = 1. - ro;
|
||||
} else if( ro < 2 )
|
||||
{
|
||||
pr = 1.0;
|
||||
pb = ro - 1.0;
|
||||
} else if( ro < 3.0 )
|
||||
pr = 1;
|
||||
pb = ro - 1.;
|
||||
} else if( ro < 3 )
|
||||
{
|
||||
pr = 3.0 - ro;
|
||||
pb = 1.0;
|
||||
} else if( ro < 4.0 )
|
||||
pr = 3. - ro;
|
||||
pb = 1;
|
||||
} else if( ro < 4 )
|
||||
{
|
||||
pb = 1.0;
|
||||
pg = ro - 3.0;
|
||||
} else if( ro < 5.0 )
|
||||
pb = 1;
|
||||
pg = ro - 3;
|
||||
} else if( ro < 5 )
|
||||
{
|
||||
pb = 5.0 - ro;
|
||||
pg = 1.0;
|
||||
pb = 5 - ro;
|
||||
pg = 1;
|
||||
} else
|
||||
{
|
||||
pg = 1.0;
|
||||
pr = ro - 5.0;
|
||||
pg = 1;
|
||||
pr = ro - 5;
|
||||
}
|
||||
|
||||
//Actually, above math is backwards, oops!
|
||||
|
@ -90,9 +90,9 @@ uint32_t HSVtoHEX( float hue, float sat, float value )
|
|||
avg += pg;
|
||||
avg += pb;
|
||||
|
||||
pr = pr * sat + avg * (1.0-sat);
|
||||
pg = pg * sat + avg * (1.0-sat);
|
||||
pb = pb * sat + avg * (1.0-sat);
|
||||
pr = pr * sat + avg * (1.-sat);
|
||||
pg = pg * sat + avg * (1.-sat);
|
||||
pb = pb * sat + avg * (1.-sat);
|
||||
|
||||
ora = pr * 255;
|
||||
og = pb * 255;
|
||||
|
@ -105,6 +105,5 @@ uint32_t HSVtoHEX( float hue, float sat, float value )
|
|||
if( ob < 0 ) ob = 0;
|
||||
if( ob > 255 ) ob = 255;
|
||||
|
||||
// Pack bits in RGBA format
|
||||
return (ora << 24) | (og << 16) | (ob << 8) | 0xFF;
|
||||
return (ob<<16) | (og<<8) | ora;
|
||||
}
|
||||
|
|
BIN
colorchord2/colorchord.exe
Executable file
BIN
colorchord2/colorchord.exe
Executable file
Binary file not shown.
|
@ -19,7 +19,6 @@ void LoadFile( const char * filename )
|
|||
char * buffer;
|
||||
int r;
|
||||
|
||||
printf( "Loading file: %s\n", filename );
|
||||
FILE * f = fopen( filename, "rb" );
|
||||
if( !f )
|
||||
{
|
||||
|
@ -31,12 +30,12 @@ void LoadFile( const char * filename )
|
|||
int size = ftell( f );
|
||||
fseek( f, 0, SEEK_SET );
|
||||
buffer = malloc( size + 1 );
|
||||
r = fread( buffer, 1, size, f);
|
||||
r = fread( buffer, size, 1, f );
|
||||
fclose( f );
|
||||
buffer[size] = 0;
|
||||
if( r != size )
|
||||
if( r != 1 )
|
||||
{
|
||||
fprintf( stderr, "Warning: %d bytes read. Expected: %d from file %s\n", r, size, filename );
|
||||
fprintf( stderr, "Warning: %d bytes read. Expected: %d from file %s\n", r, size, filename );
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -48,76 +47,25 @@ void LoadFile( const char * filename )
|
|||
|
||||
void SetEnvValues( int force )
|
||||
{
|
||||
static int ifcheck;
|
||||
int i;
|
||||
int hits = 0;
|
||||
|
||||
if( InitialFileCount )
|
||||
for( i = 0; i < InitialFileCount; i++ )
|
||||
{
|
||||
//Only check one location per frame.
|
||||
double ft = OGGetFileTime( InitialFile[ifcheck] );
|
||||
if( FileTimes[ifcheck] != ft )
|
||||
double ft = OGGetFileTime( InitialFile[i] );
|
||||
if( FileTimes[i] != ft )
|
||||
{
|
||||
FileTimes[ifcheck] = ft;
|
||||
FileTimes[i] = ft;
|
||||
hits++;
|
||||
}
|
||||
ifcheck = ( ifcheck + 1 ) % InitialFileCount;
|
||||
}
|
||||
|
||||
if( !hits && !force ) return;
|
||||
|
||||
//Otherwise, something changed.
|
||||
#ifdef ANDROID
|
||||
SetParametersFromString( "cpu_autolimit=1" );
|
||||
SetParametersFromString( "set_screenx=720" );
|
||||
SetParametersFromString( "set_screeny=480" );
|
||||
SetParametersFromString( "buffer=384" );
|
||||
SetParametersFromString( "play=0" );
|
||||
SetParametersFromString( "rec=1" );
|
||||
SetParametersFromString( "channels=2" );
|
||||
SetParametersFromString( "samplerate=44100" );
|
||||
SetParametersFromString( "sourcename=default" );
|
||||
SetParametersFromString( "amplify=2.0" );
|
||||
SetParametersFromString( "base_hz=55" );
|
||||
|
||||
SetParametersFromString( "dft_iir=0.6" );
|
||||
SetParametersFromString( "dft_q=20.0000" );
|
||||
SetParametersFromString( "dft_speedup=1000.0000" );
|
||||
SetParametersFromString( "octaves=5" );
|
||||
LoadFile( InitialFile[0] );
|
||||
|
||||
SetParametersFromString( "do_progressive_dft=4" );
|
||||
|
||||
SetParametersFromString( "filter_iter=2" );
|
||||
SetParametersFromString( "filter_strength=.5" );
|
||||
SetParametersFromString( "freqbins = 24" );
|
||||
SetParametersFromString( "do_progressive_dft=4" );
|
||||
SetParametersFromString( "note_attach_amp_iir=0.3500" );
|
||||
SetParametersFromString( "note_attach_amp_iir2=0.250" );
|
||||
|
||||
SetParametersFromString( "note_combine_distance=0.5000" );
|
||||
SetParametersFromString( "note_jumpability=1.8000" );
|
||||
SetParametersFromString( "note_minimum_new_distribution_value=0.0200" );
|
||||
SetParametersFromString( "note_out_chop=0.05000" );
|
||||
SetParametersFromString( "outdrivers=OutputVoronoi,DisplayArray" );
|
||||
SetParametersFromString( "note_attach_amp_iir2=0.250" );
|
||||
|
||||
SetParametersFromString( "lightx=32" );
|
||||
SetParametersFromString( "lighty=60" );
|
||||
SetParametersFromString( "fromsides=1" );
|
||||
SetParametersFromString( "shape_cutoff=0.03" );
|
||||
|
||||
SetParametersFromString( "satamp=5.000" );
|
||||
SetParametersFromString( "amppow=2.510" );
|
||||
SetParametersFromString( "distpow=1.500" );
|
||||
|
||||
printf( "On Android, looking for configuration file in: %s\n", InitialFile[0] );
|
||||
#endif
|
||||
|
||||
int i;
|
||||
for( i = 0; i < InitialFileCount; i++ )
|
||||
{
|
||||
LoadFile( InitialFile[i] );
|
||||
}
|
||||
for( ; i < gargc; i++ )
|
||||
for( i = 1; i < gargc; i++ )
|
||||
{
|
||||
if( strchr( gargv[i], '=' ) != 0 )
|
||||
{
|
||||
|
@ -153,15 +101,9 @@ void ProcessArgs()
|
|||
|
||||
void SetupConfigs()
|
||||
{
|
||||
#ifdef ANDROID
|
||||
InitialFile[0] = "/sdcard/colorchord-android.txt";
|
||||
InitialFile[1] = "/storage/emulated/0/colorchord-android.txt";
|
||||
InitialFile[2] = "/sdcard/colorchord-android-overlay.txt";
|
||||
InitialFile[3] = "/storage/emulated/0/colorchord-android-overlay.txt";
|
||||
InitialFileCount = 4;
|
||||
#else
|
||||
|
||||
InitialFile[0] = "default.conf";
|
||||
#endif
|
||||
|
||||
ProcessArgs();
|
||||
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ int DecomposeHistogram( float * histogram, int bins, struct NoteDists * out_dist
|
|||
float this = histogram[i];
|
||||
float next = histogram[(i+1)%bins];
|
||||
|
||||
if( prev >= this || next > this ) continue;
|
||||
if( prev > this || next > this ) continue;
|
||||
if( prev == this && next == this ) continue;
|
||||
|
||||
//i is at a peak...
|
||||
|
|
|
@ -8,80 +8,66 @@
|
|||
cpu_autolimit = 1
|
||||
|
||||
#General GUI properties.
|
||||
title = ColorChord Default Configuration (Vornoi)
|
||||
title = PA Test
|
||||
set_screenx = 720
|
||||
set_screeny = 480
|
||||
|
||||
#######################################
|
||||
# ColorChord audio driver properties. #
|
||||
#######################################
|
||||
|
||||
# Colorchord now automatically picks most of the important audio driver settings automatically
|
||||
# and will default to PULSE with ALSA as a fallback on Linux,
|
||||
# On Windows it will default to WASAPI with winmm (WIN) as a fallback
|
||||
# To force a specific driver use the "sound_source" property, the following values are valid
|
||||
# sound_source:
|
||||
# Linux: PULSE, ALSA
|
||||
# Windows: WASAPI, WIN
|
||||
# Android: ANDROID
|
||||
# sound_source =
|
||||
|
||||
# The "devplay" property sets the playback device for CNFA (what speakers to go to) most uses for
|
||||
# colorchord don't use audio output so this can be almost universally ignored
|
||||
# devplay =
|
||||
|
||||
# The "devrecord" Sets the device to get audio from. This will default to monitoring your speaker
|
||||
# output. For WASAPI, "default" searches for a mic, and "defaultRender" searches for your loopback.
|
||||
# For PulseAudio @DEFAULT_SOURCE@ selects the default input (typically a microphone) and
|
||||
# @DEFAULT_MONITOR@ selects the default loopback device.
|
||||
# devrecord =
|
||||
|
||||
# For Linux you can use the following command to find valid devices to read from:
|
||||
# pactl list | grep pci- | grep monitor
|
||||
|
||||
|
||||
#-1 indicates left and right, 0 left, 1 right.
|
||||
sample_channel = -1
|
||||
|
||||
# Other properties
|
||||
#Sound properties.
|
||||
buffer = 384
|
||||
play = 0
|
||||
rec = 1
|
||||
channels = 2
|
||||
samplerate = 44100
|
||||
wininput = -1
|
||||
|
||||
#Compiled version will default this.
|
||||
#sound_source = ALSA
|
||||
#-1 indicates left and right, 0 left, 1 right.
|
||||
|
||||
sample_channel = -1
|
||||
sourcename = default
|
||||
#alsa_output.pci-0000_00_1f.3.analog-stereo.monitor
|
||||
#default
|
||||
# alsa_output.pci-0000_00_1b.0.analog-stereo.monitor
|
||||
#alsa_output.pci-0000_00_1f.3.analog-stereo.monitor << New laptop
|
||||
#use pactl list | grep pci- | grep monitor
|
||||
|
||||
##################################
|
||||
# General ColorChord properties. #
|
||||
##################################
|
||||
|
||||
# How much to amplify the incoming signal.
|
||||
amplify = 10
|
||||
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
|
||||
base_hz = 55
|
||||
|
||||
# DFT properties for the DFT when Extra Debug is being shown.
|
||||
# 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 = 60.0000
|
||||
dft_q = 20.0000
|
||||
dft_speedup = 1000.0000
|
||||
octaves = 6
|
||||
octaves = 5
|
||||
|
||||
# What DFT method should we use?
|
||||
# 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 = 0
|
||||
do_progressive_dft = 4
|
||||
|
||||
# How many bins per octave to use?
|
||||
freqbins = 64
|
||||
|
||||
# DFT Output IIR filter (Probably don't change these)
|
||||
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.3500
|
||||
note_attach_amp_iir2 = 0.250
|
||||
|
@ -94,16 +80,23 @@ note_jumpability = 1.8000
|
|||
note_minimum_new_distribution_value = 0.0200
|
||||
note_out_chop = 0.05000
|
||||
|
||||
##########################################
|
||||
# Colorchord Display and Output Settings #
|
||||
##########################################
|
||||
#compress_coefficient = 4.0
|
||||
#compress_exponent = .5
|
||||
|
||||
# This is a vornoi thing:
|
||||
|
||||
#=======================================================================
|
||||
#Outputs
|
||||
|
||||
|
||||
This is a vornoi thing:
|
||||
outdrivers = OutputVoronoi, DisplayArray
|
||||
lightx = 128
|
||||
lighty = 64
|
||||
lightx = 64
|
||||
lighty = 32
|
||||
fromsides = 1
|
||||
shape_cutoff = 0.03
|
||||
satamp = 5.000
|
||||
amppow = 2.510
|
||||
distpow = 1.500
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -10,25 +10,24 @@
|
|||
|
||||
#ifndef CCEMBEDDED
|
||||
|
||||
void DoDFT( float *outbins, float *frequencies, int bins, float *databuffer,
|
||||
int place_in_data_buffer, int size_of_data_buffer, float q )
|
||||
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++ )
|
||||
for( i = 0; i < bins; i++ )
|
||||
{
|
||||
float freq = frequencies[ i ];
|
||||
float freq = frequencies[i];
|
||||
float phi = 0;
|
||||
int sampleplace = place_in_data_buffer;
|
||||
float advance = 3.14159 * 2.0 / freq;
|
||||
float advance = 3.14159*2.0/freq;
|
||||
|
||||
float binqtys = 0;
|
||||
float binqtyc = 0;
|
||||
|
||||
for ( j = 0; j <= freq * q; j++ )
|
||||
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 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;
|
||||
|
||||
|
@ -39,37 +38,36 @@ void DoDFT( float *outbins, float *frequencies, int bins, float *databuffer,
|
|||
}
|
||||
|
||||
float amp = sqrtf( binqtys * binqtys + binqtyc * binqtyc );
|
||||
outbins[ i ] = amp / freq / q;
|
||||
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 )
|
||||
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++ )
|
||||
for( i = 0; i < bins; i++ )
|
||||
{
|
||||
int flirts = 0;
|
||||
|
||||
float freq = frequencies[ i ];
|
||||
float freq = frequencies[i];
|
||||
float phi = 0;
|
||||
int ftq = freq * q;
|
||||
int ftq = freq * q;
|
||||
int sampleplace = place_in_data_buffer;
|
||||
float advance = 3.14159 * 2.0 / freq;
|
||||
float advance = 3.14159*2.0/freq;
|
||||
|
||||
float binqtys = 0;
|
||||
float binqtyc = 0;
|
||||
|
||||
int skip = floor( ftq / speedup );
|
||||
if ( skip == 0 ) skip = 1;
|
||||
if( skip == 0 ) skip = 1;
|
||||
advance *= skip;
|
||||
|
||||
for ( j = 0; j <= ftq; j += 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 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;
|
||||
|
||||
|
@ -81,24 +79,25 @@ void DoDFTQuick( float *outbins, float *frequencies, int bins, const float *data
|
|||
}
|
||||
|
||||
float amp = sqrtf( binqtys * binqtys + binqtyc * binqtyc );
|
||||
outbins[ i ] = amp / freq / q * skip;
|
||||
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;
|
||||
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
|
||||
|
||||
|
@ -106,53 +105,53 @@ void HandleProgressive( float sample )
|
|||
{
|
||||
int i;
|
||||
|
||||
for ( i = 0; i < gbins; i++ )
|
||||
for( i = 0; i < gbins; i++ )
|
||||
{
|
||||
float thiss = sinf( phis[ i ] ) * sample;
|
||||
float thisc = cosf( phis[ i ] ) * sample;
|
||||
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;
|
||||
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;
|
||||
phis[i] += advances[i];
|
||||
if( phis[i] > 6.283 ) phis[i]-=6.283;
|
||||
|
||||
goutbins[ i ] = sqrtf( s * s + c * c );
|
||||
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 )
|
||||
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( gbins != bins )
|
||||
{
|
||||
if ( gbinqtys ) free( gbinqtys );
|
||||
if ( gbinqtyc ) free( gbinqtyc );
|
||||
if ( phis ) free( phis );
|
||||
if ( lastbins ) free( lastbins );
|
||||
if ( advances ) free( advances );
|
||||
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 );
|
||||
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 );
|
||||
|
||||
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 );
|
||||
memcpy( outbins, lastbins, sizeof(float)*bins );
|
||||
|
||||
for ( i = 0; i < bins; i++ )
|
||||
for( i = 0; i < bins; i++ )
|
||||
{
|
||||
float freq = frequencies[ i ];
|
||||
advances[ i ] = 3.14159 * 2.0 / freq;
|
||||
float freq = frequencies[i];
|
||||
advances[i] = 3.14159*2.0/freq;
|
||||
}
|
||||
|
||||
gbins = bins;
|
||||
|
@ -161,28 +160,36 @@ void DoDFTProgressive( float *outbins, float *frequencies, int bins, const float
|
|||
gspeedup = speedup;
|
||||
gq = q;
|
||||
|
||||
place_in_data_buffer = ( place_in_data_buffer + 1 ) % size_of_data_buffer;
|
||||
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 )
|
||||
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!)
|
||||
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 ); }
|
||||
if( didrun )
|
||||
{
|
||||
memcpy( lastbins, outbins, sizeof(float)*bins );
|
||||
}
|
||||
|
||||
/* for( i = 0; i < bins; i++ )
|
||||
{
|
||||
printf( "%0.2f ", outbins[i]*100 );
|
||||
}
|
||||
printf( "\n" );*/
|
||||
|
||||
/* for( i = 0; i < bins; i++ )
|
||||
{
|
||||
printf( "%0.2f ", outbins[i]*100 );
|
||||
}
|
||||
printf( "\n" );*/
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/////////////////////////////INTEGER DFT
|
||||
|
||||
|
||||
|
@ -216,111 +223,111 @@ void HandleProgressiveInt( int8_t sample1, int8_t sample2 )
|
|||
|
||||
//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
|
||||
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
|
||||
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 cycle 1 *** AS/IS: 4
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
// 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
|
||||
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
|
||||
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
|
||||
//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
|
||||
*(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
|
||||
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++ = tmp1; //Store values back 4
|
||||
|
||||
*( ds - 3 ) = ipl; // Store values back 4 AS/IS: 6
|
||||
*(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 )
|
||||
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 )
|
||||
if( !donefirstrun )
|
||||
{
|
||||
donefirstrun = 1;
|
||||
for ( i = 0; i < 256; i++ )
|
||||
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 ) );
|
||||
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 )
|
||||
if( gbins != bins )
|
||||
{
|
||||
gbins = bins;
|
||||
if ( datspace ) free( datspace );
|
||||
if( datspace ) free( datspace );
|
||||
datspace = malloc( bins * 2 * 4 );
|
||||
}
|
||||
|
||||
|
||||
for ( i = 0; i < bins; i++ )
|
||||
|
||||
for( i = 0; i < bins; i++ )
|
||||
{
|
||||
float freq = frequencies[ i ];
|
||||
datspace[ i * 4 ] = 65536.0 / freq;
|
||||
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 )
|
||||
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 );
|
||||
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;
|
||||
last_place = place_in_data_buffer&0xfffe;
|
||||
|
||||
// Extract bins.
|
||||
for ( i = 0; i < bins; i++ )
|
||||
//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;
|
||||
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");
|
||||
// printf( "\n");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
@ -413,12 +420,9 @@ uint16_t Sdatspace[FIXBINS*4]; //(advances,places,isses,icses)
|
|||
static uint8_t Sdo_this_octave[BINCYCLE];
|
||||
static int16_t Saccum_octavebins[OCTAVES];
|
||||
static uint8_t Swhichoctaveplace;
|
||||
|
||||
//multiple definition of `embeddedbins'; dft.o (symbol from plugin):(.text+0x0): first defined here
|
||||
//Had this issue when trying to build, commenting this lines out made the build successful
|
||||
//#ifndef INCLUDING_EMBEDDED
|
||||
//uint16_t embeddedbins[FIXBINS]; //This is updated every time the DFT hits the octavecount, or 1/32 updates.
|
||||
//#endif
|
||||
#ifndef INCLUDING_EMBEDDED
|
||||
uint16_t embeddedbins[FIXBINS]; //This is updated every time the DFT hits the octavecount, or 1/32 updates.
|
||||
#endif
|
||||
|
||||
//From: http://stackoverflow.com/questions/1100090/looking-for-an-efficient-integer-square-root-algorithm-for-arm-thumb2
|
||||
/**
|
||||
|
|
|
@ -8,7 +8,13 @@ led_floor = .1 #Turn to .25 for more uniformity, .1 for less.
|
|||
steady_bright = 0
|
||||
|
||||
rgby = 1
|
||||
sourcename = alsa_output.pci-0000_00_1f.3.analog-stereo.monitor
|
||||
#sourcename = default
|
||||
|
||||
|
||||
#alsa_output.pci-0000_01_00.1.hdmi-stereo.monitor
|
||||
# alsa_output.pci-0000_01_00.1.hdmi-stereo-extra1.monitor
|
||||
#alsa_output.pci-0000_00_1f.3.analog-stereo.monitor
|
||||
skipfirst = 0
|
||||
firstval = 0
|
||||
port = 7777
|
|
@ -21,6 +21,8 @@ amplify=.5
|
|||
lightx = 20
|
||||
lighty = 20
|
||||
|
||||
sourcename = #alsa_output.pci-0000_00_1f.3.analog-stereo.monitor
|
||||
|
||||
fliprg = 1
|
||||
skittlequantity = 24
|
||||
|
|
@ -33,6 +33,12 @@ amplify=.35
|
|||
lightx = 20
|
||||
lighty = 20
|
||||
|
||||
sourcename =
|
||||
# alsa_output.pci-0000_00_1f.3.analog-stereo.monitor
|
||||
|
||||
#alsa_input.pci-0000_00_1f.3.analog-stereo
|
||||
#alsa_output.pci-0000_00_1f.3.analog-stereo.monitor
|
||||
|
||||
fliprg = 1
|
||||
skittlequantity = 24
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
outdrivers = DisplayArray,DisplayNetwork,OutputCells
|
||||
#OutputVoronoi
|
||||
leds = 199
|
||||
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 = 0
|
||||
firstval = 0
|
||||
port = 7000
|
||||
address = 192.168.43.179
|
||||
#address = 192.168.0.245
|
||||
|
||||
slope=.10
|
||||
amplify=.9
|
||||
|
||||
headless = 00
|
||||
zigzag = 0
|
||||
lightx = 13
|
||||
lighty = 19
|
||||
qtyamp = 50
|
||||
timebased = 1
|
||||
snakey = 0
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
do_progressive_dft = 3
|
||||
samplerate = 8000
|
||||
buffer = 64
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
#For use with https://github.com/cnlohr/esp8266_dmx_explorer
|
||||
|
||||
outdrivers = DisplayArray,DisplayNetwork, OutputVoronoi
|
||||
#OutputLinear
|
||||
#OutputCells
|
||||
#OutputLinear
|
||||
|
||||
#vornooi
|
||||
lightx = 24
|
||||
lighty = 7
|
||||
fromsides = 1
|
||||
outgamma=2.0
|
||||
|
||||
#cells
|
||||
qtyamp=50
|
||||
timebased=1
|
||||
snakey = 1
|
||||
steady_bright = 0
|
||||
amppow = 1.00 #Higher willcull off not as bight things.
|
||||
|
||||
|
||||
leds = 168
|
||||
light_siding = 1.0 #Turn this to ~1.9 for more uniformity, ~1.0 for less.
|
||||
satamp = 1.600
|
||||
is_loop=1
|
||||
led_floor = .1 #Turn to .25 for more uniformity, .1 for less.
|
||||
note_attach_amp_iir = .2 #.3000
|
||||
note_attach_amp_iir2 = .1 #.1500
|
||||
note_attach_freq_iir = .3 #0.3000
|
||||
steady_bright = 0
|
||||
#dft_iir = 0.0
|
||||
#dft_q = 20.0000
|
||||
#dft_speedup = 1000.0000
|
||||
|
||||
skipfirst = 3
|
||||
firstval = 0
|
||||
flipgb = 1
|
||||
|
||||
port = 7777
|
||||
#address = 192.168.4.1
|
||||
address = esp8266dmx.local
|
||||
slope=.10
|
||||
amplify=.1
|
||||
led_floor=.1
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
#For use with https://github.com/cnlohr/esp8266_dmx_explorer
|
||||
|
||||
outdrivers = DisplayPie,DisplayNetwork, OutputCells
|
||||
#OutputLinear
|
||||
#OutputCells
|
||||
#OutputLinear
|
||||
|
||||
#cells
|
||||
qtyamp=50
|
||||
timebased=1
|
||||
snakey = 1
|
||||
steady_bright = 0
|
||||
outgamma=3
|
||||
|
||||
|
||||
leds = 168
|
||||
light_siding = 1.0 #Turn this to ~1.9 for more uniformity, ~1.0 for less.
|
||||
satamp = 1.600
|
||||
is_loop=1
|
||||
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 = 3
|
||||
firstval = 0
|
||||
flipgb = 1
|
||||
|
||||
port = 7777
|
||||
#address = 192.168.4.1
|
||||
address = esp8266dmx.local
|
||||
slope=.10
|
||||
amplify=.1
|
||||
led_floor=.1
|
||||
|
||||
|
||||
lightx = 20
|
||||
lighty = 20
|
||||
|
|
@ -1,100 +0,0 @@
|
|||
# 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 = ColorChord RadialPole Output
|
||||
set_screenx = 480
|
||||
set_screeny = 480
|
||||
|
||||
#Sound properties.
|
||||
buffer = 1024
|
||||
play = 0
|
||||
rec = 1
|
||||
channels = 2
|
||||
samplerate = 44100
|
||||
|
||||
#######################################
|
||||
# ColorChord audio driver properties. #
|
||||
#######################################
|
||||
|
||||
# Colorchord now automatically picks most of the important audio driver settings automatically
|
||||
# and will default to PULSE with ALSA as a fallback on Linux,
|
||||
# On Windows it will default to WASAPI with winmm (WIN) as a fallback
|
||||
# To force a specific driver use the "sound_source" property, the following values are valid
|
||||
# sound_source:
|
||||
# Linux: PULSE, ALSA
|
||||
# Windows: WASAPI, WIN
|
||||
# Android: ANDROID
|
||||
# sound_source =
|
||||
|
||||
# The "devplay" property sets the playback device for CNFA (what speakers to go to) most uses for
|
||||
# colorchord don't use audio output so this can be almost universally ignored
|
||||
# devplay =
|
||||
|
||||
# The "devrecord" Sets the device to get audio from. This will default to monitoring your speaker
|
||||
# output. For WASAPI, "default" searches for a mic, and "defaultRender" searches for your loopback.
|
||||
# For PulseAudio @DEFAULT_SOURCE@ selects the default input (typically a microphone) and
|
||||
# @DEFAULT_MONITOR@ selects the default loopback device.
|
||||
# devrecord =
|
||||
|
||||
#-1 indicates left and right, 0 left, 1 right.
|
||||
sample_channel = -1
|
||||
|
||||
##################################
|
||||
# General ColorChord properties. #
|
||||
##################################
|
||||
|
||||
# How much to amplify the incoming signal.
|
||||
amplify = 2
|
||||
#amplify = 10
|
||||
|
||||
# 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
|
||||
|
||||
# DFT properties for the DFT up top.
|
||||
dft_iir = 0.6
|
||||
dft_q = 60.0000
|
||||
dft_speedup = 1000.0000
|
||||
octaves = 6
|
||||
|
||||
# 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 = 0
|
||||
|
||||
filter_iter = 2
|
||||
filter_strength = .5
|
||||
|
||||
# How many bins per octave to use?
|
||||
freqbins = 64
|
||||
|
||||
# For the final note information... How much to slack everything?
|
||||
note_attach_amp_iir = 0.3500
|
||||
note_attach_amp_iir2 = 0.250
|
||||
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
|
||||
|
||||
##########################################
|
||||
# Colorchord Display and Output Settings #
|
||||
##########################################
|
||||
|
||||
# RadialPole Output driver
|
||||
outdrivers = DisplayRadialPoles
|
||||
radialscale = 2000
|
||||
radialmode = 1
|
|
@ -1,22 +0,0 @@
|
|||
# Normal Colorchord stuff
|
||||
outdrivers = DisplayNetwork, OutputLinear
|
||||
headless = 1
|
||||
leds = 30
|
||||
light_siding = 1.5 #Turn this to ~1.9 for more uniformity, ~1.0 for less.
|
||||
cpu_autolimit_interval=.014
|
||||
cpu_autolimit = 1
|
||||
satamp = 1.600
|
||||
is_loop=0
|
||||
led_floor = .18
|
||||
steady_bright = 0
|
||||
fliprg=0
|
||||
slope=.5
|
||||
amplify=1
|
||||
base_hz = 51.5000
|
||||
|
||||
# WLED Integration stuff
|
||||
wled_realtime=1 #Enable WLED Interfacing params
|
||||
port = 19446 #Default port for UDP Realtime
|
||||
address = 192.168.0.24 #WLED Node IP
|
||||
wled_timeout=2
|
||||
|
1525
colorchord2/hidapi.c
1525
colorchord2/hidapi.c
File diff suppressed because it is too large
Load diff
|
@ -16,6 +16,9 @@ lighty=1
|
|||
|
||||
ledoutamp = 1
|
||||
|
||||
sourcename = alsa_output.pci-0000_01_00.1.hdmi-stereo.monitor
|
||||
|
||||
|
||||
bank1_size = 40
|
||||
bank1_id = 8
|
||||
bank2_size = 27
|
|
@ -54,14 +54,14 @@ void UnhookKeyEvent( void (*KeyE)( void * v, int key, int down ), void * v )
|
|||
|
||||
struct SoundEvent
|
||||
{
|
||||
void (*SoundE)( void * v, int samples, short * samps, int channel_ct );
|
||||
void (*SoundE)( void * v, int samples, float * samps, int channel_ct );
|
||||
void * v;
|
||||
};
|
||||
|
||||
struct SoundEvent SoundEvents[2][MAX_SOUND_EVENTS];
|
||||
|
||||
|
||||
void SoundEventHappened( int samples, short * samps, int is_out, int channel_ct )
|
||||
void SoundEventHappened( int samples, float * samps, int is_out, int channel_ct )
|
||||
{
|
||||
int i;
|
||||
for( i = 0; i < MAX_SOUND_EVENTS; i++ )
|
||||
|
@ -73,7 +73,7 @@ void SoundEventHappened( int samples, short * samps, int is_out, int channel_ct
|
|||
}
|
||||
}
|
||||
|
||||
void HookSoundInEvent( void (*SoundE)( void * v, int samples, short * samps, int channel_ct ), void * v, int is_out )
|
||||
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++ )
|
||||
|
@ -87,7 +87,7 @@ void HookSoundInEvent( void (*SoundE)( void * v, int samples, short * samps, int
|
|||
}
|
||||
}
|
||||
|
||||
void UnhookSoundInEvent( void (*SoundE)( void * v, int samples, short * 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 )
|
||||
{
|
||||
int i;
|
||||
for( i = 0; i < MAX_SOUND_EVENTS; i++ )
|
||||
|
|
|
@ -11,9 +11,9 @@ 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, short * samps, int channel_ct, int is_out );
|
||||
void HookSoundInEvent( void (*SoundE)( void * v, int samples, short * samps, int channel_ct ), void * v, int is_out );
|
||||
void UnhookSoundInEvent( void (*SoundE)( void * v, int samples, short * samps, int channel_ct ), void * v, int is_out );
|
||||
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
|
||||
|
|
|
@ -15,6 +15,9 @@ light_siding = 1.9
|
|||
samplerate = 11025
|
||||
buffer = 64
|
||||
|
||||
#sourcename = default
|
||||
sourcename = alsa_output.pci-0000_00_1f.3.analog-stereo.monitor
|
||||
#default
|
||||
do_write_method = 2
|
||||
|
||||
amplify = 2.5
|
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
|
||||
|
|
@ -1,108 +0,0 @@
|
|||
# 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 = 384
|
||||
play = 0
|
||||
rec = 1
|
||||
channels = 2
|
||||
samplerate = 44100
|
||||
wininput = 1
|
||||
|
||||
#Compiled version will default this.
|
||||
#sound_source = ALSA
|
||||
#-1 indicates left and right, 0 left, 1 right.
|
||||
|
||||
sample_channel = -1
|
||||
sourcename = alsa_output.pci-0000_00_1b.0.analog-stereo.monitor
|
||||
#alsa_output.pci-0000_00_1f.3.analog-stereo.monitor
|
||||
#default
|
||||
# alsa_output.pci-0000_00_1b.0.analog-stereo.monitor
|
||||
#alsa_output.pci-0000_00_1f.3.analog-stereo.monitor << New laptop
|
||||
#use pactl list | grep pci- | grep monitor
|
||||
|
||||
##################################
|
||||
# General ColorChord properties. #
|
||||
##################################
|
||||
|
||||
# How much to amplify the incoming signal.
|
||||
amplify = 5
|
||||
|
||||
# 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
|
||||
|
||||
# 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 = 4fa
|
||||
|
||||
|
||||
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.3500
|
||||
note_attach_amp_iir2 = 0.250
|
||||
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
|
||||
|
||||
#compress_coefficient = 4.0
|
||||
#compress_exponent = .5
|
||||
|
||||
|
||||
#=======================================================================
|
||||
#Outputs
|
||||
|
||||
|
||||
outdrivers = DisplayUSBIsochronous, OutputVoronoi, DisplayArray
|
||||
|
||||
leds = 484
|
||||
lightx = 11
|
||||
lighty = 44
|
||||
fromsides = 1
|
||||
shape_cutoff = 0.03
|
||||
satamp = 2.000
|
||||
amppow = 2.5
|
||||
distpow = 1.500
|
||||
zigzag = 1
|
||||
rot90 = 1
|
||||
ledoutamp = .01
|
||||
|
||||
note_attach_amp_iir = .3000
|
||||
note_attach_amp_iir2 = .1500
|
||||
note_attach_freq_iir = 0.3000
|
||||
steady_bright = 0
|
|
@ -15,6 +15,19 @@ set_screeny = 480
|
|||
in_amplitude = 1.0
|
||||
|
||||
sample_channel = -1
|
||||
sourcename = alsa_output.pci-0000_00_1f.3.analog-stereo.monitor
|
||||
#bluez_sink.40_EF_4C_CA_A4_5D.monitor
|
||||
#alsa_output.pci-0000_00_1f.3.analog-stereo.monitor
|
||||
## alsa_output.pci-0000_00_1b.0.analog-stereo.monitor
|
||||
# alsa_output.pci-0000_01_00.1.hdmi-stereo-extra2.monitor (On desktop)
|
||||
|
||||
|
||||
#alsa_output.pci-0000_00_1f.3.analog-stereo.monitor
|
||||
#default
|
||||
# alsa_output.pci-0000_00_1b.0.analog-stereo.monitor
|
||||
#alsa_output.pci-0000_00_1f.3.analog-stereo.monitor << New laptop
|
||||
#use pactl list | grep pci- | grep monitor
|
||||
|
||||
|
||||
#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.
|
|
@ -1,362 +1,188 @@
|
|||
// Copyright 2015-2020 <>< Charles Lohr under the ColorChord License.
|
||||
//Copyright 2015 <>< Charles Lohr under the ColorChord License.
|
||||
|
||||
#if defined( WINDOWS ) || defined( USE_WINDOWS ) || defined( WIN32 ) || defined( WIN64 ) || \
|
||||
defined( _WIN32 ) || defined( _WIN64 )
|
||||
#ifdef TCC
|
||||
#include <winsock2.h>
|
||||
#endif
|
||||
#ifndef strdup
|
||||
#define strdup _strdup
|
||||
#endif
|
||||
// define convenient macro to detect windows
|
||||
#define IS_WINDOWS 1
|
||||
#else
|
||||
// this isn't windows
|
||||
#define IS_WINDOWS 0
|
||||
#endif
|
||||
|
||||
#include "color.h"
|
||||
#include "configs.h"
|
||||
#include "decompose.h"
|
||||
#include "dft.h"
|
||||
#include "filter.h"
|
||||
#include "hook.h"
|
||||
#include "notefinder.h"
|
||||
#include "os_generic.h"
|
||||
#include "outdrivers.h"
|
||||
#include "parameters.h"
|
||||
#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"
|
||||
#include "configs.h"
|
||||
|
||||
struct SoundDriver * sd;
|
||||
|
||||
#define CNFG_IMPLEMENTATION
|
||||
#include "CNFG.h"
|
||||
#if defined(WIN32) || defined(USE_WINDOWS)
|
||||
#include <winsock2.h>
|
||||
#include <windows.h>
|
||||
|
||||
#define CNFA_IMPLEMENTATION
|
||||
#include "CNFA.h"
|
||||
|
||||
|
||||
// Sound driver.
|
||||
struct CNFADriver *sd;
|
||||
|
||||
int bQuitColorChord = 0;
|
||||
|
||||
|
||||
#ifdef ANDROID
|
||||
#include <android/log.h>
|
||||
#include <fcntl.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
void HandleDestroy()
|
||||
{
|
||||
bQuitColorChord = 1;
|
||||
CNFAClose( sd );
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#define GENLINEWIDTH 89
|
||||
#define GENLINES 67
|
||||
|
||||
char genlog[ ( GENLINEWIDTH + 1 ) * ( GENLINES + 1 ) + 2 ] = "log";
|
||||
int genloglen;
|
||||
int genloglines;
|
||||
int genlinelen = 0;
|
||||
int firstnewline = -1;
|
||||
|
||||
|
||||
// Define application colors RGBA format
|
||||
#define BACKGROUND_COLOR 0x000000ff
|
||||
#define LINE_COLOR 0xffffffff
|
||||
#define TEXT_COLOR 0xffffffff
|
||||
// Text colors for the debug options at the bottom of the screen
|
||||
#define ENABLED_COLOR 0xffffffff
|
||||
#define DISABLED_COLOR 0x800000ff
|
||||
|
||||
void example_log_function( int readSize, char *buf )
|
||||
{
|
||||
static og_mutex_t *mt;
|
||||
if ( !mt ) mt = OGCreateMutex();
|
||||
OGLockMutex( mt );
|
||||
for ( int i = 0; readSize && i <= readSize && buf[ i ]; i++ )
|
||||
{
|
||||
char c = buf[ i ];
|
||||
if ( c == '\0' ) c = '\n';
|
||||
if ( ( c != '\n' && genlinelen >= GENLINEWIDTH ) || c == '\n' )
|
||||
{
|
||||
genloglines++;
|
||||
if ( genloglines >= GENLINES )
|
||||
{
|
||||
genloglen -= firstnewline + 1;
|
||||
int offset = firstnewline;
|
||||
firstnewline = -1;
|
||||
int k;
|
||||
for ( k = 0; k < genloglen; k++ )
|
||||
{
|
||||
if ( ( genlog[ k ] = genlog[ k + offset + 1 ] ) == '\n' && firstnewline < 0 )
|
||||
firstnewline = k;
|
||||
}
|
||||
genlog[ k ] = 0;
|
||||
genloglines--;
|
||||
}
|
||||
genlinelen = 0;
|
||||
if ( c != '\n' )
|
||||
{
|
||||
genlog[ genloglen + 1 ] = 0;
|
||||
genlog[ genloglen++ ] = '\n';
|
||||
}
|
||||
if ( firstnewline < 0 ) firstnewline = genloglen;
|
||||
}
|
||||
genlog[ genloglen + 1 ] = 0;
|
||||
genlog[ genloglen++ ] = c;
|
||||
if ( c != '\n' ) genlinelen++;
|
||||
}
|
||||
|
||||
OGUnlockMutex( mt );
|
||||
}
|
||||
|
||||
|
||||
#if defined( WIN32 ) || defined( USE_WINDOWS )
|
||||
#define ESCAPE_KEY 0x1B
|
||||
|
||||
void HandleDestroy()
|
||||
void WindowsTerm()
|
||||
{
|
||||
CNFAClose( sd );
|
||||
CloseSound( sd );
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#define ESCAPE_KEY 65307
|
||||
// Stub function for Linux
|
||||
void HandleDestroy()
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
float DeltaFrameTime = 0;
|
||||
double Now = 0;
|
||||
int is_suspended = 0;
|
||||
double Now = 0;
|
||||
|
||||
int lastfps;
|
||||
short screenx, screeny;
|
||||
|
||||
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 );
|
||||
float cpu_autolimit_interval = 0.016; REGISTER_PARAM( cpu_autolimit_interval, PAFLOAT );
|
||||
int sample_channel = -1; REGISTER_PARAM( sample_channel, PAINT );
|
||||
int showfps = 1; REGISTER_PARAM( showfps, PAINT );
|
||||
struct DriverInstances * outdriver[MAX_OUT_DRIVERS];
|
||||
|
||||
#if defined( ANDROID ) || defined( __android__ )
|
||||
float in_amplitude = 2;
|
||||
#else
|
||||
float in_amplitude = 1;
|
||||
#endif
|
||||
REGISTER_PARAM( in_amplitude, PAFLOAT );
|
||||
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 );
|
||||
float cpu_autolimit_interval = 0.016; REGISTER_PARAM( cpu_autolimit_interval, PAFLOAT );
|
||||
int sample_channel = -1;REGISTER_PARAM( sample_channel, PAINT );
|
||||
int showfps = 0; REGISTER_PARAM( showfps, PAINT );
|
||||
float in_amplitude = 1; REGISTER_PARAM( in_amplitude, PAFLOAT );
|
||||
|
||||
struct NoteFinder *nf;
|
||||
struct NoteFinder * nf;
|
||||
|
||||
// Sound circular buffer
|
||||
#define SOUNDCBSIZE 8096
|
||||
//Sound circular buffer
|
||||
#define SOUNDCBSIZE 8096
|
||||
#define MAX_CHANNELS 2
|
||||
|
||||
double VisTimeEnd, VisTimeStart;
|
||||
float sound[ SOUNDCBSIZE ];
|
||||
int soundhead = 0;
|
||||
int show_debug = 0;
|
||||
int show_debug_basic = 0;
|
||||
int soundhead = 0;
|
||||
float sound[SOUNDCBSIZE];
|
||||
int show_debug = 0;
|
||||
int show_debug_basic = 1;
|
||||
|
||||
int gKey = 0;
|
||||
extern int force_white;
|
||||
|
||||
void RecalcBaseHz()
|
||||
{
|
||||
nf->base_hz = 55 * pow( 2, gKey / 12.0 );
|
||||
ChangeNFParameters( nf );
|
||||
}
|
||||
|
||||
void HandleKey( int keycode, int bDown )
|
||||
{
|
||||
char c = toupper( keycode );
|
||||
#ifdef ANDROID
|
||||
if ( keycode == 4 && bDown )
|
||||
{
|
||||
// Back button.
|
||||
printf( "Back button pressed\n" );
|
||||
AndroidSendToBack( 0 );
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
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 == ESCAPE_KEY ) exit( 0 );
|
||||
if( c == 'W' ) force_white = bDown;
|
||||
if( c == 'D' && bDown ) show_debug = !show_debug;
|
||||
if( c == '9' && bDown ) { gKey--; RecalcBaseHz(); }
|
||||
if( c == '-' && bDown ) { gKey++; RecalcBaseHz(); }
|
||||
if( c == '0' && bDown ) { gKey = 0; RecalcBaseHz(); }
|
||||
if( c == 'E' && bDown ) show_debug_basic = !show_debug_basic;
|
||||
if( c == 'K' && bDown ) DumpParameters();
|
||||
printf( "Key: %d -> %d\n", keycode, bDown );
|
||||
KeyHappened( keycode, bDown );
|
||||
}
|
||||
|
||||
// On Android we want a really basic GUI
|
||||
void HandleButton( int x, int y, int button, int bDown )
|
||||
{
|
||||
printf( "Button: %d,%d (%d) -> %d\n", x, y, button, bDown );
|
||||
if ( bDown )
|
||||
{
|
||||
if ( y < 800 )
|
||||
{
|
||||
if ( x < screenx / 3 )
|
||||
gKey--;
|
||||
else if ( x < ( screenx * 2 / 3 ) )
|
||||
gKey = 0;
|
||||
else
|
||||
gKey++;
|
||||
printf( "KEY: %d\n", gKey );
|
||||
RecalcBaseHz();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HandleMotion( int x, int y, int mask )
|
||||
{
|
||||
}
|
||||
|
||||
void SoundCB( struct CNFADriver *sd, short *out, short *in, int framesp, int framesr )
|
||||
void SoundCB( float * out, float * in, int samplesr, int * samplesp, struct SoundDriver * sd )
|
||||
{
|
||||
int channelin = sd->channelsRec;
|
||||
int channelout = sd->channelsPlay;
|
||||
// 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.
|
||||
if ( in )
|
||||
//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 ( int i = 0; i < framesr; i++ )
|
||||
if( out )
|
||||
{
|
||||
if ( sample_channel < 0 )
|
||||
for( j = 0; j < channelin; j++ )
|
||||
{
|
||||
float fo = 0;
|
||||
for ( int j = 0; j < channelin; j++ )
|
||||
{
|
||||
float f = in[ i * channelin + j ] / 32767.;
|
||||
if ( f >= -1 && f <= 1 )
|
||||
fo += f;
|
||||
else
|
||||
fo += ( f > 0 ) ? 1 : -1;
|
||||
}
|
||||
fo /= channelin;
|
||||
sound[ soundhead ] = fo * in_amplitude;
|
||||
soundhead = ( soundhead + 1 ) % SOUNDCBSIZE;
|
||||
}
|
||||
else
|
||||
{
|
||||
float f = in[ i * channelin + sample_channel ] / 32767.;
|
||||
if ( f > 1 || f < -1 ) f = ( f > 0 ) ? 1 : -1;
|
||||
sound[ soundhead ] = f * in_amplitude;
|
||||
soundhead = ( soundhead + 1 ) % SOUNDCBSIZE;
|
||||
out[i*channelin+j] = 0;
|
||||
}
|
||||
}
|
||||
SoundEventHappened( framesr, in, 0, channelin );
|
||||
|
||||
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*in_amplitude;
|
||||
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*in_amplitude;
|
||||
soundhead = (soundhead+1)%SOUNDCBSIZE;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ( out )
|
||||
SoundEventHappened( samplesr, in, 0, channelin );
|
||||
if( out )
|
||||
{
|
||||
memset( out, 0, framesp * channelout );
|
||||
SoundEventHappened( framesp, out, 1, channelout );
|
||||
SoundEventHappened( samplesr, out, 1, sd->channelsPlay );
|
||||
}
|
||||
*samplesp = samplesr;
|
||||
}
|
||||
|
||||
#ifdef ANDROID
|
||||
void HandleSuspend()
|
||||
int main(int argc, char ** argv)
|
||||
{
|
||||
is_suspended = 1;
|
||||
}
|
||||
|
||||
void HandleResume()
|
||||
{
|
||||
is_suspended = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
// function for calling initilization functions if we are using TCC
|
||||
int i;
|
||||
#ifdef TCC
|
||||
void RegisterConstructorFunctions()
|
||||
{
|
||||
|
||||
// Basic Window stuff
|
||||
REGISTERheadless();
|
||||
REGISTERset_screenx();
|
||||
REGISTERset_screeny();
|
||||
REGISTERsound_source();
|
||||
REGISTERcpu_autolimit();
|
||||
REGISTERcpu_autolimit_interval();
|
||||
REGISTERsample_channel();
|
||||
REGISTERshowfps();
|
||||
REGISTERin_amplitude();
|
||||
|
||||
// Audio stuff
|
||||
REGISTERNullCNFA();
|
||||
REGISTERWinCNFA();
|
||||
REGISTERcnfa_wasapi();
|
||||
|
||||
// Video Stuff
|
||||
REGISTERnull();
|
||||
REGISTERDisplayArray();
|
||||
REGISTERDisplayHIDAPI();
|
||||
REGISTERDisplayNetwork();
|
||||
REGISTERDisplayOutDriver();
|
||||
REGISTERDisplayPie();
|
||||
REGISTERDisplayRadialPoles();
|
||||
// block trying to load linux specific displays
|
||||
#if not IS_WINDOWS
|
||||
REGISTERDisplayDMX();
|
||||
REGISTERDisplayFileWrite();
|
||||
REGISTERDisplaySHM();
|
||||
REGISTERDisplayUSB2812();
|
||||
void ManuallyRegisterDevices();
|
||||
ManuallyRegisterDevices();
|
||||
#endif
|
||||
|
||||
// Output stuff
|
||||
REGISTEROutputCells();
|
||||
REGISTEROutputLinear();
|
||||
REGISTEROutputProminent();
|
||||
REGISTEROutputVoronoi();
|
||||
}
|
||||
#endif
|
||||
|
||||
int main( int argc, char **argv )
|
||||
{
|
||||
#ifdef TCC
|
||||
RegisterConstructorFunctions();
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
printf( "Output Drivers:\n" );
|
||||
for ( int i = 0; i < MAX_OUT_DRIVERS; i++ )
|
||||
for( i = 0; i < MAX_OUT_DRIVERS; i++ )
|
||||
{
|
||||
if ( ODList[ i ].Name ) printf( "\t%s\n", ODList[ i ].Name );
|
||||
if( ODList[i].Name ) printf( "\t%s\n", ODList[i].Name );
|
||||
}
|
||||
#if defined(WIN32) || defined(USE_WINDOWS)
|
||||
WSADATA wsaData;
|
||||
|
||||
#if defined( WIN32 ) || defined( USE_WINDOWS )
|
||||
// In case something needs network access.
|
||||
WSADATA wsaData;
|
||||
WSAStartup( 0x202, &wsaData );
|
||||
#elif defined( ANDROID )
|
||||
int hasperm = AndroidHasPermissions( "READ_EXTERNAL_STORAGE" );
|
||||
int haspermInternet = AndroidHasPermissions( "INTERNET" );
|
||||
if ( !hasperm ) AndroidRequestAppPermissions( "READ_EXTERNAL_STORAGE" );
|
||||
if ( !haspermInternet ) AndroidRequestAppPermissions( "INTERNET" );
|
||||
WSAStartup(0x202, &wsaData);
|
||||
|
||||
strcpy( sound_source, "WIN" );
|
||||
#else
|
||||
// Linux
|
||||
strcpy( sound_source, "PULSE" );
|
||||
#endif
|
||||
|
||||
gargc = argc;
|
||||
|
@ -364,283 +190,262 @@ int main( int argc, char **argv )
|
|||
|
||||
SetupConfigs();
|
||||
|
||||
// Initialize Rawdraw
|
||||
//Initialize Rawdraw
|
||||
int frames = 0;
|
||||
double ThisTime;
|
||||
double SecToWait;
|
||||
double LastFPSTime = OGGetAbsoluteTime();
|
||||
double LastFrameTime = OGGetAbsoluteTime();
|
||||
CNFGBGColor = BACKGROUND_COLOR;
|
||||
double SecToWait;
|
||||
CNFGBGColor = 0x800000;
|
||||
CNFGDialogColor = 0x444444;
|
||||
|
||||
// Generate the window title
|
||||
char title[ 1024 ];
|
||||
strcpy( title, "Colorchord " );
|
||||
for ( int i = 1; i < argc; i++ )
|
||||
char title[1024];
|
||||
char * tp = title;
|
||||
|
||||
memcpy( tp, "ColorChord ", strlen( "ColorChord " ) );
|
||||
tp += strlen( "ColorChord " );
|
||||
|
||||
for( i = 1; i < argc; i++ )
|
||||
{
|
||||
strcat( title, argv[ i ] );
|
||||
strcat( title, " " );
|
||||
memcpy( tp, argv[i], strlen( argv[i] ) );
|
||||
tp += strlen( argv[i] );
|
||||
*tp = ' ';
|
||||
tp++;
|
||||
}
|
||||
if ( !headless ) CNFGSetup( title, set_screenx, set_screeny );
|
||||
*tp = 0;
|
||||
if( !headless )
|
||||
CNFGSetup( title, set_screenx, set_screeny );
|
||||
|
||||
char *OutDriverNames = strdup( GetParameterS( "outdrivers", "null" ) );
|
||||
char *ThisDriver = OutDriverNames;
|
||||
char *TDStart;
|
||||
for ( int i = 0; i < MAX_OUT_DRIVERS; i++ )
|
||||
|
||||
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;
|
||||
while( *ThisDriver == ' ' || *ThisDriver == '\t' ) ThisDriver++;
|
||||
if( !*ThisDriver ) break;
|
||||
|
||||
TDStart = ThisDriver;
|
||||
|
||||
while ( *ThisDriver != 0 && *ThisDriver != ',' )
|
||||
while( *ThisDriver != 0 && *ThisDriver != ',' )
|
||||
{
|
||||
if ( *ThisDriver == '\t' || *ThisDriver == ' ' ) *ThisDriver = 0;
|
||||
if( *ThisDriver == '\t' || *ThisDriver == ' ' ) *ThisDriver = 0;
|
||||
ThisDriver++;
|
||||
}
|
||||
|
||||
if ( *ThisDriver )
|
||||
if( *ThisDriver )
|
||||
{
|
||||
*ThisDriver = 0;
|
||||
ThisDriver++;
|
||||
}
|
||||
|
||||
|
||||
printf( "Loading: %s\n", TDStart );
|
||||
outdriver[ i ] = SetupOutDriver( TDStart );
|
||||
outdriver[i] = SetupOutDriver( TDStart );
|
||||
}
|
||||
free( OutDriverNames );
|
||||
free(OutDriverNames);
|
||||
|
||||
|
||||
do {
|
||||
#if IS_WINDOWS
|
||||
const char *record_dev_name = "defaultRender";
|
||||
#else
|
||||
const char *record_dev_name = "@DEFAULT_MONITOR@";
|
||||
#endif
|
||||
// Initialize Sound
|
||||
sd = CNFAInit( sound_source, "colorchord", &SoundCB, GetParameterI( "samplerate", 44100 ),
|
||||
GetParameterI( "samplerate", 44100 ), GetParameterI( "channels", 2 ),
|
||||
GetParameterI( "channels", 2 ), GetParameterI( "buffer", 1024 ),
|
||||
GetParameterS( "devplay", "default" ), GetParameterS( "devrecord", record_dev_name ),
|
||||
NULL );
|
||||
//Initialize Sound
|
||||
sd = InitSound( sound_source, &SoundCB );
|
||||
|
||||
if ( sd ) break;
|
||||
if( !sd )
|
||||
{
|
||||
fprintf( stderr, "ERROR: Failed to initialize sound output device\n" );
|
||||
return -1;
|
||||
}
|
||||
|
||||
CNFGColor( LINE_COLOR );
|
||||
CNFGPenX = 10;
|
||||
CNFGPenY = 100;
|
||||
CNFGHandleInput();
|
||||
CNFGClearFrame();
|
||||
CNFGDrawText( "Colorchord must be used with sound. Sound not available.", 10 );
|
||||
CNFGSwapBuffers();
|
||||
OGSleep( 1 );
|
||||
} while ( 1 );
|
||||
nf = CreateNoteFinder( sd->spsRec );
|
||||
|
||||
// Once everything was reinitialized, re-read the ini files.
|
||||
//Once everything was reinitialized, re-read the ini files.
|
||||
SetEnvValues( 1 );
|
||||
|
||||
printf( "================================================= Set Up\n" );
|
||||
|
||||
Now = OGGetAbsoluteTime();
|
||||
double Last = Now;
|
||||
while ( !bQuitColorChord )
|
||||
while(1)
|
||||
{
|
||||
char stt[ 1024 ];
|
||||
// Handle Rawdraw frame swappign
|
||||
char stt[1024];
|
||||
//Handle Rawdraw frame swappign
|
||||
|
||||
Now = OGGetAbsoluteTime();
|
||||
DeltaFrameTime = Now - Last;
|
||||
|
||||
if ( !headless )
|
||||
if( !headless )
|
||||
{
|
||||
CNFGHandleInput();
|
||||
CNFGClearFrame();
|
||||
CNFGColor( LINE_COLOR );
|
||||
CNFGColor( 0xFFFFFF );
|
||||
CNFGGetDimensions( &screenx, &screeny );
|
||||
}
|
||||
|
||||
RunNoteFinder( nf, sound, ( soundhead - 1 + SOUNDCBSIZE ) % SOUNDCBSIZE, SOUNDCBSIZE );
|
||||
// Done all ColorChord work.
|
||||
RunNoteFinder( nf, sound, (soundhead-1+SOUNDCBSIZE)%SOUNDCBSIZE, SOUNDCBSIZE );
|
||||
//Done all ColorChord work.
|
||||
|
||||
|
||||
VisTimeStart = OGGetAbsoluteTime();
|
||||
|
||||
// call the output drivers with the updated note finder data
|
||||
for ( int i = 0; i < MAX_OUT_DRIVERS; i++ )
|
||||
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 );
|
||||
|
||||
if( force_white )
|
||||
{
|
||||
memset( OutLEDs, 0x7f, MAX_LEDS*3 );
|
||||
}
|
||||
|
||||
if( outdriver[i] )
|
||||
outdriver[i]->Func( outdriver[i]->id, nf );
|
||||
}
|
||||
|
||||
VisTimeEnd = OGGetAbsoluteTime();
|
||||
|
||||
|
||||
if ( !headless )
|
||||
if( !headless )
|
||||
{
|
||||
// Handle outputs.
|
||||
//Handle outputs.
|
||||
int freqbins = nf->freqbins;
|
||||
int note_peaks = freqbins / 2;
|
||||
int note_peaks = freqbins/2;
|
||||
int freqs = freqbins * nf->octaves;
|
||||
//int maxdists = freqbins/2;
|
||||
|
||||
// Do a bunch of debugging.
|
||||
if ( show_debug_basic && !is_suspended )
|
||||
//Do a bunch of debugging.
|
||||
if( show_debug_basic )
|
||||
{
|
||||
CNFGColor( TEXT_COLOR );
|
||||
for ( int i = 0; i < nf->dists_count; i++ )
|
||||
//char sttdebug[1024];
|
||||
//char * sttend = sttdebug;
|
||||
|
||||
for( i = 0; i < nf->dists_count; i++ )
|
||||
{
|
||||
// Move over 0.5 for visual purposes. The means is correct.
|
||||
CNFGPenX = ( nf->dists[ i ].mean + 0.5 ) / freqbins * screenx;
|
||||
CNFGPenY = 400 - nf->dists[ i ].amp * 150.0 / nf->dists[ i ].sigma;
|
||||
sprintf( stt, "%f\n%f\n", nf->dists[ i ].mean, nf->dists[ i ].amp );
|
||||
CNFGPenX = (nf->dists[i].mean + 0.5) / freqbins * screenx; //Move over 0.5 for visual purposes. The means is correct.
|
||||
CNFGPenY = 400-nf->dists[i].amp * 150.0 / nf->dists[i].sigma;
|
||||
//printf( "%f %f\n", dists[i].mean, dists[i].amp );
|
||||
sprintf( stt, "%f\n%f\n", nf->dists[i].mean, nf->dists[i].amp );
|
||||
// sttend += sprintf( sttend, "%f/%f ",nf->dists[i].mean, nf->dists[i].amp );
|
||||
CNFGDrawText( stt, 2 );
|
||||
}
|
||||
CNFGColor( 0xffffff );
|
||||
|
||||
CNFGColor( LINE_COLOR );
|
||||
// Draw the folded bins
|
||||
for ( int bin = 0; bin < freqbins; bin++ )
|
||||
//Draw the folded bins
|
||||
for( i = 0; i < freqbins; i++ )
|
||||
{
|
||||
const float x0 = bin / (float)freqbins * (float)screenx;
|
||||
const float x1 = ( bin + 1 ) / (float)freqbins * (float)screenx;
|
||||
const float amp = nf->folded_bins[ bin ] * 250.0;
|
||||
const float note = (float)( bin + 0.5 ) / freqbins;
|
||||
CNFGDialogColor = CCtoHEX( note, 1.0, 1.0 );
|
||||
CNFGDrawBox( x0, 400 - amp, x1, 400 );
|
||||
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;
|
||||
|
||||
// Draw the note peaks
|
||||
for ( int peak = 0; peak < note_peaks; peak++ )
|
||||
for( i = 0; i < note_peaks; i++ )
|
||||
{
|
||||
if ( nf->note_amplitudes_out[ peak ] < 0 ) continue;
|
||||
float note = (float)nf->note_positions[ peak ] / freqbins;
|
||||
CNFGDialogColor = CCtoHEX( note, 1.0, 1.0 );
|
||||
const int x1 = ( (float)peak / note_peaks ) * screenx;
|
||||
const int x2 = ( (float)( peak + 1 ) / note_peaks ) * screenx;
|
||||
const int y1 = 480 - nf->note_amplitudes_out[ peak ] * 100;
|
||||
const int y2 = 480;
|
||||
CNFGColor( LINE_COLOR );
|
||||
CNFGDrawBox( x1, y1, x2, y2 );
|
||||
|
||||
CNFGPenX = ( (float)( peak + .4 ) / note_peaks ) * screenx;
|
||||
//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[ peak ],
|
||||
nf->note_amplitudes2[ peak ] * 1000.0 );
|
||||
|
||||
CNFGColor( TEXT_COLOR );
|
||||
sprintf( stt, "%d\n%0.0f", nf->enduring_note_id[i], nf->note_amplitudes2[i]*1000.0 );
|
||||
//sttend += sprintf( sttend, "%5d/%5.0f ", nf->enduring_note_id[i], nf->note_amplitudes2[i]*1000.0 );
|
||||
CNFGDrawText( stt, 2 );
|
||||
|
||||
}
|
||||
|
||||
CNFGColor( LINE_COLOR );
|
||||
// Let's draw the o-scope.
|
||||
int lasty;
|
||||
int thissoundhead = ( soundhead - 1 + SOUNDCBSIZE ) % SOUNDCBSIZE;
|
||||
int thisy = sound[ thissoundhead ] * -128 + 128;
|
||||
for ( int i = screenx - 1; i > 0; i-- )
|
||||
//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++ )
|
||||
{
|
||||
CNFGTackSegment( i, lasty, i+1, thisy );
|
||||
lasty = thisy;
|
||||
thissoundhead = ( thissoundhead - 1 + SOUNDCBSIZE ) % SOUNDCBSIZE;
|
||||
thisy = sound[ thissoundhead ] * -128 + 128;
|
||||
CNFGTackSegment( i, lasty, i - 1, thisy );
|
||||
thisy = sound[thissoundhead] * 128 + 128; thissoundhead = (thissoundhead-1+SOUNDCBSIZE)%SOUNDCBSIZE;
|
||||
}
|
||||
//puts( sttdebug );
|
||||
}
|
||||
|
||||
// Extra debugging?
|
||||
if ( show_debug && !is_suspended )
|
||||
//Extra debugging?
|
||||
if( show_debug )
|
||||
{
|
||||
// Draw the histogram
|
||||
//Draw the histogram
|
||||
float lasthistval;
|
||||
CNFGColor( LINE_COLOR );
|
||||
for ( int x_val = -1; x_val < screenx; x_val++ )
|
||||
{
|
||||
// Calculate the value of the histogram at the current screen position
|
||||
float hist_point = (float)x_val / (float)screenx * freqbins - 0.5;
|
||||
float thishistval =
|
||||
CalcHistAt( hist_point, nf->freqbins, nf->dists, nf->dists_count );
|
||||
CNFGColor( 0xffffff );
|
||||
|
||||
// Display the value on the screen
|
||||
const short y = 400 - lasthistval * 250.0;
|
||||
if ( x_val >= 0 ) CNFGTackSegment( x_val, y, x_val + 1, y );
|
||||
for( i = -1; i < screenx; i++ )
|
||||
{
|
||||
float thishistval = CalcHistAt( (float)i/(float)screenx*freqbins-0.5, nf->freqbins, nf->dists, nf->dists_count );
|
||||
if( i >= 0 )
|
||||
CNFGTackSegment( i, 400-lasthistval * 250.0, i+1, 400-thishistval * 250.0 );
|
||||
lasthistval = thishistval;
|
||||
}
|
||||
|
||||
CNFGColor( LINE_COLOR );
|
||||
// Draw the bins
|
||||
for ( int bin = 0; bin < freqs; bin++ )
|
||||
CNFGColor( 0xffffff );
|
||||
|
||||
//Draw the bins
|
||||
for( i = 0; i < freqs; i++ )
|
||||
{
|
||||
float x0 = bin / (float)freqs * (float)screenx;
|
||||
float x1 = ( bin + 1 ) / (float)freqs * (float)screenx;
|
||||
float amp = nf->outbins[ bin ] * 250.0;
|
||||
float note = (float)bin / freqbins;
|
||||
CNFGDialogColor = CCtoHEX( note, 1.0, 1.0 );
|
||||
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;
|
||||
|
||||
CNFGColor( TEXT_COLOR );
|
||||
char stdebug[ 1024 ];
|
||||
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 );
|
||||
(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 );
|
||||
}
|
||||
|
||||
if ( !is_suspended )
|
||||
{
|
||||
CNFGColor( show_debug ? ENABLED_COLOR : DISABLED_COLOR );
|
||||
CNFGPenX = 0;
|
||||
CNFGPenY = screeny - 10;
|
||||
CNFGDrawText( "Extra Debug (D)", 2 );
|
||||
CNFGColor( show_debug?0xffffff:0x000000 );
|
||||
CNFGPenX = 0; CNFGPenY = screeny-10;
|
||||
CNFGDrawText( "Extra Debug (D)", 2 );
|
||||
|
||||
CNFGColor( show_debug_basic ? ENABLED_COLOR : DISABLED_COLOR );
|
||||
CNFGPenX = 120;
|
||||
CNFGPenY = screeny - 10;
|
||||
CNFGDrawText( "Basic Debug (E)", 2 );
|
||||
CNFGColor( show_debug_basic?0xffffff:0x000000 );
|
||||
CNFGPenX = 120; CNFGPenY = screeny-10;
|
||||
CNFGDrawText( "Basic Debug (E)", 2 );
|
||||
|
||||
CNFGColor( show_debug_basic ? ENABLED_COLOR : DISABLED_COLOR );
|
||||
CNFGPenX = 240;
|
||||
CNFGPenY = screeny - 10;
|
||||
sprintf( stt, "[9] Key: %d [0] (%3.1f) [-]", gKey, nf->base_hz );
|
||||
CNFGDrawText( stt, 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( TEXT_COLOR );
|
||||
CNFGPenX = 440;
|
||||
CNFGPenY = screeny - 10;
|
||||
sprintf( stt, "FPS: %d", lastfps );
|
||||
CNFGDrawText( stt, 2 );
|
||||
|
||||
#ifdef ANDROID
|
||||
CNFGColor( TEXT_COLOR );
|
||||
CNFGPenX = 10;
|
||||
CNFGPenY = 600;
|
||||
CNFGDrawText( genlog, 3 );
|
||||
#endif
|
||||
CNFGSwapBuffers();
|
||||
}
|
||||
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.
|
||||
//Finish Rawdraw with FPS counter, and a nice delay loop.
|
||||
frames++;
|
||||
|
||||
ThisTime = OGGetAbsoluteTime();
|
||||
if ( ThisTime > LastFPSTime + 1 && showfps )
|
||||
if( ThisTime > LastFPSTime + 1 && showfps )
|
||||
{
|
||||
printf( "FPS: %d\n", frames );
|
||||
lastfps = frames;
|
||||
frames = 0;
|
||||
LastFPSTime += 1;
|
||||
LastFPSTime+=1;
|
||||
}
|
||||
|
||||
if ( cpu_autolimit )
|
||||
if( cpu_autolimit )
|
||||
{
|
||||
SecToWait = cpu_autolimit_interval - ( ThisTime - LastFrameTime );
|
||||
LastFrameTime += cpu_autolimit_interval;
|
||||
if ( SecToWait < -.1 ) LastFrameTime = ThisTime - .1;
|
||||
if ( SecToWait > 0 ) OGUSleep( (int)( SecToWait * 1000000 ) );
|
||||
if( SecToWait < -.1 ) LastFrameTime = ThisTime - .1;
|
||||
if( SecToWait > 0 )
|
||||
OGUSleep( (int)( SecToWait * 1000000 ) );
|
||||
}
|
||||
|
||||
if ( !is_suspended ) SetEnvValues( 0 );
|
||||
|
||||
SetEnvValues( 0 );
|
||||
Last = Now;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -13,6 +13,9 @@ steady_bright = 0
|
|||
#dft_q = 20.0000
|
||||
#dft_speedup = 1000.0000
|
||||
|
||||
sourcename =
|
||||
#alsa_output.pci-0000_00_1b.0.analog-stereo.monitor
|
||||
# home: alsa_output.pci-0000_01_00.1.hdmi-stereo.monitor
|
||||
skipfirst = 1
|
||||
firstval = 0
|
||||
port = 7777
|
|
@ -12,6 +12,9 @@ steady_bright = 0
|
|||
#dft_q = 20.0000
|
||||
#dft_speedup = 1000.0000
|
||||
|
||||
sourcename = alsa_output.pci-0000_01_00.1.hdmi-stereo.monitor
|
||||
# alsa_output.pci-0000_01_00.1.hdmi-stereo-extra1.monitor
|
||||
#alsa_output.pci-0000_00_1f.3.analog-stereo.monitor
|
||||
skipfirst = 1
|
||||
firstval = 0
|
||||
port = 7777
|
|
@ -12,6 +12,11 @@ steady_bright = 0
|
|||
#dft_q = 20.0000
|
||||
#dft_speedup = 1000.0000
|
||||
|
||||
|
||||
sourcename = alsa_output.pci-0000_01_00.1.hdmi-stereo-extra1.monitor
|
||||
#sourcename = alsa_output.pci-0000_00_1b.0.analog-stereo.monitor
|
||||
|
||||
#alsa_output.pci-0000_00_1f.3.analog-stereo.monitor
|
||||
skipfirst = 1
|
||||
firstval = 1
|
||||
port = 7777
|
338
colorchord2/os_generic.c
Normal file
338
colorchord2/os_generic.c
Normal file
|
@ -0,0 +1,338 @@
|
|||
//Copyright 2015 <>< Charles Lohr under the NewBSD OR MIT/x11 License.
|
||||
|
||||
#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 * (routine)( void * ), void * parameter )
|
||||
{
|
||||
return (og_thread_t)CreateThread( 0, 0, (LPTHREAD_START_ROUTINE)routine, parameter, 0, 0 );
|
||||
}
|
||||
|
||||
void * OGJoinThread( og_thread_t ot )
|
||||
{
|
||||
WaitForSingleObject( ot, INFINITE );
|
||||
CloseHandle( ot );
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
int OGGetSema( og_sema_t os )
|
||||
{
|
||||
typedef LONG NTSTATUS;
|
||||
HANDLE sem = (HANDLE)os;
|
||||
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;
|
||||
|
||||
|
||||
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
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
|
||||
#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.
|
||||
*/
|
||||
|
78
colorchord2/os_generic.h
Normal file
78
colorchord2/os_generic.h
Normal file
|
@ -0,0 +1,78 @@
|
|||
//Copyright 2015 <>< Charles Lohr under the NewBSD or MIT/x11 License.
|
||||
#ifndef _OS_GENERIC_H
|
||||
#define _OS_GENERIC_H
|
||||
|
||||
#if defined( WIN32 ) || defined (WINDOWS) || defined( _WIN32)
|
||||
#define USE_WINDOWS
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
//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: 2012-02-15
|
||||
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
|
@ -7,13 +7,6 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#if defined(WINDOWS) || defined(WIN32) || defined(WIN64) \
|
||||
|| defined(_WIN32) || defined(_WIN64)
|
||||
#ifndef strdup
|
||||
#define strdup _strdup
|
||||
#endif
|
||||
#endif
|
||||
|
||||
int force_white = 0;
|
||||
unsigned char OutLEDs[MAX_LEDS*3];
|
||||
int UsedLEDs;
|
||||
|
|
|
@ -37,7 +37,7 @@ extern struct OutDriverListElem ODList[MAX_OUT_DRIVERS];
|
|||
extern const char OutDriverParameters[MAX_OUT_DRIVER_STRING];
|
||||
|
||||
//Pass setup "name=[driver]"
|
||||
struct DriverInstances * SetupOutDriver( const char * drivername );
|
||||
struct DriverInstances * SetupOutDriver( );
|
||||
void RegOutDriver( const char * ron, struct DriverInstances * (*Init)( ) );
|
||||
|
||||
#define REGISTER_OUT_DRIVER( name ) \
|
||||
|
|
|
@ -6,13 +6,6 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#if defined(WINDOWS) || defined(WIN32) || defined(WIN64) \
|
||||
|| defined(_WIN32) || defined(_WIN64)
|
||||
#ifndef strdup
|
||||
#define strdup _strdup
|
||||
#endif
|
||||
#endif
|
||||
|
||||
static struct chash * parameters;
|
||||
|
||||
//XXX TODO: Make this thread safe.
|
||||
|
|
|
@ -7,6 +7,7 @@ lightx = 2
|
|||
lighty = 1
|
||||
light_siding = 1.6
|
||||
|
||||
|
||||
note_attach_amp_iir = .3000
|
||||
note_attach_amp_iir2 = .1500
|
||||
note_attach_freq_iir = 0.3000
|
|
@ -1,10 +1,10 @@
|
|||
|
||||
This is a vornoi thing:
|
||||
outdrivers = DisplayArray, OutputCells
|
||||
lightx = 3
|
||||
lighty = 3
|
||||
leds = 9
|
||||
fromsides = 0
|
||||
outdrivers = DisplayArray, OutputProminent
|
||||
lightx = 2
|
||||
lighty = 2
|
||||
leds = 4
|
||||
fromsides = 1
|
||||
shape_cutoff = 0.03
|
||||
satamp = 5.000
|
||||
amppow = 2.510
|
||||
|
@ -13,6 +13,8 @@ distpow = 1.500
|
|||
samplerate = 11025
|
||||
buffer = 64
|
||||
|
||||
sourcename = default
|
||||
|
||||
amplify = 2.5
|
||||
note_attach_amp_iir = 0.9000
|
||||
note_attach_amp_iir2 = 0.550
|
|
@ -1 +0,0 @@
|
|||
Subproject commit 0f29047f6bfe287cf032e3ef845939f5d796a70e
|
|
@ -1,20 +0,0 @@
|
|||
{ pkgs ? import <nixpkgs> {} }:
|
||||
let
|
||||
localAdjustments = if (builtins.pathExists ./.local.shell.nix) then import ./.local.shell.nix pkgs else (x: x);
|
||||
in
|
||||
pkgs.mkShell (localAdjustments {
|
||||
nativeBuildInputs = with pkgs; [
|
||||
gnumake
|
||||
gcc
|
||||
glib
|
||||
libpulseaudio
|
||||
alsa-lib
|
||||
xorg.libX11
|
||||
xorg.libXext
|
||||
xorg.libXinerama
|
||||
libusb1
|
||||
udev
|
||||
freeglut
|
||||
pkg-config
|
||||
];
|
||||
})
|
|
@ -8,6 +8,19 @@ set_screeny = 480
|
|||
in_amplitude = 3.0
|
||||
|
||||
sample_channel = -1
|
||||
sourcename = alsa_output.pci-0000_00_1f.3.analog-stereo.monitor
|
||||
#sourcename = bluez_sink.40_EF_4C_CA_A4_5D.monitor
|
||||
#alsa_output.pci-0000_00_1f.3.analog-stereo.monitor
|
||||
## alsa_output.pci-0000_00_1b.0.analog-stereo.monitor
|
||||
# alsa_output.pci-0000_01_00.1.hdmi-stereo-extra2.monitor (On desktop)
|
||||
|
||||
|
||||
#alsa_output.pci-0000_00_1f.3.analog-stereo.monitor
|
||||
#default
|
||||
# alsa_output.pci-0000_00_1b.0.analog-stereo.monitor
|
||||
#alsa_output.pci-0000_00_1f.3.analog-stereo.monitor << New laptop
|
||||
#use pactl list | grep pci- | grep monitor
|
||||
|
||||
|
||||
#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.
|
108
colorchord2/sound.c
Normal file
108
colorchord2/sound.c
Normal file
|
@ -0,0 +1,108 @@
|
|||
//Copyright 2015 <>< Charles Lohr under the ColorChord License.
|
||||
|
||||
#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
|
||||
{
|
||||
printf( "Initializing sound. Recommended driver: %s\n", driver_name );
|
||||
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 );
|
||||
}
|
||||
}
|
||||
|
41
colorchord2/sound.h
Normal file
41
colorchord2/sound.h
Normal file
|
@ -0,0 +1,41 @@
|
|||
//Copyright 2015 <>< Charles Lohr under the ColorChord License.
|
||||
|
||||
#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 );
|
||||
|
||||
#define REGISTER_SOUND( sounddriver, priority, name, function ) \
|
||||
void __attribute__((constructor)) REGISTER##sounddriver() { RegSound( priority, name, function ); }
|
||||
|
||||
#endif
|
||||
|
363
colorchord2/sound_alsa.c
Normal file
363
colorchord2/sound_alsa.c
Normal file
|
@ -0,0 +1,363 @@
|
|||
//Copyright 2015 <>< Charles Lohr under the ColorChord License.
|
||||
|
||||
#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;
|
||||
int alsa_fmt_s16le;
|
||||
|
||||
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, struct SoundDriverAlsa * a )
|
||||
{
|
||||
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));
|
||||
|
||||
printf( "Trying backup: S16LE.\n" );
|
||||
if ((err = snd_pcm_hw_params_set_format (handle, hw_params, SND_PCM_FORMAT_S16_LE )) < 0) {
|
||||
fprintf (stderr, "cannot set sample format (%s)\n",
|
||||
snd_strerror (err));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
a->alsa_fmt_s16le = 1;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
if( a->alsa_fmt_s16le )
|
||||
{
|
||||
//Hacky: Turns out data was s16le.
|
||||
int16_t * dat = (int16_t*)bufr[i];
|
||||
float * dot = bufr[i];
|
||||
int i;
|
||||
int len = a->buffer;
|
||||
for( i = len-1; i >= 0; i-- )
|
||||
{
|
||||
dot[i] = dat[i]/32768.0;
|
||||
}
|
||||
}
|
||||
//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, r ) < 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, r ) < 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 );
|
||||
|
||||
r->alsa_fmt_s16le = 0;
|
||||
|
||||
|
||||
return InitASound(r);
|
||||
}
|
||||
|
||||
REGISTER_SOUND( AlsaSound, 10, "ALSA", InitSoundAlsa );
|
||||
|
45
colorchord2/sound_null.c
Normal file
45
colorchord2/sound_null.c
Normal file
|
@ -0,0 +1,45 @@
|
|||
//Copyright 2015 <>< Charles Lohr under the ColorChord License.
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
|
||||
REGISTER_SOUND( NullSound, 1, "NULL", InitSoundNull );
|
||||
|
379
colorchord2/sound_pulse.c
Normal file
379
colorchord2/sound_pulse.c
Normal file
|
@ -0,0 +1,379 @@
|
|||
//Copyright 2015 <>< Charles Lohr under the ColorChord License.
|
||||
|
||||
//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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
REGISTER_SOUND( PulseSound, 11, "PULSE", InitSoundPulse );
|
||||
|
||||
|
190
colorchord2/sound_win.c
Normal file
190
colorchord2/sound_win.c
Normal file
|
@ -0,0 +1,190 @@
|
|||
//Copyright 2015 <>< Charles Lohr under the ColorChord License.
|
||||
|
||||
#include <windows.h>
|
||||
#include "parameters.h"
|
||||
#include "sound.h"
|
||||
#include "os_generic.h"
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <mmsystem.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#if defined(WIN32) && !defined( TCC )
|
||||
#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;
|
||||
memset( &wfmt, 0, sizeof(wfmt) );
|
||||
printf ("WFMT Size (debugging temp for TCC): %d\n", sizeof(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, (void*)(&HANDLEMIC) , 0, CALLBACK_FUNCTION);
|
||||
|
||||
if( p )
|
||||
{
|
||||
fprintf( stderr, "Error performing waveInOpen. Received code: %d\n", p );
|
||||
}
|
||||
|
||||
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);
|
||||
p = waveInPrepareHeader(r->hMyWave,&(r->WavBuff[i]),sizeof(WAVEHDR));
|
||||
printf( "WIP: %d\n", p );
|
||||
waveInAddBuffer(r->hMyWave,&(r->WavBuff[i]),sizeof(WAVEHDR));
|
||||
printf( "WIA: %d\n", p );
|
||||
}
|
||||
\
|
||||
p = waveInStart(r->hMyWave);
|
||||
if( p )
|
||||
{
|
||||
fprintf( stderr, "Error performing waveInStart. Received code %d\n", p );
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void * InitSoundWin( SoundCBType cb )
|
||||
{
|
||||
struct SoundDriverWin * r = (struct SoundDriverWin *)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);
|
||||
}
|
||||
|
||||
REGISTER_SOUND( SoundWin, 10, "WIN", InitSoundWin );
|
||||
|
|
@ -9,6 +9,9 @@ amppow = 3.5
|
|||
distpow = 1.500
|
||||
ledoutamp = 1
|
||||
|
||||
sourcename = alsa_output.pci-0000_01_00.1.hdmi-stereo-extra1.monitor
|
||||
#alsa_output.pci-0000_00_1f.3.analog-stereo.monitor
|
||||
|
||||
note_attach_amp_iir = .3000
|
||||
note_attach_amp_iir2 = .1500
|
||||
note_attach_freq_iir = 0.3000
|
|
@ -1,24 +0,0 @@
|
|||
all : ../colorchord.exe
|
||||
|
||||
OUTS := OutputVoronoi.o DisplayArray.o OutputLinear.o DisplayPie.o DisplayNetwork.o DisplayUSB2812.o DisplayDMX.o OutputProminent.o RecorderPlugin.o DisplayHIDAPI.o hidapi.o OutputCells.o DisplaySHM.o DisplayFileWrite.o
|
||||
|
||||
SRCS := ../main.c ../dft.c ../decompose.c ../filter.c ../color.c ../notefinder.c ../util.c ../outdrivers.c
|
||||
SRCS += ../parameters.c ../chash.c ../OutputVoronoi.c ../OutputProminent.c ../DisplayArray.c
|
||||
SRCS += ../OutputLinear.c ../DisplayPie.c ../DisplayNetwork.c ../hook.c ../RecorderPlugin.c
|
||||
SRCS += ../../embeddedcommon/DFT32.c ../OutputCells.c ../configs.c ../hidapi.c ../DisplayHIDAPI.c
|
||||
|
||||
WINGCC:= clang -fcolor-diagnostics
|
||||
|
||||
WINGCCFLAGS:= -g -D_CRT_SECURE_NO_WARNINGS -Wno-deprecated-declarations -DICACHE_FLASH_ATTR= -I../../embeddedcommon -I../cnfa -I../rawdraw -I../ -O1 #-O2 -Wl,--relax -Wl,--gc-sections -ffunction-sections -fdata-sections
|
||||
WINLDFLAGS:=-lwinmm -lgdi32 -lws2_32 -lsetupapi -lkernel32 -luser32 -ldbghelp -lole32 -lmmdevapi -lAvrt
|
||||
|
||||
RAWDRAWLIBS:=-lX11 -lm -lpthread -lXinerama -lXext
|
||||
LDLIBS:=-lpthread -lasound -lm -lpulse-simple -lpulse -ludev -lrt
|
||||
|
||||
OBJS:=../main.o ../dft.o ../decompose.o ../filter.o ../color.o ../notefinder.o ../util.o ../outdrivers.o $(OUTS) ../parameters.o ../chash.o ../hook.o ../../embeddedcommon/DFT32.o ../configs.o
|
||||
|
||||
../colorchord.exe : $(SRCS)
|
||||
$(WINGCC) $(WINGCCFLAGS) -o $@ $^ $(WINLDFLAGS)
|
||||
|
||||
clean :
|
||||
rm -rf *.o *~ ../colorchord ../colorchord.exe ../embeddedcc $(OBJS)
|
|
@ -1,15 +0,0 @@
|
|||
@echo off
|
||||
set CC="C:\Program Files\LLVM\bin\clang.exe"
|
||||
rem To enable OpenGL rendering use the -DCNFGOGL option
|
||||
set CCFLAGS=-g -D_CRT_SECURE_NO_WARNINGS -DCNFGOGL -DNO_WIN_HEADERS
|
||||
set CCIFLAGS=-I../../embeddedcommon -I../cnfa -I../rawdraw -I../ -O2
|
||||
set CCLFLAGS=-lwinmm -lgdi32 -lws2_32 -lsetupapi -lkernel32 -luser32 -ldbghelp -lole32 -lmmdevapi -lAvrt -lopengl32
|
||||
set SOURCES=..\main.c ..\chash.c ..\color.c ..\configs.c ..\decompose.c ..\dft.c ..\filter.c ^
|
||||
..\outdrivers.c ..\hidapi.c ..\hook.c ..\parameters.c ..\util.c ..\notefinder.c ^
|
||||
..\..\embeddedcommon\DFT32.c symbol_enumerator.c ^
|
||||
..\DisplayArray.c ..\DisplayDMX.c ..\DisplayFileWrite.c ..\DisplayHIDAPI.c ..\DisplayNetwork.c ^
|
||||
..\DisplayOUTDriver.c ..\DisplayPie.c ..\DisplayRadialPoles.c ..\DisplaySHM.c ..\DisplayUSB2812.c ^
|
||||
..\OutputCells.c ..\OutputLinear.c ..\OutputProminent.c ..\OutputVoronoi.c
|
||||
|
||||
@echo on
|
||||
%CC% %CCFLAGS% %CCIFLAGS% -o ../colorchord.exe %SOURCES% %CCLFLAGS%
|
|
@ -1,25 +1,12 @@
|
|||
@echo off
|
||||
echo Unzip http://download.savannah.gnu.org/releases/tinycc/tcc-0.9.27-win64-bin.zip to C:\tcc
|
||||
echo Also, if compiling with OpenGL, download http://download.savannah.nongnu.org/releases/tinycc/winapi-full-for-0.9.27.zip and overwrite the include, etc. folders in C:\tcc.
|
||||
echo Don't worry. It includes the i386 compiler in the win64 build.
|
||||
|
||||
set CFLAGS= -v -DHIDAPI -DWINDOWS -DWIN32 -DTCC -DRUNTIME_SYMNUM -O2 -Itccinc -DINCLUDING_EMBEDDED -rdynamic -g
|
||||
set INCLUDES=-I../rawdraw -I../cnfa -I.. -I. -I../../embeddedcommon
|
||||
set LDFLAGS=-lkernel32 -lole32 -lgdi32 -luser32 -lsetupapi -ldbghelp -lws2_32 -lAvrt -lopengl32
|
||||
|
||||
rem lots of source files
|
||||
set SOURCES=..\main.c ..\chash.c ..\color.c ..\configs.c ..\decompose.c ..\dft.c ..\filter.c ^
|
||||
..\outdrivers.c ..\hidapi.c ..\hook.c ..\parameters.c ..\util.c ..\notefinder.c ^
|
||||
..\..\embeddedcommon\DFT32.c tcc_stubs.c symbol_enumerator.c ^
|
||||
..\DisplayArray.c ..\DisplayDMX.c ..\DisplayFileWrite.c ..\DisplayHIDAPI.c ..\DisplayNetwork.c ^
|
||||
..\DisplayOUTDriver.c ..\DisplayPie.c ..\DisplayRadialPoles.c ..\DisplaySHM.c ..\DisplayUSB2812.c ^
|
||||
..\OutputCells.c ..\OutputLinear.c ..\OutputProminent.c ..\OutputVoronoi.c
|
||||
|
||||
set ARCH_SPECIFIC=-L32 C:\windows\system32\winmm.dll -DWIN32_LEAN_AND_MEAN
|
||||
set CC=C:\tcc\tcc.exe
|
||||
rem set CC=C:\tcc\i386-win32-tcc.exe
|
||||
set CFLAGS=-v -DHIDAPI -DWINDOWS -DWIN32 -DTCC -DRUNTIME_SYMNUM -Os -Itccinc -DINCLUDING_EMBEDDED -I.. -I. -I../../embeddedcommon -rdynamic -g
|
||||
set LDFLAGS=-lkernel32 -lgdi32 -luser32 -lsetupapi -ldbghelp -lws2_32
|
||||
set SOURCES=..\chash.c ..\color.c ..\configs.c ..\decompose.c ..\dft.c ..\DisplayNetwork.c ..\DisplayArray.c ..\DisplayHIDAPI.c ..\DisplayOUTDriver.c ..\DisplayPie.c ..\DrawFunctions.c ..\filter.c ..\hidapi.c ..\hook.c ..\main.c ..\os_generic.c ..\outdrivers.c ..\OutputCells.c ..\OutputLinear.c ..\OutputProminent.c ..\OutputVoronoi.c ..\parameters.c ..\sound.c ..\sound_win.c ..\sound_null.c ..\util.c ..\WinDriver.c ..\notefinder.c ..\..\embeddedcommon\DFT32.c tcc_stubs.c symbol_enumerator.c
|
||||
set ARCH_SPECIFIC=-L32 C:\windows\system32\winmm.dll
|
||||
set CC=C:\tcc\i386-win32-tcc.exe
|
||||
rem set CC=C:\tcc\x86_64-win32-tcc.exe
|
||||
@echo on
|
||||
%CC% %CFLAGS% %INCLUDES% %ARCH_SPECIFIC% %SOURCES% %LDFLAGS% -o ..\colorchord.exe
|
||||
@echo off
|
||||
%CC% %CFLAGS% %ARCH_SPECIFIC% %SOURCES% %LDFLAGS% -o ..\colorchord.exe
|
||||
pause
|
||||
|
|
|
@ -1,220 +0,0 @@
|
|||
# Theory behind ColorChord's fast DFT.
|
||||
|
||||
Becaue
|
||||
1) ColorChord's binning process needs to focus on notes in frequency
|
||||
space instead of the bins a normal FFT produces, we have to use another
|
||||
tactic to decompose incoming audio into chromatic bins instead of normal
|
||||
bins.
|
||||
2) We want bins which can be foldend on top of each other, i.e. each octave
|
||||
can add to the previous.
|
||||
3) Originally ColorChord accomplished this by using a GPU algorithm which
|
||||
every frame looked at the last several milliseconds of sound and by brute
|
||||
force multipled a sine and cosine for every bin against the incoming audio
|
||||
with a window. This was prohibitively expensive.
|
||||
4) One night I think around 2010 or 2011, Will Murnane, Charles Lohr's
|
||||
roommate at the time had a pretty deep insight. Charles is not good at
|
||||
math or algorithms, but Will is.
|
||||
5) Much later on May 13, 2020, CaiB wanted to understand the process more,
|
||||
and Will Murnane explained this in the ColorChord discord channel.
|
||||
|
||||
This following is not an exact transcript but has minor edits for flow,
|
||||
grammar, and clarity.
|
||||
|
||||
Will: Hey @CaiB I helped Charles figure out the DFT-ish thing that's implemented in colorchord. I did the conceptual stuff, he wrote the actual code.
|
||||
|
||||
CaiB: Ooh, nice to meet you o/
|
||||
|
||||
Will: Basically the idea is this: if we can do something clever to get the note intensities within one octave, then we can average adjacent samples together as a simple low-pass filter, and repeat the clever thing to get notes an octave lower. So then the next insight is that we're looking for repeating patterns, and they can all be decomposed into sine waves. The reasoning behind this is complicated if you haven't seen it, but it's called fourier analysis, and there's a lot of tutorials around on the internet.
|
||||
|
||||
CaiB: I know enough about this stuff to kinda get the gist, but not enough to be practically useful in comprehensive understanding or implementation.
|
||||
|
||||
Will: So, okay, sine waves. let's start with a simple idea: suppose we just guess that the frequency is exactly some value. that is, our samples are `f(t) = sin(t*omega)` for some value of `omega`. So if we multiply our samples by `sin(t*omega)` again, we get `sin(t*omega)*sin(t*omega)` also notated `sin^2(t*omega)`
|
||||
|
||||
CaiB: OK, yeah (If it's easier for you, we can do a voice call so you don't have to type a bunch of stuff)
|
||||
|
||||
Charles: I would kind of like to document the text.
|
||||
|
||||
Will: The way I think of this is basically: if we happen to pick the right value of omega, those values will all tend to add up faster than if we pick the wrong value
|
||||
(really, it's more like `sin(t*omega_actual) * sin(t*omega_guessed))`)
|
||||
|
||||
CaiB: OK, so kinda like constructive vs destructive interference but multiplied not added.
|
||||
|
||||
Will: Exactly! the intuition is that if we pick `omega_guessed` to be nearly equal to `omega_actual` the sum over the window we're considering will add up to a bigger value than if we pick a guess that's, say, 10% too big.
|
||||
|
||||
CaiB: Gotcha
|
||||
|
||||
Will: The next layer to add is this: We're assuming everything is gonna get decomposed into sine waves. However, a sine wave isn't just parameterized by a frequency, there's also phase information. So to account for this potential phase shift, we can multiply our signal with two functions with a 90 degree phase shift
|
||||
two functions that happen to have a 90 degree phase shift are the sine and the cosine.
|
||||
CaiB: Oooh, that way you can look at proprtions of the outputs to get an idea of the actual phase.
|
||||
|
||||
Will: So if we take `f(t) = sin(omega*t + phi)` and then do `f(t) * sin(omega * t) + f(t) * cos(omega*t)` then regardless of the value of phi the sum over our window adds up to the same value! You could extract phase information, but colorchord doesn't actually do that iirc.
|
||||
CaiB: When you say "over the window", what kind of ballpark are we talking? 10 samples? 100?
|
||||
|
||||
Will: Usually it makes sense to think of the window as being in seconds rather than samples. Your sample rate is basically totally up to you, but humans perceive a fixed frequency range around 20hz-20khz
|
||||
|
||||
CaiB: I mean, for ColorChord it's just set by the system audio mode, so around 44.1k-48k usually..
|
||||
|
||||
Will: Yeah, but you can do it on a microcontroller like an AVR where you're doing the ADC yourself at some arbitrary rate and the window can be a sliding window, in the sense that you don't need to process entirely non-overlapping blocks. You could even get to doing this once per sample, just using the last N samples each time.
|
||||
CaiB: Now if I'm remembering correctly, your window size significantly impacts what resolution your output is?
|
||||
|
||||
Will: The window size basically only determines the speed of reaction to new inputs. If you used a 10 second window, you wouldn't notice a new note until it had been around for a few seconds for example ok, so. we figured out how to determine how much a single frequency/note is happening in a window of time. But there's still a bunch of FP math going on, and depending on what system you're dealing with you might have a slow FPU or no sin/cos function in hardware and getting only the intensity of a single frequency doesn't give us very much pitch information at all. so how can we get more? make more guesses in parallel!
|
||||
|
||||
Will: In 12-tone just intonation, the rule is that each half-step is `2^(1/12) ~= 1.06` times higher than its neighbor. so within each octave, we have a separate data structure for the intensity of each half-step so we get 12 of these, with frequencies `[1, 1.06, 1.12, 1.19, 1.26, 1.33, 1.31, 1.50, ...]`
|
||||
CaiB: Let's say you're guessing at 100 Hz and 110 Hz, both of them being let's say 50% strong. How do you know the difference between a sound signal that just has weak content at 100 and 110 vs strong content at 105?
|
||||
|
||||
Will: We'll get there... But ultimately the answer is that you can do curve fitting on the values you get out. you're right, feeding 105 hz into a thing with 100 and 110 hz guesses will give strong values for both (it's actually an exponential system, so the "halfway" point is `sqrt(100*110) = 104.88`, not `105`, but the point stands)
|
||||
but we can also look at the adjacent bins even further outside to give an indication of where the peak is. suppose we're dealing with a `108 hz` signal, and we have bins at `90, 100, 110, 120`.
|
||||
CaiB: Aah.
|
||||
|
||||
Will: We can fit a curve to the intensities in those bins, and we'll find a peak at approximately `108hz`. so as long as we have a decent number of bins, the location of the peak should be pretty easy to find.
|
||||
CaiB: Because one at `108` is a sharper peak (less spread to further bins) than two at `100` and `110`.
|
||||
|
||||
Will: (so ultimately, the exact values of the bins don't really matter, but it's easier for me to think of it as a 12-tone scale) well... there isn't a bin at 108hz, only one at `100` and `110`. Usually music tends to have harmonic content, so you don't see `100hz` played next to `110hz`, you see a single `108hz` note. so colorchord basically assumes you're playing music and you would prefer the answer `108hz`.
|
||||
|
||||
CaiB: Sorry, I meant one peak in frequency content at `108hz` would mean bins `90hz` and `120hz` see lower numbers than an input with content at `100hz` and `110hz`, where `90hz` and `120hz` would see larger numbers.
|
||||
|
||||
Editorial Note from Charles: The section below is an extremely powerful concept. It provides a significant speedup from needing a GPU to being able to run on a CPU. That and realizing we could progressively update.
|
||||
|
||||
Will: Yeah, the spread of the distribution is important. And if you added more bins, you'd be able to see the distinction between `100hz+110hz` and `108hz` alone the expensive part of adding more resolution is adding more bins within a single octave, but there's a clever thing we can do to get all the octaves we can hear for only 2x the cost of doing a single octave. Effectively all of our base frequency bins are in the highest octave we care about. then by doing the 2x averaging i mentioned earlier, we get one octave lower. And by repeatedly averaging samples, we get additional octaves lower basically, think of it like this:
|
||||
* Every sample we calculate octave 8.
|
||||
* Every 2nd sample we average the last 2 samples from octave 8, and use them to calculate octave 7.
|
||||
* Every 4th sample we average the last 2 samples from octave 7, and use them to calculate octave 6.
|
||||
* 8th, 2, 6, 5
|
||||
* 16th, 2, 5, 4
|
||||
* ...
|
||||
By cleverly interleaving the work, we can do a fixed amount of averaging-and-higher-octave-stuff every tick the octave we work on at each tick is determined by the number of zeroes at the end of the binary representation of the tick number.
|
||||
`0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, ...`
|
||||
|
||||
CaiB: Neat. I'm still not quite understanding how the 2x averaging works though, you'd still need to compare it to a sine wave at the lower frequency, right?
|
||||
|
||||
SamE: Question: Why not use the aliasing property of the DFT to get the other frequency bins?
|
||||
|
||||
Will: By averaging, we're effectively getting samples that count for twice as much time, or half as much frequency, which means notes exactly one octave lower.
|
||||
|
||||
Will: @SamE: I may well not understand it well enough to take advantage of it correctly, but let's talk it out a bit.
|
||||
|
||||
SamE: TBH: I don't understand it either.
|
||||
|
||||
Will: @CaiB fyi here's [the definition of the DFT on wikipedia](https://en.wikipedia.org/wiki/Discrete_Fourier_transform#Definition) so you can see how the formalism correlates to the extremely-informal construction I gave above.
|
||||
|
||||
Will: Is there good math formatting maybe? `$f(x) = 3x$`
|
||||
|
||||
CaiB: Yeah, that just flies right over my head by the time I get a few sentences in. Haha, Discord is not fancy enough for anything other than the most basic of formatting.
|
||||
|
||||
Will: The second part with the sines and the cosines is basically what I was talking about. @Sam E so my understanding is basically that because of aliasing, frequencies higher than your sample rate wind up reflected off your sample rate. Which we avoid by using averaging as a low pass filter, so that there can be no frequencies higher than sample rate. iow (?) the highest octave is directly from captured audio samples, so it doesn't contain any information higher than nyquist allows. each of the lower octaves is downsampled to remove one octave off the top.
|
||||
|
||||
CaiB: OH, you're taking the average of every two samples, then treating that as the _same_ sample rate.
|
||||
|
||||
Will: Yes. you want to only keep half as many samples around, though, or you'll be considering twice as long a window in real-time.
|
||||
|
||||
CaiB: Essentially speeding up the audio by 2x, then comparing it to the same standard sine waves.
|
||||
|
||||
Will: Yep!
|
||||
|
||||
CaiB: Ooooh. I was thinking you were taking a 48kHz sample rate signal, averaging them and treating it as a 24kHz signal, which would just get you a worse version of the same result.
|
||||
|
||||
Will: Whoa, here's a picture from wikipedia that describes things very similar to what i'm talking about! ![WOLA Channelizer](https://en.wikipedia.org/wiki/Filter_bank#/media/File:WOLA_channelizer_example.png)
|
||||
|
||||
CaiB: And that way you can precompute the comparisons, one sine and one cosine for each bin in only one octave?
|
||||
|
||||
Will: Yeah! the other trick you can play is to just add the samples instead of averaging. this means you take (let's say) two 16-bit samples, and get one 17-bit result which is twice as large on average as a highest-octave 16-bit sample but you only have half as many of those 17-bit samples in your window, and by definition their sum is the same as the sum of the 16-bit samples so the magnitudes you get per octave are comparable.
|
||||
|
||||
CaiB: Then divide the output by two. Half the resolution, but about half the calculations. Oh!
|
||||
|
||||
Will: In other words you don't get a reading that says "note 6 in octave 3 is twice as loud as in octave 4" when in fact they're equal.
|
||||
|
||||
SamE: What I was saying is trying to be clever (always dangerous) and try and use the aliasing to stack all of the information we want into the same bins for us. One would probably have to add in a few more DSP filters in order to flip some of the higher frequency images. I might have to experiment with this later.
|
||||
|
||||
CaiB: So on the output, you're updating the top octave every sample, the second-highest octave every other sample, etc, until the lowest is only updating once in a while (comparatively)? And that still works OK?
|
||||
|
||||
Will: Using some kind of clever sub-sampling to fold things around might work: [Alias wikipedia page, section Folding](https://en.wikipedia.org/wiki/Aliasing#Folding) Yeah, if you think about it the bottom octave can't change too fast or it wouldn't be in the bottom octave anymore :open_mouth:
|
||||
|
||||
CaiB: But if a low tone starts and stops playing repeatedly, you may end up completely losing that info, and just seeing a weaker signal instead of the actual pulsing.
|
||||
|
||||
Will: Take a pathological example: consider a wave where the top half of each pulse is at 40 hz, and the bottom is at 38hz wellllll... that's really just a 39hz wave with some distortion!
|
||||
|
||||
CaiB: I'm thinking more along the lines of take a 40Hz sine signal, then multiply it by a 0-1 square wave at 200 Hz. You'd just see the 40Hz at a intensity corresponding to the duty cycle of the square wave, not the actual on-off pattern.
|
||||
|
||||
Will: Well, you'd see an output at 40hz corresponding to the amount of time that the 40hz signal is present for sure. but you'd also see a strong 200hz (and many higher-frequency!) signals corresponding to the 200hz square wave
|
||||
|
||||
CaiB: But you have no way of knowing that the 40Hz signal is actually starting and stopping vs just being weaker.
|
||||
|
||||
Will: a description of how you got to a particular waveform isn't the same as a description of what it sounds like :wink:
|
||||
|
||||
SamE: In the frequency domain, aren't they essentially the same thing.
|
||||
|
||||
Will: And if you want a system that changes its fundamental answer 400 times a second, you probably do not want an audio system.
|
||||
|
||||
CaiB: @SamE, My point is if you were to just watch a live graph of the frequency domain, you'd see the low frequencies refresh slowly, and won't be able to see a pulsing signal down there as actually pulsing. Whereas a 5kHz signal turning on and off at that same 200Hz you could clearly see turning on and off because your graph refreshes fast enough up there.
|
||||
|
||||
Will: I think if you wanted to see things at that level you could, by subtracting out old samples from that octave and adding in new ones live... but i don't think that audio sounds like you think.
|
||||
|
||||
SamE: Okay, I need to turn this off so that I can finish a presentation for work. Thanks for explain this to us mere mortals. :slight_smile:
|
||||
|
||||
CaiB: OK, it probably wouldn't be useful to audio but for actually analyzing a signal thoroughly it may be a problem. But I guess that's outside the scope I might try to implement this from scratch when I have some free time, since trying to look at the existing code just makes my brain stop working. So let's say your window is 1000 samples long. You're doing `1000 samples * (1 for sin + 1 for cos) * 24 bins` number of calculations every single sample, just to get the highest octave? Also, is this something you came up with yourself? If so, how? :exploding_head:
|
||||
|
||||
Will: Example Audio (chop.wav) / (chop.pv).
|
||||
|
||||
CaiB: Hmm, how is the actual sin/cos multiplication step done? Because if you just go in with no previous info, (assuming 1000 sample window) you'd need to multiply every sample by the precomputed sin and cos, so 2000 multiplications. Then, assuming you don't want phase info, you'd maybe just add each sin and cos value together, but then you also need to sum all of them up in order to compare the entire window, so 1000 adds. And now do that for every bin, so 24 times just for that one octave. That's a lot of math.
|
||||
|
||||
Will: there are a bunch of tricks you can play to not do so much work. for example, instead of using processing you could use more memory: you can remember the results of `sin*sample + cos*sample` and then just subtract the sample that's falling out of your window and add the one that's coming in. Or approximate your windowing function using an IIR filter: just multiply your "last" value by something like 0.999 each sample, and the influence of old samples will be forgotten implicitly over time.
|
||||
|
||||
CaiB: Charles does love his IIRs (assuming he's the one that made the post-DFT analysis).
|
||||
|
||||
Editorial Note: ColorChord almost exclusively uses IIRs for this purpose. In practice they work/feel better than other triangular solutions because of the logarithmic nature of how we perceive sound. Also, yes, Charles rarely works on a project more than a month before he finds a use for IIRs.
|
||||
|
||||
Will: I built the description in my head of why this works, but i did so by reading a lot on DFT and DTFT and taking a class on complex analysis. stay in school, i guess? Hah, so he does.
|
||||
|
||||
CaiB: "school" did a great job of making me hate every subject I took, even ones I used to love previously...
|
||||
|
||||
Will: PS: the audio file above is generated using the method you mentioned above. i think it shows that mostly what you hear is 200hz and harmonics of that.
|
||||
|
||||
CaiB: Not quite, your square wave is -1 to 1, but I get the point.
|
||||
|
||||
Will: yeah, I agree that school can be a backwards way of approaching subjects if you can become interested in the subject first, and then learn the formality behind it, it can be a great way of figuring out how to apply things, but if you have to slog through the formality with no idea where you're going it's not exactly gonna instill a love of the subject. Yup, so it is, change the definition of `c` to be all that stuff `+1` and it'll be what you were talking about. Doesn't sound much different though. (see chop2.wav)
|
||||
|
||||
![chopfast.png](https://raw.githubusercontent.com/cnlohr/colorchord/master/docs/TheoryOfCCDFT/chopfast.png)
|
||||
|
||||
CaiB: It's more just that the methods taught are often so abstract it feels pointless. An entire class I took was just doing RLC-type circuit analysis using various methods like Laplace transforms. When in the real world, you just plug it into a calculator and will never use that unless you are the 0.1% of people who develop those calculators or other weird applications... 40 and 200 were a bad choice, speeding it up by 4x, you can definitely hear both tones. (See chopfast.wav)
|
||||
|
||||
Will: Sure, but my point earlier is that colorchord will probably tell you "this is mostly 200hz content, with some 40" because that's what it sounds like.
|
||||
|
||||
CaiB: Yeah, good enough.
|
||||
|
||||
Will: Not "this is 40hz, now nothing, now 40hz, now nothing..."
|
||||
|
||||
CaiB: I mean, by the time it shows up, 200Hz changes would be completely smoothed to oblivion anyways. It's just steady this. ![cc-screenshot3.png](https://raw.githubusercontent.com/cnlohr/colorchord/master/docs/TheoryOfCCDFT/cc-screenshot3.png)
|
||||
|
||||
Will: Yeah, what's happening in colorchord is that it squashes all the octaves together.
|
||||
|
||||
CaiB: Yeah, I understand all the processing happening after the DFT. Just a couple of days ago I went through line-by-line, commented and rewrote some parts to actually understand what it does.
|
||||
|
||||
Will: So it sees 40hz, and 200/4 = 50hz, which is a perfect major third: (Wikipedia article on Major Thirds)[https://en.wikipedia.org/wiki/Major_third] so it makes green for one of those and red for the other.
|
||||
|
||||
CaiB: E1-ish = 40Hz, G1-ish = 50Hz. Mine is looking at the 4x version because ColorChord doesn't even look below 55Hz, so more like E3ish and G3ish. Also because my speakers can't output 40Hz enough to be useful here, so I 4xed it to hear the low tone.
|
||||
|
||||
Will: yeah, eventually you need a cutoff on how low you look, aka how many times you average pairs of samples. Also, depending on what shape of sine wave you pick for the highest rate, you can limit how high you listen but still get good resolution and you don't even necessarily need to use many samples, or good-quality samples for your sin/cos tables! Like your high-res sin table, in full, could be: `[0, 1, 0, -1]` And the one at the other end of the octave could be `[1, -1, 1, -1]` or so.
|
||||
|
||||
CaiB: So assuming 5 octaves like is used, and no clever interleaving, every `2 << (5 - 1) = 32` samples you need to calculate all octaves
|
||||
|
||||
Will: Yeah, but over those 32 samples you need to do: highest octave: 32 calculations next-highest: `16, 8, 4, 2, 1` total: 63 calculations (and 31 sums/averages)
|
||||
|
||||
CaiB: Wouldn't it be 63 * 2 because `sin` and `cos`.
|
||||
|
||||
Will: Well, sure, for some complicated version of "calculations"
|
||||
|
||||
CaiB: OK, fair.
|
||||
|
||||
Will: And you need to do some adds and so on. This is all handwavy.
|
||||
|
||||
CaiB: Hold up, 4 element tables? I'm amazed you don't just get literal garbage out of that.
|
||||
|
||||
Will: well it's not great, that's for sure!
|
||||
|
||||
CaiB: Looks like the table in the actual implementation is 256 long.
|
||||
|
||||
Will: Since you're aligning a longer sequence to it, you wind up re-using elements often enough to make it kinda work buuut don't start with a 4-element table as a goal. definitely a distorted sine wave.
|
||||
|
||||
CaiB: Yeah, like said when I have time, I'm going to try to recreate it from scratch, now that I can actually see the logic behind it. A decent while back I ported ColorChord to C#, and copy-pasted/re-wrote everything but the DFT because it looks so alien and scary, so in my fork it literally is still the original C code sitting there all alone. Well thank you very much for taking the time to explain this, very much appreciated
|
||||
|
||||
Will: Happy to help! hope your reimplementation goes well.
|
Binary file not shown.
Before Width: | Height: | Size: 5.1 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Before Width: | Height: | Size: 1.6 KiB |
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue