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

Stale data when recording system audio #217

Open
gchilds opened this issue Jun 24, 2019 · 2 comments
Open

Stale data when recording system audio #217

gchilds opened this issue Jun 24, 2019 · 2 comments
Labels

Comments

@gchilds
Copy link
Contributor

gchilds commented Jun 24, 2019

When recording from the Background Music device, it's possible to get a tightly looping, stale last buffer of audio data when whatever is playing out to the device stops playing.

The strange thing about this bug is that it doesn't always reproduce, and seems dependent on the order in which core audio clients start.

One repro that works for me is to open QuickTime Player > New Audio Recording, select Background Music from the drop down menu, then open an audio editor that lets you choose the output device (e.g. Amadeus Pro) and start playing a file. You can see the level monitoring in QuickTime Player (and hear it too if you add some form of playthrough, but I'm trying to minimize moving parts in this issue) and if you stop the file playing in the audio editor you may see the levels continue to change. In that case you can hit record and then listen to the looping stale buffer. You may need to try a few times, change default output device, quit other audio apps. The bug reproduces on 10.13 and 10.14.

The reason I say it's strange that the bug isn't always reproducible is because it looks like it should always reproduce, as there's driver does track what time the audio buffer represents - access is always the sample timestamp modulo the buffer size:

https://github.com/kyleneideck/BackgroundMusic/blob/master/BGMDriver/BGMDriver/BGM_Device.cpp#L1430

and here I suspect I don't understand the driver timestamps work or how multiple clients look to the driver as it looks like a 2nd client would always read inconsistent data from the ring buffer unless it started reading at exactly the right offset. And maybe I don't understand the audio server plugin architecture, because when there's no stale data it's as if core audio knows the driver cannot have any new data for it.

I'm happy to work on tracking the ring buffer start and finish sample times and return zero samples when someone reads outside the available range, but is that the right thing to do?

@kyleneideck
Copy link
Owner

Thanks for reporting this. I've been able to reproduce it consistently with QuickTime for recording and VLC for playback.

I think I might have had it fail to reproduce once, but it could have been that VLC just played a small amount of silence before removing its client. It also once seemed to make the QuickTime recording show an error message about not being able to save and then close itself. I'm not sure what happened there.

and here I suspect I don't understand the driver timestamps work or how multiple clients look to the driver as it looks like a 2nd client would always read inconsistent data from the ring buffer unless it started reading at exactly the right offset.

I'm not seeing what would be different for a second client. Why do you think it would get inconsistent data?

It's been a while since I've looked at this part of the code, but IIRC CoreAudio will choose the inSampleTime value that gets passed to BGM_Device::ReadInputData so that the data it reads will be close to the most recent data. It knows when that will be because BGM_Device::GetZeroTimeStamp tells it how the device clock lines up with the host clock. (Although, in this case they're the same clock because BGMDevice is a virtual device.)

I'm happy to work on tracking the ring buffer start and finish sample times and return zero samples when someone reads outside the available range, but is that the right thing to do?

That makes sense to me. It might be worth having a look at CARingBuffer.h in case it's helpful.

@gchilds
Copy link
Contributor Author

gchilds commented Jun 26, 2019

Ah, so the audio server plugins move in lockstep? I think I'm starting to understand.
SoundFlower (which is a kext) seems to poll from a timer and zero its buffer when clients count goes to zero, and so you get one or two stale buffers as it's a bit racy. Maybe with the audio server plugin lockstep model it's possible to have none.

CARingBuffer seems good, it's got some lock free stuff which also doubles as a CPU overload detector which probably isn't needed as everything's done under a lock, but it does track buffer coordinates.

Here's a wip using it #218

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

No branches or pull requests

2 participants