While experimenting with minibleps I noticed that when zooming in on an audiothumbnail to much the waveform turns into a connect-the-dots puzzle, see fig 1. I understand this is because the thumbnail for efficiency reasons is drawn entirely with recatngles. But if you switch to paths when zooming in on a sub sample level, you won’t have to sacrifice on efficiency and still have the sub sample resolution, see pic 2. So what about updating AudioThumbnail::drawCahnnel like this?
void drawChannel (Graphics& g, const Rectangle& area,
const double startTime, const double endTime,
const int channelNum, const float verticalZoomFactor,
const double rate, const int numChans, const int sampsPerThumbSample,
LevelDataSource* levelData, const OwnedArray& chans)
{
if (refillCache (area.getWidth(), startTime, endTime, rate,
numChans, sampsPerThumbSample, levelData, chans)
&& isPositiveAndBelow (channelNum, numChannelsCached))
{
const Rectangle clip (g.getClipBounds().getIntersection (area.withWidth (jmin (numSamplesCached, area.getWidth()))));
if (! clip.isEmpty()) { const float topY = (float) area.getY(); const float bottomY = (float) area.getBottom(); const float midY = (topY + bottomY) * 0.5f; const float vscale = verticalZoomFactor * (bottomY - topY) / 256.0f;
const MinMaxValue* cacheData = getData (channelNum, clip.getX() - area.getX());
RectangleList<float> waveform; waveform.ensureStorageAllocated (clip.getWidth() + 1); //1 added. don't remember exactly why...
double x = (double)clip.getX(); //need double here to get enough resolution to do x++ in big zoom in
double pixelsPerSamples = g.getClipBounds().getWidth() / (endTime - startTime) / rate; const bool usePath = pixelsPerSamples > 1.0; bool havePoints = false; Path path;
for (int w = clip.getWidth(); --w >= 0;) { if (cacheData->isNonZero()) { const float top = jmax (midY - cacheData->getMaxValue() * vscale - 0.3f, topY); const float bottom = jmin (midY - cacheData->getMinValue() * vscale + 0.3f, bottomY);
if (usePath) if (havePoints) path.lineTo((float)x, top); else { path.startNewSubPath((float)x, top); havePoints = true; } else waveform.addWithoutMerging(Rectangle<float>((float)x, top, 1.0f, bottom - top)); }
x += 1.0f; ++cacheData; }
if (!path.isEmpty()) { PathStrokeType pst(1.0f, PathStrokeType::JointStyle::curved, PathStrokeType::EndCapStyle::butt); g.strokePath(path, pst); } else g.fillRectList(waveform); } } }