Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qdarwinaudiosource.mm
Go to the documentation of this file.
1// Copyright (C) 2022 The Qt Company Ltd and/or its subsidiary(-ies).
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
6#include "qcoreaudioutils_p.h"
8#include <qmediadevices.h>
9
10#if defined(Q_OS_MACOS)
11# include <AudioUnit/AudioComponent.h>
12#endif
13
14#if defined(Q_OS_IOS) || defined(Q_OS_TVOS)
16#endif
17
18#include <QtMultimedia/private/qaudiohelpers_p.h>
19#include <QtCore/QDataStream>
20#include <QtCore/QDebug>
21
23
24static const int DEFAULT_BUFFER_SIZE = 4 * 1024;
25
26QCoreAudioBufferList::QCoreAudioBufferList(const AudioStreamBasicDescription &streamFormat)
27 : m_owner(false)
28 , m_streamDescription(streamFormat)
29{
30 const bool isInterleaved = (m_streamDescription.mFormatFlags & kAudioFormatFlagIsNonInterleaved) == 0;
31 const int numberOfBuffers = isInterleaved ? 1 : m_streamDescription.mChannelsPerFrame;
32
33 m_dataSize = 0;
34
35 m_bufferList = reinterpret_cast<AudioBufferList*>(malloc(sizeof(AudioBufferList) +
36 (sizeof(AudioBuffer) * numberOfBuffers)));
37
38 m_bufferList->mNumberBuffers = numberOfBuffers;
39 for (int i = 0; i < numberOfBuffers; ++i) {
40 m_bufferList->mBuffers[i].mNumberChannels = isInterleaved ? numberOfBuffers : 1;
41 m_bufferList->mBuffers[i].mDataByteSize = 0;
42 m_bufferList->mBuffers[i].mData = 0;
43 }
44}
45
46QCoreAudioBufferList::QCoreAudioBufferList(const AudioStreamBasicDescription &streamFormat, char *buffer, int bufferSize)
47 : m_owner(false)
48 , m_streamDescription(streamFormat)
49 , m_bufferList(0)
50{
51 m_dataSize = bufferSize;
52
53 m_bufferList = reinterpret_cast<AudioBufferList*>(malloc(sizeof(AudioBufferList) + sizeof(AudioBuffer)));
54
55 m_bufferList->mNumberBuffers = 1;
56 m_bufferList->mBuffers[0].mNumberChannels = 1;
57 m_bufferList->mBuffers[0].mDataByteSize = m_dataSize;
58 m_bufferList->mBuffers[0].mData = buffer;
59}
60
61QCoreAudioBufferList::QCoreAudioBufferList(const AudioStreamBasicDescription &streamFormat, int framesToBuffer)
62 : m_owner(true)
63 , m_streamDescription(streamFormat)
64 , m_bufferList(0)
65{
66 const bool isInterleaved = (m_streamDescription.mFormatFlags & kAudioFormatFlagIsNonInterleaved) == 0;
67 const int numberOfBuffers = isInterleaved ? 1 : m_streamDescription.mChannelsPerFrame;
68
69 m_dataSize = framesToBuffer * m_streamDescription.mBytesPerFrame;
70
71 m_bufferList = reinterpret_cast<AudioBufferList*>(malloc(sizeof(AudioBufferList) +
72 (sizeof(AudioBuffer) * numberOfBuffers)));
73 m_bufferList->mNumberBuffers = numberOfBuffers;
74 for (int i = 0; i < numberOfBuffers; ++i) {
75 m_bufferList->mBuffers[i].mNumberChannels = isInterleaved ? numberOfBuffers : 1;
76 m_bufferList->mBuffers[i].mDataByteSize = m_dataSize;
77 m_bufferList->mBuffers[i].mData = malloc(m_dataSize);
78 }
79}
80
82{
83 if (m_owner) {
84 for (UInt32 i = 0; i < m_bufferList->mNumberBuffers; ++i)
85 free(m_bufferList->mBuffers[i].mData);
86 }
87
88 free(m_bufferList);
89}
90
92{
93 return static_cast<char*>(m_bufferList->mBuffers[buffer].mData);
94}
95
97{
98 return m_bufferList->mBuffers[buffer].mDataByteSize;
99}
100
102{
103 return m_bufferList->mBuffers[buffer].mDataByteSize / m_streamDescription.mBytesPerFrame;
104}
105
107{
108 return m_bufferList->mBuffers[buffer].mDataByteSize / m_streamDescription.mBytesPerPacket;
109}
110
112{
113 return m_streamDescription.mBytesPerPacket;
114}
115
117{
118 for (UInt32 i = 0; i < m_bufferList->mNumberBuffers; ++i) {
119 m_bufferList->mBuffers[i].mDataByteSize = m_dataSize;
120 m_bufferList->mBuffers[i].mData = 0;
121 }
122}
123
125 : m_audioBufferList(abl)
126{
127 m_totalPackets = m_audioBufferList->packetCount();
128 m_position = 0;
129}
130
131bool QCoreAudioPacketFeeder::feed(AudioBufferList &dst, UInt32 &packetCount)
132{
133 if (m_position == m_totalPackets) {
134 dst.mBuffers[0].mDataByteSize = 0;
135 packetCount = 0;
136 return false;
137 }
138
139 if (m_totalPackets - m_position < packetCount)
140 packetCount = m_totalPackets - m_position;
141
142 dst.mBuffers[0].mDataByteSize = packetCount * m_audioBufferList->packetSize();
143 dst.mBuffers[0].mData = m_audioBufferList->data() + (m_position * m_audioBufferList->packetSize());
144
145 m_position += packetCount;
146
147 return true;
148}
149
151{
152 return m_position == m_totalPackets;
153}
154
155QDarwinAudioSourceBuffer::QDarwinAudioSourceBuffer(int bufferSize, int maxPeriodSize, const AudioStreamBasicDescription &inputFormat, const AudioStreamBasicDescription &outputFormat, QObject *parent)
156 : QObject(parent)
157 , m_deviceError(false)
158 , m_device(0)
159 , m_audioConverter(0)
160 , m_inputFormat(inputFormat)
161 , m_outputFormat(outputFormat)
162 , m_volume(qreal(1.0f))
163{
164 m_maxPeriodSize = maxPeriodSize;
165 m_periodTime = m_maxPeriodSize / m_outputFormat.mBytesPerFrame * 1000 / m_outputFormat.mSampleRate;
166
167 m_buffer = new CoreAudioRingBuffer(bufferSize);
168
169 m_inputBufferList = new QCoreAudioBufferList(m_inputFormat);
170
171 m_flushTimer = new QTimer(this);
172 connect(m_flushTimer, SIGNAL(timeout()), this, SLOT(flushBuffer()));
173
174 if (CoreAudioUtils::toQAudioFormat(inputFormat) != CoreAudioUtils::toQAudioFormat(outputFormat)) {
175 if (AudioConverterNew(&m_inputFormat, &m_outputFormat, &m_audioConverter) != noErr) {
176 qWarning() << "QAudioSource: Unable to create an Audio Converter";
177 m_audioConverter = 0;
178 }
179 }
180
181 m_qFormat = CoreAudioUtils::toQAudioFormat(inputFormat); // we adjust volume before conversion
182}
183
188
190{
191 return m_volume;
192}
193
195{
196 m_volume = v;
197}
198
199qint64 QDarwinAudioSourceBuffer::renderFromDevice(AudioUnit audioUnit, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames)
200{
201 const bool pullMode = m_device == 0;
202
203 OSStatus err;
204 qint64 framesRendered = 0;
205
206 m_inputBufferList->reset();
207 err = AudioUnitRender(audioUnit,
208 ioActionFlags,
209 inTimeStamp,
210 inBusNumber,
211 inNumberFrames,
212 m_inputBufferList->audioBufferList());
213
214 // adjust volume, if necessary
215 if (!qFuzzyCompare(m_volume, qreal(1.0f))) {
217 m_qFormat,
218 m_inputBufferList->data(), /* input */
219 m_inputBufferList->data(), /* output */
220 m_inputBufferList->bufferSize());
221 }
222
223 if (m_audioConverter != 0) {
224 QCoreAudioPacketFeeder feeder(m_inputBufferList);
225
226 int copied = 0;
227 const int available = m_buffer->free();
228
229 while (err == noErr && !feeder.empty()) {
230 CoreAudioRingBuffer::Region region = m_buffer->acquireWriteRegion(available - copied);
231
232 if (region.second == 0)
233 break;
234
235 AudioBufferList output;
236 output.mNumberBuffers = 1;
237 output.mBuffers[0].mNumberChannels = 1;
238 output.mBuffers[0].mDataByteSize = region.second;
239 output.mBuffers[0].mData = region.first;
240
241 UInt32 packetSize = region.second / m_outputFormat.mBytesPerPacket;
242 err = AudioConverterFillComplexBuffer(m_audioConverter,
243 converterCallback,
244 &feeder,
245 &packetSize,
246 &output,
247 0);
248 region.second = output.mBuffers[0].mDataByteSize;
249 copied += region.second;
250
251 m_buffer->releaseWriteRegion(region);
252 }
253
254 framesRendered += copied / m_outputFormat.mBytesPerFrame;
255 }
256 else {
257 const int available = m_inputBufferList->bufferSize();
258 bool wecan = true;
259 int copied = 0;
260
261 while (wecan && copied < available) {
262 CoreAudioRingBuffer::Region region = m_buffer->acquireWriteRegion(available - copied);
263
264 if (region.second > 0) {
265 memcpy(region.first, m_inputBufferList->data() + copied, region.second);
266 copied += region.second;
267 }
268 else
269 wecan = false;
270
271 m_buffer->releaseWriteRegion(region);
272 }
273
274 framesRendered = copied / m_outputFormat.mBytesPerFrame;
275 }
276
277 if (pullMode && framesRendered > 0)
278 emit readyRead();
279
280 return framesRendered;
281}
282
284{
285 bool wecan = true;
286 qint64 bytesCopied = 0;
287
288 len -= len % m_maxPeriodSize;
289 while (wecan && bytesCopied < len) {
290 CoreAudioRingBuffer::Region region = m_buffer->acquireReadRegion(len - bytesCopied);
291
292 if (region.second > 0) {
293 memcpy(data + bytesCopied, region.first, region.second);
294 bytesCopied += region.second;
295 }
296 else
297 wecan = false;
298
299 m_buffer->releaseReadRegion(region);
300 }
301
302 return bytesCopied;
303}
304
306{
307 if (m_device != device)
308 m_device = device;
309}
310
312{
313 if (m_device != 0) {
314 // We use the period time for the timer, since that's
315 // around the buffer size (pre conversion >.>)
316 m_flushTimer->start(qMax(1, m_periodTime));
317 }
318}
319
321{
322 m_flushTimer->stop();
323}
324
326{
327 if (m_device == 0)
328 return;
329
330 const int used = m_buffer->used();
331 const int readSize = all ? used : used - (used % m_maxPeriodSize);
332
333 if (readSize > 0) {
334 bool wecan = true;
335 int flushed = 0;
336
337 while (!m_deviceError && wecan && flushed < readSize) {
338 CoreAudioRingBuffer::Region region = m_buffer->acquireReadRegion(readSize - flushed);
339
340 if (region.second > 0) {
341 int bytesWritten = m_device->write(region.first, region.second);
342 if (bytesWritten < 0) {
344 m_deviceError = true;
345 }
346 else {
347 region.second = bytesWritten;
348 flushed += bytesWritten;
349 wecan = bytesWritten != 0;
350 }
351 }
352 else
353 wecan = false;
354
355 m_buffer->releaseReadRegion(region);
356 }
357 }
358}
359
361{
362 m_buffer->reset();
363 m_deviceError = false;
364}
365
367{
368 return m_buffer->free();
369}
370
372{
373 return m_buffer->used();
374}
375
376void QDarwinAudioSourceBuffer::flushBuffer()
377{
378 flush();
379}
380
381OSStatus QDarwinAudioSourceBuffer::converterCallback(AudioConverterRef inAudioConverter, UInt32 *ioNumberDataPackets, AudioBufferList *ioData, AudioStreamPacketDescription **outDataPacketDescription, void *inUserData)
382{
383 Q_UNUSED(inAudioConverter);
384 Q_UNUSED(outDataPacketDescription);
385
386 QCoreAudioPacketFeeder* feeder = static_cast<QCoreAudioPacketFeeder*>(inUserData);
387
388 if (!feeder->feed(*ioData, *ioNumberDataPackets))
389 return as_empty;
390
391 return noErr;
392}
393
395 : QIODevice(parent)
396 , m_audioBuffer(audioBuffer)
397{
399 connect(m_audioBuffer, SIGNAL(readyRead()), this, SIGNAL(readyRead()));
400}
401
403{
404 return m_audioBuffer->readBytes(data, len);
405}
406
408{
409 Q_UNUSED(data);
410 Q_UNUSED(len);
411
412 return 0;
413}
414
416 : QPlatformAudioSource(parent)
417 , m_audioDeviceInfo(device)
418 , m_isOpen(false)
419 , m_internalBufferSize(DEFAULT_BUFFER_SIZE)
420 , m_totalFrames(0)
421 , m_audioUnit(0)
422 , m_clockFrequency(CoreAudioUtils::frequency() / 1000)
423 , m_errorCode(QAudio::NoError)
424 , m_stateCode(QAudio::StoppedState)
425 , m_audioBuffer(nullptr)
426 , m_volume(1.0)
427{
428 QAudioDevice di = device;
429 if (di.isNull())
431#if defined(Q_OS_MACOS)
432 const QCoreAudioDeviceInfo *info = static_cast<const QCoreAudioDeviceInfo *>(di.handle());
433 Q_ASSERT(info);
434 m_audioDeviceId = info->deviceID();
435#endif
436 m_device = di.id();
437}
438
439
444
445bool QDarwinAudioSource::open()
446{
447#if defined(Q_OS_IOS)
449 CoreAudioSessionManager::instance().setActive(true);
450#endif
451
452 if (m_isOpen)
453 return true;
454
455 AudioComponentDescription componentDescription;
456 componentDescription.componentType = kAudioUnitType_Output;
457#if defined(Q_OS_MACOS)
458 componentDescription.componentSubType = kAudioUnitSubType_HALOutput;
459#else
460 componentDescription.componentSubType = kAudioUnitSubType_RemoteIO;
461#endif
462 componentDescription.componentManufacturer = kAudioUnitManufacturer_Apple;
463 componentDescription.componentFlags = 0;
464 componentDescription.componentFlagsMask = 0;
465
466 AudioComponent component = AudioComponentFindNext(0, &componentDescription);
467 if (component == 0) {
468 qWarning() << "QAudioSource: Failed to find Output component";
469 return false;
470 }
471
472 if (AudioComponentInstanceNew(component, &m_audioUnit) != noErr) {
473 qWarning() << "QAudioSource: Unable to Open Output Component";
474 return false;
475 }
476
477 // Set mode
478 // switch to input mode
479 UInt32 enable = 1;
480 if (AudioUnitSetProperty(m_audioUnit,
481 kAudioOutputUnitProperty_EnableIO,
482 kAudioUnitScope_Input,
483 1,
484 &enable,
485 sizeof(enable)) != noErr) {
486 qWarning() << "QAudioSource: Unable to switch to input mode (Enable Input)";
487 return false;
488 }
489
490 enable = 0;
491 if (AudioUnitSetProperty(m_audioUnit,
492 kAudioOutputUnitProperty_EnableIO,
493 kAudioUnitScope_Output,
494 0,
495 &enable,
496 sizeof(enable)) != noErr) {
497 qWarning() << "QAudioSource: Unable to switch to input mode (Disable output)";
498 return false;
499 }
500
501 // register callback
502 AURenderCallbackStruct callback;
503 callback.inputProc = inputCallback;
504 callback.inputProcRefCon = this;
505
506 if (AudioUnitSetProperty(m_audioUnit,
507 kAudioOutputUnitProperty_SetInputCallback,
508 kAudioUnitScope_Global,
509 0,
510 &callback,
511 sizeof(callback)) != noErr) {
512 qWarning() << "QAudioSource: Failed to set AudioUnit callback";
513 return false;
514 }
515
516#if defined(Q_OS_MACOS)
517 //Set Audio Device
518 if (AudioUnitSetProperty(m_audioUnit,
519 kAudioOutputUnitProperty_CurrentDevice,
520 kAudioUnitScope_Global,
521 0,
522 &m_audioDeviceId,
523 sizeof(m_audioDeviceId)) != noErr) {
524 qWarning() << "QAudioSource: Unable to use configured device";
525 return false;
526 }
527#endif
528
529 //set format
530 m_streamFormat = CoreAudioUtils::toAudioStreamBasicDescription(m_audioFormat);
531
532#if defined(Q_OS_MACOS)
533 UInt32 size = 0;
534
535 if (m_audioFormat == m_audioDeviceInfo.preferredFormat()) {
536#endif
537
538 m_deviceFormat = m_streamFormat;
539 AudioUnitSetProperty(m_audioUnit,
540 kAudioUnitProperty_StreamFormat,
541 kAudioUnitScope_Output,
542 1,
543 &m_deviceFormat,
544 sizeof(m_deviceFormat));
545#if defined(Q_OS_MACOS)
546 } else {
547 size = sizeof(m_deviceFormat);
548 if (AudioUnitGetProperty(m_audioUnit,
549 kAudioUnitProperty_StreamFormat,
550 kAudioUnitScope_Input,
551 1,
552 &m_deviceFormat,
553 &size) != noErr) {
554 qWarning() << "QAudioSource: Unable to retrieve device format";
555 return false;
556 }
557
558 if (AudioUnitSetProperty(m_audioUnit,
559 kAudioUnitProperty_StreamFormat,
560 kAudioUnitScope_Output,
561 1,
562 &m_deviceFormat,
563 sizeof(m_deviceFormat)) != noErr) {
564 qWarning() << "QAudioSource: Unable to set device format";
565 return false;
566 }
567 }
568#endif
569
570 //setup buffers
571 UInt32 numberOfFrames;
572#if defined(Q_OS_MACOS)
573 size = sizeof(UInt32);
574 if (AudioUnitGetProperty(m_audioUnit,
575 kAudioDevicePropertyBufferFrameSize,
576 kAudioUnitScope_Global,
577 0,
578 &numberOfFrames,
579 &size) != noErr) {
580 qWarning() << "QAudioSource: Failed to get audio period size";
581 return false;
582 }
583 //BUG: numberOfFrames gets ignored after this point
584
585 AudioValueRange bufferRange;
586 size = sizeof(AudioValueRange);
587
588 if (AudioUnitGetProperty(m_audioUnit,
589 kAudioDevicePropertyBufferFrameSizeRange,
590 kAudioUnitScope_Global,
591 0,
592 &bufferRange,
593 &size) != noErr) {
594 qWarning() << "QAudioSource: Failed to get audio period size range";
595 return false;
596 }
597
598 // See if the requested buffer size is permissible
599 numberOfFrames = qBound((UInt32)bufferRange.mMinimum, m_internalBufferSize / m_streamFormat.mBytesPerFrame, (UInt32)bufferRange.mMaximum);
600
601 // Set it back
602 if (AudioUnitSetProperty(m_audioUnit,
603 kAudioDevicePropertyBufferFrameSize,
604 kAudioUnitScope_Global,
605 0,
606 &numberOfFrames,
607 sizeof(UInt32)) != noErr) {
608 qWarning() << "QAudioSource: Failed to set audio buffer size";
609 return false;
610 }
611#else //iOS
612 Float32 bufferSize = CoreAudioSessionManager::instance().currentIOBufferDuration();
613 bufferSize *= m_streamFormat.mSampleRate;
614 numberOfFrames = bufferSize;
615#endif
616
617 // Now allocate a few buffers to be safe.
618 m_periodSizeBytes = m_internalBufferSize = numberOfFrames * m_streamFormat.mBytesPerFrame;
619
620 {
621 QMutexLocker lock(m_audioBuffer);
622 m_audioBuffer = new QDarwinAudioSourceBuffer(m_internalBufferSize * 4,
623 m_periodSizeBytes,
624 m_deviceFormat,
625 m_streamFormat,
626 this);
627
628 m_audioBuffer->setVolume(m_volume);
629 }
630 m_audioIO = new QDarwinAudioSourceDevice(m_audioBuffer, this);
631
632 // Init
633 if (AudioUnitInitialize(m_audioUnit) != noErr) {
634 qWarning() << "QAudioSource: Failed to initialize AudioUnit";
635 return false;
636 }
637
638 m_isOpen = true;
639
640 return m_isOpen;
641
642}
643
644void QDarwinAudioSource::close()
645{
646 stop();
647 if (m_audioUnit != 0) {
648 AudioOutputUnitStop(m_audioUnit);
649 AudioUnitUninitialize(m_audioUnit);
650 AudioComponentInstanceDispose(m_audioUnit);
651 }
652
653 delete m_audioBuffer;
654 m_audioBuffer = nullptr;
655 m_isOpen = false;
656}
657
659{
660 QIODevice* op = device;
661
662 if (!m_audioDeviceInfo.isFormatSupported(m_audioFormat) || !open()) {
663 m_stateCode = QAudio::StoppedState;
664 m_errorCode = QAudio::OpenError;
665 return;
666 }
667
668 reset();
669 {
670 QMutexLocker lock(m_audioBuffer);
671 m_audioBuffer->reset();
672 m_audioBuffer->setFlushDevice(op);
673 }
674
675 if (op == 0)
676 op = m_audioIO;
677
678 // Start
679 m_totalFrames = 0;
680
681 m_stateCode = QAudio::IdleState;
682 m_errorCode = QAudio::NoError;
683 emit stateChanged(m_stateCode);
684
685 audioThreadStart();
686}
687
688
690{
691 QIODevice* op = 0;
692
693 if (!m_audioDeviceInfo.isFormatSupported(m_audioFormat) || !open()) {
694 m_stateCode = QAudio::StoppedState;
695 m_errorCode = QAudio::OpenError;
696 return m_audioIO;
697 }
698
699 reset();
700 {
701 QMutexLocker lock(m_audioBuffer);
702 m_audioBuffer->reset();
703 m_audioBuffer->setFlushDevice(op);
704 }
705
706 if (op == 0)
707 op = m_audioIO;
708
709 // Start
710 m_totalFrames = 0;
711
712 m_stateCode = QAudio::IdleState;
713 m_errorCode = QAudio::NoError;
714 emit stateChanged(m_stateCode);
715
716 audioThreadStart();
717
718 return op;
719}
720
721
723{
724 QMutexLocker lock(m_audioBuffer);
725 if (m_stateCode != QAudio::StoppedState) {
726 audioThreadStop();
727 m_audioBuffer->flush(true);
728
729 m_errorCode = QAudio::NoError;
730 m_stateCode = QAudio::StoppedState;
731 QMetaObject::invokeMethod(this, "stateChanged", Qt::QueuedConnection, Q_ARG(QAudio::State, m_stateCode));
732 }
733}
734
735
737{
738 QMutexLocker lock(m_audioBuffer);
739 if (m_stateCode != QAudio::StoppedState) {
740 audioThreadStop();
741
742 m_errorCode = QAudio::NoError;
743 m_stateCode = QAudio::StoppedState;
744 m_audioBuffer->reset();
745 QMetaObject::invokeMethod(this, "stateChanged", Qt::QueuedConnection, Q_ARG(QAudio::State, m_stateCode));
746 }
747}
748
749
751{
752 QMutexLocker lock(m_audioBuffer);
753 if (m_stateCode == QAudio::ActiveState || m_stateCode == QAudio::IdleState) {
754 audioThreadStop();
755
756 m_errorCode = QAudio::NoError;
757 m_stateCode = QAudio::SuspendedState;
758 QMetaObject::invokeMethod(this, "stateChanged", Qt::QueuedConnection, Q_ARG(QAudio::State, m_stateCode));
759 }
760}
761
762
764{
765 QMutexLocker lock(m_audioBuffer);
766 if (m_stateCode == QAudio::SuspendedState) {
767 audioThreadStart();
768
769 m_errorCode = QAudio::NoError;
770 m_stateCode = QAudio::ActiveState;
771 QMetaObject::invokeMethod(this, "stateChanged", Qt::QueuedConnection, Q_ARG(QAudio::State, m_stateCode));
772 }
773}
774
775
777{
778 QMutexLocker lock(m_audioBuffer);
779 if (!m_audioBuffer)
780 return 0;
781 return m_audioBuffer->used();
782}
783
785{
786 m_internalBufferSize = value;
787}
788
789
791{
792 return m_internalBufferSize;
793}
794
796{
797 return m_totalFrames * 1000000 / m_audioFormat.sampleRate();
798}
799
801{
802 return m_errorCode;
803}
804
805
807{
808 return m_stateCode;
809}
810
811
813{
814 if (m_stateCode == QAudio::StoppedState)
815 m_audioFormat = format;
816}
817
818
820{
821 return m_audioFormat;
822}
823
824
826{
827 QMutexLocker lock(m_audioBuffer);
828 m_volume = volume;
829 if (m_audioBuffer)
830 m_audioBuffer->setVolume(m_volume);
831}
832
833
835{
836 return m_volume;
837}
838
839void QDarwinAudioSource::deviceStoppped()
840{
841 stopTimers();
842 emit stateChanged(m_stateCode);
843}
844
845void QDarwinAudioSource::audioThreadStart()
846{
847 startTimers();
848 m_audioThreadState.storeRelaxed(Running);
849 AudioOutputUnitStart(m_audioUnit);
850}
851
852void QDarwinAudioSource::audioThreadStop()
853{
854 stopTimers();
855 if (m_audioThreadState.testAndSetAcquire(Running, Stopped))
856 m_audioBuffer->wait();
857}
858
859void QDarwinAudioSource::audioDeviceStop()
860{
861 AudioOutputUnitStop(m_audioUnit);
862 m_audioThreadState.storeRelaxed(Stopped);
863 m_audioBuffer->wake();
864}
865
866void QDarwinAudioSource::audioDeviceActive()
867{
868 if (m_stateCode == QAudio::IdleState) {
869 QMutexLocker lock(m_audioBuffer);
870 m_stateCode = QAudio::ActiveState;
871 emit stateChanged(m_stateCode);
872 }
873}
874
875void QDarwinAudioSource::audioDeviceFull()
876{
877 if (m_stateCode == QAudio::ActiveState) {
878 QMutexLocker lock(m_audioBuffer);
879 m_errorCode = QAudio::UnderrunError;
880 m_stateCode = QAudio::IdleState;
881 emit stateChanged(m_stateCode);
882 }
883}
884
885void QDarwinAudioSource::audioDeviceError()
886{
887 if (m_stateCode == QAudio::ActiveState) {
888 QMutexLocker lock(m_audioBuffer);
889 audioDeviceStop();
890
891 m_errorCode = QAudio::IOError;
892 m_stateCode = QAudio::StoppedState;
893 QMetaObject::invokeMethod(this, "deviceStopped", Qt::QueuedConnection);
894 }
895}
896
897void QDarwinAudioSource::startTimers()
898{
899 m_audioBuffer->startFlushTimer();
900}
901
902void QDarwinAudioSource::stopTimers()
903{
904 m_audioBuffer->stopFlushTimer();
905}
906
907OSStatus QDarwinAudioSource::inputCallback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData)
908{
909 Q_UNUSED(ioData);
910
911 QDarwinAudioSource* d = static_cast<QDarwinAudioSource*>(inRefCon);
912
913 const int threadState = d->m_audioThreadState.loadAcquire();
914 if (threadState == Stopped)
915 d->audioDeviceStop();
916 else {
917 qint64 framesWritten;
918
919 {
920 QMutexLocker locker(d->m_audioBuffer);
921 framesWritten = d->m_audioBuffer->renderFromDevice(d->m_audioUnit,
922 ioActionFlags,
923 inTimeStamp,
924 inBusNumber,
925 inNumberFrames);
926 }
927
928 if (framesWritten > 0) {
929 d->m_totalFrames += framesWritten;
930 d->audioDeviceActive();
931 } else if (framesWritten == 0)
932 d->audioDeviceFull();
933 else if (framesWritten < 0)
934 d->audioDeviceError();
935 }
936
937 return noErr;
938}
939
941
942#include "moc_qdarwinaudiosource_p.cpp"
IOBluetoothDevice * device
void releaseWriteRegion(Region const &region)
Region acquireWriteRegion(int size)
Region acquireReadRegion(int size)
void releaseReadRegion(Region const &region)
QPair< char *, int > Region
static CoreAudioSessionManager & instance()
static Q_MULTIMEDIA_EXPORT QAudioFormat toQAudioFormat(const AudioStreamBasicDescription &streamFormat)
static AudioStreamBasicDescription toAudioStreamBasicDescription(QAudioFormat const &audioFormat)
The QAudioDevice class provides an information about audio devices and their functionality.
bool isFormatSupported(const QAudioFormat &format) const
Returns true if the supplied settings are supported by the audio device described by this QAudioDevic...
QAudioFormat preferredFormat() const
Returns the default audio format settings for this device.
The QAudioFormat class stores audio stream parameter information.
constexpr int sampleRate() const noexcept
Returns the current sample rate in Hertz.
void stateChanged(QAudio::State state)
void storeRelaxed(T newValue) noexcept
T loadAcquire() const noexcept
bool testAndSetAcquire(T expectedValue, T newValue) noexcept
QCoreAudioBufferList(AudioStreamBasicDescription const &streamFormat)
AudioBufferList * audioBufferList() const
int frameCount(int buffer=0) const
char * data(int buffer=0) const
qint64 bufferSize(int buffer=0) const
int packetCount(int buffer=0) const
QCoreAudioPacketFeeder(QCoreAudioBufferList *abl)
bool feed(AudioBufferList &dst, UInt32 &packetCount)
qint64 renderFromDevice(AudioUnit audioUnit, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames)
qint64 readBytes(char *data, qint64 len)
QDarwinAudioSourceBuffer(int bufferSize, int maxPeriodSize, AudioStreamBasicDescription const &inputFormat, AudioStreamBasicDescription const &outputFormat, QObject *parent)
void setFlushDevice(QIODevice *device)
QDarwinAudioSourceDevice(QDarwinAudioSourceBuffer *audioBuffer, QObject *parent)
qint64 readData(char *data, qint64 len)
Reads up to maxSize bytes from the device into data, and returns the number of bytes read or -1 if an...
qint64 writeData(const char *data, qint64 len)
Writes up to maxSize bytes from data to the device.
QDarwinAudioSource(const QAudioDevice &device, QObject *parent)
qsizetype bufferSize() const
void setVolume(qreal volume)
qint64 processedUSecs() const
qsizetype bytesReady() const
QAudio::Error error() const
void setBufferSize(qsizetype value)
void setFormat(const QAudioFormat &format)
QAudioFormat format() const
QAudio::State state() const
\inmodule QtCore \reentrant
Definition qiodevice.h:34
virtual bool open(QIODeviceBase::OpenMode mode)
Opens the device and sets its OpenMode to mode.
void readyRead()
This signal is emitted once every time new data is available for reading from the device's current re...
qint64 write(const char *data, qint64 len)
Writes at most maxSize bytes of data from data to the device.
QAudioDevice defaultAudioInput
\qmlproperty audioDevice QtMultimedia::MediaDevices::defaultAudioInput Returns the default audio inpu...
\inmodule QtCore
Definition qmutex.h:313
\inmodule QtCore
Definition qobject.h:103
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2960
\inmodule QtCore
Definition qtimer.h:20
void start(int msec)
Starts or restarts the timer with a timeout interval of msec milliseconds.
Definition qtimer.cpp:241
void stop()
Stops the timer.
Definition qtimer.cpp:267
void qMultiplySamples(qreal factor, const QAudioFormat &format, const void *src, void *dest, int len)
State
Definition qaudio.h:29
@ StoppedState
Definition qaudio.h:29
@ SuspendedState
Definition qaudio.h:29
@ IdleState
Definition qaudio.h:29
@ ActiveState
Definition qaudio.h:29
Error
Definition qaudio.h:28
@ UnderrunError
Definition qaudio.h:28
@ OpenError
Definition qaudio.h:28
@ NoError
Definition qaudio.h:28
@ IOError
Definition qaudio.h:28
Combined button and popup list for selecting options.
@ QueuedConnection
static const size_t packetSize
Definition qctflib.cpp:29
static QT_BEGIN_NAMESPACE const int DEFAULT_BUFFER_SIZE
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
bool qFuzzyCompare(qfloat16 p1, qfloat16 p2) noexcept
Definition qfloat16.h:333
#define qWarning
Definition qlogging.h:166
constexpr const T & qBound(const T &min, const T &val, const T &max)
Definition qminmax.h:44
constexpr const T & qMax(const T &a, const T &b)
Definition qminmax.h:42
#define SLOT(a)
Definition qobjectdefs.h:52
#define Q_ARG(Type, data)
Definition qobjectdefs.h:63
#define SIGNAL(a)
Definition qobjectdefs.h:53
GLenum GLsizei GLuint GLint * bytesWritten
GLsizei const GLfloat * v
[13]
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLbitfield GLuint64 timeout
[4]
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLenum GLuint buffer
GLenum GLenum dst
GLboolean enable
GLint GLsizei GLsizei GLenum format
GLenum GLsizei len
static qreal component(const QPointF &point, unsigned int i)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
@ NoError
Definition main.cpp:34
#define emit
#define Q_UNUSED(x)
ptrdiff_t qsizetype
Definition qtypes.h:165
long long qint64
Definition qtypes.h:60
double qreal
Definition qtypes.h:187
QT_BEGIN_NAMESPACE typedef uchar * output
QObject::connect nullptr
QReadWriteLock lock
[0]
QHostInfo info
[0]
static bool invokeMethod(QObject *obj, const char *member, Qt::ConnectionType, QGenericReturnArgument ret, QGenericArgument val0=QGenericArgument(nullptr), QGenericArgument val1=QGenericArgument(), QGenericArgument val2=QGenericArgument(), QGenericArgument val3=QGenericArgument(), QGenericArgument val4=QGenericArgument(), QGenericArgument val5=QGenericArgument(), QGenericArgument val6=QGenericArgument(), QGenericArgument val7=QGenericArgument(), QGenericArgument val8=QGenericArgument(), QGenericArgument val9=QGenericArgument())
\threadsafe This is an overloaded member function, provided for convenience. It differs from the abov...