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>
30 + (bufferSize % maxPeriodSize == 0 ? 0 : maxPeriodSize - (bufferSize % maxPeriodSize));
35 : m_maxPeriodSize(maxPeriodSize),
36 m_bytesPerFrame(audioFormat.bytesPerFrame()),
37 m_periodTime(maxPeriodSize / m_bytesPerFrame * 1000 / audioFormat.sampleRate()),
41 m_fillTimer =
new QTimer(
this);
43 m_fillTimer->
setInterval(m_buffer->size() / 2 / m_maxPeriodSize * m_periodTime);
54 while (wecan && framesRead < maxFrames) {
57 if (region.second > 0) {
59 region.second -= region.second % m_bytesPerFrame;
61 if (region.second > 0) {
62 memcpy(
data + (framesRead * m_bytesPerFrame), region.first, region.second);
63 framesRead += region.second / m_bytesPerFrame;
70 m_buffer->releaseReadRegion(region);
73 if (framesRead == 0 && m_deviceError)
84 maxSize -= maxSize % m_bytesPerFrame;
88 if (region.second > 0) {
95 m_buffer->releaseWriteRegion(region);
106 return m_buffer->free();
111 return m_deviceAtEnd;
126 m_deviceError =
false;
128 const auto wasFillingEnabled = m_fillingEnabled;
147 m_fillTimer->
start();
151void QDarwinAudioSinkBuffer::fillBuffer()
153 const int free = m_buffer->free();
154 const int writeSize = free - (free % m_maxPeriodSize);
160 while (!m_deviceError && wecan && filled < writeSize) {
163 if (region.second > 0) {
164 region.second = m_device->
read(region.first, region.second);
165 m_deviceAtEnd = m_device->
atEnd();
166 if (region.second > 0)
167 filled += region.second;
168 else if (region.second == 0)
170 else if (region.second < 0) {
173 m_deviceError =
true;
179 m_buffer->releaseWriteRegion(region);
189 , m_audioBuffer(audioBuffer)
213#if defined(Q_OS_MACOS)
216 m_audioDeviceId =
info->deviceID();
244 m_audioBuffer->setPrefetchDevice(
device);
249 m_stateMachine.
start();
264 m_stateMachine.
start(
false);
272 m_audioBuffer->setFillingEnabled(
false);
277 const bool wasDraining = m_stateMachine.
onDrained();
279 qWarning() <<
"Failed wait for getting sink drained; was draining:" << wasDraining;
291 onAudioDeviceDrained();
307 return m_audioBuffer->available();
313 m_internalBufferSize =
value;
318 return m_internalBufferSize;
323 return m_totalFrames * 1000000 / m_audioFormat.
sampleRate();
328 return m_stateMachine.
error();
333 return m_stateMachine.
state();
344 return m_audioFormat;
353#if defined(Q_OS_MACOS)
355 if (AudioUnitSetParameter(m_audioUnit,
356 kHALOutputParam_Volume,
357 kAudioUnitScope_Global,
361 m_volume = m_cachedVolume;
367 return m_cachedVolume;
370void QDarwinAudioSink::inputReady()
375OSStatus QDarwinAudioSink::renderCallback(
void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData)
386 if (drained && stopped) {
387 ioData->mBuffers[0].mDataByteSize = 0;
389 const UInt32 bytesPerFrame =
d->m_streamFormat.mBytesPerFrame;
392 Q_ASSERT(ioData->mBuffers[0].mDataByteSize / bytesPerFrame == inNumberFrames);
393 framesRead =
d->m_audioBuffer->readFrames((
char*)ioData->mBuffers[0].mData,
394 ioData->mBuffers[0].mDataByteSize / bytesPerFrame);
396 if (framesRead > 0) {
397 ioData->mBuffers[0].mDataByteSize = framesRead * bytesPerFrame;
398 d->m_totalFrames += framesRead;
400#if defined(Q_OS_MACOS)
402 qreal oldVolume =
d->m_cachedVolume;
404 d->setVolume(
d->m_volume / 2);
405 d->m_cachedVolume = oldVolume;
407#elif defined(Q_OS_IOS) || defined(Q_OS_TVOS)
412 ioData->mBuffers[0].mData,
413 ioData->mBuffers[0].mData,
414 ioData->mBuffers[0].mDataByteSize);
420 ioData->mBuffers[0].mDataByteSize = 0;
422 d->onAudioDeviceIdle();
424 d->onAudioDeviceError();
431bool QDarwinAudioSink::open()
448 AudioComponentDescription componentDescription;
449 componentDescription.componentType = kAudioUnitType_Output;
450#if defined(Q_OS_MACOS)
451 componentDescription.componentSubType = kAudioUnitSubType_HALOutput;
453 componentDescription.componentSubType = kAudioUnitSubType_RemoteIO;
455 componentDescription.componentManufacturer = kAudioUnitManufacturer_Apple;
456 componentDescription.componentFlags = 0;
457 componentDescription.componentFlagsMask = 0;
459 AudioComponent
component = AudioComponentFindNext(0, &componentDescription);
461 qWarning() <<
"QAudioOutput: Failed to find Output component";
465 if (AudioComponentInstanceNew(
component, &m_audioUnit) != noErr) {
466 qWarning() <<
"QAudioOutput: Unable to Open Output Component";
471 AURenderCallbackStruct callback;
472 callback.inputProc = renderCallback;
473 callback.inputProcRefCon =
this;
475 if (AudioUnitSetProperty(m_audioUnit,
476 kAudioUnitProperty_SetRenderCallback,
477 kAudioUnitScope_Global,
480 sizeof(callback)) != noErr) {
481 qWarning() <<
"QAudioOutput: Failed to set AudioUnit callback";
485#if defined(Q_OS_MACOS)
487 if (AudioUnitSetProperty(m_audioUnit,
488 kAudioOutputUnitProperty_CurrentDevice,
489 kAudioUnitScope_Global,
492 sizeof(m_audioDeviceId)) != noErr) {
493 qWarning() <<
"QAudioOutput: Unable to use configured device";
502 size =
sizeof(m_streamFormat);
504 if (AudioUnitSetProperty(m_audioUnit,
505 kAudioUnitProperty_StreamFormat,
506 kAudioUnitScope_Input,
510 qWarning() <<
"QAudioOutput: Unable to Set Stream information";
515 UInt32 numberOfFrames = 0;
516#if defined(Q_OS_MACOS)
517 size =
sizeof(UInt32);
518 if (AudioUnitGetProperty(m_audioUnit,
519 kAudioDevicePropertyBufferFrameSize,
520 kAudioUnitScope_Global,
524 qWarning() <<
"QAudioSource: Failed to get audio period size";
533 m_periodSizeBytes = numberOfFrames * m_streamFormat.mBytesPerFrame;
534 if (m_internalBufferSize < m_periodSizeBytes * 2)
535 m_internalBufferSize = m_periodSizeBytes * 2;
537 m_internalBufferSize -= m_internalBufferSize % m_streamFormat.mBytesPerFrame;
539 m_audioBuffer = std::make_unique<QDarwinAudioSinkBuffer>(m_internalBufferSize,
540 m_periodSizeBytes, m_audioFormat);
542 &QDarwinAudioSink::inputReady);
547 if (AudioUnitInitialize(m_audioUnit)) {
548 qWarning() <<
"QAudioOutput: Failed to initialize AudioUnit";
559void QDarwinAudioSink::close()
561 if (m_audioUnit != 0) {
562 m_stateMachine.
stop();
564 AudioUnitUninitialize(m_audioUnit);
565 AudioComponentInstanceDispose(m_audioUnit);
569 m_audioBuffer.reset();
572void QDarwinAudioSink::onAudioDeviceIdle()
574 const bool atEnd = m_audioBuffer->deviceAtEnd();
576 onAudioDeviceDrained();
579void QDarwinAudioSink::onAudioDeviceDrained()
585void QDarwinAudioSink::onAudioDeviceError()
590void QDarwinAudioSink::updateAudioDevice()
598 m_audioBuffer->reset();
603 if (std::exchange(m_audioUnitStarted, unitStarted) != unitStarted)
604 (unitStarted ? AudioOutputUnitStart : AudioOutputUnitStop)(m_audioUnit);
609#include "moc_qdarwinaudiosink_p.cpp"
DarwinBluetooth::LECBManagerNotifier * notifier
IOBluetoothDevice * device
QPair< char *, int > Region
static CoreAudioSessionManager & instance()
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...
void stateChanged(QAudio::State state)
QAudio::Error error() const
Notifier start(bool isActive=true)
Notifier updateActiveOrIdle(bool isActive, QAudio::Error error=QAudio::NoError)
QAudio::State state() const
Notifier stopOrUpdateError(QAudio::Error error=QAudio::NoError)
Notifier setError(QAudio::Error error)
Notifier activateFromIdle()
Notifier stop(QAudio::Error error=QAudio::NoError, bool shouldDrain=false, bool forceUpdateError=false)
std::pair< bool, bool > getDrainedAndStopped() const
void setFillingEnabled(bool enabled)
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)
QIODevice * prefetchDevice() const
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.
virtual bool atEnd() const
Returns true if the current read and write position is at the end of the device (i....
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 acquire(int n=1)
Tries to acquire n resources guarded by the semaphore.
bool tryAcquire(int n=1)
Tries to acquire n resources guarded by the semaphore and returns true on success.
void release(int n=1)
Releases n resources guarded by the semaphore.
void start(int msec)
Starts or restarts the timer with a timeout interval of msec milliseconds.
void setInterval(int msec)
void stop()
Stops the timer.
void timeout(QPrivateSignal)
This signal is emitted when the timer times out.
void setTimerType(Qt::TimerType atype)
void qMultiplySamples(qreal factor, const QAudioFormat &format, const void *src, void *dest, int len)
Combined button and popup list for selecting options.
static QT_BEGIN_NAMESPACE int audioRingBufferSize(int bufferSize, int maxPeriodSize)
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]
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLenum GLenum GLsizei const GLuint GLboolean enabled
GLint GLsizei GLsizei GLenum format
static qreal component(const QPointF &point, unsigned int i)