source: trunk/examples/multimedia/audiooutput/audiooutput.cpp@ 769

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

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

  • Property svn:eol-style set to native
File size: 10.3 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation (qt-info@nokia.com)
6**
7** This file is part of the examples 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 qt-info@nokia.com.
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include <QDebug>
43#include <QVBoxLayout>
44
45#include <QAudioOutput>
46#include <QAudioDeviceInfo>
47#include <QtCore/qmath.h>
48#include <QtCore/qendian.h>
49#include "audiooutput.h"
50
51const QString AudioTest::PushModeLabel(tr("Enable push mode"));
52const QString AudioTest::PullModeLabel(tr("Enable pull mode"));
53const QString AudioTest::SuspendLabel(tr("Suspend playback"));
54const QString AudioTest::ResumeLabel(tr("Resume playback"));
55
56const int DurationSeconds = 1;
57const int ToneFrequencyHz = 600;
58const int DataFrequencyHz = 44100;
59const int BufferSize = 32768;
60
61
62Generator::Generator(const QAudioFormat &format,
63 qint64 durationUs,
64 int frequency,
65 QObject *parent)
66 : QIODevice(parent)
67 , m_pos(0)
68{
69 generateData(format, durationUs, frequency);
70}
71
72Generator::~Generator()
73{
74
75}
76
77void Generator::start()
78{
79 open(QIODevice::ReadOnly);
80}
81
82void Generator::stop()
83{
84 m_pos = 0;
85 close();
86}
87
88void Generator::generateData(const QAudioFormat &format, qint64 durationUs, int frequency)
89{
90 const int channelBytes = format.sampleSize() / 8;
91 const int sampleBytes = format.channels() * channelBytes;
92
93 qint64 length = (format.frequency() * format.channels() * (format.sampleSize() / 8))
94 * durationUs / 100000;
95
96 Q_ASSERT(length % sampleBytes == 0);
97 Q_UNUSED(sampleBytes) // suppress warning in release builds
98
99 m_buffer.resize(length);
100 unsigned char *ptr = reinterpret_cast<unsigned char *>(m_buffer.data());
101 int sampleIndex = 0;
102
103 while (length) {
104 const qreal x = qSin(2 * M_PI * frequency * qreal(sampleIndex % format.frequency()) / format.frequency());
105 for (int i=0; i<format.channels(); ++i) {
106 if (format.sampleSize() == 8 && format.sampleType() == QAudioFormat::UnSignedInt) {
107 const quint8 value = static_cast<quint8>((1.0 + x) / 2 * 255);
108 *reinterpret_cast<quint8*>(ptr) = value;
109 } else if (format.sampleSize() == 8 && format.sampleType() == QAudioFormat::SignedInt) {
110 const qint8 value = static_cast<qint8>(x * 127);
111 *reinterpret_cast<quint8*>(ptr) = value;
112 } else if (format.sampleSize() == 16 && format.sampleType() == QAudioFormat::UnSignedInt) {
113 quint16 value = static_cast<quint16>((1.0 + x) / 2 * 65535);
114 if (format.byteOrder() == QAudioFormat::LittleEndian)
115 qToLittleEndian<quint16>(value, ptr);
116 else
117 qToBigEndian<quint16>(value, ptr);
118 } else if (format.sampleSize() == 16 && format.sampleType() == QAudioFormat::SignedInt) {
119 qint16 value = static_cast<qint16>(x * 32767);
120 if (format.byteOrder() == QAudioFormat::LittleEndian)
121 qToLittleEndian<qint16>(value, ptr);
122 else
123 qToBigEndian<qint16>(value, ptr);
124 }
125
126 ptr += channelBytes;
127 length -= channelBytes;
128 }
129 ++sampleIndex;
130 }
131}
132
133qint64 Generator::readData(char *data, qint64 len)
134{
135 qint64 total = 0;
136 while (len - total) {
137 const qint64 chunk = qMin((m_buffer.size() - m_pos), len - total);
138 memcpy(data, m_buffer.constData() + m_pos, chunk);
139 m_pos = (m_pos + chunk) % m_buffer.size();
140 total += chunk;
141 }
142 return total;
143}
144
145qint64 Generator::writeData(const char *data, qint64 len)
146{
147 Q_UNUSED(data);
148 Q_UNUSED(len);
149
150 return 0;
151}
152
153qint64 Generator::bytesAvailable() const
154{
155 return m_buffer.size() + QIODevice::bytesAvailable();
156}
157
158AudioTest::AudioTest()
159 : m_pullTimer(new QTimer(this))
160 , m_modeButton(0)
161 , m_suspendResumeButton(0)
162 , m_deviceBox(0)
163 , m_device(QAudioDeviceInfo::defaultOutputDevice())
164 , m_generator(0)
165 , m_audioOutput(0)
166 , m_output(0)
167 , m_buffer(BufferSize, 0)
168{
169 initializeWindow();
170 initializeAudio();
171}
172
173void AudioTest::initializeWindow()
174{
175 QScopedPointer<QWidget> window(new QWidget);
176 QScopedPointer<QVBoxLayout> layout(new QVBoxLayout);
177
178 m_deviceBox = new QComboBox(this);
179 foreach (const QAudioDeviceInfo &deviceInfo, QAudioDeviceInfo::availableDevices(QAudio::AudioOutput))
180 m_deviceBox->addItem(deviceInfo.deviceName(), qVariantFromValue(deviceInfo));
181 connect(m_deviceBox,SIGNAL(activated(int)),SLOT(deviceChanged(int)));
182 layout->addWidget(m_deviceBox);
183
184 m_modeButton = new QPushButton(this);
185 m_modeButton->setText(PushModeLabel);
186 connect(m_modeButton, SIGNAL(clicked()), SLOT(toggleMode()));
187 layout->addWidget(m_modeButton);
188
189 m_suspendResumeButton = new QPushButton(this);
190 m_suspendResumeButton->setText(SuspendLabel);
191 connect(m_suspendResumeButton, SIGNAL(clicked()), SLOT(toggleSuspendResume()));
192 layout->addWidget(m_suspendResumeButton);
193
194 window->setLayout(layout.data());
195 layout.take(); // ownership transferred
196
197 setCentralWidget(window.data());
198 QWidget *const windowPtr = window.take(); // ownership transferred
199 windowPtr->show();
200}
201
202void AudioTest::initializeAudio()
203{
204 connect(m_pullTimer, SIGNAL(timeout()), SLOT(pullTimerExpired()));
205
206 m_pullMode = true;
207
208 m_format.setFrequency(DataFrequencyHz);
209 m_format.setChannels(1);
210 m_format.setSampleSize(16);
211 m_format.setCodec("audio/pcm");
212 m_format.setByteOrder(QAudioFormat::LittleEndian);
213 m_format.setSampleType(QAudioFormat::SignedInt);
214
215 QAudioDeviceInfo info(QAudioDeviceInfo::defaultOutputDevice());
216 if (!info.isFormatSupported(m_format)) {
217 qWarning() << "Default format not supported - trying to use nearest";
218 m_format = info.nearestFormat(m_format);
219 }
220
221 m_generator = new Generator(m_format, DurationSeconds*1000000, ToneFrequencyHz, this);
222
223 createAudioOutput();
224}
225
226void AudioTest::createAudioOutput()
227{
228 delete m_audioOutput;
229 m_audioOutput = 0;
230 m_audioOutput = new QAudioOutput(m_device, m_format, this);
231 connect(m_audioOutput, SIGNAL(notify()), SLOT(notified()));
232 connect(m_audioOutput, SIGNAL(stateChanged(QAudio::State)), SLOT(stateChanged(QAudio::State)));
233 m_generator->start();
234 m_audioOutput->start(m_generator);
235}
236
237AudioTest::~AudioTest()
238{
239
240}
241
242void AudioTest::deviceChanged(int index)
243{
244 m_pullTimer->stop();
245 m_generator->stop();
246 m_audioOutput->stop();
247 m_audioOutput->disconnect(this);
248 m_device = m_deviceBox->itemData(index).value<QAudioDeviceInfo>();
249 createAudioOutput();
250}
251
252void AudioTest::notified()
253{
254 qWarning() << "bytesFree = " << m_audioOutput->bytesFree()
255 << ", " << "elapsedUSecs = " << m_audioOutput->elapsedUSecs()
256 << ", " << "processedUSecs = " << m_audioOutput->processedUSecs();
257}
258
259void AudioTest::pullTimerExpired()
260{
261 if (m_audioOutput && m_audioOutput->state() != QAudio::StoppedState) {
262 int chunks = m_audioOutput->bytesFree()/m_audioOutput->periodSize();
263 while (chunks) {
264 const qint64 len = m_generator->read(m_buffer.data(), m_audioOutput->periodSize());
265 if (len)
266 m_output->write(m_buffer.data(), len);
267 if (len != m_audioOutput->periodSize())
268 break;
269 --chunks;
270 }
271 }
272}
273
274void AudioTest::toggleMode()
275{
276 m_pullTimer->stop();
277 m_audioOutput->stop();
278
279 if (m_pullMode) {
280 m_modeButton->setText(PullModeLabel);
281 m_output = m_audioOutput->start();
282 m_pullMode = false;
283 m_pullTimer->start(20);
284 } else {
285 m_modeButton->setText(PushModeLabel);
286 m_pullMode = true;
287 m_audioOutput->start(m_generator);
288 }
289
290 m_suspendResumeButton->setText(SuspendLabel);
291}
292
293void AudioTest::toggleSuspendResume()
294{
295 if (m_audioOutput->state() == QAudio::SuspendedState) {
296 qWarning() << "status: Suspended, resume()";
297 m_audioOutput->resume();
298 m_suspendResumeButton->setText(SuspendLabel);
299 } else if (m_audioOutput->state() == QAudio::ActiveState) {
300 qWarning() << "status: Active, suspend()";
301 m_audioOutput->suspend();
302 m_suspendResumeButton->setText(ResumeLabel);
303 } else if (m_audioOutput->state() == QAudio::StoppedState) {
304 qWarning() << "status: Stopped, resume()";
305 m_audioOutput->resume();
306 m_suspendResumeButton->setText(SuspendLabel);
307 } else if (m_audioOutput->state() == QAudio::IdleState) {
308 qWarning() << "status: IdleState";
309 }
310}
311
312void AudioTest::stateChanged(QAudio::State state)
313{
314 qWarning() << "state = " << state;
315}
Note: See TracBrowser for help on using the repository browser.