diff --git a/core/audio_display.cpp b/core/audio_display.cpp index e38a955b4..cdd523bb5 100644 --- a/core/audio_display.cpp +++ b/core/audio_display.cpp @@ -505,7 +505,7 @@ public: float *base_in; // audio sample data (shared) int samples; // number of samples per column 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: wxThread::ExitCode Entry() { @@ -540,23 +540,33 @@ protected: write_ptr = 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: - // X_k = SUM ( n=0, N-1, x_n * e^(-2*pi*i / N * n * k) ) - // The maximum output value for our case (real-valued-only input, range -16384 to +16383, N=1024) - // must be: - // O(X_k) = O( SUM ( n=0, 1023, 16383 * exp(-2*pi*i / 1024 * 1023 * 1023) ) ) - // = 1024 * 16383 * exp(-pi*i / 512 * 1023 * 1023) - // ~= 16777216 * exp(-2*pi * i * 1024) - // Since exp(ix) = cos(x) + i * sin(x), |a * exp(i*b)| = a for all real a and b, max - // power will be 16777216. - // More generally, in this context, it will be: - // samples * 2^(audio_bit_depth-1) - // Currently 16 bit audio is assumed, meaning samples*16384 - // But scale this by a user amount (vertical zoom0 -- scale is from 0 to 8 - int maxpower = window*16384 / (16*256*scale); + // The maximum power output from the FFT + // Derived by maximising the result from the DFT function: + // f[u] = sum(x=0,N-1)[ f(x) * exp(-2 * pi * i * u * x) ] + // Where N is the number of samples transformed. + // = N * 2^(B-1) * exp(-2 * pi * i * u * x) + // Maximising by f(x) constant at maximum sample value. + // B is bit-depth of the samples, so 2^(B-1) is the maximum sample value. + // = N * 2^(B-1) * [ cos(-2*pi*u*x) + i sin(-2*pi*u*x) ] + // Expanding using Euler's formula. + // = N * 2^(B-1) * [ cos(2*pi*u*x) - i sin(2*pi*u*x) ] + // cos(-x) = cos(x) and sin(-x) = -sin(x) + // = N * 2^(B-1) * cos(2*pi*u*x) - N * 2^(B-1) * i sin(2*pi*u*x) [A] + // Expand the bracket. + // 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 -#ifdef SPECTRUM_LOGAGITHMIC +#if 0 + // Logarithmic scale for (int j = 0; j < window; j++) { float t = out_r[j]*out_r[j] + out_i[j]*out_i[j]; if (t < 1) @@ -565,7 +575,21 @@ protected: power[j] = 10. * log10(t) * 64; // try changing the constant 64 if playing with this } 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 + // Linear scale for (int j = 0; j < window; 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++) { if (power[samp] > maxval) maxval = power[samp]; } - int intensity = int(maxval / maxpower); + int intensity = int(256 * maxval / maxpower); WRITE_PIXEL } } @@ -611,52 +635,12 @@ protected: float sample1 = power[(int)floor(ideal)+cutoff]; float sample2 = power[(int)ceil(ideal)+cutoff]; 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 } } #undef WRITE_PIXEL - - // Draw bar - /*float accum = 0; - int accumPos = posThres; - int y = h; - int intensity; - float t1,t2; - - for (int j=cutoff;j 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; @@ -715,7 +699,7 @@ void AudioDisplay::DrawSpectrum(wxDC &finaldc,bool weak) { const int cpu_count = MAX(wxThread::GetCPUCount(), 1); std::vector threads(cpu_count); 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]->data = data; threads[i]->window = window; @@ -736,118 +720,10 @@ void AudioDisplay::DrawSpectrum(wxDC &finaldc,bool weak) { 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 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