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 std::optional<qsizetype> ringbufferSize,
70 QWindowsAudioSource *parent,
72 std::optional<int32_t> hardwareBufferFrames):
96 auto immDevice = QAudioDevicePrivate::handle<QWindowsAudioDevice>(m_audioDevice)->open();
100 bool clientOpen = openAudioClient(std::move(immDevice));
104 setQIODevice(ioDevice);
105 createQIODeviceConnections(ioDevice);
107 return startAudioClient();
112 auto immDevice = QAudioDevicePrivate::handle<QWindowsAudioDevice>(m_audioDevice)->open();
116 bool clientOpen = openAudioClient(std::move(immDevice));
120 QIODevice *ioDevice = createRingbufferReaderDevice();
122 m_parent->updateStreamIdle(
true, QWindowsAudioSource::EmitStateSignal::False);
124 setQIODevice(ioDevice);
125 createQIODeviceConnections(ioDevice);
127 bool started = startAudioClient();
128 return started ? ioDevice :
nullptr;
133 auto immDevice = QAudioDevicePrivate::handle<QWindowsAudioDevice>(m_audioDevice)->open();
137 bool clientOpen = openAudioClient(std::move(immDevice));
141 m_audioCallback = std::move(cb);
143 return startAudioClient();
149 QWindowsAudioUtils::audioClientStop(m_audioClient);
155 QWindowsAudioUtils::audioClientStart(m_audioClient);
161 m_shutdownPolicy = shutdownPolicy;
164 disconnectQIODeviceConnections();
165 QWindowsAudioUtils::audioClientStop(m_audioClient);
168 QWindowsAudioUtils::audioClientReset(m_audioClient);
170 finalizeQIODevice(shutdownPolicy);
171 if (shutdownPolicy == ShutdownPolicy::DiscardRingbuffer)
178 m_parent->updateStreamIdle(streamIsIdle);
183 using namespace QWindowsAudioUtils;
185 std::optional<AudioClientCreationResult> clientData =
186 createAudioClient(device, m_hostFormat, m_hardwareBufferFrames, m_wasapiHandle);
191 m_audioClient = std::move(clientData->client);
192 m_periodSize = clientData->periodSize;
193 m_audioClientFrames = clientData->audioClientFrames;
195 HRESULT hr = m_audioClient->GetService(IID_PPV_ARGS(m_captureClient.GetAddressOf()));
197 qWarning() <<
"IAudioClient3::GetService failed to obtain IAudioCaptureClient"
198 << audioClientErrorString(hr);
207 using namespace QWindowsAudioUtils;
208 m_workerThread.reset(QThread::create([
this] {
209 setMCSSForPeriodSize(m_periodSize);
211 std::optional<QComHelper> m_comHelper;
213 if (m_hostFormat != m_format) {
214 m_comHelper.emplace();
215 m_resampler = std::make_unique<QWindowsResampler>();
216 m_resampler->setup(m_hostFormat, m_format);
218 m_memoryResource = std::make_unique<QTlsfMemoryResource>(512 * 1024);
224 m_workerThread->setObjectName(u"QWASAPIAudioSourceStream");
225 m_workerThread->start();
227 bool clientStarted = audioClientStart(m_audioClient);
228 if (!clientStarted) {
238 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())
253 bool success = m_audioCallback ? processCallback() : processRingbuffer();
255 handleAudioClientError();
261template <
typename Functor>
265 unsigned char *hostBuffer;
266 uint32_t hostBufferFrames;
268 uint64_t devicePosition;
269 uint64_t QPCPosition;
270 HRESULT hr = m_captureClient->GetBuffer(&hostBuffer, &hostBufferFrames, &flags,
271 &devicePosition, &QPCPosition);
273 qWarning() <<
"IAudioCaptureClient::GetBuffer failed" << audioClientErrorString(hr);
279 if (hostBufferFrames == 0)
282 QSpan hostBufferSpan{
284 m_hostFormat.bytesForFrames(hostBufferFrames),
289 auto resampledBuffer =
290 m_resampler->resample(as_bytes(hostBufferSpan), m_memoryResource.get());
291 QPlatformAudioSourceStream::process(resampledBuffer,
292 m_format.framesForBytes(resampledBuffer.size()));
294 f(as_bytes(hostBufferSpan), hostBufferFrames);
297 hr = m_captureClient->ReleaseBuffer(hostBufferFrames);
300 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< int32_t > hardwareBufferFrames)