Mercurial > hg > svgui
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 } |