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 auto status = winDevice->m_probeDataFuture.wait_for(QAudioDevicePrivate::formatProbeTimeout);
33 case std::future_status::ready:
34 case std::future_status::deferred:
37 case std::future_status::timeout:
38 return QAudioFormat{};
43 auto [minProbedChannels, maxProbedChannels] = winDevice->m_probeDataFuture.get().channelCountRange;
44 auto [minProbedSampleRate, maxProbedSampleRate] = winDevice->m_probeDataFuture.get().sampleRateRange;
46 QAudioFormat hostFormat = format;
47 const int requestedChannelCount = format.channelCount();
48 if (requestedChannelCount < device.minimumChannelCount()) {
49 hostFormat.setChannelCount(minProbedChannels);
50 hostFormat.setChannelConfig(
51 QAudioFormat::defaultChannelConfigForChannelCount(minProbedChannels));
52 }
else if (requestedChannelCount > device.maximumChannelCount()) {
53 hostFormat.setChannelCount(maxProbedChannels);
54 hostFormat.setChannelConfig(
55 QAudioFormat::defaultChannelConfigForChannelCount(maxProbedChannels));
58 const int requestedSampleRate = format.sampleRate();
59 if (requestedSampleRate < device.minimumSampleRate())
60 hostFormat.setSampleRate(minProbedSampleRate);
61 else if (requestedSampleRate > device.maximumSampleRate())
62 hostFormat.setSampleRate(maxProbedSampleRate);
69 QAudioDevice device,
const QAudioFormat &format, std::optional<qsizetype> ringbufferSize,
70 QWindowsAudioSource *parent,
float volume,
71 std::optional<NativePeriodFrames> nativePeriodFrames)
89 auto immDevice = QAudioDevicePrivate::handle<QWindowsAudioDevice>(m_audioDevice)->open();
93 bool clientOpen = openAudioClient(std::move(immDevice));
97 setQIODevice(ioDevice);
98 createQIODeviceConnections(ioDevice);
100 return startAudioClient();
105 auto immDevice = QAudioDevicePrivate::handle<QWindowsAudioDevice>(m_audioDevice)->open();
109 bool clientOpen = openAudioClient(std::move(immDevice));
113 QIODevice *ioDevice = createRingbufferReaderDevice();
115 m_parent->updateStreamIdle(
true, QWindowsAudioSource::EmitStateSignal::False);
117 setQIODevice(ioDevice);
118 createQIODeviceConnections(ioDevice);
120 bool started = startAudioClient();
121 return started ? ioDevice :
nullptr;
126 auto immDevice = QAudioDevicePrivate::handle<QWindowsAudioDevice>(m_audioDevice)->open();
130 bool clientOpen = openAudioClient(std::move(immDevice));
134 m_audioCallback = std::move(cb);
136 return startAudioClient();
142 QWindowsAudioUtils::audioClientStop(m_audioClient);
148 QWindowsAudioUtils::audioClientStart(m_audioClient);
154 m_shutdownPolicy = shutdownPolicy;
157 disconnectQIODeviceConnections();
158 QWindowsAudioUtils::audioClientStop(m_audioClient);
161 QWindowsAudioUtils::audioClientReset(m_audioClient);
163 finalizeQIODevice(shutdownPolicy);
164 if (shutdownPolicy == ShutdownPolicy::DiscardRingbuffer)
171 m_parent->updateStreamIdle(streamIsIdle);
176 using namespace QWindowsAudioUtils;
178 std::optional<AudioClientCreationResult> clientData =
179 createAudioClient(device, m_hostFormat, m_nativePeriodFrames, m_audioClientEventHandle);
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_captureClient.GetAddressOf()));
190 qWarning() <<
"IAudioClient3::GetService failed to obtain IAudioCaptureClient"
191 << audioClientErrorString(hr);
200 using namespace QWindowsAudioUtils;
201 m_workerThread.reset(QThread::create([
this] {
202 const auto mmcssRegistration = registerMMCSSThreadForPeriod(m_periodSize);
204 std::optional<QComHelper> m_comHelper;
206 if (m_hostFormat != m_format) {
207 m_comHelper.emplace();
208 m_resampler = std::make_unique<QWindowsResampler>();
209 m_resampler->setup(m_hostFormat, m_format);
211 m_memoryResource = std::make_unique<QTlsfMemoryResource>(512 * 1024);
217 m_workerThread->setObjectName(u"QWASAPIAudioSourceStream");
218 m_workerThread->start();
220 bool clientStarted = audioClientStart(m_audioClient);
221 if (!clientStarted) {
231 using namespace QWindowsAudioUtils;
233 constexpr std::chrono::milliseconds timeout = 2s;
234 const bool wasSuspended = m_suspended;
235 DWORD retval = WaitForSingleObject(m_audioClientEventHandle.get(), timeout.count());
236 if (retval != WAIT_OBJECT_0) {
237 if (wasSuspended || m_suspended)
240 handleAudioClientError();
244 if (isStopRequested())
247 bool success = m_audioCallback ? processCallback() : processRingbuffer();
249 handleAudioClientError();
255template <
typename Functor>
259 unsigned char *hostBuffer;
260 uint32_t hostBufferFrames;
262 uint64_t devicePosition;
263 uint64_t QPCPosition;
264 HRESULT hr = m_captureClient->GetBuffer(&hostBuffer, &hostBufferFrames, &flags,
265 &devicePosition, &QPCPosition);
267 qWarning() <<
"IAudioCaptureClient::GetBuffer failed" << audioClientErrorString(hr);
273 if (hostBufferFrames == 0)
276 QSpan hostBufferSpan{
278 m_hostFormat.bytesForFrames(hostBufferFrames),
283 auto resampledBuffer =
284 m_resampler->resample(as_bytes(hostBufferSpan), m_memoryResource.get());
285 QPlatformAudioSourceStream::process(resampledBuffer,
286 m_format.framesForBytes(resampledBuffer.size()));
288 f(as_bytes(hostBufferSpan), hostBufferFrames);
291 hr = m_captureClient->ReleaseBuffer(hostBufferFrames);
294 qWarning() <<
"IAudioCaptureClient::ReleaseBuffer failed" << audioClientErrorString(hr);
Combined button and popup list for selecting options.
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< NativePeriodFrames > nativePeriodFrames)