1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2017 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the examples of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:BSD$ |
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 | ** BSD License Usage |
18 | ** Alternatively, you may use this file under the terms of the BSD license |
19 | ** as follows: |
20 | ** |
21 | ** "Redistribution and use in source and binary forms, with or without |
22 | ** modification, are permitted provided that the following conditions are |
23 | ** met: |
24 | ** * Redistributions of source code must retain the above copyright |
25 | ** notice, this list of conditions and the following disclaimer. |
26 | ** * Redistributions in binary form must reproduce the above copyright |
27 | ** notice, this list of conditions and the following disclaimer in |
28 | ** the documentation and/or other materials provided with the |
29 | ** distribution. |
30 | ** * Neither the name of The Qt Company Ltd nor the names of its |
31 | ** contributors may be used to endorse or promote products derived |
32 | ** from this software without specific prior written permission. |
33 | ** |
34 | ** |
35 | ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
36 | ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
37 | ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
38 | ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
39 | ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
40 | ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
41 | ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
42 | ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
43 | ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
44 | ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
45 | ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." |
46 | ** |
47 | ** $QT_END_LICENSE$ |
48 | ** |
49 | ****************************************************************************/ |
50 | |
51 | #ifndef ENGINE_H |
52 | #define ENGINE_H |
53 | |
54 | #include "spectrum.h" |
55 | #include "spectrumanalyser.h" |
56 | #include "wavfile.h" |
57 | |
58 | #include <QAudioDeviceInfo> |
59 | #include <QAudioFormat> |
60 | #include <QBuffer> |
61 | #include <QByteArray> |
62 | #include <QDir> |
63 | #include <QObject> |
64 | #include <QVector> |
65 | |
66 | #ifdef DUMP_CAPTURED_AUDIO |
67 | #define DUMP_DATA |
68 | #endif |
69 | |
70 | #ifdef DUMP_SPECTRUM |
71 | #define DUMP_DATA |
72 | #endif |
73 | |
74 | class FrequencySpectrum; |
75 | QT_BEGIN_NAMESPACE |
76 | class QAudioInput; |
77 | class QAudioOutput; |
78 | QT_END_NAMESPACE |
79 | |
80 | /** |
81 | * This class interfaces with the Qt Multimedia audio classes, and also with |
82 | * the SpectrumAnalyser class. Its role is to manage the capture and playback |
83 | * of audio data, meanwhile performing real-time analysis of the audio level |
84 | * and frequency spectrum. |
85 | */ |
86 | class Engine : public QObject |
87 | { |
88 | Q_OBJECT |
89 | |
90 | public: |
91 | explicit Engine(QObject *parent = 0); |
92 | ~Engine(); |
93 | |
94 | const QList<QAudioDeviceInfo> &availableAudioInputDevices() const |
95 | { return m_availableAudioInputDevices; } |
96 | |
97 | const QList<QAudioDeviceInfo> &availableAudioOutputDevices() const |
98 | { return m_availableAudioOutputDevices; } |
99 | |
100 | QAudio::Mode mode() const { return m_mode; } |
101 | QAudio::State state() const { return m_state; } |
102 | |
103 | /** |
104 | * \return Current audio format |
105 | * \note May be QAudioFormat() if engine is not initialized |
106 | */ |
107 | const QAudioFormat& format() const { return m_format; } |
108 | |
109 | /** |
110 | * Stop any ongoing recording or playback, and reset to ground state. |
111 | */ |
112 | void reset(); |
113 | |
114 | /** |
115 | * Load data from WAV file |
116 | */ |
117 | bool loadFile(const QString &fileName); |
118 | |
119 | /** |
120 | * Generate tone |
121 | */ |
122 | bool generateTone(const Tone &tone); |
123 | |
124 | /** |
125 | * Generate tone |
126 | */ |
127 | bool generateSweptTone(qreal amplitude); |
128 | |
129 | /** |
130 | * Initialize for recording |
131 | */ |
132 | bool initializeRecord(); |
133 | |
134 | /** |
135 | * Position of the audio input device. |
136 | * \return Position in bytes. |
137 | */ |
138 | qint64 recordPosition() const { return m_recordPosition; } |
139 | |
140 | /** |
141 | * RMS level of the most recently processed set of audio samples. |
142 | * \return Level in range (0.0, 1.0) |
143 | */ |
144 | qreal rmsLevel() const { return m_rmsLevel; } |
145 | |
146 | /** |
147 | * Peak level of the most recently processed set of audio samples. |
148 | * \return Level in range (0.0, 1.0) |
149 | */ |
150 | qreal peakLevel() const { return m_peakLevel; } |
151 | |
152 | /** |
153 | * Position of the audio output device. |
154 | * \return Position in bytes. |
155 | */ |
156 | qint64 playPosition() const { return m_playPosition; } |
157 | |
158 | /** |
159 | * Length of the internal engine buffer. |
160 | * \return Buffer length in bytes. |
161 | */ |
162 | qint64 bufferLength() const; |
163 | |
164 | /** |
165 | * Amount of data held in the buffer. |
166 | * \return Data length in bytes. |
167 | */ |
168 | qint64 dataLength() const { return m_dataLength; } |
169 | |
170 | /** |
171 | * Set window function applied to audio data before spectral analysis. |
172 | */ |
173 | void setWindowFunction(WindowFunction type); |
174 | |
175 | public slots: |
176 | void startRecording(); |
177 | void startPlayback(); |
178 | void suspend(); |
179 | void setAudioInputDevice(const QAudioDeviceInfo &device); |
180 | void setAudioOutputDevice(const QAudioDeviceInfo &device); |
181 | |
182 | signals: |
183 | void stateChanged(QAudio::Mode mode, QAudio::State state); |
184 | |
185 | /** |
186 | * Informational message for non-modal display |
187 | */ |
188 | void infoMessage(const QString &message, int durationMs); |
189 | |
190 | /** |
191 | * Error message for modal display |
192 | */ |
193 | void errorMessage(const QString &heading, const QString &detail); |
194 | |
195 | /** |
196 | * Format of audio data has changed |
197 | */ |
198 | void formatChanged(const QAudioFormat &format); |
199 | |
200 | /** |
201 | * Length of buffer has changed. |
202 | * \param duration Duration in microseconds |
203 | */ |
204 | void bufferLengthChanged(qint64 duration); |
205 | |
206 | /** |
207 | * Amount of data in buffer has changed. |
208 | * \param Length of data in bytes |
209 | */ |
210 | void dataLengthChanged(qint64 duration); |
211 | |
212 | /** |
213 | * Position of the audio input device has changed. |
214 | * \param position Position in bytes |
215 | */ |
216 | void recordPositionChanged(qint64 position); |
217 | |
218 | /** |
219 | * Position of the audio output device has changed. |
220 | * \param position Position in bytes |
221 | */ |
222 | void playPositionChanged(qint64 position); |
223 | |
224 | /** |
225 | * Level changed |
226 | * \param rmsLevel RMS level in range 0.0 - 1.0 |
227 | * \param peakLevel Peak level in range 0.0 - 1.0 |
228 | * \param numSamples Number of audio samples analyzed |
229 | */ |
230 | void levelChanged(qreal rmsLevel, qreal peakLevel, int numSamples); |
231 | |
232 | /** |
233 | * Spectrum has changed. |
234 | * \param position Position of start of window in bytes |
235 | * \param length Length of window in bytes |
236 | * \param spectrum Resulting frequency spectrum |
237 | */ |
238 | void spectrumChanged(qint64 position, qint64 length, const FrequencySpectrum &spectrum); |
239 | |
240 | /** |
241 | * Buffer containing audio data has changed. |
242 | * \param position Position of start of buffer in bytes |
243 | * \param buffer Buffer |
244 | */ |
245 | void bufferChanged(qint64 position, qint64 length, const QByteArray &buffer); |
246 | |
247 | private slots: |
248 | void audioNotify(); |
249 | void audioStateChanged(QAudio::State state); |
250 | void audioDataReady(); |
251 | void spectrumChanged(const FrequencySpectrum &spectrum); |
252 | |
253 | private: |
254 | void resetAudioDevices(); |
255 | bool initialize(); |
256 | bool selectFormat(); |
257 | void stopRecording(); |
258 | void stopPlayback(); |
259 | void setState(QAudio::State state); |
260 | void setState(QAudio::Mode mode, QAudio::State state); |
261 | void setFormat(const QAudioFormat &format); |
262 | void setRecordPosition(qint64 position, bool forceEmit = false); |
263 | void setPlayPosition(qint64 position, bool forceEmit = false); |
264 | void calculateLevel(qint64 position, qint64 length); |
265 | void calculateSpectrum(qint64 position); |
266 | void setLevel(qreal rmsLevel, qreal peakLevel, int numSamples); |
267 | |
268 | #ifdef DUMP_DATA |
269 | void createOutputDir(); |
270 | QString outputPath() const { return m_outputDir.path(); } |
271 | #endif |
272 | |
273 | #ifdef DUMP_CAPTURED_AUDIO |
274 | void dumpData(); |
275 | #endif |
276 | |
277 | private: |
278 | QAudio::Mode m_mode; |
279 | QAudio::State m_state; |
280 | |
281 | bool m_generateTone; |
282 | SweptTone m_tone; |
283 | |
284 | WavFile* m_file; |
285 | // We need a second file handle via which to read data into m_buffer |
286 | // for analysis |
287 | WavFile* m_analysisFile; |
288 | |
289 | QAudioFormat m_format; |
290 | |
291 | const QList<QAudioDeviceInfo> m_availableAudioInputDevices; |
292 | QAudioDeviceInfo m_audioInputDevice; |
293 | QAudioInput* m_audioInput; |
294 | QIODevice* m_audioInputIODevice; |
295 | qint64 m_recordPosition; |
296 | |
297 | const QList<QAudioDeviceInfo> m_availableAudioOutputDevices; |
298 | QAudioDeviceInfo m_audioOutputDevice; |
299 | QAudioOutput* m_audioOutput; |
300 | QString m_audioOutputCategory; |
301 | qint64 m_playPosition; |
302 | QBuffer m_audioOutputIODevice; |
303 | |
304 | QByteArray m_buffer; |
305 | qint64 m_bufferPosition; |
306 | qint64 m_bufferLength; |
307 | qint64 m_dataLength; |
308 | |
309 | int m_levelBufferLength; |
310 | qreal m_rmsLevel; |
311 | qreal m_peakLevel; |
312 | |
313 | int m_spectrumBufferLength; |
314 | QByteArray m_spectrumBuffer; |
315 | SpectrumAnalyser m_spectrumAnalyser; |
316 | qint64 m_spectrumPosition; |
317 | |
318 | int m_count; |
319 | |
320 | #ifdef DUMP_DATA |
321 | QDir m_outputDir; |
322 | #endif |
323 | |
324 | }; |
325 | |
326 | #endif // ENGINE_H |
327 | |