Work on #209, tweak audio spectrum scaling

Originally committed to SVN as r554.
This commit is contained in:
Niels Martin Hansen 2006-11-23 21:43:45 +00:00
parent 5c80e6fad2
commit c69f347035
1 changed files with 44 additions and 168 deletions

View File

@ -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);
} }