10#include <QtCore/QDataStream>
11#include <QtCore/QTimer>
12#include <QtCore/QDebug>
14#include <AudioUnit/AudioUnit.h>
15#include <AudioToolbox/AudioToolbox.h>
16#if defined(Q_OS_MACOS)
17# include <AudioUnit/AudioComponent.h>
20#if defined(Q_OS_IOS) || defined(Q_OS_TVOS)
21# include <QtMultimedia/private/qaudiohelpers_p.h>
27 : m_deviceError(
false)
28 , m_maxPeriodSize(maxPeriodSize)
31 m_buffer =
new CoreAudioRingBuffer(bufferSize + (bufferSize % maxPeriodSize == 0 ? 0 : maxPeriodSize - (bufferSize % maxPeriodSize)));
33 m_periodTime = m_maxPeriodSize / m_bytesPerFrame * 1000 / audioFormat.
sampleRate();
35 m_fillTimer =
new QTimer(
this);
49 while (wecan && framesRead < maxFrames) {
52 if (region.second > 0) {
54 region.second -= region.second % m_bytesPerFrame;
56 if (region.second > 0) {
57 memcpy(
data + (framesRead * m_bytesPerFrame), region.first, region.second);
58 framesRead += region.second / m_bytesPerFrame;
68 if (framesRead == 0 && m_deviceError)
79 maxSize -= maxSize % m_bytesPerFrame;
83 if (region.second > 0) {
101 return m_buffer->
free();
108 m_deviceError =
false;
123 m_fillTimer->
start(m_buffer->
size() / 2 / m_maxPeriodSize * m_periodTime);
131void QDarwinAudioSinkBuffer::fillBuffer()
133 const int free = m_buffer->
free();
134 const int writeSize = free - (free % m_maxPeriodSize);
140 while (!m_deviceError && wecan && filled < writeSize) {
143 if (region.second > 0) {
144 region.second = m_device->
read(region.first, region.second);
145 if (region.second > 0)
146 filled += region.second;
147 else if (region.second == 0)
149 else if (region.second < 0) {
152 m_deviceError =
true;
168 , m_audioBuffer(audioBuffer)
192#if defined(Q_OS_MACOS)
195 m_audioDeviceId =
info->deviceID();
219 m_audioBuffer->
reset();
249 m_audioBuffer->
reset();
295 m_suspendedInStateCode = m_stateCode;
308 m_stateCode = m_suspendedInStateCode;
321 m_internalBufferSize =
value;
326 return m_internalBufferSize;
331 return m_totalFrames * 1000000 / m_audioFormat.
sampleRate();
352 return m_audioFormat;
361#if defined(Q_OS_MACOS)
363 if (AudioUnitSetParameter(m_audioUnit,
364 kHALOutputParam_Volume,
365 kAudioUnitScope_Global,
369 m_volume = m_cachedVolume;
375 return m_cachedVolume;
378void QDarwinAudioSink::deviceStopped()
383void QDarwinAudioSink::inputReady()
396OSStatus QDarwinAudioSink::renderCallback(
void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData)
405 const int threadState =
d->m_audioThreadState.fetchAndAddAcquire(0);
406 if (threadState == Stopped) {
407 ioData->mBuffers[0].mDataByteSize = 0;
408 d->audioDeviceStop();
411 const UInt32 bytesPerFrame =
d->m_streamFormat.mBytesPerFrame;
414 framesRead =
d->m_audioBuffer->readFrames((
char*)ioData->mBuffers[0].mData,
415 ioData->mBuffers[0].mDataByteSize / bytesPerFrame);
417 if (framesRead > 0) {
418 ioData->mBuffers[0].mDataByteSize = framesRead * bytesPerFrame;
419 d->m_totalFrames += framesRead;
421#if defined(Q_OS_MACOS)
423 if (threadState != Running) {
424 qreal oldVolume =
d->m_cachedVolume;
426 d->setVolume(
d->m_volume / 2);
427 d->m_cachedVolume = oldVolume;
429#elif defined(Q_OS_IOS) || defined(Q_OS_TVOS)
434 ioData->mBuffers[0].mData,
435 ioData->mBuffers[0].mData,
436 ioData->mBuffers[0].mDataByteSize);
442 ioData->mBuffers[0].mDataByteSize = 0;
443 if (framesRead == 0) {
444 if (threadState == Draining)
445 d->audioDeviceStop();
447 d->audioDeviceIdle();
450 d->audioDeviceError();
457bool QDarwinAudioSink::open()
474 AudioComponentDescription componentDescription;
475 componentDescription.componentType = kAudioUnitType_Output;
476#if defined(Q_OS_MACOS)
477 componentDescription.componentSubType = kAudioUnitSubType_HALOutput;
479 componentDescription.componentSubType = kAudioUnitSubType_RemoteIO;
481 componentDescription.componentManufacturer = kAudioUnitManufacturer_Apple;
482 componentDescription.componentFlags = 0;
483 componentDescription.componentFlagsMask = 0;
485 AudioComponent
component = AudioComponentFindNext(0, &componentDescription);
487 qWarning() <<
"QAudioOutput: Failed to find Output component";
491 if (AudioComponentInstanceNew(
component, &m_audioUnit) != noErr) {
492 qWarning() <<
"QAudioOutput: Unable to Open Output Component";
497 AURenderCallbackStruct callback;
498 callback.inputProc = renderCallback;
499 callback.inputProcRefCon =
this;
501 if (AudioUnitSetProperty(m_audioUnit,
502 kAudioUnitProperty_SetRenderCallback,
503 kAudioUnitScope_Global,
506 sizeof(callback)) != noErr) {
507 qWarning() <<
"QAudioOutput: Failed to set AudioUnit callback";
511#if defined(Q_OS_MACOS)
513 if (AudioUnitSetProperty(m_audioUnit,
514 kAudioOutputUnitProperty_CurrentDevice,
515 kAudioUnitScope_Global,
518 sizeof(m_audioDeviceId)) != noErr) {
519 qWarning() <<
"QAudioOutput: Unable to use configured device";
528 size =
sizeof(m_streamFormat);
530 if (AudioUnitSetProperty(m_audioUnit,
531 kAudioUnitProperty_StreamFormat,
532 kAudioUnitScope_Input,
536 qWarning() <<
"QAudioOutput: Unable to Set Stream information";
541 UInt32 numberOfFrames = 0;
542#if defined(Q_OS_MACOS)
543 size =
sizeof(UInt32);
544 if (AudioUnitGetProperty(m_audioUnit,
545 kAudioDevicePropertyBufferFrameSize,
546 kAudioUnitScope_Global,
550 qWarning() <<
"QAudioSource: Failed to get audio period size";
559 m_periodSizeBytes = numberOfFrames * m_streamFormat.mBytesPerFrame;
560 if (m_internalBufferSize < m_periodSizeBytes * 2)
561 m_internalBufferSize = m_periodSizeBytes * 2;
563 m_internalBufferSize -= m_internalBufferSize % m_streamFormat.mBytesPerFrame;
571 if (AudioUnitInitialize(m_audioUnit)) {
572 qWarning() <<
"QAudioOutput: Failed to initialize AudioUnit";
583void QDarwinAudioSink::close()
585 if (m_audioUnit != 0) {
587 AudioUnitUninitialize(m_audioUnit);
588 AudioComponentInstanceDispose(m_audioUnit);
591 delete m_audioBuffer;
594void QDarwinAudioSink::audioThreadStart()
600void QDarwinAudioSink::audioThreadStop()
610void QDarwinAudioSink::audioThreadDrain()
617 constexpr int MaxDrainWaitingTime = 500;
619 m_threadFinished.
wait(&m_mutex, MaxDrainWaitingTime);
622 qWarning() <<
"Couldn't wait for draining; force stop";
624 AudioOutputUnitStop(m_audioUnit);
629void QDarwinAudioSink::audioDeviceStart()
634 if (
state == Stopped) {
636 AudioOutputUnitStart(m_audioUnit);
638 qWarning() <<
"Unexpected state on audio device start:" <<
state;
642void QDarwinAudioSink::audioDeviceStop()
647 AudioOutputUnitStop(m_audioUnit);
654void QDarwinAudioSink::audioDeviceIdle()
667void QDarwinAudioSink::audioDeviceError()
680void QDarwinAudioSink::startTimers()
685void QDarwinAudioSink::stopTimers()
692#include "moc_qdarwinaudiosink_p.cpp"
IOBluetoothDevice * device
void releaseWriteRegion(Region const ®ion)
Region acquireWriteRegion(int size)
Region acquireReadRegion(int size)
void releaseReadRegion(Region const ®ion)
QPair< char *, int > Region
bool setCategory(AudioSessionCategorys category, AudioSessionCategoryOptions options=None)
bool setActive(bool active)
static CoreAudioSessionManager & instance()
float currentIOBufferDuration()
static AudioStreamBasicDescription toAudioStreamBasicDescription(QAudioFormat const &audioFormat)
static double frequency()
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...
QByteArray id
\qmlproperty string QtMultimedia::audioDevice::id
const QAudioDevicePrivate * handle() const
bool isNull() const
Returns whether this QAudioDevice object holds a valid device definition.
void storeRelaxed(T newValue) noexcept
T loadAcquire() const noexcept
bool testAndSetAcquire(T expectedValue, T newValue) noexcept
T fetchAndStoreRelaxed(T newValue) noexcept
qint64 readFrames(char *data, qint64 maxFrames)
~QDarwinAudioSinkBuffer()
qint64 writeBytes(const char *data, qint64 maxSize)
void setPrefetchDevice(QIODevice *device)
QDarwinAudioSinkBuffer(int bufferSize, int maxPeriodSize, QAudioFormat const &audioFormat)
QDarwinAudioSinkDevice(QDarwinAudioSinkBuffer *audioBuffer, QObject *parent)
qint64 writeData(const char *data, qint64 len)
Writes up to maxSize bytes from data to the device.
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...
void setFormat(const QAudioFormat &format)
qint64 processedUSecs() const
QAudioFormat format() const
QDarwinAudioSink(const QAudioDevice &device, QObject *parent)
void setBufferSize(qsizetype value)
QAudio::State state() const
QAudio::Error error() const
void setVolume(qreal volume)
qsizetype bytesFree() const
qsizetype bufferSize() const
\inmodule QtCore \reentrant
virtual bool open(QIODeviceBase::OpenMode mode)
Opens the device and sets its OpenMode to mode.
qint64 read(char *data, qint64 maxlen)
Reads at most maxSize bytes from the device into data, and returns the number of bytes read.
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
void start(int msec)
Starts or restarts the timer with a timeout interval of msec milliseconds.
void stop()
Stops the timer.
bool wait(QMutex *, QDeadlineTimer=QDeadlineTimer(QDeadlineTimer::Forever))
void qMultiplySamples(qreal factor, const QAudioFormat &format, const void *src, void *dest, int len)
State
\value ActiveState Audio data is being processed, this state is set after start() is called and while...
Error
\value NoError No errors have occurred \value OpenError An error occurred opening the audio device \v...
Combined button and popup list for selecting options.
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
bool qFuzzyCompare(qfloat16 p1, qfloat16 p2) noexcept
constexpr const T & qBound(const T &min, const T &val, const T &max)
GLenum GLsizei GLuint GLint * bytesWritten
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLbitfield GLuint64 timeout
[4]
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLint GLsizei GLsizei GLenum format
static qreal component(const QPointF &point, unsigned int i)
QFileInfo info(fileName)
[8]
IUIAutomationTreeWalker __RPC__deref_out_opt IUIAutomationElement ** parent