20using namespace std::chrono_literals;
24QAudioFormat makeHostFormatForSink(
const QAudioDevice &device,
const QAudioFormat &format)
26 const QWindowsAudioDevice *winDevice = QAudioDevicePrivate::handle<QWindowsAudioDevice>(device);
28 QAudioFormat hostFormat = format;
29 const int requestedChannelCount = format.channelCount();
30 auto [minProbedChannels, maxProbedChannels] = winDevice->m_probedChannelCountRange;
32 if (requestedChannelCount < device.minimumChannelCount()) {
33 hostFormat.setChannelCount(minProbedChannels);
34 hostFormat.setChannelConfig(
35 QAudioFormat::defaultChannelConfigForChannelCount(minProbedChannels));
36 }
else if (requestedChannelCount > device.maximumChannelCount()) {
37 hostFormat.setChannelCount(maxProbedChannels);
38 hostFormat.setChannelConfig(
39 QAudioFormat::defaultChannelConfigForChannelCount(maxProbedChannels));
48 QWindowsAudioSink *parent,
float volume, std::optional<int32_t> hardwareBufferFrames, AudioEndpointRole role):
78 auto immDevice = QAudioDevicePrivate::handle<QWindowsAudioDevice>(m_audioDevice)->open();
79 bool clientOpen = openAudioClient(std::move(immDevice), m_role);
83 setQIODevice(ioDevice);
84 createQIODeviceConnections(ioDevice);
96 auto immDevice = QAudioDevicePrivate::handle<QWindowsAudioDevice>(m_audioDevice)->open();
97 bool clientOpen = openAudioClient(std::move(immDevice), m_role);
101 QIODevice *ioDevice = createRingbufferWriterDevice();
103 m_parent->updateStreamIdle(
true, QWindowsAudioSink::EmitStateSignal::False);
105 setQIODevice(ioDevice);
106 createQIODeviceConnections(ioDevice);
117 auto immDevice = QAudioDevicePrivate::handle<QWindowsAudioDevice>(m_audioDevice)->open();
118 bool clientOpen = openAudioClient(std::move(immDevice), m_role);
122 m_audioCallback = std::move(audioCallback);
130 QWindowsAudioUtils::audioClientStop(m_audioClient);
136 QWindowsAudioUtils::audioClientStart(m_audioClient);
141 using namespace QWindowsAudioUtils;
144 m_shutdownPolicy = shutdownPolicy;
146 switch (shutdownPolicy) {
147 case ShutdownPolicy::DiscardRingbuffer: {
149 audioClientStop(m_audioClient);
150 m_workerThread->wait();
152 audioClientReset(m_audioClient);
156 case ShutdownPolicy::DrainRingbuffer: {
157 m_ringbufferDrained.callOnActivated([self = shared_from_this()]()
mutable {
158 self->m_workerThread->wait();
164 Q_UNREACHABLE_RETURN();
171 m_parent->updateStreamIdle(streamIsIdle);
176 using namespace QWindowsAudioUtils;
178 std::optional<AudioClientCreationResult> clientData =
179 createAudioClient(device, m_hostFormat, m_hardwareBufferFrames, m_wasapiHandle, role);
184 m_audioClient = std::move(clientData->client);
185 m_periodSize = clientData->periodSize;
186 m_audioClientFrames = clientData->audioClientFrames;
188 HRESULT hr = m_audioClient->GetService(IID_PPV_ARGS(m_renderClient.GetAddressOf()));
190 qWarning() <<
"IAudioClient3::GetService failed to obtain IAudioRenderClient"
191 << audioClientErrorString(hr);
195 if (m_audioDevice.preferredFormat().sampleRate() != m_hostFormat.sampleRate())
196 audioClientSetRate(m_audioClient, m_hostFormat.sampleRate());
203 using namespace QWindowsAudioUtils;
204 m_workerThread.reset(QThread::create([
this, streamType] {
205 setMCSSForPeriodSize(m_periodSize);
206 fillInitialHostBuffer();
207 std::optional<QComHelper> m_comHelper;
209 if (m_hostFormat != m_format) {
210 m_comHelper.emplace();
211 m_resampler = std::make_unique<QWindowsResampler>();
212 m_resampler->setup(m_format, m_hostFormat);
214 m_memoryResource = std::make_unique<QTlsfMemoryResource>(512 * 1024);
217 switch (streamType) {
218 case StreamType::Ringbuffer:
219 return runProcessRingbufferLoop();
220 case StreamType::Callback:
221 return runProcessCallbackLoop();
224 m_workerThread->setObjectName(u"QWASAPIAudioSinkStream");
225 m_workerThread->start();
227 return QWindowsAudioUtils::audioClientStart(m_audioClient);
237 using namespace QWindowsAudioUtils;
240 constexpr std::chrono::milliseconds timeout = 2s;
241 DWORD retval = WaitForSingleObject(m_wasapiHandle.get(), timeout.count());
242 if (retval != WAIT_OBJECT_0) {
246 handleAudioClientError();
250 if (isStopRequested()) {
251 switch (m_shutdownPolicy.load(std::memory_order_relaxed)) {
252 case ShutdownPolicy::DiscardRingbuffer:
254 case ShutdownPolicy::DrainRingbuffer: {
255 bool bufferDrained = visitRingbuffer([](
const auto &ringbuffer) {
256 return ringbuffer.used() == 0;
259 audioClientStop(m_audioClient);
260 audioClientReset(m_audioClient);
262 m_ringbufferDrained.set();
268 Q_UNREACHABLE_RETURN();
272 bool success = processRingbuffer();
274 handleAudioClientError();
282 using namespace QWindowsAudioUtils;
285 constexpr std::chrono::milliseconds timeout = 2s;
286 DWORD retval = WaitForSingleObject(m_wasapiHandle.get(), timeout.count());
287 if (retval != WAIT_OBJECT_0) {
291 handleAudioClientError();
295 if (isStopRequested())
298 bool success = processCallback();
300 handleAudioClientError();
306template <
typename Functor>
309 uint32_t numFramesPadding;
310 HRESULT hr = m_audioClient->GetCurrentPadding(&numFramesPadding);
312 qWarning() <<
"IAudioClient3::GetCurrentPadding failed" << audioClientErrorString(hr);
316 const uint32_t requiredFrames = m_audioClientFrames - numFramesPadding;
317 if (requiredFrames == 0)
321 unsigned char *hostBuffer{};
322 hr = m_renderClient->GetBuffer(requiredFrames, &hostBuffer);
324 qWarning() <<
"IAudioRenderClient::getBuffer failed" << audioClientErrorString(hr);
328 QSpan<std::byte> hostBufferSpan{
329 reinterpret_cast<std::byte *>(hostBuffer),
330 m_hostFormat.bytesForFrames(requiredFrames),
333 uint64_t consumedFrames;
337 std::pmr::vector<std::byte> resampleBuffer{
338 size_t(m_format.bytesForFrames(requiredFrames)),
339 m_memoryResource.get(),
341 consumedFrames = f(as_writable_bytes(QSpan{ resampleBuffer }), requiredFrames);
343 auto resampledBuffer = m_resampler->resample(resampleBuffer, m_memoryResource.get());
345 Q_ASSERT(resampledBuffer.size() == size_t(hostBufferSpan.size()));
346 std::copy_n(resampledBuffer.data(), resampledBuffer.size(), hostBufferSpan.data());
348 consumedFrames = f(hostBufferSpan, requiredFrames);
351 const DWORD flags = consumedFrames != 0 ? 0 : AUDCLNT_BUFFERFLAGS_SILENT;
353 hr = m_renderClient->ReleaseBuffer(requiredFrames, flags);
355 qWarning() <<
"IAudioRenderClient::ReleaseBuffer failed" << audioClientErrorString(hr);