Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Performance optimization of waveform drawing

I'm building an app that draw the waveform of input audio data.

Here is a visual representation of how it looks:

It behaves in similar way to Apple's native VoiceMemos app. But it lacks the performance. Waveform itself is a UIScrollView subclass where I draw instances of CALayer to represent purple 'bars' and add them as a sublayers. At the beginning waveform is empty, when sound input starts I update waveform with this function:

class ScrollingWaveformPlot: UIScrollView {

  var offset: CGFloat = 0
  var normalColor: UIColor?
  var waveforms: [CALayer] = []
  var lastBarRect: CGRect?
  var kBarWidth: Int = 5

  func updateGraph(with value: Float) {

    //Create instance
    self.lastBarRect = CGRect(x: self.offset,
                              y:   self.frame.height / 2,
                              width: CGFloat(self.barWidth),
                              height: -(CGFloat)(value * 2))


    let barLayer = CALayer()
    barLayer.bounds = self.lastBarRect!
    barLayer.position = CGPoint(x: self.offset + CGFloat(self.barWidth) / 2,
                                y: self.frame.height / 2)
    barLayer.backgroundColor = self.normalColor?.cgColor

    self.layer.addSublayer(barLayer)
    self.waveforms.append(barLayer)

    self.offset += 7

  }

...

}

When last rect of purple bar reaches the middle of screen I begin to increase contentOffset.x of waveform to keep it running like Apple's VoiceMemos app.

The problem is: when bar count reaches ~500...1000 some noticeable lag of waveform begin to happen during setContentOffset.

self.inputAudioPlot.setContentOffset(CGPoint(x: CGFloat(self.offset) - CGFloat(self.view.frame.width / 2 - 7),y: 0), animated: false)

What can be optimized here? Any help appreciated.

like image 637
Alexey Savchenko Avatar asked May 28 '17 09:05

Alexey Savchenko


1 Answers

Simply remove the bars that get scrolled off-screen from their superlayer. If you want to get fancy you could put them in a queue to reuse when adding new samples, but this might not be worth the effort.

If you don’t let the user scroll inside that view it might even be worth it to not use a scroll view at all and instead move the visible bars to the left when you add a new one.

Of course if you do need to let the user scroll you have some more work to do. Then you first have to store all values you are displaying somewhere so you can get them back. With this you can override layoutSubviews to add all missing bars during scrolling.

This is basically how UITableView and UICollectionView works, so you might be able to implement this using a collection view and a custom layout. Doing it manually though might be easier and also perform a little better as well.

like image 159
Sven Avatar answered Oct 24 '22 11:10

Sven