Qt 4.8
qaudioinput_mac_p.cpp
Go to the documentation of this file.
1 /****************************************************************************
2 **
3 ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
5 **
6 ** This file is part of the QtMultimedia module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia. For licensing terms and
14 ** conditions see http://qt.digia.com/licensing. For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights. These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file. Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 
42 //
43 // W A R N I N G
44 // -------------
45 //
46 // This file is not part of the Qt API. It exists for the convenience
47 // of other Qt classes. This header file may change from version to
48 // version without notice, or even be removed.
49 //
50 // We mean it.
51 //
52 
53 #include <QtCore/qendian.h>
54 #include <QtCore/qtimer.h>
55 #include <QtCore/qdebug.h>
56 
57 #include <QtMultimedia/qaudioinput.h>
58 
59 #include "qaudio_mac_p.h"
60 #include "qaudioinput_mac_p.h"
61 #include "qaudiodeviceinfo_mac_p.h"
62 
64 
65 
67 {
68 
69 static const int default_buffer_size = 4 * 1024;
70 
72 {
73 public:
74  QAudioBufferList(AudioStreamBasicDescription const& streamFormat):
75  owner(false),
76  sf(streamFormat)
77  {
78  const bool isInterleaved = (sf.mFormatFlags & kAudioFormatFlagIsNonInterleaved) == 0;
79  const int numberOfBuffers = isInterleaved ? 1 : sf.mChannelsPerFrame;
80 
81  dataSize = 0;
82 
83  bfs = reinterpret_cast<AudioBufferList*>(qMalloc(sizeof(AudioBufferList) +
84  (sizeof(AudioBuffer) * numberOfBuffers)));
85 
86  bfs->mNumberBuffers = numberOfBuffers;
87  for (int i = 0; i < numberOfBuffers; ++i) {
88  bfs->mBuffers[i].mNumberChannels = isInterleaved ? numberOfBuffers : 1;
89  bfs->mBuffers[i].mDataByteSize = 0;
90  bfs->mBuffers[i].mData = 0;
91  }
92  }
93 
94  QAudioBufferList(AudioStreamBasicDescription const& streamFormat, char* buffer, int bufferSize):
95  owner(false),
96  sf(streamFormat),
97  bfs(0)
98  {
100 
101  bfs = reinterpret_cast<AudioBufferList*>(qMalloc(sizeof(AudioBufferList) + sizeof(AudioBuffer)));
102 
103  bfs->mNumberBuffers = 1;
104  bfs->mBuffers[0].mNumberChannels = 1;
105  bfs->mBuffers[0].mDataByteSize = dataSize;
106  bfs->mBuffers[0].mData = buffer;
107  }
108 
109  QAudioBufferList(AudioStreamBasicDescription const& streamFormat, int framesToBuffer):
110  owner(true),
111  sf(streamFormat),
112  bfs(0)
113  {
114  const bool isInterleaved = (sf.mFormatFlags & kAudioFormatFlagIsNonInterleaved) == 0;
115  const int numberOfBuffers = isInterleaved ? 1 : sf.mChannelsPerFrame;
116 
117  dataSize = framesToBuffer * sf.mBytesPerFrame;
118 
119  bfs = reinterpret_cast<AudioBufferList*>(qMalloc(sizeof(AudioBufferList) +
120  (sizeof(AudioBuffer) * numberOfBuffers)));
121  bfs->mNumberBuffers = numberOfBuffers;
122  for (int i = 0; i < numberOfBuffers; ++i) {
123  bfs->mBuffers[i].mNumberChannels = isInterleaved ? numberOfBuffers : 1;
124  bfs->mBuffers[i].mDataByteSize = dataSize;
125  bfs->mBuffers[i].mData = qMalloc(dataSize);
126  }
127  }
128 
130  {
131  if (owner) {
132  for (UInt32 i = 0; i < bfs->mNumberBuffers; ++i)
133  qFree(bfs->mBuffers[i].mData);
134  }
135 
136  qFree(bfs);
137  }
138 
139  AudioBufferList* audioBufferList() const
140  {
141  return bfs;
142  }
143 
144  char* data(int buffer = 0) const
145  {
146  return static_cast<char*>(bfs->mBuffers[buffer].mData);
147  }
148 
149  qint64 bufferSize(int buffer = 0) const
150  {
151  return bfs->mBuffers[buffer].mDataByteSize;
152  }
153 
154  int frameCount(int buffer = 0) const
155  {
156  return bfs->mBuffers[buffer].mDataByteSize / sf.mBytesPerFrame;
157  }
158 
159  int packetCount(int buffer = 0) const
160  {
161  return bfs->mBuffers[buffer].mDataByteSize / sf.mBytesPerPacket;
162  }
163 
164  int packetSize() const
165  {
166  return sf.mBytesPerPacket;
167  }
168 
169  void reset()
170  {
171  for (UInt32 i = 0; i < bfs->mNumberBuffers; ++i) {
172  bfs->mBuffers[i].mDataByteSize = dataSize;
173  bfs->mBuffers[i].mData = 0;
174  }
175  }
176 
177 private:
178  bool owner;
179  int dataSize;
180  AudioStreamBasicDescription sf;
181  AudioBufferList* bfs;
182 };
183 
185 {
186 public:
188  audioBufferList(abl)
189  {
190  totalPackets = audioBufferList->packetCount();
191  position = 0;
192  }
193 
194  bool feed(AudioBufferList& dst, UInt32& packetCount)
195  {
196  if (position == totalPackets) {
197  dst.mBuffers[0].mDataByteSize = 0;
198  packetCount = 0;
199  return false;
200  }
201 
202  if (totalPackets - position < packetCount)
203  packetCount = totalPackets - position;
204 
205  dst.mBuffers[0].mDataByteSize = packetCount * audioBufferList->packetSize();
206  dst.mBuffers[0].mData = audioBufferList->data() + (position * audioBufferList->packetSize());
207 
208  position += packetCount;
209 
210  return true;
211  }
212 
213  bool empty() const
214  {
215  return position == totalPackets;
216  }
217 
218 private:
219  UInt32 totalPackets;
220  UInt32 position;
222 };
223 
225 {
226  Q_OBJECT
227 
228 public:
230  int maxPeriodSize,
231  AudioStreamBasicDescription const& inputFormat,
232  AudioStreamBasicDescription const& outputFormat,
233  QObject* parent):
234  QObject(parent),
235  m_deviceError(false),
236  m_audioConverter(0),
237  m_inputFormat(inputFormat),
238  m_outputFormat(outputFormat)
239  {
240  m_maxPeriodSize = maxPeriodSize;
241  m_periodTime = m_maxPeriodSize / m_outputFormat.mBytesPerFrame * 1000 / m_outputFormat.mSampleRate;
242  m_buffer = new QAudioRingBuffer(bufferSize + (bufferSize % maxPeriodSize == 0 ? 0 : maxPeriodSize - (bufferSize % maxPeriodSize)));
243  m_inputBufferList = new QAudioBufferList(m_inputFormat);
244 
245  m_flushTimer = new QTimer(this);
246  connect(m_flushTimer, SIGNAL(timeout()), SLOT(flushBuffer()));
247 
248  if (toQAudioFormat(inputFormat) != toQAudioFormat(outputFormat)) {
249  if (AudioConverterNew(&m_inputFormat, &m_outputFormat, &m_audioConverter) != noErr) {
250  qWarning() << "QAudioInput: Unable to create an Audio Converter";
251  m_audioConverter = 0;
252  }
253  }
254  }
255 
257  {
258  delete m_buffer;
259  }
260 
261  qint64 renderFromDevice(AudioUnit audioUnit,
262  AudioUnitRenderActionFlags* ioActionFlags,
263  const AudioTimeStamp* inTimeStamp,
264  UInt32 inBusNumber,
265  UInt32 inNumberFrames)
266  {
267  const bool pullMode = m_device == 0;
268 
269  OSStatus err;
270  qint64 framesRendered = 0;
271 
272  m_inputBufferList->reset();
273  err = AudioUnitRender(audioUnit,
274  ioActionFlags,
275  inTimeStamp,
276  inBusNumber,
277  inNumberFrames,
278  m_inputBufferList->audioBufferList());
279 
280  if (m_audioConverter != 0) {
281  QAudioPacketFeeder feeder(m_inputBufferList);
282 
283  int copied = 0;
284  const int available = m_buffer->free();
285 
286  while (err == noErr && !feeder.empty()) {
287  QAudioRingBuffer::Region region = m_buffer->acquireWriteRegion(available);
288 
289  if (region.second == 0)
290  break;
291 
292  AudioBufferList output;
293  output.mNumberBuffers = 1;
294  output.mBuffers[0].mNumberChannels = 1;
295  output.mBuffers[0].mDataByteSize = region.second;
296  output.mBuffers[0].mData = region.first;
297 
298  UInt32 packetSize = region.second / m_outputFormat.mBytesPerPacket;
299  err = AudioConverterFillComplexBuffer(m_audioConverter,
300  converterCallback,
301  &feeder,
302  &packetSize,
303  &output,
304  0);
305  region.second = output.mBuffers[0].mDataByteSize;
306  copied += region.second;
307 
308  m_buffer->releaseWriteRegion(region);
309  }
310 
311  framesRendered += copied / m_outputFormat.mBytesPerFrame;
312  }
313  else {
314  const int available = m_inputBufferList->bufferSize();
315  bool wecan = true;
316  int copied = 0;
317 
318  while (wecan && copied < available) {
319  QAudioRingBuffer::Region region = m_buffer->acquireWriteRegion(available - copied);
320 
321  if (region.second > 0) {
322  memcpy(region.first, m_inputBufferList->data() + copied, region.second);
323  copied += region.second;
324  }
325  else
326  wecan = false;
327 
328  m_buffer->releaseWriteRegion(region);
329  }
330 
331  framesRendered = copied / m_outputFormat.mBytesPerFrame;
332  }
333 
334  if (pullMode && framesRendered > 0)
335  emit readyRead();
336 
337  return framesRendered;
338  }
339 
341  {
342  bool wecan = true;
343  qint64 bytesCopied = 0;
344 
345  len -= len % m_maxPeriodSize;
346  while (wecan && bytesCopied < len) {
347  QAudioRingBuffer::Region region = m_buffer->acquireReadRegion(len - bytesCopied);
348 
349  if (region.second > 0) {
350  memcpy(data + bytesCopied, region.first, region.second);
351  bytesCopied += region.second;
352  }
353  else
354  wecan = false;
355 
356  m_buffer->releaseReadRegion(region);
357  }
358 
359  return bytesCopied;
360  }
361 
362  void setFlushDevice(QIODevice* device)
363  {
364  if (m_device != device)
365  m_device = device;
366  }
367 
369  {
370  if (m_device != 0) {
371  m_flushTimer->start((m_buffer->size() - (m_maxPeriodSize * 2)) / m_maxPeriodSize * m_periodTime);
372  }
373  }
374 
376  {
377  m_flushTimer->stop();
378  }
379 
380  void flush(bool all = false)
381  {
382  if (m_device == 0)
383  return;
384 
385  const int used = m_buffer->used();
386  const int readSize = all ? used : used - (used % m_maxPeriodSize);
387 
388  if (readSize > 0) {
389  bool wecan = true;
390  int flushed = 0;
391 
392  while (!m_deviceError && wecan && flushed < readSize) {
393  QAudioRingBuffer::Region region = m_buffer->acquireReadRegion(readSize - flushed);
394 
395  if (region.second > 0) {
396  int bytesWritten = m_device->write(region.first, region.second);
397  if (bytesWritten < 0) {
398  stopFlushTimer();
399  m_deviceError = true;
400  }
401  else {
402  region.second = bytesWritten;
403  flushed += bytesWritten;
404  wecan = bytesWritten != 0;
405  }
406  }
407  else
408  wecan = false;
409 
410  m_buffer->releaseReadRegion(region);
411  }
412  }
413  }
414 
415  void reset()
416  {
417  m_buffer->reset();
418  m_deviceError = false;
419  }
420 
421  int available() const
422  {
423  return m_buffer->free();
424  }
425 
426  int used() const
427  {
428  return m_buffer->used();
429  }
430 
431 signals:
432  void readyRead();
433 
434 private slots:
435  void flushBuffer()
436  {
437  flush();
438  }
439 
440 private:
448  AudioConverterRef m_audioConverter;
449  AudioStreamBasicDescription m_inputFormat;
450  AudioStreamBasicDescription m_outputFormat;
451 
452  const static OSStatus as_empty = 'qtem';
453 
454  // Converter callback
455  static OSStatus converterCallback(AudioConverterRef inAudioConverter,
456  UInt32* ioNumberDataPackets,
457  AudioBufferList* ioData,
458  AudioStreamPacketDescription** outDataPacketDescription,
459  void* inUserData)
460  {
461  Q_UNUSED(inAudioConverter);
462  Q_UNUSED(outDataPacketDescription);
463 
464  QAudioPacketFeeder* feeder = static_cast<QAudioPacketFeeder*>(inUserData);
465 
466  if (!feeder->feed(*ioData, *ioNumberDataPackets))
467  return as_empty;
468 
469  return noErr;
470  }
471 };
472 
473 
474 class MacInputDevice : public QIODevice
475 {
476  Q_OBJECT
477 
478 public:
479  MacInputDevice(QAudioInputBuffer* audioBuffer, QObject* parent):
480  QIODevice(parent),
481  m_audioBuffer(audioBuffer)
482  {
484  connect(m_audioBuffer, SIGNAL(readyRead()), SIGNAL(readyRead()));
485  }
486 
488  {
489  return m_audioBuffer->readBytes(data, len);
490  }
491 
492  qint64 writeData(const char* data, qint64 len)
493  {
494  Q_UNUSED(data);
495  Q_UNUSED(len);
496 
497  return 0;
498  }
499 
500  bool isSequential() const
501  {
502  return true;
503  }
504 
505 private:
507 };
508 
509 }
510 
511 
513  audioFormat(format)
514 {
515  QDataStream ds(device);
516  quint32 did, mode;
517 
518  ds >> did >> mode;
519 
520  if (QAudio::Mode(mode) == QAudio::AudioOutput)
521  errorCode = QAudio::OpenError;
522  else {
523  audioDeviceInfo = new QAudioDeviceInfoInternal(device, QAudio::AudioInput);
524  isOpen = false;
525  audioDeviceId = AudioDeviceID(did);
526  audioUnit = 0;
527  startTime = 0;
528  totalFrames = 0;
529  audioBuffer = 0;
530  internalBufferSize = QtMultimediaInternal::default_buffer_size;
531  clockFrequency = AudioGetHostClockFrequency() / 1000;
532  errorCode = QAudio::NoError;
533  stateCode = QAudio::StoppedState;
534 
535  intervalTimer = new QTimer(this);
536  intervalTimer->setInterval(1000);
537  connect(intervalTimer, SIGNAL(timeout()), SIGNAL(notify()));
538  }
539 }
540 
542 {
543  close();
544  delete audioDeviceInfo;
545 }
546 
548 {
549  UInt32 size = 0;
550 
551  if (isOpen)
552  return true;
553 
554  ComponentDescription cd;
555  cd.componentType = kAudioUnitType_Output;
556  cd.componentSubType = kAudioUnitSubType_HALOutput;
557  cd.componentManufacturer = kAudioUnitManufacturer_Apple;
558  cd.componentFlags = 0;
559  cd.componentFlagsMask = 0;
560 
561  // Open
562  Component cp = FindNextComponent(NULL, &cd);
563  if (cp == 0) {
564  qWarning() << "QAudioInput: Failed to find HAL Output component";
565  return false;
566  }
567 
568  if (OpenAComponent(cp, &audioUnit) != noErr) {
569  qWarning() << "QAudioInput: Unable to Open Output Component";
570  return false;
571  }
572 
573  // Set mode
574  // switch to input mode
575  UInt32 enable = 1;
576  if (AudioUnitSetProperty(audioUnit,
577  kAudioOutputUnitProperty_EnableIO,
578  kAudioUnitScope_Input,
579  1,
580  &enable,
581  sizeof(enable)) != noErr) {
582  qWarning() << "QAudioInput: Unable to switch to input mode (Enable Input)";
583  return false;
584  }
585 
586  enable = 0;
587  if (AudioUnitSetProperty(audioUnit,
588  kAudioOutputUnitProperty_EnableIO,
589  kAudioUnitScope_Output,
590  0,
591  &enable,
592  sizeof(enable)) != noErr) {
593  qWarning() << "QAudioInput: Unable to switch to input mode (Disable output)";
594  return false;
595  }
596 
597  // register callback
598  AURenderCallbackStruct cb;
599  cb.inputProc = inputCallback;
600  cb.inputProcRefCon = this;
601 
602  if (AudioUnitSetProperty(audioUnit,
603  kAudioOutputUnitProperty_SetInputCallback,
604  kAudioUnitScope_Global,
605  0,
606  &cb,
607  sizeof(cb)) != noErr) {
608  qWarning() << "QAudioInput: Failed to set AudioUnit callback";
609  return false;
610  }
611 
612  // Set Audio Device
613  if (AudioUnitSetProperty(audioUnit,
614  kAudioOutputUnitProperty_CurrentDevice,
615  kAudioUnitScope_Global,
616  0,
617  &audioDeviceId,
618  sizeof(audioDeviceId)) != noErr) {
619  qWarning() << "QAudioInput: Unable to use configured device";
620  return false;
621  }
622 
623  // Set format
624  // Wanted
625  streamFormat = toAudioStreamBasicDescription(audioFormat);
626 
627  // Required on unit
628  if (audioFormat == audioDeviceInfo->preferredFormat()) {
629  deviceFormat = streamFormat;
630  AudioUnitSetProperty(audioUnit,
631  kAudioUnitProperty_StreamFormat,
632  kAudioUnitScope_Output,
633  1,
634  &deviceFormat,
635  sizeof(deviceFormat));
636  }
637  else {
638  size = sizeof(deviceFormat);
639  if (AudioUnitGetProperty(audioUnit,
640  kAudioUnitProperty_StreamFormat,
641  kAudioUnitScope_Input,
642  1,
643  &deviceFormat,
644  &size) != noErr) {
645  qWarning() << "QAudioInput: Unable to retrieve device format";
646  return false;
647  }
648 
649  if (AudioUnitSetProperty(audioUnit,
650  kAudioUnitProperty_StreamFormat,
651  kAudioUnitScope_Output,
652  1,
653  &deviceFormat,
654  sizeof(deviceFormat)) != noErr) {
655  qWarning() << "QAudioInput: Unable to set device format";
656  return false;
657  }
658  }
659 
660  // Setup buffers
661  UInt32 numberOfFrames;
662  size = sizeof(UInt32);
663  if (AudioUnitGetProperty(audioUnit,
664  kAudioDevicePropertyBufferFrameSize,
665  kAudioUnitScope_Global,
666  0,
667  &numberOfFrames,
668  &size) != noErr) {
669  qWarning() << "QAudioInput: Failed to get audio period size";
670  return false;
671  }
672 
673  // Allocate buffer
674  periodSizeBytes = numberOfFrames * streamFormat.mBytesPerFrame;
675 
676  if (internalBufferSize < periodSizeBytes * 2)
677  internalBufferSize = periodSizeBytes * 2;
678  else
679  internalBufferSize -= internalBufferSize % streamFormat.mBytesPerFrame;
680 
681  audioBuffer = new QtMultimediaInternal::QAudioInputBuffer(internalBufferSize,
682  periodSizeBytes,
683  deviceFormat,
684  streamFormat,
685  this);
686 
687  audioIO = new QtMultimediaInternal::MacInputDevice(audioBuffer, this);
688 
689  // Init
690  if (AudioUnitInitialize(audioUnit) != noErr) {
691  qWarning() << "QAudioInput: Failed to initialize AudioUnit";
692  return false;
693  }
694 
695  isOpen = true;
696 
697  return isOpen;
698 }
699 
701 {
702  if (audioUnit != 0) {
703  AudioOutputUnitStop(audioUnit);
704  AudioUnitUninitialize(audioUnit);
705  CloseComponent(audioUnit);
706  }
707 
708  delete audioBuffer;
709 }
710 
712 {
713  return audioFormat;
714 }
715 
717 {
718  QIODevice* op = device;
719 
720  if (!audioDeviceInfo->isFormatSupported(audioFormat) || !open()) {
721  stateCode = QAudio::StoppedState;
722  errorCode = QAudio::OpenError;
723  return audioIO;
724  }
725 
726  reset();
727  audioBuffer->reset();
728  audioBuffer->setFlushDevice(op);
729 
730  if (op == 0)
731  op = audioIO;
732 
733  // Start
734  startTime = AudioGetCurrentHostTime();
735  totalFrames = 0;
736 
737  audioThreadStart();
738 
739  stateCode = QAudio::ActiveState;
740  errorCode = QAudio::NoError;
741  emit stateChanged(stateCode);
742 
743  return op;
744 }
745 
747 {
748  QMutexLocker lock(&mutex);
749  if (stateCode != QAudio::StoppedState) {
750  audioThreadStop();
751  audioBuffer->flush(true);
752 
753  errorCode = QAudio::NoError;
754  stateCode = QAudio::StoppedState;
755  QMetaObject::invokeMethod(this, "stateChanged", Qt::QueuedConnection, Q_ARG(QAudio::State, stateCode));
756  }
757 }
758 
760 {
761  QMutexLocker lock(&mutex);
762  if (stateCode != QAudio::StoppedState) {
763  audioThreadStop();
764 
765  errorCode = QAudio::NoError;
766  stateCode = QAudio::StoppedState;
767  QMetaObject::invokeMethod(this, "stateChanged", Qt::QueuedConnection, Q_ARG(QAudio::State, stateCode));
768  }
769 }
770 
772 {
773  QMutexLocker lock(&mutex);
774  if (stateCode == QAudio::ActiveState || stateCode == QAudio::IdleState) {
775  audioThreadStop();
776 
777  errorCode = QAudio::NoError;
778  stateCode = QAudio::SuspendedState;
779  QMetaObject::invokeMethod(this, "stateChanged", Qt::QueuedConnection, Q_ARG(QAudio::State, stateCode));
780  }
781 }
782 
784 {
785  QMutexLocker lock(&mutex);
786  if (stateCode == QAudio::SuspendedState) {
787  audioThreadStart();
788 
789  errorCode = QAudio::NoError;
790  stateCode = QAudio::ActiveState;
791  QMetaObject::invokeMethod(this, "stateChanged", Qt::QueuedConnection, Q_ARG(QAudio::State, stateCode));
792  }
793 }
794 
796 {
797  return audioBuffer->used();
798 }
799 
801 {
802  return periodSizeBytes;
803 }
804 
806 {
807  internalBufferSize = bs;
808 }
809 
811 {
812  return internalBufferSize;
813 }
814 
815 void QAudioInputPrivate::setNotifyInterval(int milliSeconds)
816 {
817  if (intervalTimer->interval() == milliSeconds)
818  return;
819 
820  if (milliSeconds <= 0)
821  milliSeconds = 0;
822 
823  intervalTimer->setInterval(milliSeconds);
824 }
825 
827 {
828  return intervalTimer->interval();
829 }
830 
832 {
833  return totalFrames * 1000000 / audioFormat.frequency();
834 }
835 
837 {
838  if (stateCode == QAudio::StoppedState)
839  return 0;
840 
841  return (AudioGetCurrentHostTime() - startTime) / (clockFrequency / 1000);
842 }
843 
845 {
846  return errorCode;
847 }
848 
850 {
851  return stateCode;
852 }
853 
855 {
856  stopTimers();
857  if (audioThreadState.testAndSetAcquire(Running, Stopped))
858  threadFinished.wait(&mutex);
859 }
860 
862 {
863  startTimers();
864  audioThreadState = Running;
865  AudioOutputUnitStart(audioUnit);
866 }
867 
869 {
870  AudioOutputUnitStop(audioUnit);
871  audioThreadState = Stopped;
872  threadFinished.wakeOne();
873 }
874 
876 {
877  QMutexLocker lock(&mutex);
878  if (stateCode == QAudio::ActiveState) {
879  audioDeviceStop();
880 
881  errorCode = QAudio::UnderrunError;
882  stateCode = QAudio::IdleState;
883  QMetaObject::invokeMethod(this, "deviceStopped", Qt::QueuedConnection);
884  }
885 }
886 
888 {
889  QMutexLocker lock(&mutex);
890  if (stateCode == QAudio::ActiveState) {
891  audioDeviceStop();
892 
893  errorCode = QAudio::IOError;
894  stateCode = QAudio::StoppedState;
895  QMetaObject::invokeMethod(this, "deviceStopped", Qt::QueuedConnection);
896  }
897 }
898 
900 {
901  audioBuffer->startFlushTimer();
902  if (intervalTimer->interval() > 0)
903  intervalTimer->start();
904 }
905 
907 {
908  audioBuffer->stopFlushTimer();
909  intervalTimer->stop();
910 }
911 
913 {
914  stopTimers();
915  emit stateChanged(stateCode);
916 }
917 
918 // Input callback
920  AudioUnitRenderActionFlags* ioActionFlags,
921  const AudioTimeStamp* inTimeStamp,
922  UInt32 inBusNumber,
923  UInt32 inNumberFrames,
924  AudioBufferList* ioData)
925 {
926  Q_UNUSED(ioData);
927 
928  QAudioInputPrivate* d = static_cast<QAudioInputPrivate*>(inRefCon);
929 
930  const int threadState = d->audioThreadState.fetchAndAddAcquire(0);
931  if (threadState == Stopped)
932  d->audioDeviceStop();
933  else {
934  qint64 framesWritten;
935 
936  framesWritten = d->audioBuffer->renderFromDevice(d->audioUnit,
937  ioActionFlags,
938  inTimeStamp,
939  inBusNumber,
940  inNumberFrames);
941 
942  if (framesWritten > 0)
943  d->totalFrames += framesWritten;
944  else if (framesWritten == 0)
945  d->audioDeviceFull();
946  else if (framesWritten < 0)
947  d->audioDeviceError();
948  }
949 
950  return noErr;
951 }
952 
953 
955 
956 #include "qaudioinput_mac_p.moc"
957 
double d
Definition: qnumeric_p.h:62
QAudioBufferList(AudioStreamBasicDescription const &streamFormat, int framesToBuffer)
Error
Definition: qaudio.h:58
virtual qint64 size() const
For open random-access devices, this function returns the size of the device.
Definition: qiodevice.cpp:642
QAudioFormat format() const
Returns the QAudioFormat being used.
void resume()
Resumes processing audio data after a suspend().
void suspend()
Stops processing audio data, preserving buffered audio data.
#define QT_END_NAMESPACE
This macro expands to.
Definition: qglobal.h:90
qint64 processedUSecs() const
Returns the amount of audio data processed since start() was called in milliseconds.
int bufferSize() const
Returns the audio buffer size in milliseconds.
void stop()
Stops the audio input.
virtual void close()
First emits aboutToClose(), then closes the device and sets its OpenMode to NotOpen.
Definition: qiodevice.cpp:590
void setNotifyInterval(int milliSeconds)
Sets the interval for notify() signal to be emitted.
Q_CORE_EXPORT void qFree(void *ptr)
Definition: qmalloc.cpp:58
The QByteArray class provides an array of bytes.
Definition: qbytearray.h:135
QIODevice * start(QIODevice *device=0)
Uses the device as the QIODevice to transfer data.
#define SLOT(a)
Definition: qobjectdefs.h:226
QAudioInputBuffer(int bufferSize, int maxPeriodSize, AudioStreamBasicDescription const &inputFormat, AudioStreamBasicDescription const &outputFormat, QObject *parent)
T1 first
Definition: qpair.h:65
static qreal position(QGraphicsObject *item, QDeclarativeAnchorLine::AnchorLine anchorLine)
T2 second
Definition: qpair.h:66
qint64 readBytes(char *data, qint64 len)
#define Q_ARG(type, data)
Definition: qobjectdefs.h:246
QAudioBufferList(AudioStreamBasicDescription const &streamFormat)
Q_CORE_EXPORT void * qMalloc(size_t size)
Definition: qmalloc.cpp:53
The QObject class is the base class of all Qt objects.
Definition: qobject.h:111
int bytesReady() const
Returns the amount of audio data available to read in bytes.
static OSStatus inputCallback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData)
void setBufferSize(int value)
Sets the audio buffer size to value in milliseconds.
#define SIGNAL(a)
Definition: qobjectdefs.h:227
QAudioFormat toQAudioFormat(AudioStreamBasicDescription const &sf)
Definition: qaudio_mac.cpp:63
QAudio::State state() const
Returns the state of audio processing.
int notifyInterval() const
Returns the notify interval in milliseconds.
#define QT_BEGIN_NAMESPACE
This macro expands to.
Definition: qglobal.h:89
static const int default_buffer_size
bool isOpen() const
Returns true if the device is open; otherwise returns false.
Definition: qiodevice.cpp:530
static bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
Creates a connection of the given type from the signal in the sender object to the method in the rece...
Definition: qobject.cpp:2580
qint64 elapsedUSecs() const
Returns the milliseconds since start() was called, including time in Idle and suspend states...
#define emit
Definition: qobjectdefs.h:76
Q_CORE_EXPORT void qWarning(const char *,...)
AudioStreamBasicDescription toAudioStreamBasicDescription(QAudioFormat const &audioFormat)
Definition: qaudio_mac.cpp:82
void reset()
Drops all audio data in the buffers, resets buffers to zero.
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...
QAudioBufferList(AudioStreamBasicDescription const &streamFormat, char *buffer, int bufferSize)
MacInputDevice(QAudioInputBuffer *audioBuffer, QObject *parent)
__int64 qint64
Definition: qglobal.h:942
bool feed(AudioBufferList &dst, UInt32 &packetCount)
signed long OSStatus
AudioStreamBasicDescription m_outputFormat
#define Q_OBJECT
Definition: qobjectdefs.h:157
The Component element encapsulates a QML component definition.
int fetchAndAddAcquire(int valueToAdd)
Atomic fetch-and-add.
Mode
Definition: qaudio.h:60
The QMutexLocker class is a convenience class that simplifies locking and unlocking mutexes...
Definition: qmutex.h:101
State
Definition: qaudio.h:59
qint64 writeData(const char *data, qint64 len)
Writes up to maxSize bytes from data to the device.
unsigned int quint32
Definition: qglobal.h:938
static QReadWriteLock lock
Definition: proxyconf.cpp:399
virtual bool reset()
Seeks to the start of input for random-access devices.
Definition: qiodevice.cpp:732
static OSStatus converterCallback(AudioConverterRef inAudioConverter, UInt32 *ioNumberDataPackets, AudioBufferList *ioData, AudioStreamPacketDescription **outDataPacketDescription, void *inUserData)
virtual bool open(OpenMode mode)
Opens the device and sets its OpenMode to mode.
Definition: qiodevice.cpp:570
int periodSize() const
Returns the period size in bytes.
AudioStreamBasicDescription m_inputFormat
Q_CORE_EXPORT QTextStream & flush(QTextStream &s)
static bool invokeMethod(QObject *obj, const char *member, Qt::ConnectionType, QGenericReturnArgument ret, QGenericArgument val0=QGenericArgument(0), 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())
Invokes the member (a signal or a slot name) on the object obj.
QAudio::Error error() const
Returns the error state.
qint64 renderFromDevice(AudioUnit audioUnit, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames)
The QDataStream class provides serialization of binary data to a QIODevice.
Definition: qdatastream.h:71
The QAudioFormat class stores audio parameter information.
Definition: qaudioformat.h:60
The QTimer class provides repetitive and single-shot timers.
Definition: qtimer.h:56
#define slots
Definition: qobjectdefs.h:68
#define signals
Definition: qobjectdefs.h:69
The QIODevice class is the base interface class of all I/O devices in Qt.
Definition: qiodevice.h:66
bool isSequential() const
Returns true if this device is sequential; otherwise returns false.
#define Q_UNUSED(x)
Indicates to the compiler that the parameter with the specified name is not used in the body of a fun...
Definition: qglobal.h:1729
int open(const char *, int,...)
QAudioInputPrivate(const QByteArray &device, const QAudioFormat &audioFormat)