comparison layer/SpectrogramLayer.cpp @ 481:74a7729e3653

* sort out cropping and scaling for x-smoothed draw buffer
author Chris Cannam
date Tue, 03 Feb 2009 18:03:48 +0000
parents 567b94e627b8
children 7f1ed4bfea1e
comparison
equal deleted inserted replaced
480:567b94e627b8 481:74a7729e3653
1804 1804
1805 bool recreateWholeImageCache = true; 1805 bool recreateWholeImageCache = true;
1806 1806
1807 x0 = rect.left(); 1807 x0 = rect.left();
1808 x1 = rect.right() + 1; 1808 x1 = rect.right() + 1;
1809 1809 /*
1810 float xPixelRatio = float(fft->getResolution()) / float(zoomLevel); 1810 float xPixelRatio = float(fft->getResolution()) / float(zoomLevel);
1811 std::cerr << "xPixelRatio = " << xPixelRatio << std::endl; 1811 std::cerr << "xPixelRatio = " << xPixelRatio << std::endl;
1812 if (xPixelRatio < 1.f) xPixelRatio = 1.f; 1812 if (xPixelRatio < 1.f) xPixelRatio = 1.f;
1813 1813 */
1814 if (cache.validArea.width() > 0) { 1814 if (cache.validArea.width() > 0) {
1815 1815
1816 if (int(cache.zoomLevel) == zoomLevel && 1816 if (int(cache.zoomLevel) == zoomLevel &&
1817 cache.image.width() == v->width() && 1817 cache.image.width() == v->width() &&
1818 cache.image.height() == v->height()) { 1818 cache.image.height() == v->height()) {
2011 if (cache.validArea.width() > 0) { 2011 if (cache.validArea.width() > 0) {
2012 2012
2013 // If part of the cache is known to be valid, select a strip 2013 // If part of the cache is known to be valid, select a strip
2014 // immediately to left or right of the valid part 2014 // immediately to left or right of the valid part
2015 2015
2016 //!!! this really needs to be coordinated with the selection
2017 //!!! of m_drawBuffer boundaries in the bufferBinResolution
2018 //!!! case below
2019
2016 int vx0 = 0, vx1 = 0; 2020 int vx0 = 0, vx1 = 0;
2017 vx0 = cache.validArea.x(); 2021 vx0 = cache.validArea.x();
2018 vx1 = cache.validArea.x() + cache.validArea.width(); 2022 vx1 = cache.validArea.x() + cache.validArea.width();
2019 2023
2020 #ifdef DEBUG_SPECTROGRAM_REPAINT 2024 #ifdef DEBUG_SPECTROGRAM_REPAINT
2073 << "x" << h << std::endl; 2077 << "x" << h << std::endl;
2074 #endif 2078 #endif
2075 cache.validArea = QRect(x0, 0, x1 - x0, h); 2079 cache.validArea = QRect(x0, 0, x1 - x0, h);
2076 } 2080 }
2077 2081
2082 /*
2078 if (xPixelRatio != 1.f) { 2083 if (xPixelRatio != 1.f) {
2079 x0 = int((int(x0 / xPixelRatio) - 4) * xPixelRatio + 0.0001); 2084 x0 = int((int(x0 / xPixelRatio) - 4) * xPixelRatio + 0.0001);
2080 x1 = int((int(x1 / xPixelRatio) + 4) * xPixelRatio + 0.0001); 2085 x1 = int((int(x1 / xPixelRatio) + 4) * xPixelRatio + 0.0001);
2081 } 2086 }
2082 2087 */
2083 int w = x1 - x0; 2088 int w = x1 - x0;
2084 2089
2085 #ifdef DEBUG_SPECTROGRAM_REPAINT 2090 #ifdef DEBUG_SPECTROGRAM_REPAINT
2086 std::cerr << "x0 " << x0 << ", x1 " << x1 << ", w " << w << ", h " << h << std::endl; 2091 std::cerr << "x0 " << x0 << ", x1 " << x1 << ", w " << w << ", h " << h << std::endl;
2087 #endif 2092 #endif
2088
2089 if (m_drawBuffer.width() < w / xPixelRatio + 1 ||
2090 m_drawBuffer.height() < h) {
2091 m_drawBuffer = QImage(w / xPixelRatio + 1, h, QImage::Format_Indexed8);
2092 m_drawBuffer.setNumColors(256);
2093 for (int pixel = 0; pixel < 256; ++pixel) {
2094 m_drawBuffer.setColor(pixel, m_palette.getColour(pixel).rgb());
2095 }
2096 }
2097
2098 // m_drawBuffer.fill(m_palette.getColour(0).rgb());
2099 m_drawBuffer.fill(0);
2100 2093
2101 int sr = m_model->getSampleRate(); 2094 int sr = m_model->getSampleRate();
2102 2095
2103 // Set minFreq and maxFreq to the frequency extents of the possibly 2096 // Set minFreq and maxFreq to the frequency extents of the possibly
2104 // zero-padded visible bin range, and displayMinFreq and displayMaxFreq 2097 // zero-padded visible bin range, and displayMinFreq and displayMaxFreq
2140 displayMaxFreq = getEffectiveMaxFrequency(); 2133 displayMaxFreq = getEffectiveMaxFrequency();
2141 } 2134 }
2142 2135
2143 // std::cerr << "(giving actual minFreq " << minFreq << " and display minFreq " << displayMinFreq << ")" << std::endl; 2136 // std::cerr << "(giving actual minFreq " << minFreq << " and display minFreq " << displayMinFreq << ")" << std::endl;
2144 2137
2138 size_t increment = getWindowIncrement();
2139
2140 bool logarithmic = (m_frequencyScale == LogFrequencyScale);
2141
2145 float yforbin[maxbin - minbin + 1]; 2142 float yforbin[maxbin - minbin + 1];
2146
2147 size_t increment = getWindowIncrement();
2148
2149 bool logarithmic = (m_frequencyScale == LogFrequencyScale);
2150 2143
2151 for (size_t q = minbin; q <= maxbin; ++q) { 2144 for (size_t q = minbin; q <= maxbin; ++q) {
2152 float f0 = (float(q) * sr) / fftSize; 2145 float f0 = (float(q) * sr) / fftSize;
2153 yforbin[q - minbin] = 2146 yforbin[q - minbin] =
2154 v->getYForFrequency(f0, displayMinFreq, displayMaxFreq, 2147 v->getYForFrequency(f0, displayMinFreq, displayMaxFreq,
2174 size_t pixels = 0; 2167 size_t pixels = 0;
2175 #endif 2168 #endif
2176 2169
2177 Profiler outerprof("SpectrogramLayer::paint: all cols"); 2170 Profiler outerprof("SpectrogramLayer::paint: all cols");
2178 2171
2179 int bufwid = w / xPixelRatio; 2172 // The draw buffer contains a fragment at either our pixel
2180 int binforx[bufwid + 1]; 2173 // resolution (if there is more than one time-bin per pixel) or
2181 int binfory[h + 1]; 2174 // time-bin resolution (if a time-bin spans more than one pixel).
2182 2175 // We need to ensure that it starts and ends at points where a
2183 for (int x = 0; x <= bufwid; ++x) { 2176 // time-bin boundary occurs at an exact pixel boundary, and with a
2184 int vx = int(x0 + x * xPixelRatio + 0.0001); 2177 // certain amount of overlap across existing pixels so that we can
2185 float s0 = 0, s1 = 0; 2178 // scale and draw from it without smoothing errors at the edges.
2186 if (!getXBinRange(v, vx, s0, s1)) { 2179
2187 binforx[x] = -1; 2180 // If (getFrameForX(x) / increment) * increment ==
2188 } else { 2181 // getFrameForX(x), then x is a time-bin boundary. We want two
2189 binforx[x] = int(s0 + 0.0001); 2182 // such boundaries at either side of the draw buffer -- one which
2190 } 2183 // we draw up to, and one which we subsequently crop at.
2191 } 2184
2185 bool bufferBinResolution = false;
2186 if (increment > zoomLevel) bufferBinResolution = true;
2187
2188 long leftBoundaryFrame = -1, leftCropFrame = -1;
2189 long rightBoundaryFrame = -1, rightCropFrame = -1;
2190
2191 int bufwid;
2192
2193 if (bufferBinResolution) {
2194
2195 for (int x = x0 - 1; ; --x) {
2196 long f = v->getFrameForX(x);
2197 if ((f / increment) * increment == f) {
2198 if (leftCropFrame == -1) leftCropFrame = f;
2199 else if (x < x0 - 3) { leftBoundaryFrame = f; break; }
2200 }
2201 }
2202 for (int x = x0 + w + 1; ; ++x) {
2203 long f = v->getFrameForX(x);
2204 if ((f / increment) * increment == f) {
2205 if (rightCropFrame == -1) rightCropFrame = f;
2206 else if (x > x0 + w + 3) { rightBoundaryFrame = f; break; }
2207 }
2208 }
2209 cerr << "Left: crop: " << leftCropFrame << " (bin " << leftCropFrame/increment << "); boundary: " << leftBoundaryFrame << " (bin " << leftBoundaryFrame/increment << ")" << endl;
2210 cerr << "Right: crop: " << rightCropFrame << " (bin " << rightCropFrame/increment << "); boundary: " << rightBoundaryFrame << " (bin " << rightBoundaryFrame/increment << ")" << endl;
2211
2212 bufwid = (rightBoundaryFrame - leftBoundaryFrame) / increment;
2213
2214 } else {
2215
2216 bufwid = w;
2217 }
2218
2219 int binforx[bufwid];
2220 int binfory[h];
2221
2222 if (bufferBinResolution) {
2223 for (int x = 0; x < bufwid; ++x) {
2224 binforx[x] = (leftBoundaryFrame / increment) + x;
2225 cerr << "binforx[" << x << "] = " << binforx[x] << endl;
2226 }
2227 m_drawBuffer = QImage(bufwid, h, QImage::Format_Indexed8);
2228 } else {
2229 for (int x = 0; x < bufwid; ++x) {
2230 float s0 = 0, s1 = 0;
2231 if (getXBinRange(v, x + x0, s0, s1)) {
2232 binforx[x] = int(s0 + 0.0001);
2233 } else {
2234 binforx[x] = 0; //???
2235 }
2236 }
2237 if (m_drawBuffer.width() < bufwid || m_drawBuffer.height() < h) {
2238 m_drawBuffer = QImage(bufwid, h, QImage::Format_Indexed8);
2239 }
2240 }
2241
2242 m_drawBuffer.setNumColors(256);
2243 for (int pixel = 0; pixel < 256; ++pixel) {
2244 m_drawBuffer.setColor(pixel, m_palette.getColour(pixel).rgb());
2245 }
2246
2247 m_drawBuffer.fill(0);
2192 2248
2193 for (int y = 0; y < h; ++y) { 2249 for (int y = 0; y < h; ++y) {
2194 float q0 = 0, q1 = 0; 2250 float q0 = 0, q1 = 0;
2195 if (!getYBinRange(v, h-y, q0, q1)) { 2251 if (!getYBinRange(v, h-y-1, q0, q1)) {
2196 binfory[y] = -1; 2252 binfory[y] = -1;
2197 } else { 2253 } else {
2198 binfory[y] = int(q0 + 0.0001); 2254 binfory[y] = int(q0 + 0.0001);
2199 } 2255 }
2200 } 2256 }
2201 binfory[h] = binfory[h-1]; 2257
2202 2258 paintDrawBuffer(v, fft, bufwid, h, binforx, binfory);
2203 paintColumnValues2(v, fft, bufwid, h, binforx, binfory);
2204 2259
2205 /* 2260 /*
2206 for (int x = 0; x < w / xPixelRatio; ++x) { 2261 for (int x = 0; x < w / xPixelRatio; ++x) {
2207 2262
2208 Profiler innerprof("SpectrogramLayer::paint: 1 pixel column"); 2263 Profiler innerprof("SpectrogramLayer::paint: 1 pixel column");
2248 cache.image = QImage(v->width(), h, QImage::Format_RGB32); 2303 cache.image = QImage(v->width(), h, QImage::Format_RGB32);
2249 } 2304 }
2250 2305
2251 if (w > 0) { 2306 if (w > 0) {
2252 #ifdef DEBUG_SPECTROGRAM_REPAINT 2307 #ifdef DEBUG_SPECTROGRAM_REPAINT
2253 std::cerr << "Painting " << w/xPixelRatio << "x" << h 2308 std::cerr << "Painting " << w << "x" << h
2254 << " from draw buffer at " << 0 << "," << 0 2309 << " from draw buffer at " << 0 << "," << 0
2255 << " to " << w << "x" << h << " on cache at " 2310 << " to " << w << "x" << h << " on cache at "
2256 << x0 << "," << 0 << std::endl; 2311 << x0 << "," << 0 << std::endl;
2257 #endif 2312 #endif
2258 2313
2259 QPainter cachePainter(&cache.image); 2314 QPainter cachePainter(&cache.image);
2260 cachePainter.setRenderHint(QPainter::SmoothPixmapTransform, true); 2315
2261 cachePainter.drawImage(QRect(x0, 0, w, h), 2316 if (bufferBinResolution) {
2262 m_drawBuffer, 2317 int scaledLeft = v->getXForFrame(leftBoundaryFrame);
2263 QRect(0, 0, w / xPixelRatio, h)); 2318 int scaledRight = v->getXForFrame(rightBoundaryFrame);
2319 cerr << "Rescaling image from " << bufwid
2320 << "x" << h << " to "
2321 << scaledRight-scaledLeft << "x" << h << endl;
2322 QImage scaled = m_drawBuffer.scaled
2323 (scaledRight - scaledLeft, h,
2324 Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
2325 // cachePainter.setRenderHint(QPainter::SmoothPixmapTransform, true);
2326 int scaledLeftCrop = v->getXForFrame(leftCropFrame);
2327 int scaledRightCrop = v->getXForFrame(rightCropFrame);
2328 cerr << "Drawing image region of width " << scaledRightCrop - scaledLeftCrop << " to "
2329 << scaledLeftCrop << " from " << scaledLeftCrop - scaledLeft << endl;
2330 cachePainter.drawImage
2331 (QRect(scaledLeftCrop, 0,
2332 scaledRightCrop - scaledLeftCrop, h),
2333 scaled,
2334 QRect(scaledLeftCrop - scaledLeft, 0,
2335 scaledRightCrop - scaledLeftCrop, h));
2336 } else {
2337 cachePainter.drawImage(QRect(x0, 0, w, h),
2338 m_drawBuffer,
2339 QRect(0, 0, w, h));
2340 }
2341
2264 cachePainter.end(); 2342 cachePainter.end();
2265 } 2343 }
2266 2344
2267 QRect pr = rect & cache.validArea; 2345 QRect pr = rect & cache.validArea;
2268 2346
2331 2409
2332 //!!! if (fftSuspended) fft->resume(); 2410 //!!! if (fftSuspended) fft->resume();
2333 } 2411 }
2334 2412
2335 bool 2413 bool
2336 SpectrogramLayer::paintColumnValues2(View *v, 2414 SpectrogramLayer::paintDrawBuffer(View *v,
2337 FFTModel *fft, 2415 FFTModel *fft,
2338 int w, 2416 int w,
2339 int h, 2417 int h,
2340 int *binforx, // w+1 values 2418 int *binforx,
2341 int *binfory // h+1 values 2419 int *binfory) const
2342 ) const 2420 {
2343 { 2421 Profiler profiler("SpectrogramLayer::paintDrawBuffer");
2344 // paint onto m_drawBuffer
2345 2422
2346 int minbin = binfory[0]; 2423 int minbin = binfory[0];
2347 int maxbin = binfory[h-1]; 2424 int maxbin = binfory[h-1];
2348 2425
2349 cerr << "minbin " << minbin << ", maxbin " << maxbin << endl; 2426 cerr << "minbin " << minbin << ", maxbin " << maxbin << "; w " << w << ", h " << h << endl;
2350 if (minbin < 0) minbin = 0; 2427 if (minbin < 0) minbin = 0;
2351 if (maxbin < 0) maxbin = minbin+1; 2428 if (maxbin < 0) maxbin = minbin+1;
2352 2429
2353 int psx = -1; 2430 int psx = -1;
2354 float values[maxbin - minbin + 1]; 2431 float values[maxbin - minbin + 1];
2358 for (int y = 0; y < h; ++y) { 2435 for (int y = 0; y < h; ++y) {
2359 2436
2360 unsigned char peakpix = 0; 2437 unsigned char peakpix = 0;
2361 2438
2362 int sx0 = binforx[x]; 2439 int sx0 = binforx[x];
2363 int sx1 = binforx[x+1]; 2440 int sx1 = sx0;
2441 if (x+1 < w) sx1 = binforx[x+1];
2364 if (sx0 < 0) sx0 = sx1 - 1; 2442 if (sx0 < 0) sx0 = sx1 - 1;
2365 if (sx0 < 0) continue; 2443 if (sx0 < 0) continue;
2366 if (sx1 <= sx0) sx1 = sx0 + 1; 2444 if (sx1 <= sx0) sx1 = sx0 + 1;
2367 2445
2368 for (int sx = sx0; sx < sx1; ++sx) { 2446 for (int sx = sx0; sx < sx1; ++sx) {
2382 fft->getMagnitudesAt(sx, values, minbin, maxbin - minbin + 1); 2460 fft->getMagnitudesAt(sx, values, minbin, maxbin - minbin + 1);
2383 psx = sx; 2461 psx = sx;
2384 } 2462 }
2385 2463
2386 int sy0 = binfory[y]; 2464 int sy0 = binfory[y];
2387 int sy1 = binfory[y+1]; 2465 int sy1 = sy0;
2466 if (y+1 < h) sy1 = binfory[y+1];
2388 if (sy0 < 0) sy0 = sy1 - 1; 2467 if (sy0 < 0) sy0 = sy1 - 1;
2389 if (sy0 < 0) continue; 2468 if (sy0 < 0) continue;
2390 if (sy1 <= sy0) sy1 = sy0 + 1; 2469 if (sy1 <= sy0) sy1 = sy0 + 1;
2391 2470
2392 // cerr << "sy0 " << sy0 << " sy1 " << sy1 << endl; 2471 // cerr << "sy0 " << sy0 << " sy1 " << sy1 << endl;
2393 2472
2394 //!!! review 2473 //!!! review -- if we know we're dealing with
2474 //!!! magnitudes here, we can just use peak of the
2475 //!!! float values
2476
2477 float peak = 0.f;
2478
2395 for (int sy = sy0; sy < sy1; ++sy) { 2479 for (int sy = sy0; sy < sy1; ++sy) {
2396 2480
2397 float value = values[sy - minbin]; 2481 float value = values[sy - minbin];
2398 2482 /*
2399 if (m_colourScale != PhaseColourScale) { 2483 if (m_colourScale != PhaseColourScale) {
2400 if (!m_normalizeColumns) { 2484 if (!m_normalizeColumns) {
2401 value /= (m_fftSize/2.f); 2485 value /= (m_fftSize/2.f);
2402 } 2486 }
2403 //!!! mag.sample(value); 2487 //!!! mag.sample(value);
2405 } 2489 }
2406 2490
2407 unsigned char pix = getDisplayValue(v, value); 2491 unsigned char pix = getDisplayValue(v, value);
2408 if (pix > peakpix) peakpix = pix; 2492 if (pix > peakpix) peakpix = pix;
2409 // cerr <<x<<","<<y<<" -> "<<sx<<","<<sy<<" -> "<<values[sy]<<" -> "<<(int)pix<< endl; 2493 // cerr <<x<<","<<y<<" -> "<<sx<<","<<sy<<" -> "<<values[sy]<<" -> "<<(int)pix<< endl;
2494 */
2495 if (value > peak) peak = value; //!!! not right for phase!
2410 } 2496 }
2497
2498 if (m_colourScale != PhaseColourScale) {
2499 if (!m_normalizeColumns) {
2500 peak /= (m_fftSize/2.f);
2501 }
2502 //!!! mag.sample(value);
2503 peak *= m_gain;
2504 }
2505
2506 peakpix = getDisplayValue(v, peak);
2411 } 2507 }
2412 2508
2413 m_drawBuffer.setPixel(x, h-y-1, peakpix); 2509 m_drawBuffer.setPixel(x, h-y-1, peakpix);
2414 } 2510 }
2415 } 2511 }