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 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> bestNominalSamplingRate = audioObjectFindBestNominalSampleRate(
99 *nativeDeviceId, QAudioDevice::Input, m_format.sampleRate());
101 if (bestNominalSamplingRate) {
102 if (!audioObjectSetSamplingRate(*nativeDeviceId, *bestNominalSamplingRate))
105 qWarning() <<
"QAudioSource: Device does not support any sampling rate. This should not "
110 if (m_hardwareBufferFrames)
111 audioObjectSetFramesPerBuffer(*nativeDeviceId, *m_hardwareBufferFrames);
113 if (bestNominalSamplingRate != m_format.sampleRate()) {
114 AudioStreamBasicDescription desiredFormat = streamFormat;
116 streamFormat.mSampleRate = *bestNominalSamplingRate;
118 OSStatus status = AudioConverterNew(&streamFormat, &desiredFormat, &m_audioConverter);
119 if (status != noErr) {
120 qWarning() <<
"QAudioSource: Failed to create AudioConverter:" << status;
125 audioUnitSetInputStreamFormat(m_audioUnit, 0, streamFormat);
126 audioUnitSetOutputStreamFormat(m_audioUnit, 1, streamFormat);
129 AVAudioSession *session = [AVAudioSession sharedInstance];
130 double hwRate = session.sampleRate;
131 std::optional<
int> bestNominalSamplingRate =
int(hwRate);
133 audioUnitSetInputStreamFormat(m_audioUnit, 0, streamFormat);
134 audioUnitSetOutputStreamFormat(m_audioUnit, 1, streamFormat);
137 std::optional<
int> framesPerBuffer = audioUnitGetFramesPerSlice(m_audioUnit);
139 m_bufferList.mNumberBuffers = 1;
140 m_bufferList.mBuffers[0].mNumberChannels = m_format.channelCount();
141 m_bufferList.mBuffers[0].mDataByteSize =
142 m_format.bytesForFrames(framesPerBuffer.value_or(2048));
143 m_bufferList.mBuffers[0].mData = malloc(m_bufferList.mBuffers[0].mDataByteSize);
145 if (m_audioConverter) {
146 size_t outputBufferSize = m_bufferList.mBuffers[0].mDataByteSize * m_format.sampleRate()
147 /
static_cast<
float>(*bestNominalSamplingRate)
149 m_outputBuffer.resize(outputBufferSize);
150 m_outputBufferList.mNumberBuffers = 1;
151 m_outputBufferList.mBuffers[0].mNumberChannels = m_format.channelCount();
152 m_outputBufferList.mBuffers[0].mDataByteSize = outputBufferSize;
153 m_outputBufferList.mBuffers[0].mData = m_outputBuffer.data();
156 return m_audioUnit.initialize();
159bool QCoreAudioSourceStream::
start(QIODevice *device)
161 setQIODevice(device);
163 const OSStatus status = AudioOutputUnitStart(m_audioUnit.get());
164 if (status != noErr) {
165 qDebug() <<
"AudioOutputUnitStart failed:" << status;
169 m_audioUnitRunning =
true;
170 createQIODeviceConnections(device);
177 QIODevice *device = createRingbufferReaderDevice();
178 bool opened = start(device);
185bool QCoreAudioSourceStream::
start(AudioCallback &&cb)
187 m_audioCallback = std::move(cb);
189 const OSStatus status = AudioOutputUnitStart(m_audioUnit.get());
190 if (status != noErr) {
191 qDebug() <<
"AudioOutputUnitStart failed:" << status;
195 m_audioUnitRunning =
true;
200void QCoreAudioSourceStream::
stop(ShutdownPolicy shutdownPolicy)
206 disconnectQIODeviceConnections();
208 finalizeQIODevice(shutdownPolicy);
209 if (shutdownPolicy == ShutdownPolicy::DiscardRingbuffer)
215 const auto status = AudioOutputUnitStop(m_audioUnit.get());
219 qDebug() <<
"AudioOutputUnitStop failed:" << status;
224 const auto status = AudioOutputUnitStart(m_audioUnit.get());
228 qDebug() <<
"AudioOutputUnitStart failed:" << status;
233 if (!audioUnitIsRunning(m_audioUnit))
240 m_parent->updateStreamIdle(idle);
243void QCoreAudioSourceStream::stopAudioUnit()
245 const auto status = AudioOutputUnitStop(m_audioUnit.get());
247 qDebug() <<
"AudioOutputUnitStop failed:" << status;
249 m_audioUnitRunning =
false;
252 removeDisconnectListener();
257OSStatus QCoreAudioSourceStream::inputCallback(
void *inRefCon,
258 AudioUnitRenderActionFlags *ioActionFlags,
259 const AudioTimeStamp *inTimeStamp,
260 UInt32 inBusNumber, UInt32 inNumberFrames,
261 AudioBufferList *ioData)
263 auto *self =
reinterpret_cast<QCoreAudioSourceStream *>(inRefCon);
264 return self->processInput(ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioData);
268QCoreAudioSourceStream::processInput(AudioUnitRenderActionFlags *ioActionFlags,
269 const AudioTimeStamp *timeStamp, UInt32 inBusNumber,
270 UInt32 inNumberFrames,
271 AudioBufferList * )
noexcept QT_MM_NONBLOCKING
273 OSStatus status = AudioUnitRender(m_audioUnit.get(), ioActionFlags, timeStamp, inBusNumber,
274 inNumberFrames, &m_bufferList);
280 case kAudioUnitErr_CannotDoInCurrentContext:
286 qDebug() <<
"AudioUnitRender failed" << status;
290 QSpan<
const std::byte> inputSpan;
291 if (m_audioConverter) {
293 struct InputProcState
295 QCoreAudioSourceStream *self;
296 UInt32 inNumberFrames;
299 InputProcState state{
304 auto inputProc = [](AudioConverterRef, UInt32 *ioNumberDataPackets, AudioBufferList *ioData,
305 AudioStreamPacketDescription **outDataPacketDescription,
306 void *inUserData) -> OSStatus {
307 auto *state =
static_cast<InputProcState *>(inUserData);
308 *ioNumberDataPackets = state->inNumberFrames;
309 ioData->mNumberBuffers = 1;
310 ioData->mBuffers[0] = state->self->m_bufferList.mBuffers[0];
311 if (outDataPacketDescription)
312 *outDataPacketDescription =
nullptr;
316 UInt32 outputFrames = m_format.framesForBytes(m_outputBuffer.size());
317 OSStatus convStatus = AudioConverterFillComplexBuffer(
318 m_audioConverter, inputProc, &state, &outputFrames, &m_outputBufferList,
nullptr);
319 if (convStatus != noErr) {
320 qDebug() <<
"AudioConverterFillComplexBuffer failed:" << convStatus;
324 uint32_t outputBytes = m_format.bytesForFrames(outputFrames);
325 inputSpan = QSpan<
const std::byte>{
326 reinterpret_cast<
const std::byte *>(m_outputBuffer.data()),
329 inNumberFrames = outputFrames;
331 inputSpan = QSpan<
const std::byte>{
332 reinterpret_cast<
const std::byte *>(m_bufferList.mBuffers[0].mData),
333 m_bufferList.mBuffers[0].mDataByteSize,
337 return m_audioCallback ? processAudioCallback(inputSpan)
338 : processRingbuffer(inputSpan, inNumberFrames);
342QCoreAudioSourceStream::processRingbuffer(QSpan<
const std::byte> inputSpan,
343 UInt32 inNumberFrames)
noexcept QT_MM_NONBLOCKING
345 QPlatformAudioSourceStream::process(inputSpan, inNumberFrames);
349OSStatus QCoreAudioSourceStream::processAudioCallback(QSpan<
const std::byte> inputSpan)
noexcept
352 using namespace QtMultimediaPrivate;
353 runAudioCallback(*m_audioCallback, inputSpan, m_format, volume());
359bool QCoreAudioSourceStream::addDisconnectListener(AudioObjectID id)
361 m_stopOnDisconnected.cancel();
363 auto disconnectionFuture = m_disconnectMonitor.addDisconnectListener(id);
364 if (!disconnectionFuture)
367 m_stopOnDisconnected = disconnectionFuture->then(m_parent, [
this] {
375 finalizeQIODevice(ShutdownPolicy::DrainRingbuffer);
377 QPlatformAudioSourceStream::handleIOError(m_parent);
383void QCoreAudioSourceStream::removeDisconnectListener()
385 m_stopOnDisconnected.cancel();
386 m_disconnectMonitor.removeDisconnectListener();
392QDarwinAudioSource::QDarwinAudioSource(QAudioDevice device,
const QAudioFormat &format,
394 : BaseClass(std::move(device), format, parent)
398 QObject::connect(qGuiApp, &QGuiApplication::applicationStateChanged,
this,
399 [
this](Qt::ApplicationState state) {
400 if (state == Qt::ApplicationState::ApplicationActive)
401 resumeStreamIfNecessary();
406QDarwinAudioSource::~QDarwinAudioSource()
412 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.