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/qcoreaudiodevice_p.h>
17#include <QtMultimedia/private/qcoreaudiodevices_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 m_stopOnDisconnected.cancelChain();
52 AudioConverterDispose(m_audioConverter);
53 free(m_bufferList.mBuffers[0].mData);
56bool QCoreAudioSourceStream::
open()
58 using namespace QCoreAudioUtils;
60 if (
auto audioUnit = makeAudioUnitForIO())
61 m_audioUnit = std::move(*audioUnit);
65 audioUnitSetInputEnabled(m_audioUnit,
true);
66 audioUnitSetOutputEnabled(m_audioUnit,
false);
69 AURenderCallbackStruct callback;
70 callback.inputProc = inputCallback;
71 callback.inputProcRefCon =
this;
73 if (AudioUnitSetProperty(m_audioUnit.get(), kAudioOutputUnitProperty_SetInputCallback,
74 kAudioUnitScope_Global, 0, &callback,
sizeof(callback))
76 qWarning() <<
"QAudioSource: Failed to set AudioUnit callback";
80 AudioStreamBasicDescription streamFormat = toAudioStreamBasicDescription(m_format);
85 const std::optional<AudioDeviceID> nativeDeviceId = findAudioDeviceId(m_audioDevice);
86 if (!nativeDeviceId) {
87 qWarning() <<
"QAudioSource: Unable to use find most recent CoreAudio AudioDeviceID for "
88 "given device-id. The device might not be connected.";
91 if (!addDisconnectListener(*nativeDeviceId))
95 if (!audioUnitSetCurrentDevice(m_audioUnit, *nativeDeviceId))
98 std::optional<
int> deviceSamplingRate = audioObjectGetSamplingRate(*nativeDeviceId);
100 if (!deviceSamplingRate) {
101 qWarning() <<
"QAudioSource: Device does not support any sampling rate. This should not "
106 if (m_nativePeriodFrames)
107 audioObjectSetFramesPerBuffer(*nativeDeviceId,
108 qToUnderlying(*m_nativePeriodFrames));
110 if (deviceSamplingRate != m_format.sampleRate()) {
111 AudioStreamBasicDescription desiredFormat = streamFormat;
113 streamFormat.mSampleRate = *deviceSamplingRate;
115 OSStatus status = AudioConverterNew(&streamFormat, &desiredFormat, &m_audioConverter);
116 if (status != noErr) {
117 qWarning() <<
"QAudioSource: Failed to create AudioConverter:" << status;
122 audioUnitSetInputStreamFormat(m_audioUnit, 0, streamFormat);
123 audioUnitSetOutputStreamFormat(m_audioUnit, 1, streamFormat);
126 AVAudioSession *session = [AVAudioSession sharedInstance];
127 double hwRate = session.sampleRate;
128 std::optional<
int> deviceSamplingRate =
int(hwRate);
130 audioUnitSetInputStreamFormat(m_audioUnit, 0, streamFormat);
131 audioUnitSetOutputStreamFormat(m_audioUnit, 1, streamFormat);
134 std::optional<
int> framesPerBuffer = audioUnitGetFramesPerSlice(m_audioUnit);
136 m_bufferList.mNumberBuffers = 1;
137 m_bufferList.mBuffers[0].mNumberChannels = m_format.channelCount();
138 m_bufferList.mBuffers[0].mDataByteSize =
139 m_format.bytesForFrames(framesPerBuffer.value_or(2048));
140 m_bufferList.mBuffers[0].mData = malloc(m_bufferList.mBuffers[0].mDataByteSize);
142 if (m_audioConverter) {
143 size_t outputBufferSize = m_bufferList.mBuffers[0].mDataByteSize * m_format.sampleRate()
144 /
static_cast<
float>(*deviceSamplingRate)
146 m_outputBuffer.resize(outputBufferSize);
147 m_outputBufferList.mNumberBuffers = 1;
148 m_outputBufferList.mBuffers[0].mNumberChannels = m_format.channelCount();
149 m_outputBufferList.mBuffers[0].mDataByteSize = outputBufferSize;
150 m_outputBufferList.mBuffers[0].mData = m_outputBuffer.data();
153 return m_audioUnit.initialize();
156bool QCoreAudioSourceStream::
start(QIODevice *device)
158 setQIODevice(device);
160 const OSStatus status = AudioOutputUnitStart(m_audioUnit.get());
161 if (status != noErr) {
162 qDebug() <<
"AudioOutputUnitStart failed:" << status;
166 m_audioUnitRunning =
true;
167 createQIODeviceConnections(device);
174 QIODevice *device = createRingbufferReaderDevice();
175 bool opened = start(device);
182bool QCoreAudioSourceStream::
start(AudioCallback &&cb)
184 m_audioCallback = std::move(cb);
186 const OSStatus status = AudioOutputUnitStart(m_audioUnit.get());
187 if (status != noErr) {
188 qDebug() <<
"AudioOutputUnitStart failed:" << status;
192 m_audioUnitRunning =
true;
197void QCoreAudioSourceStream::
stop(ShutdownPolicy shutdownPolicy)
203 disconnectQIODeviceConnections();
205 finalizeQIODevice(shutdownPolicy);
206 if (shutdownPolicy == ShutdownPolicy::DiscardRingbuffer)
212 const auto status = AudioOutputUnitStop(m_audioUnit.get());
216 qDebug() <<
"AudioOutputUnitStop failed:" << status;
221 const auto status = AudioOutputUnitStart(m_audioUnit.get());
225 qDebug() <<
"AudioOutputUnitStart failed:" << status;
230 if (!audioUnitIsRunning(m_audioUnit))
237 m_parent->updateStreamIdle(idle);
240void QCoreAudioSourceStream::stopAudioUnit()
242 const auto status = AudioOutputUnitStop(m_audioUnit.get());
244 qDebug() <<
"AudioOutputUnitStop failed:" << status;
246 m_audioUnitRunning =
false;
249 removeDisconnectListener();
254OSStatus QCoreAudioSourceStream::inputCallback(
void *inRefCon,
255 AudioUnitRenderActionFlags *ioActionFlags,
256 const AudioTimeStamp *inTimeStamp,
257 UInt32 inBusNumber, UInt32 inNumberFrames,
258 AudioBufferList *ioData)
260 auto *self =
reinterpret_cast<QCoreAudioSourceStream *>(inRefCon);
261 return self->processInput(ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioData);
265QCoreAudioSourceStream::processInput(AudioUnitRenderActionFlags *ioActionFlags,
266 const AudioTimeStamp *timeStamp, UInt32 inBusNumber,
267 UInt32 inNumberFrames,
268 AudioBufferList * )
noexcept QT_MM_NONBLOCKING
270 OSStatus status = AudioUnitRender(m_audioUnit.get(), ioActionFlags, timeStamp, inBusNumber,
271 inNumberFrames, &m_bufferList);
277 case kAudioUnitErr_CannotDoInCurrentContext:
283 qDebug() <<
"AudioUnitRender failed" << status;
287 QSpan<
const std::byte> inputSpan;
288 if (m_audioConverter) {
290 struct InputProcState
292 QCoreAudioSourceStream *self;
293 UInt32 inNumberFrames;
296 InputProcState state{
301 auto inputProc = [](AudioConverterRef, UInt32 *ioNumberDataPackets, AudioBufferList *ioData,
302 AudioStreamPacketDescription **outDataPacketDescription,
303 void *inUserData) -> OSStatus {
304 auto *state =
static_cast<InputProcState *>(inUserData);
305 *ioNumberDataPackets = state->inNumberFrames;
306 ioData->mNumberBuffers = 1;
307 ioData->mBuffers[0] = state->self->m_bufferList.mBuffers[0];
308 if (outDataPacketDescription)
309 *outDataPacketDescription =
nullptr;
313 UInt32 outputFrames = m_format.framesForBytes(m_outputBuffer.size());
314 OSStatus convStatus = AudioConverterFillComplexBuffer(
315 m_audioConverter, inputProc, &state, &outputFrames, &m_outputBufferList,
nullptr);
316 if (convStatus != noErr) {
317 qDebug() <<
"AudioConverterFillComplexBuffer failed:" << convStatus;
321 uint32_t outputBytes = m_format.bytesForFrames(outputFrames);
322 inputSpan = QSpan<
const std::byte>{
323 reinterpret_cast<
const std::byte *>(m_outputBuffer.data()),
326 inNumberFrames = outputFrames;
328 inputSpan = QSpan<
const std::byte>{
329 reinterpret_cast<
const std::byte *>(m_bufferList.mBuffers[0].mData),
330 m_bufferList.mBuffers[0].mDataByteSize,
334 return m_audioCallback ? processAudioCallback(inputSpan)
335 : processRingbuffer(inputSpan, inNumberFrames);
339QCoreAudioSourceStream::processRingbuffer(QSpan<
const std::byte> inputSpan,
340 UInt32 inNumberFrames)
noexcept QT_MM_NONBLOCKING
342 QPlatformAudioSourceStream::process(inputSpan, inNumberFrames);
346OSStatus QCoreAudioSourceStream::processAudioCallback(QSpan<
const std::byte> inputSpan)
noexcept
349 using namespace QtMultimediaPrivate;
350 runAudioCallback(*m_audioCallback, inputSpan, m_format, volume());
356bool QCoreAudioSourceStream::addDisconnectListener(AudioObjectID id)
358 m_stopOnDisconnected.cancel();
360 auto disconnectionFuture = m_disconnectMonitor.addDisconnectListener(id);
361 if (!disconnectionFuture)
364 m_stopOnDisconnected = disconnectionFuture->then(m_parent, [
this] {
372 finalizeQIODevice(ShutdownPolicy::DrainRingbuffer);
374 QPlatformAudioSourceStream::handleIOError(m_parent);
380void QCoreAudioSourceStream::removeDisconnectListener()
382 m_stopOnDisconnected.cancel();
383 m_disconnectMonitor.removeDisconnectListener();
389QCoreAudioSource::QCoreAudioSource(QAudioDevice device,
const QAudioFormat &format,
391 : BaseClass(std::move(device), format, parent)
395 QObject::connect(qGuiApp, &QGuiApplication::applicationStateChanged,
this,
396 [
this](Qt::ApplicationState state) {
397 if (state == Qt::ApplicationState::ApplicationActive)
398 resumeStreamIfNecessary();
403QCoreAudioSource::~QCoreAudioSource()
409 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.