Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

macOS render errors when resizing window (AudioQueueObject.cpp:3329) #104

Open
erin-dot-io opened this issue Oct 8, 2024 · 7 comments
Open

Comments

@erin-dot-io
Copy link

When trying to render a waveform in a macOS app, I'm getting errors that seem to be related to the window size. When the app first builds and launches from Xcode, the waveform renders correctly. As soon as I begin to resize the window however, the error occurs repeatedly and the waveform either takes a long time to re-render (5-10 seconds or more) or doesn't re-render at all. Sometimes the waveform re-renders at the correct size, but often it does not.

The initial error I get when building is:
AddInstanceForFactory: No factory registered for id <CFUUID 0x60000305e4e0> F8BB1C28-BAE8-11D6-9C31-00039315CD46

And then this error 3 times:
AudioQueueObject.cpp:3329 _Start: Error (-4) getting reporterIDs

When I resize the window width, I get the above error a bunch of times, 10-20 or more, before the waveform eventually re-renders.

Here's my code:

import DSWaveformImage
import DSWaveformImageViews
import SwiftUI

struct ProgressWaveformView: View {
    let audioURL: URL
    let progress: Double
    let lineWidth = 3.0
        
    var body: some View {
        GeometryReader { geometry in
            WaveformView(audioURL: audioURL, configuration: .init(style: .striped(.init(color: .black, width: lineWidth, spacing: lineWidth / 3)))) { shape in
                shape.stroke(LinearGradient(colors: [.yellow, .green], startPoint: .top, endPoint: .bottom), lineWidth: lineWidth / 2).mask(alignment: .leading) {
                    Rectangle().frame(width: geometry.size.width)
                }
                shape.stroke(LinearGradient(colors: [.green, .yellow], startPoint: .top, endPoint: .bottom), lineWidth: lineWidth).mask(alignment: .leading) {
                    Rectangle().frame(width: geometry.size.width * progress)
                }
            }
        }
    }
}

struct ContentView: View {
    private let audioURL = Bundle.main.url(forResource: "ExampleTrack", withExtension: "mp3")!
    @State private var progress: Double = .random(in: 0...1)

    var body: some View {
        VStack {
            ProgressWaveformView(audioURL: audioURL, progress: progress)

            Button(action: { withAnimation { progress = .random(in: 0...1) }}) {
                Label("Progress", systemImage: "dice.fill")
            }.buttonStyle(.borderedProminent)
        }
        .padding()
    }
}

Here's what this looks like in the preview:
CleanShot 2024-10-08 at 18 37 37@2x

And when I build & run locally, initially it will look something like this:
CleanShot 2024-10-08 at 18 40 42@2x

Resizing the window larger looks like this for 10-30 seconds, sometimes never re-rendering:
CleanShot 2024-10-08 at 18 41 12@2x

When quitting and reopening or rerunning the app from Xcode, sometimes the waveform is the correct width, and sometimes it's still sized incorrectly.

Any idea what's going on here? My skills are quite junior in Swift/SwiftUI (long time UX designer learning to code here!) so fully prepared to accept that it's something dumb that I overlooked.

@erin-dot-io
Copy link
Author

erin-dot-io commented Oct 8, 2024

A couple other notes:

  • I tried this with a much more simple WaveFormView without the GeometryReader or the progress stuff and ran into the same issue
  • This is running in Xcode 16 with macOS 15 as the target

@dmrschmidt
Copy link
Owner

Hey @erin-dot-io,

thanks for reporting this. What's the size of your audio file? Since you mentioned this takes a while, from reading this, my assumption is that it's a "fairly large" file, ie 30 minutes of playtime or longer, is that correct?

So first, I've looked for the error messages you're seeing, and I can also see those in Xcode 16 now. Looks like something recent and one of those "Apple just changed some internal logging" related. I don't think these are related at all and I don't think these are anything to worry about. I'll keep an eye on this, but will fiel it under "non-issue" for now.

Secondly, the resizing "lacking behind" that you're seeing is to be expected currently. To display well, the whole waveform needs to be recalculated on size changes, to guarantee the best resolution. For larger files, this takes time, which is what you're seeing. The library originates from iOS, where this is not really a common issue, so the macOS build does not currently have any optimizations in place (like waiting a little for resizing to finish or canceling previous tasks etc.). It'll just settle eventually.

Given the relative edge case nature of "visual performance during window resizes" I don't think I'll find much time to improve this here. But please feel free to submit a Pull Request any time.

@erin-dot-io
Copy link
Author

erin-dot-io commented Oct 9, 2024

Thanks for the quick reply!

  • The mp3 in question is only 5min long (18.2mb). I can share the file if needed.
  • If I had to take a wild guess, it feels like the plugin is firing off a resize for every registered pixel change in container width
  • The resize seems random in a few ways. Whether or not it ever finishes is one. The other is how the progress masked shape is layered over the shape below it.

Here's a video that might help:

CleanShot.2024-10-09.at.16.31.24.2.mp4

The waveform does eventually get back to the correct size but it does look like it tries to resize it a bunch of times for the in-between window sizes (see the scroll bar in the console for the number of times this error appears). Maybe unrelated to the resize but the error does seem to fire off in relation to the number of times a resize is triggered.

Hopefully this helps but I get that it might not be worth addressing since it's macOS only. I'll see if I can dig around and attempt a PR.

@dmrschmidt
Copy link
Owner

Hey @erin-dot-io your observation is correct. As I mentioned, there is no optimizations in place yet for the rendering on resize as this is not a practical issue on iOS where this library originated from. I acknowledge of course, that this has become a lot more of a concern since the library works on macOS as well.

It would be trivial to debounce the update on size change to prevent unnecessarily frequent re-calculations. However, that would still result in non-ideal rendering of the waveform during rendering due to the actual Shape rendering logic, ie it would just "stick to the right side" either leaving blank space on the left or "cropping behind" the left side depending on whether you increase or decrease the window size.

Fixing that 2nd part is not so trivial due to the core rendering logic, which I don't want to get into detail right now as it's fairly complex to explain. So one approach could be to simply re-scale the Shape during resizing.

@erin-dot-io
Copy link
Author

Makes sense, thanks for the tips!

dmrschmidt added a commit that referenced this issue Oct 10, 2024
and do a low-quality re-scale instead; this is just for illustration for #104
dmrschmidt added a commit that referenced this issue Oct 10, 2024
was only meant for illustration purposes for #104
@dmrschmidt
Copy link
Owner

I won't publish this as a new dot release for now, as this is not ideal yet, particularly see 5f1ce68. The re-scaling, as mentioned, isn't as trivial and also needs different treatments based on the chosen renderer (ie where the rescaling anchor needs to be) and I don't want to extend the public API without thinking about this harder.

But you could point to main now and at least have a performant resizing experience.

@erin-dot-io
Copy link
Author

Nice! I'll take a look in the next day or two. Really appreciate your help here 🍻

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants