0

I'm creating an audio waveform, which should look like this:

Audio waveform with curved line ends

Notice how the corners of the lines are curved, according to the direction.

My waveform currently has only straight lines:

Audio waveform with straight line ends

How can I achieve the desired results?

My current code for the waveform:

fileprivate func createPath(with points: [CGPoint], pointCount: Int, in rect: CGRect) -> CGPath {
    let path = UIBezierPath()
    
    guard pointCount > 0 else {
        return path.cgPath
    }
    
    guard rect.height > 0, rect.width > 0 else {
        return path.cgPath
    }
    
    path.move(to: CGPoint(x: 0, y: 0))
    let minValue = 1 / (rect.height / 2)
    for index in 0..<(pointCount / 2) {
        var point = points[index * 2]
        path.move(to: point)
        point.y = max(point.y, minValue)
        point.y = -point.y
        path.addLine(to: point)
    }
    let scaleX = (rect.width - 1) / CGFloat(pointCount - 1)
    let halfHeight = rect.height / 2
    let scaleY = halfHeight
    var transform = CGAffineTransform(scaleX: scaleX, y: scaleY)
    transform.ty = halfHeight
    path.apply(transform)
    return path.cgPath
}
TylerP
  • 9,600
  • 4
  • 39
  • 43
David
  • 857
  • 1
  • 11
  • 25
  • Clearly the points you have are the points you have. Why would that look like your first drawing? The points clearly are the points shown in the _second_ drawing. – matt Oct 29 '22 at 16:08
  • @matt I'm not talking about points position, but the curves on the top and the bottom of the lines. – David Oct 29 '22 at 16:18
  • 1
    The top image is drawn as a shape filled with a pattern of vertical lines, the bottom shape is drawn as stroked vertical lines. You cannot have the same appearance unless you use the same method of drawing. Instead of drawing vertical lines, create the border path from beziers, and then just fill it with a repeating pattern. – Sulthan Oct 29 '22 at 21:49

1 Answers1

3

There are two ways to accomplish this:

  1. Rather than treating each bar as a wide line, fill it as a shape, each with its own left, top, right, and bottom. And the top would then be a bézier.

  2. Rather than adjust the top of each bar, you can make the bars all go from minimum to maximum values and then add a mask over the whole graph to render the smoothed shape.

    E.g., this shows a few data points, overlays the Catmull Rom bézier, I then extend the bars (because sometimes the curve of the bézier goes above the existing bars, and then use the bézier as mask instead of an overlay.

    enter image description here

Additional observations:

  1. Please note, your first image with the curved tops of the bars has another feature that makes it look smooth: The data points, themselves, are smoothed. Your second image features far greater volatility than the first.

    The source of this volatility (which is common in audio tracks, or pretty much any DSP dataset), or lack thereof, is not relevant here. What is relevant is that if the data samples are highly variable, an interpolation algorithm for curving the tops of the bars can actually exaggerate the volatility. Single point spikes will be unusually sharp. Double point spikes will actually appear higher than they really are.

    E.g. consider this dataset:

    enter image description here

    With something this sort of variability, it could be argued that the “rounding” of the bars makes the results harder to read and/or is misleading:

    enter image description here

  2. While I have attempted to answer the question, one must ask whether this whole exercise is prudent. While there might be an aesthetic appeal to curves to the tops of the bars, it suggests a degree of continuity/precision that is greater than what the underlying data likely supports. The square bars more accurately represent the reality of ranges of values for which data was aggregated and, for this reason, are the common visual representation.

Rob
  • 415,655
  • 72
  • 787
  • 1,044
  • 2
    It isn't a matter of chaos or smoothing. The OP's first screen shot evidently shows samples taken over a very short period of time. The graph is smooth because the samples are very close together in time, and so their values differ only slightly from their neighbors. The second shows a smaller number of data points distributed over a much, much larger period of time. As I said in my first comment, the data portrayed in the second screenshot can _never_ look like the first screenshot. – matt Oct 30 '22 at 10:28
  • 2
    Lol. In retrospect, I really don't think it's a question of sampling interval vs averaging. Having spent a lot of time in audio editing tools, real tracks simply never look like this (regardless of time interval). There is always some noise and sample irregularity amidst the signal. And tracks certainly never have their tops smoothly curving into each other like this. If I were to guess, I’d say that this looks like a graphic design artist‘s mockup of an audio track, made with a bézier mask, and it is being taken too literally, being recreated a bit too faithfully by the developer. – Rob Oct 30 '22 at 12:15
  • 1
    Totally! I'm just saying that if your real waveform is something [like this](https://www.apeth.com/waveform.png), then if you concentrate on just the local maxima and redraw as vertical bars cut off by the smoothed envelope of those maxima, you stand a chance of getting something like the OP's first screenshot. But (and this is my point) that is about three-tenths of a second of sound! You could never portray the _whole track_ that way (in the width given by the second second screen shot). – matt Oct 30 '22 at 15:21
  • 1
    Thanks, @Rob for the great answer. You are right, the top screenshot is from the designer mockup, and I'm always too strict to recreate pixel perfect :) – David Oct 30 '22 at 17:14