forked from mia/Aegisub
Work on #209, tweak audio spectrum scaling
Originally committed to SVN as r554.
This commit is contained in:
parent
5c80e6fad2
commit
c69f347035
1 changed files with 44 additions and 168 deletions
|
@ -505,7 +505,7 @@ public:
|
||||||
float *base_in; // audio sample data (shared)
|
float *base_in; // audio sample data (shared)
|
||||||
int samples; // number of samples per column
|
int samples; // number of samples per column
|
||||||
int depth; // display bit depth
|
int depth; // display bit depth
|
||||||
float scale; // vertical scale of display
|
float scale; // vertical scale of display, exponential, min=0, mid=1, max=8
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
wxThread::ExitCode Entry() {
|
wxThread::ExitCode Entry() {
|
||||||
|
@ -540,23 +540,33 @@ protected:
|
||||||
write_ptr = data+i+h*w;
|
write_ptr = data+i+h*w;
|
||||||
write_ptr16 = ((unsigned short*)data)+(i+h*w);
|
write_ptr16 = ((unsigned short*)data)+(i+h*w);
|
||||||
|
|
||||||
// According to the formula at http://en.wikipedia.org/wiki/Fast_Fourier_transform:
|
// The maximum power output from the FFT
|
||||||
// X_k = SUM ( n=0, N-1, x_n * e^(-2*pi*i / N * n * k) )
|
// Derived by maximising the result from the DFT function:
|
||||||
// The maximum output value for our case (real-valued-only input, range -16384 to +16383, N=1024)
|
// f[u] = sum(x=0,N-1)[ f(x) * exp(-2 * pi * i * u * x) ]
|
||||||
// must be:
|
// Where N is the number of samples transformed.
|
||||||
// O(X_k) = O( SUM ( n=0, 1023, 16383 * exp(-2*pi*i / 1024 * 1023 * 1023) ) )
|
// = N * 2^(B-1) * exp(-2 * pi * i * u * x)
|
||||||
// = 1024 * 16383 * exp(-pi*i / 512 * 1023 * 1023)
|
// Maximising by f(x) constant at maximum sample value.
|
||||||
// ~= 16777216 * exp(-2*pi * i * 1024)
|
// B is bit-depth of the samples, so 2^(B-1) is the maximum sample value.
|
||||||
// Since exp(ix) = cos(x) + i * sin(x), |a * exp(i*b)| = a for all real a and b, max
|
// = N * 2^(B-1) * [ cos(-2*pi*u*x) + i sin(-2*pi*u*x) ]
|
||||||
// power will be 16777216.
|
// Expanding using Euler's formula.
|
||||||
// More generally, in this context, it will be:
|
// = N * 2^(B-1) * [ cos(2*pi*u*x) - i sin(2*pi*u*x) ]
|
||||||
// samples * 2^(audio_bit_depth-1)
|
// cos(-x) = cos(x) and sin(-x) = -sin(x)
|
||||||
// Currently 16 bit audio is assumed, meaning samples*16384
|
// = N * 2^(B-1) * cos(2*pi*u*x) - N * 2^(B-1) * i sin(2*pi*u*x) [A]
|
||||||
// But scale this by a user amount (vertical zoom0 -- scale is from 0 to 8
|
// Expand the bracket.
|
||||||
int maxpower = window*16384 / (16*256*scale);
|
// Now determine the maximum magnitude of [A], letting u be constant and x variable.
|
||||||
|
// | N * 2^(B-1) * cos(2*pi*u*x) - N * 2^(B-1) * i sin(2*pi*u*x) |
|
||||||
|
// = sqrt( [N * 2^(B-1) * cos(2*pi*u*x)]^2 + [N * 2^(B-1) * sin(2*pi*u*x)]^2 )
|
||||||
|
// = sqrt( N^2 * 4^(B-1) * cos^2(2*pi*u*x) + N^2 * 4^(B-1) * sin^2(2*pi*u*x) )
|
||||||
|
// = sqrt( N^2 * 4^(B-1) * [ cos^2(2*pi*u*x) + sin^2(2*pi*u*x) ] )
|
||||||
|
// = sqrt( N^2 * 4^(B-1) )
|
||||||
|
// It's known that sin^2(x) + cos^2(x) = 1.
|
||||||
|
// = N * 2^(B-1)
|
||||||
|
|
||||||
|
int maxpower = (1 << (16 - 1))*256;
|
||||||
|
|
||||||
// Calculate the signal power over frequency
|
// Calculate the signal power over frequency
|
||||||
#ifdef SPECTRUM_LOGAGITHMIC
|
#if 0
|
||||||
|
// Logarithmic scale
|
||||||
for (int j = 0; j < window; j++) {
|
for (int j = 0; j < window; j++) {
|
||||||
float t = out_r[j]*out_r[j] + out_i[j]*out_i[j];
|
float t = out_r[j]*out_r[j] + out_i[j]*out_i[j];
|
||||||
if (t < 1)
|
if (t < 1)
|
||||||
|
@ -565,7 +575,21 @@ protected:
|
||||||
power[j] = 10. * log10(t) * 64; // try changing the constant 64 if playing with this
|
power[j] = 10. * log10(t) * 64; // try changing the constant 64 if playing with this
|
||||||
}
|
}
|
||||||
maxpower = 10 * log10((float)maxpower);
|
maxpower = 10 * log10((float)maxpower);
|
||||||
|
#elif 1
|
||||||
|
// "Compressed" scale
|
||||||
|
double onethirdmaxpower = maxpower / 3, twothirdmaxpower = maxpower * 2/3;
|
||||||
|
double overscale = maxpower*8*scale - twothirdmaxpower;
|
||||||
|
for (int j = 0; j < window; j++) {
|
||||||
|
// First do a simple linear scale power calculation -- 8 gives a reasonable default scaling
|
||||||
|
power[j] = sqrt(out_r[j]*out_r[j] + out_i[j]*out_i[j]) * 8 * scale;
|
||||||
|
if (power[j] > maxpower * 2/3) {
|
||||||
|
double p = power[j] - twothirdmaxpower;
|
||||||
|
p = log(p) * onethirdmaxpower / log(overscale);
|
||||||
|
power[j] = p + twothirdmaxpower;
|
||||||
|
}
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
|
// Linear scale
|
||||||
for (int j = 0; j < window; j++) {
|
for (int j = 0; j < window; j++) {
|
||||||
power[j] = sqrt(out_r[j]*out_r[j] + out_i[j]*out_i[j]);
|
power[j] = sqrt(out_r[j]*out_r[j] + out_i[j]*out_i[j]);
|
||||||
}
|
}
|
||||||
|
@ -596,7 +620,7 @@ protected:
|
||||||
for (int samp = sample1; samp <= sample2; samp++) {
|
for (int samp = sample1; samp <= sample2; samp++) {
|
||||||
if (power[samp] > maxval) maxval = power[samp];
|
if (power[samp] > maxval) maxval = power[samp];
|
||||||
}
|
}
|
||||||
int intensity = int(maxval / maxpower);
|
int intensity = int(256 * maxval / maxpower);
|
||||||
WRITE_PIXEL
|
WRITE_PIXEL
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -611,52 +635,12 @@ protected:
|
||||||
float sample1 = power[(int)floor(ideal)+cutoff];
|
float sample1 = power[(int)floor(ideal)+cutoff];
|
||||||
float sample2 = power[(int)ceil(ideal)+cutoff];
|
float sample2 = power[(int)ceil(ideal)+cutoff];
|
||||||
float frac = ideal - floor(ideal);
|
float frac = ideal - floor(ideal);
|
||||||
int intensity = int(((1-frac)*sample1 + frac*sample2) / maxpower);
|
int intensity = int(((1-frac)*sample1 + frac*sample2) / maxpower * 256);
|
||||||
WRITE_PIXEL
|
WRITE_PIXEL
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#undef WRITE_PIXEL
|
#undef WRITE_PIXEL
|
||||||
|
|
||||||
// Draw bar
|
|
||||||
/*float accum = 0;
|
|
||||||
int accumPos = posThres;
|
|
||||||
int y = h;
|
|
||||||
int intensity;
|
|
||||||
float t1,t2;
|
|
||||||
|
|
||||||
for (int j=cutoff;j<halfwindow;j++) {
|
|
||||||
// Calculate magnitude and add to accumulator
|
|
||||||
t1 = out_r[j];
|
|
||||||
t1 *= t1;
|
|
||||||
t2 = out_i[j];
|
|
||||||
t2 *= t2;
|
|
||||||
t2 += t1;
|
|
||||||
accum += sqrt(t2);
|
|
||||||
|
|
||||||
// When accumulator goes over, plot point
|
|
||||||
accumPos--;
|
|
||||||
if (accumPos == 0) {
|
|
||||||
intensity = int(accum*mult);
|
|
||||||
if (intensity > 255) intensity = 255;
|
|
||||||
if (intensity < 0) intensity = 0;
|
|
||||||
|
|
||||||
// Write and go to next row
|
|
||||||
if (depth == 32) {
|
|
||||||
write_ptr -= w;
|
|
||||||
*write_ptr = spectrumColorMap[intensity];
|
|
||||||
}
|
|
||||||
else if (depth == 16) {
|
|
||||||
write_ptr16 -= w;
|
|
||||||
*write_ptr16 = spectrumColorMap16[intensity];
|
|
||||||
}
|
|
||||||
|
|
||||||
y--;
|
|
||||||
if (y == 0) break;
|
|
||||||
accumPos = posThres;
|
|
||||||
accum = 0;
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
delete out_r;
|
delete out_r;
|
||||||
|
@ -715,7 +699,7 @@ void AudioDisplay::DrawSpectrum(wxDC &finaldc,bool weak) {
|
||||||
const int cpu_count = MAX(wxThread::GetCPUCount(), 1);
|
const int cpu_count = MAX(wxThread::GetCPUCount(), 1);
|
||||||
std::vector<SpectrumRendererThread*> threads(cpu_count);
|
std::vector<SpectrumRendererThread*> threads(cpu_count);
|
||||||
for (int i = 0; i < cpu_count; i++) {
|
for (int i = 0; i < cpu_count; i++) {
|
||||||
// Ugh, way too many data to copy in
|
// Ugh, way too much data to copy in
|
||||||
threads[i] = new SpectrumRendererThread();
|
threads[i] = new SpectrumRendererThread();
|
||||||
threads[i]->data = data;
|
threads[i]->data = data;
|
||||||
threads[i]->window = window;
|
threads[i]->window = window;
|
||||||
|
@ -736,118 +720,10 @@ void AudioDisplay::DrawSpectrum(wxDC &finaldc,bool weak) {
|
||||||
delete threads[i];
|
delete threads[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Additional constants
|
|
||||||
const int halfwindow = window/2;
|
|
||||||
const int posThres = MAX(1,int(double(halfwindow-cutOff)/double(h)*0.5/scale + 0.5));
|
|
||||||
const float mult = float(h)/float(halfwindow-cutOff)/255.f;
|
|
||||||
// And more
|
|
||||||
int *write_ptr = data;
|
|
||||||
unsigned short *write_ptr16 = (unsigned short *)data;
|
|
||||||
|
|
||||||
/*// More arrays
|
|
||||||
float *out_r = new float[window];
|
|
||||||
float *out_i = new float[window];
|
|
||||||
int *write_ptr = data;
|
|
||||||
unsigned short *write_ptr16 = (unsigned short *)data;
|
|
||||||
|
|
||||||
// Prepare variables
|
|
||||||
int halfwindow = window/2;
|
|
||||||
int posThres = MAX(1,int(double(halfwindow-cutOff)/double(h)*0.5/scale + 0.5));
|
|
||||||
float mult = float(h)/float(halfwindow-cutOff)/255.f;
|
|
||||||
|
|
||||||
// Calculation loop
|
|
||||||
for (int i=0;i<w;i++) {
|
|
||||||
// Calculate start for current bar
|
|
||||||
__int64 curStart = i*samples-(window/2);
|
|
||||||
if (curStart < 0) curStart = 0;
|
|
||||||
|
|
||||||
// Position input
|
|
||||||
in = raw_float + curStart;
|
|
||||||
|
|
||||||
// Perform the FFT
|
|
||||||
FFT fft;
|
|
||||||
fft.Transform(window,in,out_r,out_i);
|
|
||||||
|
|
||||||
// Draw bar
|
|
||||||
float accum = 0;
|
|
||||||
int accumPos = posThres;
|
|
||||||
int y = h;
|
|
||||||
int intensity;
|
|
||||||
float t1,t2;
|
|
||||||
|
|
||||||
// Position pointer
|
|
||||||
write_ptr = data+i+h*w;
|
|
||||||
write_ptr16 = ((unsigned short*)data)+(i+h*w);
|
|
||||||
|
|
||||||
// Draw loop
|
|
||||||
for (int j=cutOff;j<halfwindow;j++) {
|
|
||||||
// Calculate magnitude and add to accumulator
|
|
||||||
t1 = out_r[j];
|
|
||||||
t1 *= t1;
|
|
||||||
t2 = out_i[j];
|
|
||||||
t2 *= t2;
|
|
||||||
t2 += t1;
|
|
||||||
accum += sqrt(t2);
|
|
||||||
|
|
||||||
// When accumulator goes over, plot point
|
|
||||||
accumPos--;
|
|
||||||
if (accumPos == 0) {
|
|
||||||
intensity = int(accum*mult);
|
|
||||||
if (intensity > 255) intensity = 255;
|
|
||||||
if (intensity < 0) intensity = 0;
|
|
||||||
|
|
||||||
// Write and go to next row
|
|
||||||
if (depth == 32) {
|
|
||||||
write_ptr -= w;
|
|
||||||
*write_ptr = spectrumColorMap[intensity];
|
|
||||||
}
|
|
||||||
else if (depth == 16) {
|
|
||||||
write_ptr16 -= w;
|
|
||||||
*write_ptr16 = spectrumColorMap16[intensity];
|
|
||||||
}
|
|
||||||
|
|
||||||
//dc.SetPen(pen[intensity]);
|
|
||||||
//dc.DrawPoint(i,y);
|
|
||||||
y--;
|
|
||||||
if (y == 0) break;
|
|
||||||
accumPos = posThres;
|
|
||||||
accum = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
////// END OF PARALLELISED CODE //////
|
|
||||||
|
|
||||||
// Clear top of image
|
|
||||||
/*
|
|
||||||
// not needed with new algo, it always fill the entire image
|
|
||||||
int filly = h - (halfwindow-cutOff)/posThres;
|
|
||||||
if (filly < 0) filly = 0;
|
|
||||||
if (depth == 32) {
|
|
||||||
int color = spectrumColorMap[0];
|
|
||||||
for (int y=0;y<filly;y++) {
|
|
||||||
write_ptr = data+y*w;
|
|
||||||
for (int x=0;x<w;x++) {
|
|
||||||
*write_ptr = color;
|
|
||||||
write_ptr++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
unsigned short color = spectrumColorMap16[0];
|
|
||||||
for (int y=0;y<filly;y++) {
|
|
||||||
write_ptr16 = ((unsigned short*)data)+y*w;
|
|
||||||
for (int x=0;x<w;x++) {
|
|
||||||
*write_ptr16 = color;
|
|
||||||
write_ptr16++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Clear memory
|
// Clear memory
|
||||||
delete raw_float;
|
delete raw_float;
|
||||||
|
|
||||||
// Create image
|
// Create image FIXME *BREAKS ON NON-WIN32* (see wx docs)
|
||||||
spectrumDisplay = new wxBitmap((const char*)data,w,h,depth);
|
spectrumDisplay = new wxBitmap((const char*)data,w,h,depth);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue