6#include <QtCore/qdatastream.h>
7#include <QtCore/qdebug.h>
8#include <QtCore/qloggingcategory.h>
9#include <QtGui/qguiapplication.h>
10#include <QtMultimedia/qmediadevices.h>
11#include <QtMultimedia/private/qaudio_qiodevice_support_p.h>
12#include <QtMultimedia/private/qaudiohelpers_p.h>
13#include <QtMultimedia/private/qaudiosystem_platform_stream_support_p.h>
14#include <QtMultimedia/private/qautoresetevent_p.h>
15#include <QtMultimedia/private/qcoreaudioutils_p.h>
16#include <QtMultimedia/private/qdarwinaudiodevice_p.h>
17#include <QtMultimedia/private/qdarwinaudiodevices_p.h>
19#include <AudioUnit/AudioComponent.h>
21# include <QtMultimedia/private/qmacosaudiodatautils_p.h>
23# include <QtMultimedia/private/qcoreaudiosessionmanager_p.h>
32 : QPlatformAudioSourceStream{
33 std::move(audioDevice),
43QCoreAudioSourceStream::~QCoreAudioSourceStream()
45 free(m_bufferList.mBuffers[0].mData);
48bool QCoreAudioSourceStream::
open()
50 using namespace QCoreAudioUtils;
52 if (
auto audioUnit = makeAudioUnitForIO())
53 m_audioUnit = std::move(*audioUnit);
57 audioUnitSetInputEnabled(m_audioUnit,
true);
58 audioUnitSetOutputEnabled(m_audioUnit,
false);
61 AURenderCallbackStruct callback;
62 callback.inputProc = inputCallback;
63 callback.inputProcRefCon =
this;
65 if (AudioUnitSetProperty(m_audioUnit.get(), kAudioOutputUnitProperty_SetInputCallback,
66 kAudioUnitScope_Global, 0, &callback,
sizeof(callback))
68 qWarning() <<
"QAudioSource: Failed to set AudioUnit callback";
75 const std::optional<AudioDeviceID> nativeDeviceId = findAudioDeviceId(m_audioDevice);
76 if (!nativeDeviceId) {
77 qWarning() <<
"QAudioSource: Unable to use find most recent CoreAudio AudioDeviceID for "
78 "given device-id. The device might not be connected.";
81 if (!addDisconnectListener(*nativeDeviceId))
85 if (!audioUnitSetCurrentDevice(m_audioUnit, *nativeDeviceId))
88 std::optional<
int> bestNominalSamplingRate = audioObjectFindBestNominalSampleRate(
89 *nativeDeviceId, QAudioDevice::Input, m_format.sampleRate());
91 if (bestNominalSamplingRate) {
92 if (!audioObjectSetSamplingRate(*nativeDeviceId, *bestNominalSamplingRate))
96 if (m_hardwareBufferFrames)
97 audioObjectSetFramesPerBuffer(*nativeDeviceId, *m_hardwareBufferFrames);
100 AudioStreamBasicDescription streamFormat = toAudioStreamBasicDescription(m_format);
102 audioUnitSetInputStreamFormat(m_audioUnit, 0, streamFormat);
103 audioUnitSetOutputStreamFormat(m_audioUnit, 1, streamFormat);
105 std::optional<
int> framesPerBuffer = audioUnitGetFramesPerSlice(m_audioUnit);
107 m_bufferList.mNumberBuffers = 1;
108 m_bufferList.mBuffers[0].mNumberChannels = m_format.channelCount();
109 m_bufferList.mBuffers[0].mDataByteSize =
110 m_format.bytesForFrames(framesPerBuffer.value_or(2048));
111 m_bufferList.mBuffers[0].mData = malloc(m_bufferList.mBuffers[0].mDataByteSize);
113 return m_audioUnit.initialize();
116bool QCoreAudioSourceStream::
start(QIODevice *device)
118 setQIODevice(device);
120 const OSStatus status = AudioOutputUnitStart(m_audioUnit.get());
121 if (status != noErr) {
122 qDebug() <<
"AudioOutputUnitStart failed:" << status;
126 m_audioUnitRunning =
true;
127 createQIODeviceConnections(device);
134 QIODevice *device = createRingbufferReaderDevice();
135 bool opened = start(device);
142bool QCoreAudioSourceStream::
start(AudioCallback &&cb)
144 m_audioCallback = std::move(cb);
146 const OSStatus status = AudioOutputUnitStart(m_audioUnit.get());
147 if (status != noErr) {
148 qDebug() <<
"AudioOutputUnitStart failed:" << status;
152 m_audioUnitRunning =
true;
157void QCoreAudioSourceStream::
stop(ShutdownPolicy shutdownPolicy)
163 disconnectQIODeviceConnections();
165 finalizeQIODevice(shutdownPolicy);
166 if (shutdownPolicy == ShutdownPolicy::DiscardRingbuffer)
172 const auto status = AudioOutputUnitStop(m_audioUnit.get());
176 qDebug() <<
"AudioOutputUnitStop failed:" << status;
181 const auto status = AudioOutputUnitStart(m_audioUnit.get());
185 qDebug() <<
"AudioOutputUnitStart failed:" << status;
190 if (!audioUnitIsRunning(m_audioUnit))
197 m_parent->updateStreamIdle(idle);
200void QCoreAudioSourceStream::stopAudioUnit()
202 const auto status = AudioOutputUnitStop(m_audioUnit.get());
204 qDebug() <<
"AudioOutputUnitStop failed:" << status;
206 m_audioUnitRunning =
false;
209 removeDisconnectListener();
214OSStatus QCoreAudioSourceStream::inputCallback(
void *inRefCon,
215 AudioUnitRenderActionFlags *ioActionFlags,
216 const AudioTimeStamp *inTimeStamp,
217 UInt32 inBusNumber, UInt32 inNumberFrames,
218 AudioBufferList *ioData)
220 auto *self =
reinterpret_cast<QCoreAudioSourceStream *>(inRefCon);
221 if (self->m_audioCallback)
222 return self->processAudioCallback(ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames,
225 return self->processRingbuffer(ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames,
230QCoreAudioSourceStream::processRingbuffer(AudioUnitRenderActionFlags *ioActionFlags,
231 const AudioTimeStamp *timeStamp, UInt32 inBusNumber,
232 UInt32 inNumberFrames,
233 AudioBufferList * )
noexcept QT_MM_NONBLOCKING
235 OSStatus status = AudioUnitRender(m_audioUnit.get(), ioActionFlags, timeStamp, inBusNumber,
236 inNumberFrames, &m_bufferList);
242 case kAudioUnitErr_CannotDoInCurrentContext:
248 qDebug() <<
"AudioUnitRender failed" << status;
252 QSpan<
const std::byte> inputSpan{
253 reinterpret_cast<
const std::byte *>(m_bufferList.mBuffers[0].mData),
254 m_bufferList.mBuffers[0].mDataByteSize,
257 QPlatformAudioSourceStream::process(inputSpan, inNumberFrames);
262OSStatus QCoreAudioSourceStream::processAudioCallback(AudioUnitRenderActionFlags *ioActionFlags,
263 const AudioTimeStamp *timeStamp,
264 UInt32 inBusNumber, UInt32 inNumberFrames,
265 AudioBufferList * )
noexcept
267 OSStatus status = AudioUnitRender(m_audioUnit.get(), ioActionFlags, timeStamp, inBusNumber,
268 inNumberFrames, &m_bufferList);
274 case kAudioUnitErr_CannotDoInCurrentContext:
280 qDebug() <<
"AudioUnitRender failed" << status;
284 QSpan<
const std::byte> inputSpan{
285 reinterpret_cast<
const std::byte *>(m_bufferList.mBuffers[0].mData),
286 m_bufferList.mBuffers[0].mDataByteSize,
289 using namespace QtMultimediaPrivate;
290 runAudioCallback(*m_audioCallback, inputSpan, m_format, volume());
296bool QCoreAudioSourceStream::addDisconnectListener(AudioObjectID id)
298 m_stopOnDisconnected.cancel();
300 if (!m_disconnectMonitor.addDisconnectListener(id))
303 m_stopOnDisconnected = m_disconnectMonitor.then(m_parent, [
this] {
311 finalizeQIODevice(ShutdownPolicy::DrainRingbuffer);
313 QPlatformAudioSourceStream::handleIOError(m_parent);
319void QCoreAudioSourceStream::removeDisconnectListener()
321 m_stopOnDisconnected.cancel();
322 m_disconnectMonitor.removeDisconnectListener();
328QDarwinAudioSource::QDarwinAudioSource(QAudioDevice device,
const QAudioFormat &format,
330 : BaseClass(std::move(device), format, parent)
334 QObject::connect(qGuiApp, &QGuiApplication::applicationStateChanged,
this,
335 [
this](Qt::ApplicationState state) {
336 if (state == Qt::ApplicationState::ApplicationActive)
337 resumeStreamIfNecessary();
342void QDarwinAudioSource::resumeStreamIfNecessary()
345 m_stream->resumeIfNecessary();
The QAudioDevice class provides an information about audio devices and their functionality.
bool start(AudioCallback &&)
void stop(ShutdownPolicy)
void updateStreamIdle(bool idle) override