1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part 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 The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/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 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 #ifndef IOSAUDIOINPUT_H
40 #define IOSAUDIOINPUT_H
41 
42 #include <qaudiosystem.h>
43 #include <AudioUnit/AudioUnit.h>
44 #include <CoreAudio/CoreAudioTypes.h>
45 #include <AudioToolbox/AudioToolbox.h>
46 
47 #include <QtCore/QIODevice>
48 #include <QtCore/QWaitCondition>
49 #include <QtCore/QMutex>
50 #include <QtCore/QTimer>
51 
52 QT_BEGIN_NAMESPACE
53 
54 class CoreAudioRingBuffer;
55 class CoreAudioPacketFeeder;
56 class CoreAudioInputBuffer;
57 class CoreAudioInputDevice;
58 
59 class CoreAudioBufferList
60 {
61 public:
62     CoreAudioBufferList(AudioStreamBasicDescription const& streamFormat);
63     CoreAudioBufferList(AudioStreamBasicDescription const& streamFormat, char *buffer, int bufferSize);
64     CoreAudioBufferList(AudioStreamBasicDescription const& streamFormat, int framesToBuffer);
65 
66     ~CoreAudioBufferList();
67 
audioBufferList()68     AudioBufferList* audioBufferList() const { return m_bufferList; }
69     char *data(int buffer = 0) const;
70     qint64 bufferSize(int buffer = 0) const;
71     int frameCount(int buffer = 0) const;
72     int packetCount(int buffer = 0) const;
73     int packetSize() const;
74     void reset();
75 
76 private:
77     bool m_owner;
78     int m_dataSize;
79     AudioStreamBasicDescription m_streamDescription;
80     AudioBufferList *m_bufferList;
81 };
82 
83 class CoreAudioPacketFeeder
84 {
85 public:
86     CoreAudioPacketFeeder(CoreAudioBufferList *abl);
87 
88     bool feed(AudioBufferList& dst, UInt32& packetCount);
89     bool empty() const;
90 
91 private:
92     UInt32 m_totalPackets;
93     UInt32 m_position;
94     CoreAudioBufferList *m_audioBufferList;
95 };
96 
97 class CoreAudioInputBuffer : public QObject
98 {
99     Q_OBJECT
100 
101 public:
102     CoreAudioInputBuffer(int bufferSize,
103                         int maxPeriodSize,
104                         AudioStreamBasicDescription const& inputFormat,
105                         AudioStreamBasicDescription const& outputFormat,
106                         QObject *parent);
107 
108     ~CoreAudioInputBuffer();
109 
110     qreal volume() const;
111     void setVolume(qreal v);
112 
113     qint64 renderFromDevice(AudioUnit audioUnit,
114                              AudioUnitRenderActionFlags *ioActionFlags,
115                              const AudioTimeStamp *inTimeStamp,
116                              UInt32 inBusNumber,
117                              UInt32 inNumberFrames);
118 
119     qint64 readBytes(char *data, qint64 len);
120 
121     void setFlushDevice(QIODevice *device);
122 
123     void startFlushTimer();
124     void stopFlushTimer();
125 
126     void flush(bool all = false);
127     void reset();
128     int available() const;
129     int used() const;
130 
131 signals:
132     void readyRead();
133 
134 private slots:
135     void flushBuffer();
136 
137 private:
138     bool m_deviceError;
139     int m_maxPeriodSize;
140     int m_periodTime;
141     QIODevice *m_device;
142     QTimer *m_flushTimer;
143     CoreAudioRingBuffer *m_buffer;
144     CoreAudioBufferList *m_inputBufferList;
145     AudioConverterRef m_audioConverter;
146     AudioStreamBasicDescription m_inputFormat;
147     AudioStreamBasicDescription m_outputFormat;
148     QAudioFormat m_qFormat;
149     qreal m_volume;
150 
151     const static OSStatus as_empty = 'qtem';
152 
153     // Converter callback
154     static OSStatus converterCallback(AudioConverterRef inAudioConverter,
155                                 UInt32 *ioNumberDataPackets,
156                                 AudioBufferList *ioData,
157                                 AudioStreamPacketDescription **outDataPacketDescription,
158                                 void *inUserData);
159 };
160 
161 class CoreAudioInputDevice : public QIODevice
162 {
163     Q_OBJECT
164 
165 public:
166     CoreAudioInputDevice(CoreAudioInputBuffer *audioBuffer, QObject *parent);
167 
168     qint64 readData(char *data, qint64 len);
169     qint64 writeData(const char *data, qint64 len);
170 
isSequential()171     bool isSequential() const { return true; }
172 
173 private:
174     CoreAudioInputBuffer *m_audioBuffer;
175 };
176 
177 class CoreAudioInput : public QAbstractAudioInput
178 {
179     Q_OBJECT
180 
181 public:
182     CoreAudioInput(const QByteArray &device);
183     ~CoreAudioInput();
184 
185     void start(QIODevice *device);
186     QIODevice *start();
187     void stop();
188     void reset();
189     void suspend();
190     void resume();
191     int bytesReady() const;
192     int periodSize() const;
193     void setBufferSize(int value);
194     int bufferSize() const;
195     void setNotifyInterval(int milliSeconds);
196     int notifyInterval() const;
197     qint64 processedUSecs() const;
198     qint64 elapsedUSecs() const;
199     QAudio::Error error() const;
200     QAudio::State state() const;
201     void setFormat(const QAudioFormat &format);
202     QAudioFormat format() const;
203 
204     void setVolume(qreal volume);
205     qreal volume() const;
206 
207 private slots:
208     void deviceStoppped();
209 
210 private:
211     enum {
212         Running,
213         Stopped
214     };
215 
216     bool open();
217     void close();
218 
219     void audioThreadStart();
220     void audioThreadStop();
221 
222     void audioDeviceStop();
223     void audioDeviceActive();
224     void audioDeviceFull();
225     void audioDeviceError();
226 
227     void startTimers();
228     void stopTimers();
229 
230     // Input callback
231     static OSStatus inputCallback(void *inRefCon,
232                                     AudioUnitRenderActionFlags *ioActionFlags,
233                                     const AudioTimeStamp *inTimeStamp,
234                                     UInt32 inBusNumber,
235                                     UInt32 inNumberFrames,
236                                     AudioBufferList *ioData);
237 
238     QByteArray m_device;
239     bool m_isOpen;
240     int m_periodSizeBytes;
241     int m_internalBufferSize;
242     qint64 m_totalFrames;
243     QAudioFormat m_audioFormat;
244     QIODevice *m_audioIO;
245     AudioUnit m_audioUnit;
246 #if defined(Q_OS_OSX)
247     AudioDeviceID m_audioDeviceId;
248 #endif
249     Float64 m_clockFrequency;
250     UInt64 m_startTime;
251     QAudio::Error m_errorCode;
252     QAudio::State m_stateCode;
253     CoreAudioInputBuffer *m_audioBuffer;
254     QMutex m_mutex;
255     QWaitCondition m_threadFinished;
256     QAtomicInt m_audioThreadState;
257     QTimer *m_intervalTimer;
258     AudioStreamBasicDescription m_streamFormat;
259     AudioStreamBasicDescription m_deviceFormat;
260     QAbstractAudioDeviceInfo *m_audioDeviceInfo;
261     qreal m_volume;
262 };
263 
264 QT_END_NAMESPACE
265 
266 #endif // IOSAUDIOINPUT_H
267