6#include <QtCore/qthread.h>
7#include <QtCore/qloggingcategory.h>
8#include <QtCore/private/qfunctions_win_p.h>
9#include <QtCore/private/quniquehandle_types_p.h>
10#include <QtMultimedia/private/qaudioformat_p.h>
11#include <QtMultimedia/private/qaudiosystem_platform_stream_support_p.h>
12#include <QtMultimedia/private/qmemory_resource_tlsf_p.h>
13#include <QtMultimedia/private/qwindowsaudiodevice_p.h>
14#include <QtMultimedia/private/qwindowsaudioutils_p.h>
16#include <audioclient.h>
17#include <mmdeviceapi.h>
24using namespace std::chrono_literals;
27QAudioFormat makeHostFormatForSource(
const QAudioDevice &device,
const QAudioFormat &format)
29 const QWindowsAudioDevice *winDevice = QAudioDevicePrivate::handle<QWindowsAudioDevice>(device);
31 QAudioFormat hostFormat = format;
32 const int requestedChannelCount = format.channelCount();
33 auto [minProbedChannels, maxProbedChannels] = winDevice->m_probedChannelCountRange;
35 if (requestedChannelCount < device.minimumChannelCount()) {
36 hostFormat.setChannelCount(minProbedChannels);
37 hostFormat.setChannelConfig(
38 QAudioFormat::defaultChannelConfigForChannelCount(minProbedChannels));
39 }
else if (requestedChannelCount > device.maximumChannelCount()) {
40 hostFormat.setChannelCount(maxProbedChannels);
41 hostFormat.setChannelConfig(
42 QAudioFormat::defaultChannelConfigForChannelCount(maxProbedChannels));
45 const int requestedSampleRate = format.sampleRate();
46 auto [minProbedSampleRate, maxProbedSampleRate] = winDevice->m_probedSampleRateRange;
48 if (requestedSampleRate < device.minimumSampleRate())
49 hostFormat.setSampleRate(minProbedSampleRate);
50 else if (requestedSampleRate > device.maximumSampleRate())
51 hostFormat.setSampleRate(maxProbedSampleRate);
58 std::optional<qsizetype> ringbufferSize,
59 QWindowsAudioSource *parent,
61 std::optional<int32_t> hardwareBufferFrames):
85 auto immDevice = QAudioDevicePrivate::handle<QWindowsAudioDevice>(m_audioDevice)->open();
87 bool clientOpen = openAudioClient(std::move(immDevice));
91 setQIODevice(ioDevice);
92 createQIODeviceConnections(ioDevice);
94 bool started = startAudioClient();
103 auto immDevice = QAudioDevicePrivate::handle<QWindowsAudioDevice>(m_audioDevice)->open();
105 bool clientOpen = openAudioClient(std::move(immDevice));
109 QIODevice *ioDevice = createRingbufferReaderDevice();
111 m_parent->updateStreamIdle(
true, QWindowsAudioSource::EmitStateSignal::False);
113 setQIODevice(ioDevice);
114 createQIODeviceConnections(ioDevice);
116 bool started = startAudioClient();
125 auto immDevice = QAudioDevicePrivate::handle<QWindowsAudioDevice>(m_audioDevice)->open();
127 bool clientOpen = openAudioClient(std::move(immDevice));
131 m_audioCallback = std::move(cb);
133 return startAudioClient();
139 QWindowsAudioUtils::audioClientStop(m_audioClient);
145 QWindowsAudioUtils::audioClientStart(m_audioClient);
151 m_shutdownPolicy = shutdownPolicy;
154 disconnectQIODeviceConnections();
156 QWindowsAudioUtils::audioClientStop(m_audioClient);
157 m_workerThread->wait();
158 QWindowsAudioUtils::audioClientReset(m_audioClient);
160 finalizeQIODevice(shutdownPolicy);
161 if (shutdownPolicy == ShutdownPolicy::DiscardRingbuffer)
168 m_parent->updateStreamIdle(streamIsIdle);
173 using namespace QWindowsAudioUtils;
175 std::optional<AudioClientCreationResult> clientData =
176 createAudioClient(device, m_hostFormat, m_hardwareBufferFrames, m_wasapiHandle);
181 m_audioClient = std::move(clientData->client);
182 m_periodSize = clientData->periodSize;
183 m_audioClientFrames = clientData->audioClientFrames;
185 HRESULT hr = m_audioClient->GetService(IID_PPV_ARGS(m_captureClient.GetAddressOf()));
187 qWarning() <<
"IAudioClient3::GetService failed to obtain IAudioCaptureClient"
188 << audioClientErrorString(hr);
197 using namespace QWindowsAudioUtils;
198 m_workerThread.reset(QThread::create([
this] {
199 setMCSSForPeriodSize(m_periodSize);
201 std::optional<QComHelper> m_comHelper;
203 if (m_hostFormat != m_format) {
204 m_comHelper.emplace();
205 m_resampler = std::make_unique<QWindowsResampler>();
206 m_resampler->setup(m_hostFormat, m_format);
208 m_memoryResource = std::make_unique<QTlsfMemoryResource>(512 * 1024);
214 m_workerThread->setObjectName(u"QWASAPIAudioSourceStream");
215 m_workerThread->start();
217 return audioClientStart(m_audioClient);
222 using namespace QWindowsAudioUtils;
224 constexpr std::chrono::milliseconds timeout = 2s;
225 DWORD retval = WaitForSingleObject(m_wasapiHandle.get(), timeout.count());
226 if (retval != WAIT_OBJECT_0) {
230 handleAudioClientError();
234 if (isStopRequested())
237 bool success = m_audioCallback ? processCallback() : processRingbuffer();
239 handleAudioClientError();
245template <
typename Functor>
249 unsigned char *hostBuffer;
250 uint32_t hostBufferFrames;
252 uint64_t devicePosition;
253 uint64_t QPCPosition;
254 HRESULT hr = m_captureClient->GetBuffer(&hostBuffer, &hostBufferFrames, &flags,
255 &devicePosition, &QPCPosition);
257 qWarning() <<
"IAudioCaptureClient::GetBuffer failed" << audioClientErrorString(hr);
263 if (hostBufferFrames == 0)
266 QSpan hostBufferSpan{
268 m_hostFormat.bytesForFrames(hostBufferFrames),
273 auto resampledBuffer =
274 m_resampler->resample(as_bytes(hostBufferSpan), m_memoryResource.get());
275 QPlatformAudioSourceStream::process(resampledBuffer,
276 m_format.framesForBytes(resampledBuffer.size()));
278 f(as_bytes(hostBufferSpan), hostBufferFrames);
281 hr = m_captureClient->ReleaseBuffer(hostBufferFrames);
284 qWarning() <<
"IAudioCaptureClient::ReleaseBuffer failed" << audioClientErrorString(hr);
QAudioFormat::SampleFormat SampleFormat
void stop(ShutdownPolicy)
void updateStreamIdle(bool) override
bool start(AudioCallback &&)
QWASAPIAudioSourceStream(QAudioDevice, const QAudioFormat &, std::optional< qsizetype > ringbufferSize, QWindowsAudioSource *parent, float volume, std::optional< int32_t > hardwareBufferFrames)