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 test suite of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
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 General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28
29#include <QtTest/QtTest>
30#include <QtCore/qlocale.h>
31#include <QtCore/QTemporaryDir>
32#include <QtCore/QSharedPointer>
33#include <QtCore/QScopedPointer>
34
35#include <qaudioinput.h>
36#include <qaudiodeviceinfo.h>
37#include <qaudioformat.h>
38#include <qaudio.h>
39
40#include "wavheader.h"
41
42//TESTED_COMPONENT=src/multimedia
43
44#define AUDIO_BUFFER 192000
45#define RANGE_ERR 0.5
46
47template<typename T> inline bool qTolerantCompare(T value, T expected)
48{
49 return qAbs(value - expected) < (RANGE_ERR * expected);
50}
51
52#ifndef QTRY_VERIFY2
53#define QTRY_VERIFY2(__expr,__msg) \
54 do { \
55 const int __step = 50; \
56 const int __timeout = 5000; \
57 if (!(__expr)) { \
58 QTest::qWait(0); \
59 } \
60 for (int __i = 0; __i < __timeout && !(__expr); __i+=__step) { \
61 QTest::qWait(__step); \
62 } \
63 QVERIFY2(__expr,__msg); \
64 } while(0)
65#endif
66
67class tst_QAudioInput : public QObject
68{
69 Q_OBJECT
70public:
71 tst_QAudioInput(QObject* parent=0) : QObject(parent) {}
72
73private slots:
74 void initTestCase();
75
76 void format();
77 void invalidFormat_data();
78 void invalidFormat();
79
80 void bufferSize();
81 void notifyInterval();
82 void disableNotifyInterval();
83
84 void stopWhileStopped();
85 void suspendWhileStopped();
86 void resumeWhileStopped();
87
88 void pull_data(){generate_audiofile_testrows();}
89 void pull();
90
91 void pullSuspendResume_data(){generate_audiofile_testrows();}
92 void pullSuspendResume();
93
94 void push_data(){generate_audiofile_testrows();}
95 void push();
96
97 void pushSuspendResume_data(){generate_audiofile_testrows();}
98 void pushSuspendResume();
99
100 void reset_data(){generate_audiofile_testrows();}
101 void reset();
102
103 void volume_data(){generate_audiofile_testrows();}
104 void volume();
105
106private:
107 typedef QSharedPointer<QFile> FilePtr;
108
109 QString formatToFileName(const QAudioFormat &format);
110
111 void generate_audiofile_testrows();
112
113 QAudioDeviceInfo audioDevice;
114 QList<QAudioFormat> testFormats;
115 QList<FilePtr> audioFiles;
116 QScopedPointer<QTemporaryDir> m_temporaryDir;
117
118 QScopedPointer<QByteArray> m_byteArray;
119 QScopedPointer<QBuffer> m_buffer;
120
121 bool m_inCISystem;
122};
123
124void tst_QAudioInput::generate_audiofile_testrows()
125{
126 QTest::addColumn<FilePtr>(name: "audioFile");
127 QTest::addColumn<QAudioFormat>(name: "audioFormat");
128
129 for (int i=0; i<audioFiles.count(); i++) {
130 QTest::newRow(dataTag: QString("Audio File %1").arg(a: i).toLocal8Bit().constData())
131 << audioFiles.at(i) << testFormats.at(i);
132
133 // Only run first format in CI system to reduce test times
134 if (m_inCISystem)
135 break;
136 }
137}
138
139QString tst_QAudioInput::formatToFileName(const QAudioFormat &format)
140{
141 const QString formatEndian = (format.byteOrder() == QAudioFormat::LittleEndian)
142 ? QString("LE") : QString("BE");
143
144 const QString formatSigned = (format.sampleType() == QAudioFormat::SignedInt)
145 ? QString("signed") : QString("unsigned");
146
147 return QString("%1_%2_%3_%4_%5")
148 .arg(a: format.sampleRate())
149 .arg(a: format.sampleSize())
150 .arg(a: formatSigned)
151 .arg(a: formatEndian)
152 .arg(a: format.channelCount());
153}
154
155void tst_QAudioInput::initTestCase()
156{
157 qRegisterMetaType<QAudioFormat>();
158
159 // Only perform tests if audio output device exists
160 const QList<QAudioDeviceInfo> devices =
161 QAudioDeviceInfo::availableDevices(mode: QAudio::AudioInput);
162
163 if (devices.size() <= 0)
164 QSKIP("No audio backend");
165
166 audioDevice = QAudioDeviceInfo::defaultInputDevice();
167
168
169 QAudioFormat format;
170
171 format.setCodec("audio/pcm");
172
173 if (audioDevice.isFormatSupported(format: audioDevice.preferredFormat()))
174 testFormats.append(t: audioDevice.preferredFormat());
175
176 // PCM 8000 mono S8
177 format.setSampleRate(8000);
178 format.setSampleSize(8);
179 format.setSampleType(QAudioFormat::SignedInt);
180 format.setByteOrder(QAudioFormat::LittleEndian);
181 format.setChannelCount(1);
182 if (audioDevice.isFormatSupported(format))
183 testFormats.append(t: format);
184
185 // PCM 11025 mono S16LE
186 format.setSampleRate(11025);
187 format.setSampleSize(16);
188 if (audioDevice.isFormatSupported(format))
189 testFormats.append(t: format);
190
191 // PCM 22050 mono S16LE
192 format.setSampleRate(22050);
193 if (audioDevice.isFormatSupported(format))
194 testFormats.append(t: format);
195
196 // PCM 22050 stereo S16LE
197 format.setChannelCount(2);
198 if (audioDevice.isFormatSupported(format))
199 testFormats.append(t: format);
200
201 // PCM 44100 stereo S16LE
202 format.setSampleRate(44100);
203 if (audioDevice.isFormatSupported(format))
204 testFormats.append(t: format);
205
206 // PCM 48000 stereo S16LE
207 format.setSampleRate(48000);
208 if (audioDevice.isFormatSupported(format))
209 testFormats.append(t: format);
210
211 QVERIFY(testFormats.size());
212
213 const QChar slash = QLatin1Char('/');
214 QString temporaryPattern = QDir::tempPath();
215 if (!temporaryPattern.endsWith(c: slash))
216 temporaryPattern += slash;
217 temporaryPattern += "tst_qaudioinputXXXXXX";
218 m_temporaryDir.reset(other: new QTemporaryDir(temporaryPattern));
219 m_temporaryDir->setAutoRemove(true);
220 QVERIFY(m_temporaryDir->isValid());
221
222 const QString temporaryAudioPath = m_temporaryDir->path() + slash;
223 for (const QAudioFormat &format : qAsConst(t&: testFormats)) {
224 const QString fileName = temporaryAudioPath + formatToFileName(format) + QStringLiteral(".wav");
225 audioFiles.append(t: FilePtr::create(arguments: fileName));
226 }
227 qgetenv(varName: "QT_TEST_CI").toInt(ok: &m_inCISystem,base: 10);
228}
229
230void tst_QAudioInput::format()
231{
232 QAudioInput audioInput(audioDevice.preferredFormat(), this);
233
234 QAudioFormat requested = audioDevice.preferredFormat();
235 QAudioFormat actual = audioInput.format();
236
237 QVERIFY2((requested.channelCount() == actual.channelCount()),
238 QString("channels: requested=%1, actual=%2").arg(requested.channelCount()).arg(actual.channelCount()).toLocal8Bit().constData());
239 QVERIFY2((requested.sampleRate() == actual.sampleRate()),
240 QString("sampleRate: requested=%1, actual=%2").arg(requested.sampleRate()).arg(actual.sampleRate()).toLocal8Bit().constData());
241 QVERIFY2((requested.sampleSize() == actual.sampleSize()),
242 QString("sampleSize: requested=%1, actual=%2").arg(requested.sampleSize()).arg(actual.sampleSize()).toLocal8Bit().constData());
243 QVERIFY2((requested.codec() == actual.codec()),
244 QString("codec: requested=%1, actual=%2").arg(requested.codec()).arg(actual.codec()).toLocal8Bit().constData());
245 QVERIFY2((requested.byteOrder() == actual.byteOrder()),
246 QString("byteOrder: requested=%1, actual=%2").arg(requested.byteOrder()).arg(actual.byteOrder()).toLocal8Bit().constData());
247 QVERIFY2((requested.sampleType() == actual.sampleType()),
248 QString("sampleType: requested=%1, actual=%2").arg(requested.sampleType()).arg(actual.sampleType()).toLocal8Bit().constData());
249}
250
251void tst_QAudioInput::invalidFormat_data()
252{
253 QTest::addColumn<QAudioFormat>(name: "invalidFormat");
254
255 QAudioFormat format;
256
257 QTest::newRow(dataTag: "Null Format")
258 << format;
259
260 format = audioDevice.preferredFormat();
261 format.setChannelCount(0);
262 QTest::newRow(dataTag: "Channel count 0")
263 << format;
264
265 format = audioDevice.preferredFormat();
266 format.setSampleRate(0);
267 QTest::newRow(dataTag: "Sample rate 0")
268 << format;
269
270 format = audioDevice.preferredFormat();
271 format.setSampleSize(0);
272 QTest::newRow(dataTag: "Sample size 0")
273 << format;
274}
275
276void tst_QAudioInput::invalidFormat()
277{
278 QFETCH(QAudioFormat, invalidFormat);
279
280 QVERIFY2(!audioDevice.isFormatSupported(invalidFormat),
281 "isFormatSupported() is returning true on an invalid format");
282
283 QAudioInput audioInput(invalidFormat, this);
284
285 // Check that we are in the default state before calling start
286 QVERIFY2((audioInput.state() == QAudio::StoppedState), "state() was not set to StoppedState before start()");
287 QVERIFY2((audioInput.error() == QAudio::NoError), "error() was not set to QAudio::NoError before start()");
288
289 audioInput.start();
290
291 // Check that error is raised
292 QTRY_VERIFY2((audioInput.error() == QAudio::OpenError),"error() was not set to QAudio::OpenError after start()");
293}
294
295void tst_QAudioInput::bufferSize()
296{
297 QAudioInput audioInput(audioDevice.preferredFormat(), this);
298
299 QVERIFY2((audioInput.error() == QAudio::NoError), "error() was not set to QAudio::NoError on creation");
300
301 audioInput.setBufferSize(512);
302 QVERIFY2((audioInput.error() == QAudio::NoError), "error() is not QAudio::NoError after setBufferSize(512)");
303 QVERIFY2((audioInput.bufferSize() == 512),
304 QString("bufferSize: requested=512, actual=%2").arg(audioInput.bufferSize()).toLocal8Bit().constData());
305
306 audioInput.setBufferSize(4096);
307 QVERIFY2((audioInput.error() == QAudio::NoError), "error() is not QAudio::NoError after setBufferSize(4096)");
308 QVERIFY2((audioInput.bufferSize() == 4096),
309 QString("bufferSize: requested=4096, actual=%2").arg(audioInput.bufferSize()).toLocal8Bit().constData());
310
311 audioInput.setBufferSize(8192);
312 QVERIFY2((audioInput.error() == QAudio::NoError), "error() is not QAudio::NoError after setBufferSize(8192)");
313 QVERIFY2((audioInput.bufferSize() == 8192),
314 QString("bufferSize: requested=8192, actual=%2").arg(audioInput.bufferSize()).toLocal8Bit().constData());
315}
316
317void tst_QAudioInput::notifyInterval()
318{
319 QAudioInput audioInput(audioDevice.preferredFormat(), this);
320
321 QVERIFY2((audioInput.error() == QAudio::NoError), "error() was not set to QAudio::NoError on creation");
322
323 audioInput.setNotifyInterval(50);
324 QVERIFY2((audioInput.error() == QAudio::NoError), "error() is not QAudio::NoError after setNotifyInterval(50)");
325 QVERIFY2((audioInput.notifyInterval() == 50),
326 QString("notifyInterval: requested=50, actual=%2").arg(audioInput.notifyInterval()).toLocal8Bit().constData());
327
328 audioInput.setNotifyInterval(100);
329 QVERIFY2((audioInput.error() == QAudio::NoError), "error() is not QAudio::NoError after setNotifyInterval(100)");
330 QVERIFY2((audioInput.notifyInterval() == 100),
331 QString("notifyInterval: requested=100, actual=%2").arg(audioInput.notifyInterval()).toLocal8Bit().constData());
332
333 audioInput.setNotifyInterval(250);
334 QVERIFY2((audioInput.error() == QAudio::NoError), "error() is not QAudio::NoError after setNotifyInterval(250)");
335 QVERIFY2((audioInput.notifyInterval() == 250),
336 QString("notifyInterval: requested=250, actual=%2").arg(audioInput.notifyInterval()).toLocal8Bit().constData());
337
338 audioInput.setNotifyInterval(1000);
339 QVERIFY2((audioInput.error() == QAudio::NoError), "error() is not QAudio::NoError after setNotifyInterval(1000)");
340 QVERIFY2((audioInput.notifyInterval() == 1000),
341 QString("notifyInterval: requested=1000, actual=%2").arg(audioInput.notifyInterval()).toLocal8Bit().constData());
342}
343
344void tst_QAudioInput::disableNotifyInterval()
345{
346 // Sets an invalid notification interval (QAudioInput::setNotifyInterval(0))
347 // Checks that
348 // - No error is raised (QAudioInput::error() returns QAudio::NoError)
349 // - if <= 0, set to zero and disable notify signal
350
351 QAudioInput audioInput(audioDevice.preferredFormat(), this);
352
353 QVERIFY2((audioInput.error() == QAudio::NoError), "error() was not set to QAudio::NoError on creation");
354
355 audioInput.setNotifyInterval(0);
356 QVERIFY2((audioInput.error() == QAudio::NoError), "error() is not QAudio::NoError after setNotifyInterval(0)");
357 QVERIFY2((audioInput.notifyInterval() == 0),
358 "notifyInterval() is not zero after setNotifyInterval(0)");
359
360 audioInput.setNotifyInterval(-1);
361 QVERIFY2((audioInput.error() == QAudio::NoError), "error() is not QAudio::NoError after setNotifyInterval(-1)");
362 QVERIFY2((audioInput.notifyInterval() == 0),
363 "notifyInterval() is not zero after setNotifyInterval(-1)");
364
365 //start and run to check if notify() is emitted
366 if (audioFiles.size() > 0) {
367 QAudioInput audioInputCheck(testFormats.at(i: 0), this);
368 audioInputCheck.setNotifyInterval(0);
369 QSignalSpy notifySignal(&audioInputCheck, SIGNAL(notify()));
370 QFile *audioFile = audioFiles.at(i: 0).data();
371 audioFile->open(flags: QIODevice::WriteOnly);
372 audioInputCheck.start(device: audioFile);
373 QTest::qWait(ms: 3000); // 3 seconds should be plenty
374 audioInputCheck.stop();
375 QVERIFY2((notifySignal.count() == 0),
376 QString("didn't disable notify interval: shouldn't have got any but got %1").arg(notifySignal.count()).toLocal8Bit().constData());
377 audioFile->close();
378 }
379}
380
381void tst_QAudioInput::stopWhileStopped()
382{
383 // Calls QAudioInput::stop() when object is already in StoppedState
384 // Checks that
385 // - No state change occurs
386 // - No error is raised (QAudioInput::error() returns QAudio::NoError)
387
388 QAudioInput audioInput(audioDevice.preferredFormat(), this);
389
390 QVERIFY2((audioInput.state() == QAudio::StoppedState), "state() was not set to StoppedState before start()");
391 QVERIFY2((audioInput.error() == QAudio::NoError), "error() was not set to QAudio::NoError before start()");
392
393 QSignalSpy stateSignal(&audioInput, SIGNAL(stateChanged(QAudio::State)));
394 audioInput.stop();
395
396 // Check that no state transition occurred
397 QVERIFY2((stateSignal.count() == 0), "stop() while stopped is emitting a signal and it shouldn't");
398 QVERIFY2((audioInput.error() == QAudio::NoError), "error() was not set to QAudio::NoError after stop()");
399}
400
401void tst_QAudioInput::suspendWhileStopped()
402{
403 // Calls QAudioInput::suspend() when object is already in StoppedState
404 // Checks that
405 // - No state change occurs
406 // - No error is raised (QAudioInput::error() returns QAudio::NoError)
407
408 QAudioInput audioInput(audioDevice.preferredFormat(), this);
409
410 QVERIFY2((audioInput.state() == QAudio::StoppedState), "state() was not set to StoppedState before start()");
411 QVERIFY2((audioInput.error() == QAudio::NoError), "error() was not set to QAudio::NoError before start()");
412
413 QSignalSpy stateSignal(&audioInput, SIGNAL(stateChanged(QAudio::State)));
414 audioInput.suspend();
415
416 // Check that no state transition occurred
417 QVERIFY2((stateSignal.count() == 0), "stop() while suspended is emitting a signal and it shouldn't");
418 QVERIFY2((audioInput.error() == QAudio::NoError), "error() was not set to QAudio::NoError after stop()");
419}
420
421void tst_QAudioInput::resumeWhileStopped()
422{
423 // Calls QAudioInput::resume() when object is already in StoppedState
424 // Checks that
425 // - No state change occurs
426 // - No error is raised (QAudioInput::error() returns QAudio::NoError)
427
428 QAudioInput audioInput(audioDevice.preferredFormat(), this);
429
430 QVERIFY2((audioInput.state() == QAudio::StoppedState), "state() was not set to StoppedState before start()");
431 QVERIFY2((audioInput.error() == QAudio::NoError), "error() was not set to QAudio::NoError before start()");
432
433 QSignalSpy stateSignal(&audioInput, SIGNAL(stateChanged(QAudio::State)));
434 audioInput.resume();
435
436 // Check that no state transition occurred
437 QVERIFY2((stateSignal.count() == 0), "resume() while stopped is emitting a signal and it shouldn't");
438 QVERIFY2((audioInput.error() == QAudio::NoError), "error() was not set to QAudio::NoError after resume()");
439}
440
441void tst_QAudioInput::pull()
442{
443 QFETCH(FilePtr, audioFile);
444 QFETCH(QAudioFormat, audioFormat);
445
446 QAudioInput audioInput(audioFormat, this);
447
448 audioInput.setNotifyInterval(100);
449
450 QSignalSpy notifySignal(&audioInput, SIGNAL(notify()));
451 QSignalSpy stateSignal(&audioInput, SIGNAL(stateChanged(QAudio::State)));
452
453 // Check that we are in the default state before calling start
454 QVERIFY2((audioInput.state() == QAudio::StoppedState), "state() was not set to StoppedState before start()");
455 QVERIFY2((audioInput.error() == QAudio::NoError), "error() was not set to QAudio::NoError before start()");
456 QVERIFY2((audioInput.elapsedUSecs() == qint64(0)),"elapsedUSecs() not zero on creation");
457
458 audioFile->close();
459 audioFile->open(flags: QIODevice::WriteOnly);
460 WavHeader wavHeader(audioFormat);
461 QVERIFY(wavHeader.write(*audioFile));
462
463 audioInput.start(device: audioFile.data());
464
465 // Check that QAudioInput immediately transitions to ActiveState or IdleState
466 QTRY_VERIFY2((stateSignal.count() > 0),"didn't emit signals on start()");
467 QVERIFY2((audioInput.state() == QAudio::ActiveState || audioInput.state() == QAudio::IdleState),
468 "didn't transition to ActiveState or IdleState after start()");
469 QVERIFY2((audioInput.error() == QAudio::NoError), "error state is not equal to QAudio::NoError after start()");
470 QVERIFY(audioInput.periodSize() > 0);
471 stateSignal.clear();
472
473 // Check that 'elapsed' increases
474 QTest::qWait(ms: 40);
475 QVERIFY2((audioInput.elapsedUSecs() > 0), "elapsedUSecs() is still zero after start()");
476
477 // Allow some recording to happen
478 QTest::qWait(ms: 3000); // 3 seconds should be plenty
479
480 stateSignal.clear();
481
482 qint64 processedUs = audioInput.processedUSecs();
483
484 audioInput.stop();
485 QTest::qWait(ms: 40);
486 QTRY_VERIFY2((stateSignal.count() == 1),
487 QString("didn't emit StoppedState signal after stop(), got %1 signals instead").arg(stateSignal.count()).toLocal8Bit().constData());
488 QVERIFY2((audioInput.state() == QAudio::StoppedState), "didn't transitions to StoppedState after stop()");
489
490 QVERIFY2(qTolerantCompare(processedUs, 3040000LL),
491 QString("processedUSecs() doesn't fall in acceptable range, should be 3040000 (%1)").arg(processedUs).toLocal8Bit().constData());
492 QVERIFY2((audioInput.error() == QAudio::NoError), "error() is not QAudio::NoError after stop()");
493 QVERIFY2((audioInput.elapsedUSecs() == (qint64)0), "elapsedUSecs() not equal to zero in StoppedState");
494 QVERIFY2(notifySignal.count() > 0, "not emitting notify() signal");
495
496 WavHeader::writeDataLength(device&: *audioFile, dataLength: audioFile->pos() - WavHeader::headerLength());
497 audioFile->close();
498
499}
500
501void tst_QAudioInput::pullSuspendResume()
502{
503#ifdef Q_OS_LINUX
504 if (m_inCISystem)
505 QSKIP("QTBUG-26504 Fails 20% of time with pulseaudio backend");
506#endif
507 QFETCH(FilePtr, audioFile);
508 QFETCH(QAudioFormat, audioFormat);
509
510 QAudioInput audioInput(audioFormat, this);
511
512 audioInput.setNotifyInterval(100);
513
514 QSignalSpy notifySignal(&audioInput, SIGNAL(notify()));
515 QSignalSpy stateSignal(&audioInput, SIGNAL(stateChanged(QAudio::State)));
516
517 // Check that we are in the default state before calling start
518 QVERIFY2((audioInput.state() == QAudio::StoppedState), "state() was not set to StoppedState before start()");
519 QVERIFY2((audioInput.error() == QAudio::NoError), "error() was not set to QAudio::NoError before start()");
520 QVERIFY2((audioInput.elapsedUSecs() == qint64(0)),"elapsedUSecs() not zero on creation");
521
522 audioFile->close();
523 audioFile->open(flags: QIODevice::WriteOnly);
524 WavHeader wavHeader(audioFormat);
525 QVERIFY(wavHeader.write(*audioFile));
526
527 audioInput.start(device: audioFile.data());
528
529 // Check that QAudioInput immediately transitions to ActiveState or IdleState
530 QTRY_VERIFY2((stateSignal.count() > 0),"didn't emit signals on start()");
531 QVERIFY2((audioInput.state() == QAudio::ActiveState || audioInput.state() == QAudio::IdleState),
532 "didn't transition to ActiveState or IdleState after start()");
533 QVERIFY2((audioInput.error() == QAudio::NoError), "error state is not equal to QAudio::NoError after start()");
534 QVERIFY(audioInput.periodSize() > 0);
535 stateSignal.clear();
536
537 // Check that 'elapsed' increases
538 QTest::qWait(ms: 40);
539 QVERIFY2((audioInput.elapsedUSecs() > 0), "elapsedUSecs() is still zero after start()");
540
541 // Allow some recording to happen
542 QTest::qWait(ms: 3000); // 3 seconds should be plenty
543
544 QVERIFY2((audioInput.state() == QAudio::ActiveState),
545 "didn't transition to ActiveState after some recording");
546 QVERIFY2((audioInput.error() == QAudio::NoError), "error state is not equal to QAudio::NoError after some recording");
547
548 stateSignal.clear();
549
550 audioInput.suspend();
551
552 // Give backends running in separate threads a chance to suspend.
553 QTest::qWait(ms: 100);
554
555 QVERIFY2((stateSignal.count() == 1),
556 QString("didn't emit SuspendedState signal after suspend(), got %1 signals instead").arg(stateSignal.count()).toLocal8Bit().constData());
557 QVERIFY2((audioInput.state() == QAudio::SuspendedState), "didn't transitions to SuspendedState after stop()");
558 QVERIFY2((audioInput.error() == QAudio::NoError), "error() is not QAudio::NoError after stop()");
559 stateSignal.clear();
560
561 // Check that only 'elapsed', and not 'processed' increases while suspended
562 qint64 elapsedUs = audioInput.elapsedUSecs();
563 qint64 processedUs = audioInput.processedUSecs();
564 QTest::qWait(ms: 1000);
565 QVERIFY(audioInput.elapsedUSecs() > elapsedUs);
566 QVERIFY(audioInput.processedUSecs() == processedUs);
567
568 audioInput.resume();
569
570 // Give backends running in separate threads a chance to resume.
571 QTest::qWait(ms: 100);
572
573 // Check that QAudioInput immediately transitions to ActiveState
574 QVERIFY2((stateSignal.count() == 1),
575 QString("didn't emit signal after resume(), got %1 signals instead").arg(stateSignal.count()).toLocal8Bit().constData());
576 QVERIFY2((audioInput.state() == QAudio::ActiveState), "didn't transition to ActiveState after resume()");
577 QVERIFY2((audioInput.error() == QAudio::NoError), "error state is not equal to QAudio::NoError after resume()");
578 stateSignal.clear();
579
580 processedUs = audioInput.processedUSecs();
581
582 audioInput.stop();
583 QTest::qWait(ms: 40);
584 QTRY_VERIFY2((stateSignal.count() == 1),
585 QString("didn't emit StoppedState signal after stop(), got %1 signals instead").arg(stateSignal.count()).toLocal8Bit().constData());
586 QVERIFY2((audioInput.state() == QAudio::StoppedState), "didn't transitions to StoppedState after stop()");
587
588 QVERIFY2(qTolerantCompare(processedUs, 3040000LL),
589 QString("processedUSecs() doesn't fall in acceptable range, should be 3040000 (%1)").arg(processedUs).toLocal8Bit().constData());
590 QVERIFY2((audioInput.error() == QAudio::NoError), "error() is not QAudio::NoError after stop()");
591 QVERIFY2((audioInput.elapsedUSecs() == (qint64)0), "elapsedUSecs() not equal to zero in StoppedState");
592 QVERIFY2(notifySignal.count() > 0, "not emitting notify() signal");
593
594 WavHeader::writeDataLength(device&: *audioFile,dataLength: audioFile->pos()-WavHeader::headerLength());
595 audioFile->close();
596}
597
598void tst_QAudioInput::push()
599{
600 QFETCH(FilePtr, audioFile);
601 QFETCH(QAudioFormat, audioFormat);
602
603 QAudioInput audioInput(audioFormat, this);
604
605 audioInput.setNotifyInterval(100);
606
607 QSignalSpy notifySignal(&audioInput, SIGNAL(notify()));
608 QSignalSpy stateSignal(&audioInput, SIGNAL(stateChanged(QAudio::State)));
609
610 // Check that we are in the default state before calling start
611 QVERIFY2((audioInput.state() == QAudio::StoppedState), "state() was not set to StoppedState before start()");
612 QVERIFY2((audioInput.error() == QAudio::NoError), "error() was not set to QAudio::NoError before start()");
613 QVERIFY2((audioInput.elapsedUSecs() == qint64(0)),"elapsedUSecs() not zero on creation");
614
615 audioFile->close();
616 audioFile->open(flags: QIODevice::WriteOnly);
617 WavHeader wavHeader(audioFormat);
618 QVERIFY(wavHeader.write(*audioFile));
619
620 // Set a large buffer to avoid underruns during QTest::qWaits
621 audioInput.setBufferSize(audioFormat.bytesForDuration(duration: 1000000));
622
623 QIODevice* feed = audioInput.start();
624
625 // Check that QAudioInput immediately transitions to IdleState
626 QTRY_VERIFY2((stateSignal.count() == 1),"didn't emit IdleState signal on start()");
627 QVERIFY2((audioInput.state() == QAudio::IdleState),
628 "didn't transition to IdleState after start()");
629 QVERIFY2((audioInput.error() == QAudio::NoError), "error state is not equal to QAudio::NoError after start()");
630 QVERIFY(audioInput.periodSize() > 0);
631 stateSignal.clear();
632
633 // Check that 'elapsed' increases
634 QTest::qWait(ms: 40);
635 QVERIFY2((audioInput.elapsedUSecs() > 0), "elapsedUSecs() is still zero after start()");
636
637 qint64 totalBytesRead = 0;
638 bool firstBuffer = true;
639 QByteArray buffer(AUDIO_BUFFER, 0);
640 qint64 len = (audioFormat.sampleRate()*audioFormat.channelCount()*(audioFormat.sampleSize()/8)*2); // 2 seconds
641 while (totalBytesRead < len) {
642 QTRY_VERIFY_WITH_TIMEOUT(audioInput.bytesReady() >= audioInput.periodSize(), 10000);
643 qint64 bytesRead = feed->read(data: buffer.data(), maxlen: audioInput.periodSize());
644 audioFile->write(data: buffer.constData(),len: bytesRead);
645 totalBytesRead+=bytesRead;
646 if (firstBuffer && bytesRead) {
647 // Check for transition to ActiveState when data is provided
648 QTRY_VERIFY2((stateSignal.count() == 1),"didn't emit ActiveState signal on data");
649 QVERIFY2((audioInput.state() == QAudio::ActiveState),
650 "didn't transition to ActiveState after data");
651 QVERIFY2((audioInput.error() == QAudio::NoError), "error state is not equal to QAudio::NoError after start()");
652 firstBuffer = false;
653 }
654 }
655
656 QTest::qWait(ms: 1000);
657
658 stateSignal.clear();
659
660 qint64 processedUs = audioInput.processedUSecs();
661
662 audioInput.stop();
663 QTest::qWait(ms: 40);
664 QTRY_VERIFY2((stateSignal.count() == 1),
665 QString("didn't emit StoppedState signal after stop(), got %1 signals instead").arg(stateSignal.count()).toLocal8Bit().constData());
666 QVERIFY2((audioInput.state() == QAudio::StoppedState), "didn't transitions to StoppedState after stop()");
667
668 QVERIFY2(qTolerantCompare(processedUs, 2040000LL),
669 QString("processedUSecs() doesn't fall in acceptable range, should be 2040000 (%1)").arg(processedUs).toLocal8Bit().constData());
670 QVERIFY2((audioInput.error() == QAudio::NoError), "error() is not QAudio::NoError after stop()");
671 QVERIFY2((audioInput.elapsedUSecs() == (qint64)0), "elapsedUSecs() not equal to zero in StoppedState");
672 QVERIFY2(notifySignal.count() > 0, "not emitting notify() signal");
673
674 WavHeader::writeDataLength(device&: *audioFile,dataLength: audioFile->pos()-WavHeader::headerLength());
675 audioFile->close();
676}
677
678void tst_QAudioInput::pushSuspendResume()
679{
680#ifdef Q_OS_LINUX
681 if (m_inCISystem)
682 QSKIP("QTBUG-26504 Fails 20% of time with pulseaudio backend");
683#endif
684 QFETCH(FilePtr, audioFile);
685 QFETCH(QAudioFormat, audioFormat);
686 QAudioInput audioInput(audioFormat, this);
687
688 audioInput.setNotifyInterval(100);
689 audioInput.setBufferSize(audioFormat.bytesForDuration(duration: 1000000));
690
691 QSignalSpy notifySignal(&audioInput, SIGNAL(notify()));
692 QSignalSpy stateSignal(&audioInput, SIGNAL(stateChanged(QAudio::State)));
693
694 // Check that we are in the default state before calling start
695 QVERIFY2((audioInput.state() == QAudio::StoppedState), "state() was not set to StoppedState before start()");
696 QVERIFY2((audioInput.error() == QAudio::NoError), "error() was not set to QAudio::NoError before start()");
697 QVERIFY2((audioInput.elapsedUSecs() == qint64(0)),"elapsedUSecs() not zero on creation");
698
699 audioFile->close();
700 audioFile->open(flags: QIODevice::WriteOnly);
701 WavHeader wavHeader(audioFormat);
702 QVERIFY(wavHeader.write(*audioFile));
703
704 QIODevice* feed = audioInput.start();
705
706 // Check that QAudioInput immediately transitions to IdleState
707 QTRY_VERIFY2((stateSignal.count() == 1),"didn't emit IdleState signal on start()");
708 QVERIFY2((audioInput.state() == QAudio::IdleState),
709 "didn't transition to IdleState after start()");
710 QVERIFY2((audioInput.error() == QAudio::NoError), "error state is not equal to QAudio::NoError after start()");
711 QVERIFY(audioInput.periodSize() > 0);
712 stateSignal.clear();
713
714 // Check that 'elapsed' increases
715 QTest::qWait(ms: 40);
716 QTRY_VERIFY2((audioInput.elapsedUSecs() > 0), "elapsedUSecs() is still zero after start()");
717
718 qint64 totalBytesRead = 0;
719 bool firstBuffer = true;
720 QByteArray buffer(AUDIO_BUFFER, 0);
721 qint64 len = (audioFormat.sampleRate()*audioFormat.channelCount()*(audioFormat.sampleSize()/8)); // 1 seconds
722 while (totalBytesRead < len) {
723 QTRY_VERIFY_WITH_TIMEOUT(audioInput.bytesReady() >= audioInput.periodSize(), 10000);
724 qint64 bytesRead = feed->read(data: buffer.data(), maxlen: audioInput.periodSize());
725 audioFile->write(data: buffer.constData(),len: bytesRead);
726 totalBytesRead+=bytesRead;
727 if (firstBuffer && bytesRead) {
728 // Check for transition to ActiveState when data is provided
729 QTRY_VERIFY2((stateSignal.count() == 1),"didn't emit ActiveState signal on data");
730 QVERIFY2((audioInput.state() == QAudio::ActiveState),
731 "didn't transition to ActiveState after data");
732 QVERIFY2((audioInput.error() == QAudio::NoError), "error state is not equal to QAudio::NoError after start()");
733 firstBuffer = false;
734 }
735 }
736 stateSignal.clear();
737
738 audioInput.suspend();
739
740 // Give backends running in separate threads a chance to suspend
741 QTest::qWait(ms: 100);
742
743 QVERIFY2((stateSignal.count() == 1),
744 QString("didn't emit SuspendedState signal after suspend(), got %1 signals instead").arg(stateSignal.count()).toLocal8Bit().constData());
745 QVERIFY2((audioInput.state() == QAudio::SuspendedState), "didn't transitions to SuspendedState after stop()");
746 QVERIFY2((audioInput.error() == QAudio::NoError), "error() is not QAudio::NoError after stop()");
747 stateSignal.clear();
748
749 // Check that only 'elapsed', and not 'processed' increases while suspended
750 qint64 elapsedUs = audioInput.elapsedUSecs();
751 qint64 processedUs = audioInput.processedUSecs();
752 QTest::qWait(ms: 1000);
753 QVERIFY(audioInput.elapsedUSecs() > elapsedUs);
754 QVERIFY(audioInput.processedUSecs() == processedUs);
755
756 // Drain any data, in case we run out of space when resuming
757 const int reads = audioInput.bytesReady() / audioInput.periodSize();
758 for (int r = 0; r < reads; ++r)
759 feed->read(data: buffer.data(), maxlen: audioInput.periodSize());
760
761 audioInput.resume();
762
763 // Check that QAudioInput immediately transitions to Active or IdleState
764 QTRY_VERIFY2((stateSignal.count() > 0),"didn't emit signals on resume()");
765 QVERIFY2((audioInput.state() == QAudio::ActiveState || audioInput.state() == QAudio::IdleState),
766 "didn't transition to ActiveState or IdleState after resume()");
767 QVERIFY2((audioInput.error() == QAudio::NoError), "error state is not equal to QAudio::NoError after resume()");
768 QVERIFY(audioInput.periodSize() > 0);
769
770 // Let it play out what is in buffer and go to Idle before continue
771 QTest::qWait(ms: 1000);
772 stateSignal.clear();
773
774 // Read another seconds worth
775 totalBytesRead = 0;
776 firstBuffer = true;
777 while (totalBytesRead < len && audioInput.state() != QAudio::StoppedState) {
778 QTRY_VERIFY_WITH_TIMEOUT(audioInput.bytesReady() >= audioInput.periodSize(), 10000);
779 qint64 bytesRead = feed->read(data: buffer.data(), maxlen: audioInput.periodSize());
780 audioFile->write(data: buffer.constData(),len: bytesRead);
781 totalBytesRead+=bytesRead;
782 }
783 stateSignal.clear();
784
785 processedUs = audioInput.processedUSecs();
786
787 audioInput.stop();
788 QTest::qWait(ms: 40);
789 QVERIFY2((stateSignal.count() == 1),
790 QString("didn't emit StoppedState signal after stop(), got %1 signals instead").arg(stateSignal.count()).toLocal8Bit().constData());
791 QVERIFY2((audioInput.state() == QAudio::StoppedState), "didn't transitions to StoppedState after stop()");
792
793 QVERIFY2(qTolerantCompare(processedUs, 2040000LL),
794 QString("processedUSecs() doesn't fall in acceptable range, should be 2040000 (%1)").arg(processedUs).toLocal8Bit().constData());
795 QVERIFY2((audioInput.elapsedUSecs() == (qint64)0), "elapsedUSecs() not equal to zero in StoppedState");
796
797 WavHeader::writeDataLength(device&: *audioFile,dataLength: audioFile->pos()-WavHeader::headerLength());
798 audioFile->close();
799}
800
801void tst_QAudioInput::reset()
802{
803 QFETCH(QAudioFormat, audioFormat);
804
805 // Try both push/pull.. the vagaries of Active vs Idle are tested elsewhere
806 {
807 QAudioInput audioInput(audioFormat, this);
808
809 audioInput.setNotifyInterval(100);
810
811 QSignalSpy notifySignal(&audioInput, SIGNAL(notify()));
812 QSignalSpy stateSignal(&audioInput, SIGNAL(stateChanged(QAudio::State)));
813
814 // Check that we are in the default state before calling start
815 QVERIFY2((audioInput.state() == QAudio::StoppedState), "state() was not set to StoppedState before start()");
816 QVERIFY2((audioInput.error() == QAudio::NoError), "error() was not set to QAudio::NoError before start()");
817 QVERIFY2((audioInput.elapsedUSecs() == qint64(0)),"elapsedUSecs() not zero on creation");
818
819 QIODevice* device = audioInput.start();
820 // Check that QAudioInput immediately transitions to IdleState
821 QTRY_VERIFY2((stateSignal.count() == 1),"didn't emit IdleState signal on start()");
822 QVERIFY2((audioInput.state() == QAudio::IdleState), "didn't transition to IdleState after start()");
823 QVERIFY2((audioInput.error() == QAudio::NoError), "error state is not equal to QAudio::NoError after start()");
824 QVERIFY(audioInput.periodSize() > 0);
825 QTRY_VERIFY2_WITH_TIMEOUT((audioInput.bytesReady() > audioInput.periodSize()), "no bytes available after starting", 10000);
826
827 // Trigger a read
828 QByteArray data = device->read(maxlen: audioInput.periodSize());
829 QVERIFY2((audioInput.error() == QAudio::NoError), "error state is not equal to QAudio::NoError after start()");
830 stateSignal.clear();
831
832 audioInput.reset();
833 QTRY_VERIFY2((stateSignal.count() == 1),"didn't emit StoppedState signal after reset()");
834 QVERIFY2((audioInput.state() == QAudio::StoppedState), "didn't transitions to StoppedState after reset()");
835 QVERIFY2((audioInput.bytesReady() == 0), "buffer not cleared after reset()");
836 }
837
838 {
839 QAudioInput audioInput(audioFormat, this);
840 QBuffer buffer;
841
842 audioInput.setNotifyInterval(100);
843
844 QSignalSpy notifySignal(&audioInput, SIGNAL(notify()));
845 QSignalSpy stateSignal(&audioInput, SIGNAL(stateChanged(QAudio::State)));
846
847 // Check that we are in the default state before calling start
848 QVERIFY2((audioInput.state() == QAudio::StoppedState), "state() was not set to StoppedState before start()");
849 QVERIFY2((audioInput.error() == QAudio::NoError), "error() was not set to QAudio::NoError before start()");
850 QVERIFY2((audioInput.elapsedUSecs() == qint64(0)),"elapsedUSecs() not zero on creation");
851
852 audioInput.start(device: &buffer);
853
854 // Check that QAudioInput immediately transitions to ActiveState
855 QTRY_VERIFY2((stateSignal.count() >= 1),"didn't emit state changed signal on start()");
856 QTRY_VERIFY2((audioInput.state() == QAudio::ActiveState), "didn't transition to ActiveState after start()");
857 QVERIFY2((audioInput.error() == QAudio::NoError), "error state is not equal to QAudio::NoError after start()");
858 QVERIFY(audioInput.periodSize() > 0);
859 stateSignal.clear();
860
861 audioInput.reset();
862 QTRY_VERIFY2((stateSignal.count() >= 1),"didn't emit StoppedState signal after reset()");
863 QVERIFY2((audioInput.state() == QAudio::StoppedState), "didn't transitions to StoppedState after reset()");
864 QVERIFY2((audioInput.bytesReady() == 0), "buffer not cleared after reset()");
865 }
866}
867
868void tst_QAudioInput::volume()
869{
870 QFETCH(QAudioFormat, audioFormat);
871
872 const qreal half(0.5f);
873 const qreal one(1.0f);
874
875 QAudioInput audioInput(audioFormat, this);
876
877 qreal volume = audioInput.volume();
878 audioInput.setVolume(half);
879 QTRY_VERIFY(qRound(audioInput.volume()*10.0f) == 5);
880 // Wait a while to see if this changes
881 QTest::qWait(ms: 500);
882 QTRY_VERIFY(qRound(audioInput.volume()*10.0f) == 5);
883
884 audioInput.setVolume(one);
885 QTRY_VERIFY(qRound(audioInput.volume()*10.0f) == 10);
886 // Wait a while to see if this changes
887 QTest::qWait(ms: 500);
888 QTRY_VERIFY(qRound(audioInput.volume()*10.0f) == 10);
889
890 audioInput.setVolume(half);
891 audioInput.start();
892 QTRY_VERIFY(qRound(audioInput.volume()*10.0f) == 5);
893 audioInput.setVolume(one);
894 QTRY_VERIFY(qRound(audioInput.volume()*10.0f) == 10);
895
896 audioInput.setVolume(volume);
897}
898
899QTEST_MAIN(tst_QAudioInput)
900
901#include "tst_qaudioinput.moc"
902

source code of qtmultimedia/tests/auto/integration/qaudioinput/tst_qaudioinput.cpp