PDA

View Full Version : Increase QPainter performance



travelan
3rd July 2009, 10:36
Hey,

I decided to develop my own audio editor targeted at audio for music. I've spend like 40 to 50 hours on it yet and I've managed to accomplish a few milestones already. Here's a list of things I have implemented:

- Everyting is written in C and C++. Some critical code is implemented in C for performance. I'm planning to port this to ASM some time.
- I'm using Qt 4.5 for the graphical user interface and some if it's classes to simplify cross-platform compatibility. (it's running on Mac OS X and Windows. Linux should work, but is untested)
- Reading of a few audio file formats is implemented. (at this moment: wav, aiff, snd, w64 and my own raw format)
- Drawing of a nice waveform is implemented.
- Drawing of a frequency spectrum over time is implemented (like in soundtrack pro)
- Some usual user-interface interactions like selecting and scrolling.
- Almost everything that has nothing to do with audio and DSP programming (dialog boxes like settings and things like session management, etc...)

I'm really proud of what I've accomplished so far (I have no experience with DSP and audio programming yet).
The main problem I'm facing right now is performance issues with drawing a waveform... As it is not possible to load an entire audio file in the memory (a 3 minute wave file already is like 50 megs...) I have to do some smart stuff with caching and loading individual samples from the wave file. I've implemented this by reading a fixed amount of samples (512 for instance) from the hard disk each time the application needs a sample. Those 512 samples are then cached for quick retrieval so the next 511 samples could be read from the memory instantly.
When I'm using a profiler (in my case Apple Shark) I can see that the function used for retrieving samples is a lot faster indeed. Now my bottleneck seems to be QPainter!

I'm drawing a line from the center of the waveform widget to a certain value in the positive and a certain value in the negative space. That are two QPainter->drawLine() calls per width-pixel (so when my waveformarea is 500 px wide, there are 1000 calls to QPainter->drawLine).
These two calls are using more than 80% of the time in the update function (and there is a lot going on in that function, including waveform sample retrieval).
I suppose there is a better way to implement this, but how? Maybe drawing the lines into a buffer and drawing that buffer in the QPainter in one call?

Thanks in advance!

Bas

wysota
3rd July 2009, 11:08
Can we see the code?

travelan
3rd July 2009, 11:28
of course!



int heighthalf = height / 2;
int channels = this->audioFile->sfinfo.channels;
int i, q = 0;
int frames = this->audioFile->sfinfo.frames;

int resolution = (to - from) / width;

QPainter painter(canvas);

painter.setBrush(QBrush(QColor::fromRgb(230, 230, 230, 255), Qt::SolidPattern));
painter.drawRect(-10, -10, width + 10, height + 10);

painter.setPen(QColor::fromRgb(69, 102, 99, 255));
float pos = 0.0;
float neg = 0.0;
int left = 0;
for (i = 0; left < width && from + i < frames; i++) {
double t = this->audioFile->getFrame(from + i, 0);
if (t <= 1.0 && t >= -1.0) {
if (t > 0.0) {
if (t > pos)
pos = t;
} else {
if (t < neg)
neg = t;
}
}
if (q >= resolution) {
neg = -neg;
QPoint currentpos = QPoint(left, heighthalf - (pos * heighthalf));
QPoint currentneg = QPoint(left, heighthalf + (neg * heighthalf));
painter.drawLine(currentpos, QPoint(left, heighthalf)); // THESE TWO LINES ARE (WHAT I THINK) THE BOTTLENECK
painter.drawLine(currentneg, QPoint(left, heighthalf));
q = 0;
pos = 0;
neg = 0;
left++;
} else {
q++;
}
}

painter.setPen(QPen(QColor::fromRgb(69, 102, 99, 60)));
painter.drawLine(0, heighthalf + heighthalf / 2, width, heighthalf + heighthalf / 2);
painter.drawLine(0, heighthalf / 2, width, heighthalf / 2);
painter.setPen(QPen(QColor::fromRgb(56, 84, 90, 255)));
painter.drawLine(0, heighthalf, width, heighthalf);

wysota
3rd July 2009, 11:52
Try using QPainter::drawLines() instead of QPainter::drawLine(), it's much faster