0

The documentation on the thread safety of various WASAPI interactions is lacking in clarity. For IAudioClock it does mention that IAudioClient::GetService() and the Release() of the interface obtained from that must be called from the same thread: https://learn.microsoft.com/en-us/windows/win32/api/audioclient/nn-audioclient-iaudioclock

But for IAudioClock::GetPosition() it doesn't say at all whether that needs to ONLY be called within that same thread or if it can be called by any thread?

I can find a footnote of 'Note In Windows 8, the first use of IAudioClient to access the audio device should be on the STA thread. Calls from an MTA thread may result in undefined behavior.' here: https://learn.microsoft.com/en-us/windows/win32/api/audioclient/nn-audioclient-iaudioclient

And this 'When releasing an IAudioRenderClient interface instance, the client must call the interface's Release method from the same thread as the call to IAudioClient::GetService that created the object.' here: https://learn.microsoft.com/en-us/windows/win32/api/audioclient/nn-audioclient-iaudiorenderclient

Further to this the docs also say 'To release the IAudioClient object and free all its associated resources, the client must release all references to any service objects that were created by calling GetService, in addition to calling Release on the IAudioClient interface itself. The client must release a service from the same thread that releases the IAudioClient object.': https://learn.microsoft.com/en-us/windows/win32/api/audioclient/nf-audioclient-iaudioclient-getservice

Collectively that implies that IAudioClient::GetService(), IAudioClock::Release() and IAudioRenderClient::Release() must all be called from the same thread.

Also it says 'You should never call any blocking code from your streaming thread. If you do, you will glitch. IAudioClock methods are blocking.' here: https://social.msdn.microsoft.com/Forums/windowsdesktop/en-US/2f9ec79b-8cc3-4b75-a041-a2847613947e/synchronizing-audio-input-and-output-with-iaudioclockgetposition?forum=windowspro-audiodevelopment

I'm specifically interested in the use case of calling IAudioClock::GetPosition() in a different thread to the one that is filling buffers via IAudioRenderClient. As IAudioClock::GetPosition() is apparently not reliably fast enough to be called from the same thread that is filling the IAudioRenderClient buffers: https://social.msdn.microsoft.com/Forums/sqlserver/en-US/2f9ec79b-8cc3-4b75-a041-a2847613947e/synchronizing-audio-input-and-output-with-iaudioclockgetposition?forum=windowspro-audiodevelopment

So I am trying to figure out if I can efficiently post a message (Signal an event or IOCP) from a thread that handles IAudioRenderClient::GetBuffer()/ReleaseBuffer() to another thread that can then call IAudioClock::GetPosition(). But I have no idea from the documentation whether that is allowed.

Note this is related to my other more general question here: Efficient sampling of audio render client position

iam
  • 1,623
  • 1
  • 14
  • 28
  • What is your application doing with the result of `IAudioClock::GetPosition`? – Matthew van Eerde Dec 07 '20 at 21:54
  • I'm using it to sync visuals (graphics code is heavily task system based so it's also an interesting question on when the position is best grabbed). I've found that if I use the non Ex versions of WaitableTimer that I can call GetPosition() at 4ms intervals with little CPU hit (Maybe I had my parameters wrong when I tried the Ex version). So using the QPC stamps and interpolating off that seems like it should be fine. But I am calling GetPosition() in another thread and I don't know if that is correct usage even though it seems to work. – iam Dec 09 '20 at 03:38
  • Okay, good, this is the intended use case for `IAudioClock`. You should call it every 50ms to 100ms or so and extrapolate for intermediate frames. You should not be involving your `IAudioRenderClient` packet handoff in this at all, that's a good way to introduce glitching. – Matthew van Eerde Dec 09 '20 at 17:57
  • Great to know I am not lucking out! :-) It would have been helpful if MSDN stated GetPosition() can be called on a thread other than the one used to get the IAudioClock service, and your last sentence about IAudioRenderClient. – iam Dec 09 '20 at 18:22
  • You shouldn't be using dedicated threads either for `IAudioRenderClient` or for `IAudioClock` - instead, break up your work into work items. See the [rtworkq.h](https://learn.microsoft.com/en-us/windows/win32/api/rtworkq/) docs and the [Windows audio session (WASAPI) sample](https://learn.microsoft.com/en-us/samples/microsoft/windows-universal-samples/windowsaudiosession/). The `IAudioRenderClient` work items should be scheduled to a work item queue that is registered with the Multimedia Class Scheduler Service. I don't know where the video frame work items should go. – Matthew van Eerde Dec 09 '20 at 19:15
  • I wasn't aware of the rtwork stuff but I need to support Win7 for now. I guess that's wrapper on top of IOCP queues anyway? Right now I use dedicated threads instead of throwing into our own custom task system as I wasn't sure on what parts could be concurrent like GetPosition() etc :-) – iam Dec 10 '20 at 05:05

0 Answers0