6#include <QtCore/qloggingcategory.h>
7#include <QtCore/qdebug.h>
8#include <QtGui/qguiapplication.h>
9#include <QtMultimedia/qmediadevices.h>
10#include <QtMultimedia/private/qaudio_qiodevice_support_p.h>
11#include <QtMultimedia/private/qaudio_rtsan_support_p.h>
12#include <QtMultimedia/private/qaudiohelpers_p.h>
13#include <QtMultimedia/private/qaudioringbuffer_p.h>
14#include <QtMultimedia/private/qaudiosystem_platform_stream_support_p.h>
15#include <QtMultimedia/private/qautoresetevent_p.h>
16#include <QtMultimedia/private/qcoreaudioutils_p.h>
17#include <QtMultimedia/private/qcoreaudiodevice_p.h>
18#include <QtMultimedia/private/qcoreaudiodevices_p.h>
20#include <AudioUnit/AudioUnit.h>
22# include <AudioUnit/AudioComponent.h>
23# include <QtMultimedia/private/qmacosaudiodatautils_p.h>
25# include <QtMultimedia/private/qcoreaudiosessionmanager_p.h>
34QCoreAudioSinkStream::QCoreAudioSinkStream(QAudioDevice audioDevice,
const QAudioFormat& format,
35 std::optional<qsizetype> ringbufferSize,
36 QCoreAudioSink *parent,
float volume,
37 std::optional<int32_t> hardwareBufferFrames,
39 : QPlatformAudioSinkStream {
40 std::move(audioDevice), format, ringbufferSize, hardwareBufferFrames, volume,
46QCoreAudioSinkStream::~QCoreAudioSinkStream()
49 m_stopOnDisconnected.cancelChain();
53bool QCoreAudioSinkStream::
open()
55 using namespace QCoreAudioUtils;
60 std::optional<AudioDeviceID> audioDeviceId = findAudioDeviceId(m_audioDevice);
62 qWarning() <<
"QAudioSource: Unable to use find most recent CoreAudio AudioDeviceID for "
63 "given device-id. The device might not be connected.";
66 const AudioDeviceID nativeDeviceId = audioDeviceId.value();
69 if (
auto audioUnit = makeAudioUnitForIO())
70 m_audioUnit = std::move(*audioUnit);
76 if (!addDisconnectListener(*audioDeviceId))
80 audioUnitSetCurrentDevice(m_audioUnit, nativeDeviceId);
82 if (m_hardwareBufferFrames)
83 audioObjectSetFramesPerBuffer(*audioDeviceId, *m_hardwareBufferFrames);
87 const AudioStreamBasicDescription streamFormat = toAudioStreamBasicDescription(m_format);
88 if (!audioUnitSetInputStreamFormat(m_audioUnit, 0, streamFormat))
91 return m_audioUnit.initialize();
94bool QCoreAudioSinkStream::
start(QIODevice *device)
96 auto renderCallback = [](
void *self, [[maybe_unused]] AudioUnitRenderActionFlags *ioActionFlags,
97 [[maybe_unused]]
const AudioTimeStamp *inTimeStamp,
98 [[maybe_unused]] UInt32 inBusNumber,
99 [[maybe_unused]] UInt32 inNumberFrames,
100 AudioBufferList *ioData) -> OSStatus {
101 return reinterpret_cast<QCoreAudioSinkStream *>(self)->processRingbuffer(inNumberFrames,
105 AURenderCallbackStruct callback{
106 .inputProc = renderCallback,
107 .inputProcRefCon =
this,
109 if (!audioUnitSetRenderCallback(m_audioUnit, callback))
112 setQIODevice(device);
115 const OSStatus status = AudioOutputUnitStart(m_audioUnit.get());
116 if (status != noErr) {
117 qDebug() <<
"AudioOutputUnitStart failed:" << status;
121 m_audioUnitRunning =
true;
123 createQIODeviceConnections(device);
130 QIODevice *reader = createRingbufferWriterDevice();
132 bool success = start(reader);
139bool QCoreAudioSinkStream::
start(AudioCallback cb)
141 auto renderCallback = [](
void *self, [[maybe_unused]] AudioUnitRenderActionFlags *ioActionFlags,
142 [[maybe_unused]]
const AudioTimeStamp *inTimeStamp,
143 [[maybe_unused]] UInt32 inBusNumber,
144 [[maybe_unused]] UInt32 inNumberFrames,
145 AudioBufferList *ioData) -> OSStatus {
146 return reinterpret_cast<QCoreAudioSinkStream *>(self)->processAudioCallback(inNumberFrames,
150 m_audioCallback = std::move(cb);
152 AURenderCallbackStruct callback;
153 callback.inputProc = renderCallback;
154 callback.inputProcRefCon =
this;
155 if (!audioUnitSetRenderCallback(m_audioUnit, callback))
158 const OSStatus status = AudioOutputUnitStart(m_audioUnit.get());
159 if (status != noErr) {
160 qDebug() <<
"AudioOutputUnitStart failed:" << status;
166void QCoreAudioSinkStream::
stop(ShutdownPolicy policy)
170 if (m_audioCallback) {
175 disconnectQIODeviceConnections();
179 case ShutdownPolicy::DrainRingbuffer:
182 case ShutdownPolicy::DiscardRingbuffer:
186 Q_UNREACHABLE_RETURN();
198 connectIdleHandler([
this, ownedSelf = shared_from_this()]()
mutable {
212 removeDisconnectListener();
220 const auto status = AudioOutputUnitStop(m_audioUnit.get());
224 qDebug() <<
"AudioOutputUnitStop failed:" << status;
229 const auto status = AudioOutputUnitStart(m_audioUnit.get());
233 qDebug() <<
"AudioOutputUnitStart failed:" << status;
238 if (!QCoreAudioUtils::audioUnitIsRunning(m_audioUnit))
242OSStatus QCoreAudioSinkStream::processRingbuffer(uint32_t numberOfFrames,
243 AudioBufferList *ioData)
noexcept QT_MM_NONBLOCKING
245 Q_ASSERT(int64_t(ioData->mBuffers[0].mDataByteSize) == m_format.bytesForFrames(numberOfFrames));
247 QSpan audioBufferSpan{
248 reinterpret_cast<std::byte *>(ioData->mBuffers[0].mData),
249 ioData->mBuffers[0].mDataByteSize,
252 QPlatformAudioSinkStream::process(audioBufferSpan, numberOfFrames);
257OSStatus QCoreAudioSinkStream::processAudioCallback(uint32_t numberOfFrames,
258 AudioBufferList *ioData)
noexcept QT_MM_NONBLOCKING
260 using namespace QtMultimediaPrivate;
262 Q_ASSERT(int64_t(ioData->mBuffers[0].mDataByteSize) == m_format.bytesForFrames(numberOfFrames));
264 QSpan<std::byte> inputSpan{
265 reinterpret_cast<std::byte *>(ioData->mBuffers[0].mData),
266 ioData->mBuffers[0].mDataByteSize,
269 runAudioCallback(*m_audioCallback, inputSpan, m_format, volume());
275void QCoreAudioSinkStream::updateStreamIdle(
bool arg)
277 m_parent->updateStreamIdle(arg);
280void QCoreAudioSinkStream::stopAudioUnit()
282 const auto status = AudioOutputUnitStop(m_audioUnit.get());
284 qDebug() <<
"AudioOutputUnitStop failed:" << status;
286 m_audioUnitRunning =
false;
289 removeDisconnectListener();
295bool QCoreAudioSinkStream::addDisconnectListener(AudioObjectID id)
297 m_stopOnDisconnected.cancel();
299 auto disconnectionFuture = m_disconnectMonitor.addDisconnectListener(id);
300 if (!disconnectionFuture)
303 m_stopOnDisconnected = disconnectionFuture->then(m_parent, [
this] {
311 handleIOError(m_parent);
317void QCoreAudioSinkStream::removeDisconnectListener()
319 m_stopOnDisconnected.cancel();
320 m_disconnectMonitor.removeDisconnectListener();
326QCoreAudioSink::QCoreAudioSink(QAudioDevice device,
const QAudioFormat &format, QObject *parent)
327 : BaseClass(std::move(device), format, parent)
331 QObject::connect(qGuiApp, &QGuiApplication::applicationStateChanged,
this,
332 [
this](Qt::ApplicationState state) {
333 if (state == Qt::ApplicationState::ApplicationActive)
334 resumeStreamIfNecessary();
339QCoreAudioSink::~QCoreAudioSink()
345 m_stream->resumeIfNecessary();
void stop(ShutdownPolicy policy)
bool start(AudioCallback cb)
bool start(QIODevice *device)
void stopStreamWhenBufferDrained()
void resumeStreamIfNecessary()