source: trunk/src/multimedia/audio/qaudioinput_mac_p.cpp@ 846

Last change on this file since 846 was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

  • Property svn:eol-style set to native
File size: 27.2 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** This file is part of the QtMultimedia module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at [email protected].
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
63QT_BEGIN_NAMESPACE
64
65
66namespace QtMultimediaInternal
67{
68
69static const int default_buffer_size = 4 * 1024;
70
71class QAudioBufferList
72{
73public:
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 {
99 dataSize = bufferSize;
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
129 ~QAudioBufferList()
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
177private:
178 bool owner;
179 int dataSize;
180 AudioStreamBasicDescription sf;
181 AudioBufferList* bfs;
182};
183
184class QAudioPacketFeeder
185{
186public:
187 QAudioPacketFeeder(QAudioBufferList* abl):
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
218private:
219 UInt32 totalPackets;
220 UInt32 position;
221 QAudioBufferList* audioBufferList;
222};
223
224class QAudioInputBuffer : public QObject
225{
226 Q_OBJECT
227
228public:
229 QAudioInputBuffer(int bufferSize,
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
256 ~QAudioInputBuffer()
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
340 qint64 readBytes(char* data, qint64 len)
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
368 void startFlushTimer()
369 {
370 if (m_device != 0) {
371 m_flushTimer->start((m_buffer->size() - (m_maxPeriodSize * 2)) / m_maxPeriodSize * m_periodTime);
372 }
373 }
374
375 void stopFlushTimer()
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
431signals:
432 void readyRead();
433
434private slots:
435 void flushBuffer()
436 {
437 flush();
438 }
439
440private:
441 bool m_deviceError;
442 int m_maxPeriodSize;
443 int m_periodTime;
444 QIODevice* m_device;
445 QTimer* m_flushTimer;
446 QAudioRingBuffer* m_buffer;
447 QAudioBufferList* m_inputBufferList;
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
474class MacInputDevice : public QIODevice
475{
476 Q_OBJECT
477
478public:
479 MacInputDevice(QAudioInputBuffer* audioBuffer, QObject* parent):
480 QIODevice(parent),
481 m_audioBuffer(audioBuffer)
482 {
483 open(QIODevice::ReadOnly | QIODevice::Unbuffered);
484 connect(m_audioBuffer, SIGNAL(readyRead()), SIGNAL(readyRead()));
485 }
486
487 qint64 readData(char* data, qint64 len)
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
505private:
506 QAudioInputBuffer* m_audioBuffer;
507};
508
509}
510
511
512QAudioInputPrivate::QAudioInputPrivate(const QByteArray& device, QAudioFormat const& format):
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
541QAudioInputPrivate::~QAudioInputPrivate()
542{
543 close();
544 delete audioDeviceInfo;
545}
546
547bool QAudioInputPrivate::open()
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
700void QAudioInputPrivate::close()
701{
702 if (audioUnit != 0) {
703 AudioOutputUnitStop(audioUnit);
704 AudioUnitUninitialize(audioUnit);
705 CloseComponent(audioUnit);
706 }
707
708 delete audioBuffer;
709}
710
711QAudioFormat QAudioInputPrivate::format() const
712{
713 return audioFormat;
714}
715
716QIODevice* QAudioInputPrivate::start(QIODevice* device)
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
746void QAudioInputPrivate::stop()
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
759void QAudioInputPrivate::reset()
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
771void QAudioInputPrivate::suspend()
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
783void QAudioInputPrivate::resume()
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
795int QAudioInputPrivate::bytesReady() const
796{
797 return audioBuffer->used();
798}
799
800int QAudioInputPrivate::periodSize() const
801{
802 return periodSizeBytes;
803}
804
805void QAudioInputPrivate::setBufferSize(int bs)
806{
807 internalBufferSize = bs;
808}
809
810int QAudioInputPrivate::bufferSize() const
811{
812 return internalBufferSize;
813}
814
815void 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
826int QAudioInputPrivate::notifyInterval() const
827{
828 return intervalTimer->interval();
829}
830
831qint64 QAudioInputPrivate::processedUSecs() const
832{
833 return totalFrames * 1000000 / audioFormat.frequency();
834}
835
836qint64 QAudioInputPrivate::elapsedUSecs() const
837{
838 if (stateCode == QAudio::StoppedState)
839 return 0;
840
841 return (AudioGetCurrentHostTime() - startTime) / (clockFrequency / 1000);
842}
843
844QAudio::Error QAudioInputPrivate::error() const
845{
846 return errorCode;
847}
848
849QAudio::State QAudioInputPrivate::state() const
850{
851 return stateCode;
852}
853
854void QAudioInputPrivate::audioThreadStop()
855{
856 stopTimers();
857 if (audioThreadState.testAndSetAcquire(Running, Stopped))
858 threadFinished.wait(&mutex);
859}
860
861void QAudioInputPrivate::audioThreadStart()
862{
863 startTimers();
864 audioThreadState = Running;
865 AudioOutputUnitStart(audioUnit);
866}
867
868void QAudioInputPrivate::audioDeviceStop()
869{
870 AudioOutputUnitStop(audioUnit);
871 audioThreadState = Stopped;
872 threadFinished.wakeOne();
873}
874
875void QAudioInputPrivate::audioDeviceFull()
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
887void QAudioInputPrivate::audioDeviceError()
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
899void QAudioInputPrivate::startTimers()
900{
901 audioBuffer->startFlushTimer();
902 if (intervalTimer->interval() > 0)
903 intervalTimer->start();
904}
905
906void QAudioInputPrivate::stopTimers()
907{
908 audioBuffer->stopFlushTimer();
909 intervalTimer->stop();
910}
911
912void QAudioInputPrivate::deviceStopped()
913{
914 stopTimers();
915 emit stateChanged(stateCode);
916}
917
918// Input callback
919OSStatus QAudioInputPrivate::inputCallback(void* inRefCon,
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
954QT_END_NAMESPACE
955
956#include "qaudioinput_mac_p.moc"
957
Note: See TracBrowser for help on using the repository browser.