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>
20#include <AudioToolbox/AudioConverter.h>
22# include <QtMultimedia/private/qmacosaudiodatautils_p.h>
24# import <AVFoundation/AVAudioSession.h>
25# include <QtMultimedia/private/qcoreaudiosessionmanager_p.h>
34 : QPlatformAudioSourceStream{
35 std::move(audioDevice),
45QCoreAudioSourceStream::~QCoreAudioSourceStream()
48 AudioConverterDispose(m_audioConverter);
49 free(m_bufferList.mBuffers[0].mData);
52bool QCoreAudioSourceStream::
open()
54 using namespace QCoreAudioUtils;
56 if (
auto audioUnit = makeAudioUnitForIO())
57 m_audioUnit = std::move(*audioUnit);
61 audioUnitSetInputEnabled(m_audioUnit,
true);
62 audioUnitSetOutputEnabled(m_audioUnit,
false);
65 AURenderCallbackStruct callback;
66 callback.inputProc = inputCallback;
67 callback.inputProcRefCon =
this;
69 if (AudioUnitSetProperty(m_audioUnit.get(), kAudioOutputUnitProperty_SetInputCallback,
70 kAudioUnitScope_Global, 0, &callback,
sizeof(callback))
72 qWarning() <<
"QAudioSource: Failed to set AudioUnit callback";
76 AudioStreamBasicDescription streamFormat = toAudioStreamBasicDescription(m_format);
81 const std::optional<AudioDeviceID> nativeDeviceId = findAudioDeviceId(m_audioDevice);
82 if (!nativeDeviceId) {
83 qWarning() <<
"QAudioSource: Unable to use find most recent CoreAudio AudioDeviceID for "
84 "given device-id. The device might not be connected.";
87 if (!addDisconnectListener(*nativeDeviceId))
91 if (!audioUnitSetCurrentDevice(m_audioUnit, *nativeDeviceId))
94 std::optional<
int> bestNominalSamplingRate = audioObjectFindBestNominalSampleRate(
95 *nativeDeviceId, QAudioDevice::Input, m_format.sampleRate());
97 if (bestNominalSamplingRate) {
98 if (!audioObjectSetSamplingRate(*nativeDeviceId, *bestNominalSamplingRate))
101 qWarning() <<
"QAudioSource: Device does not support any sampling rate. This should not "
106 if (m_hardwareBufferFrames)
107 audioObjectSetFramesPerBuffer(*nativeDeviceId, *m_hardwareBufferFrames);
109 if (bestNominalSamplingRate != m_format.sampleRate()) {
110 AudioStreamBasicDescription desiredFormat = streamFormat;
112 streamFormat.mSampleRate = *bestNominalSamplingRate;
114 OSStatus status = AudioConverterNew(&streamFormat, &desiredFormat, &m_audioConverter);
115 if (status != noErr) {
116 qWarning() <<
"QAudioSource: Failed to create AudioConverter:" << status;
121 audioUnitSetInputStreamFormat(m_audioUnit, 0, streamFormat);
122 audioUnitSetOutputStreamFormat(m_audioUnit, 1, streamFormat);
125 AVAudioSession *session = [AVAudioSession sharedInstance];
126 double hwRate = session.sampleRate;
127 std::optional<
int> bestNominalSamplingRate =
int(hwRate);
129 audioUnitSetInputStreamFormat(m_audioUnit, 0, streamFormat);
130 audioUnitSetOutputStreamFormat(m_audioUnit, 1, streamFormat);
133 std::optional<
int> framesPerBuffer = audioUnitGetFramesPerSlice(m_audioUnit);
135 m_bufferList.mNumberBuffers = 1;
136 m_bufferList.mBuffers[0].mNumberChannels = m_format.channelCount();
137 m_bufferList.mBuffers[0].mDataByteSize =
138 m_format.bytesForFrames(framesPerBuffer.value_or(2048));
139 m_bufferList.mBuffers[0].mData = malloc(m_bufferList.mBuffers[0].mDataByteSize);
141 if (m_audioConverter) {
142 size_t outputBufferSize = m_bufferList.mBuffers[0].mDataByteSize * m_format.sampleRate()
143 /
static_cast<
float>(*bestNominalSamplingRate)
145 m_outputBuffer.resize(outputBufferSize);
146 m_outputBufferList.mNumberBuffers = 1;
147 m_outputBufferList.mBuffers[0].mNumberChannels = m_format.channelCount();
148 m_outputBufferList.mBuffers[0].mDataByteSize = outputBufferSize;
149 m_outputBufferList.mBuffers[0].mData = m_outputBuffer.data();
152 return m_audioUnit.initialize();
155bool QCoreAudioSourceStream::
start(QIODevice *device)
157 setQIODevice(device);
159 const OSStatus status = AudioOutputUnitStart(m_audioUnit.get());
160 if (status != noErr) {
161 qDebug() <<
"AudioOutputUnitStart failed:" << status;
165 m_audioUnitRunning =
true;
166 createQIODeviceConnections(device);
173 QIODevice *device = createRingbufferReaderDevice();
174 bool opened = start(device);
181bool QCoreAudioSourceStream::
start(AudioCallback &&cb)
183 m_audioCallback = std::move(cb);
185 const OSStatus status = AudioOutputUnitStart(m_audioUnit.get());
186 if (status != noErr) {
187 qDebug() <<
"AudioOutputUnitStart failed:" << status;
191 m_audioUnitRunning =
true;
196void QCoreAudioSourceStream::
stop(ShutdownPolicy shutdownPolicy)
202 disconnectQIODeviceConnections();
204 finalizeQIODevice(shutdownPolicy);
205 if (shutdownPolicy == ShutdownPolicy::DiscardRingbuffer)
211 const auto status = AudioOutputUnitStop(m_audioUnit.get());
215 qDebug() <<
"AudioOutputUnitStop failed:" << status;
220 const auto status = AudioOutputUnitStart(m_audioUnit.get());
224 qDebug() <<
"AudioOutputUnitStart failed:" << status;
229 if (!audioUnitIsRunning(m_audioUnit))
236 m_parent->updateStreamIdle(idle);
239void QCoreAudioSourceStream::stopAudioUnit()
241 const auto status = AudioOutputUnitStop(m_audioUnit.get());
243 qDebug() <<
"AudioOutputUnitStop failed:" << status;
245 m_audioUnitRunning =
false;
248 removeDisconnectListener();
253OSStatus QCoreAudioSourceStream::inputCallback(
void *inRefCon,
254 AudioUnitRenderActionFlags *ioActionFlags,
255 const AudioTimeStamp *inTimeStamp,
256 UInt32 inBusNumber, UInt32 inNumberFrames,
257 AudioBufferList *ioData)
259 auto *self =
reinterpret_cast<QCoreAudioSourceStream *>(inRefCon);
260 return self->processInput(ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioData);
264QCoreAudioSourceStream::processInput(AudioUnitRenderActionFlags *ioActionFlags,
265 const AudioTimeStamp *timeStamp, UInt32 inBusNumber,
266 UInt32 inNumberFrames,
267 AudioBufferList * )
noexcept QT_MM_NONBLOCKING
269 OSStatus status = AudioUnitRender(m_audioUnit.get(), ioActionFlags, timeStamp, inBusNumber,
270 inNumberFrames, &m_bufferList);
276 case kAudioUnitErr_CannotDoInCurrentContext:
282 qDebug() <<
"AudioUnitRender failed" << status;
286 QSpan<
const std::byte> inputSpan;
287 if (m_audioConverter) {
289 struct InputProcState
291 QCoreAudioSourceStream *self;
292 UInt32 inNumberFrames;
295 InputProcState state{
300 auto inputProc = [](AudioConverterRef, UInt32 *ioNumberDataPackets, AudioBufferList *ioData,
301 AudioStreamPacketDescription **outDataPacketDescription,
302 void *inUserData) -> OSStatus {
303 auto *state =
static_cast<InputProcState *>(inUserData);
304 *ioNumberDataPackets = state->inNumberFrames;
305 ioData->mNumberBuffers = 1;
306 ioData->mBuffers[0] = state->self->m_bufferList.mBuffers[0];
307 if (outDataPacketDescription)
308 *outDataPacketDescription =
nullptr;
312 UInt32 outputFrames = m_format.framesForBytes(m_outputBuffer.size());
313 OSStatus convStatus = AudioConverterFillComplexBuffer(
314 m_audioConverter, inputProc, &state, &outputFrames, &m_outputBufferList,
nullptr);
315 if (convStatus != noErr) {
316 qDebug() <<
"AudioConverterFillComplexBuffer failed:" << convStatus;
320 uint32_t outputBytes = m_format.bytesForFrames(outputFrames);
321 inputSpan = QSpan<
const std::byte>{
322 reinterpret_cast<
const std::byte *>(m_outputBuffer.data()),
325 inNumberFrames = outputFrames;
327 inputSpan = QSpan<
const std::byte>{
328 reinterpret_cast<
const std::byte *>(m_bufferList.mBuffers[0].mData),
329 m_bufferList.mBuffers[0].mDataByteSize,
333 return m_audioCallback ? processAudioCallback(inputSpan)
334 : processRingbuffer(inputSpan, inNumberFrames);
338QCoreAudioSourceStream::processRingbuffer(QSpan<
const std::byte> inputSpan,
339 UInt32 inNumberFrames)
noexcept QT_MM_NONBLOCKING
341 QPlatformAudioSourceStream::process(inputSpan, inNumberFrames);
345OSStatus QCoreAudioSourceStream::processAudioCallback(QSpan<
const std::byte> inputSpan)
noexcept
348 using namespace QtMultimediaPrivate;
349 runAudioCallback(*m_audioCallback, inputSpan, m_format, volume());
355bool QCoreAudioSourceStream::addDisconnectListener(AudioObjectID id)
357 m_stopOnDisconnected.cancel();
359 auto disconnectionFuture = m_disconnectMonitor.addDisconnectListener(id);
360 if (!disconnectionFuture)
363 m_stopOnDisconnected = disconnectionFuture->then(m_parent, [
this] {
371 finalizeQIODevice(ShutdownPolicy::DrainRingbuffer);
373 QPlatformAudioSourceStream::handleIOError(m_parent);
379void QCoreAudioSourceStream::removeDisconnectListener()
381 m_stopOnDisconnected.cancel();
382 m_disconnectMonitor.removeDisconnectListener();
388QDarwinAudioSource::QDarwinAudioSource(QAudioDevice device,
const QAudioFormat &format,
390 : BaseClass(std::move(device), format, parent)
394 QObject::connect(qGuiApp, &QGuiApplication::applicationStateChanged,
this,
395 [
this](Qt::ApplicationState state) {
396 if (state == Qt::ApplicationState::ApplicationActive)
397 resumeStreamIfNecessary();
402QDarwinAudioSource::~QDarwinAudioSource()
408 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
void resumeStreamIfNecessary()
Combined button and popup list for selecting options.