source: trunk/examples/multimedia/audioinput/audioinput.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: 11.4 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 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:BSD$
10** You may use this file under the terms of the BSD license as follows:
11**
12** "Redistribution and use in source and binary forms, with or without
13** modification, are permitted provided that the following conditions are
14** met:
15** * Redistributions of source code must retain the above copyright
16** notice, this list of conditions and the following disclaimer.
17** * Redistributions in binary form must reproduce the above copyright
18** notice, this list of conditions and the following disclaimer in
19** the documentation and/or other materials provided with the
20** distribution.
21** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
22** the names of its contributors may be used to endorse or promote
23** products derived from this software without specific prior written
24** permission.
25**
26** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
27** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
28** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
29** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
30** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
31** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
32** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
33** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
34** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
35** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
36** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
37** $QT_END_LICENSE$
38**
39****************************************************************************/
40
41#include <stdlib.h>
42#include <math.h>
43
44#include <QDebug>
45#include <QPainter>
46#include <QVBoxLayout>
47
48#include <QAudioDeviceInfo>
49#include <QAudioInput>
50
51#include <QtCore/qendian.h>
52
53#include "audioinput.h"
54
55const QString InputTest::PushModeLabel(tr("Enable push mode"));
56const QString InputTest::PullModeLabel(tr("Enable pull mode"));
57const QString InputTest::SuspendLabel(tr("Suspend recording"));
58const QString InputTest::ResumeLabel(tr("Resume recording"));
59
60const int BufferSize = 4096;
61
62AudioInfo::AudioInfo(const QAudioFormat &format, QObject *parent)
63 : QIODevice(parent)
64 , m_format(format)
65 , m_maxAmplitude(0)
66 , m_level(0.0)
67
68{
69 switch (m_format.sampleSize()) {
70 case 8:
71 switch (m_format.sampleType()) {
72 case QAudioFormat::UnSignedInt:
73 m_maxAmplitude = 255;
74 break;
75 case QAudioFormat::SignedInt:
76 m_maxAmplitude = 127;
77 break;
78 default:
79 break;
80 }
81 break;
82 case 16:
83 switch (m_format.sampleType()) {
84 case QAudioFormat::UnSignedInt:
85 m_maxAmplitude = 65535;
86 break;
87 case QAudioFormat::SignedInt:
88 m_maxAmplitude = 32767;
89 break;
90 default:
91 break;
92 }
93 break;
94 default:
95 break;
96 }
97}
98
99AudioInfo::~AudioInfo()
100{
101}
102
103void AudioInfo::start()
104{
105 open(QIODevice::WriteOnly);
106}
107
108void AudioInfo::stop()
109{
110 close();
111}
112
113qint64 AudioInfo::readData(char *data, qint64 maxlen)
114{
115 Q_UNUSED(data)
116 Q_UNUSED(maxlen)
117
118 return 0;
119}
120
121qint64 AudioInfo::writeData(const char *data, qint64 len)
122{
123 if (m_maxAmplitude) {
124 Q_ASSERT(m_format.sampleSize() % 8 == 0);
125 const int channelBytes = m_format.sampleSize() / 8;
126 const int sampleBytes = m_format.channels() * channelBytes;
127 Q_ASSERT(len % sampleBytes == 0);
128 const int numSamples = len / sampleBytes;
129
130 quint16 maxValue = 0;
131 const unsigned char *ptr = reinterpret_cast<const unsigned char *>(data);
132
133 for (int i = 0; i < numSamples; ++i) {
134 for(int j = 0; j < m_format.channels(); ++j) {
135 quint16 value = 0;
136
137 if (m_format.sampleSize() == 8 && m_format.sampleType() == QAudioFormat::UnSignedInt) {
138 value = *reinterpret_cast<const quint8*>(ptr);
139 } else if (m_format.sampleSize() == 8 && m_format.sampleType() == QAudioFormat::SignedInt) {
140 value = qAbs(*reinterpret_cast<const qint8*>(ptr));
141 } else if (m_format.sampleSize() == 16 && m_format.sampleType() == QAudioFormat::UnSignedInt) {
142 if (m_format.byteOrder() == QAudioFormat::LittleEndian)
143 value = qFromLittleEndian<quint16>(ptr);
144 else
145 value = qFromBigEndian<quint16>(ptr);
146 } else if (m_format.sampleSize() == 16 && m_format.sampleType() == QAudioFormat::SignedInt) {
147 if (m_format.byteOrder() == QAudioFormat::LittleEndian)
148 value = qAbs(qFromLittleEndian<qint16>(ptr));
149 else
150 value = qAbs(qFromBigEndian<qint16>(ptr));
151 }
152
153 maxValue = qMax(value, maxValue);
154 ptr += channelBytes;
155 }
156 }
157
158 maxValue = qMin(maxValue, m_maxAmplitude);
159 m_level = qreal(maxValue) / m_maxAmplitude;
160 }
161
162 emit update();
163 return len;
164}
165
166RenderArea::RenderArea(QWidget *parent)
167 : QWidget(parent)
168{
169 setBackgroundRole(QPalette::Base);
170 setAutoFillBackground(true);
171
172 m_level = 0;
173 setMinimumHeight(30);
174 setMinimumWidth(200);
175}
176
177void RenderArea::paintEvent(QPaintEvent * /* event */)
178{
179 QPainter painter(this);
180
181 painter.setPen(Qt::black);
182 painter.drawRect(QRect(painter.viewport().left()+10,
183 painter.viewport().top()+10,
184 painter.viewport().right()-20,
185 painter.viewport().bottom()-20));
186 if (m_level == 0.0)
187 return;
188
189 painter.setPen(Qt::red);
190
191 int pos = ((painter.viewport().right()-20)-(painter.viewport().left()+11))*m_level;
192 for (int i = 0; i < 10; ++i) {
193 int x1 = painter.viewport().left()+11;
194 int y1 = painter.viewport().top()+10+i;
195 int x2 = painter.viewport().left()+20+pos;
196 int y2 = painter.viewport().top()+10+i;
197 if (x2 < painter.viewport().left()+10)
198 x2 = painter.viewport().left()+10;
199
200 painter.drawLine(QPoint(x1, y1),QPoint(x2, y2));
201 }
202}
203
204void RenderArea::setLevel(qreal value)
205{
206 m_level = value;
207 repaint();
208}
209
210
211InputTest::InputTest()
212 : m_canvas(0)
213 , m_modeButton(0)
214 , m_suspendResumeButton(0)
215 , m_deviceBox(0)
216 , m_device(QAudioDeviceInfo::defaultInputDevice())
217 , m_audioInfo(0)
218 , m_audioInput(0)
219 , m_input(0)
220 , m_pullMode(false)
221 , m_buffer(BufferSize, 0)
222{
223 initializeWindow();
224 initializeAudio();
225}
226
227InputTest::~InputTest() {}
228
229void InputTest::initializeWindow()
230{
231 QScopedPointer<QWidget> window(new QWidget);
232 QScopedPointer<QVBoxLayout> layout(new QVBoxLayout);
233
234 m_canvas = new RenderArea(this);
235 layout->addWidget(m_canvas);
236
237 m_deviceBox = new QComboBox(this);
238 QList<QAudioDeviceInfo> devices = QAudioDeviceInfo::availableDevices(QAudio::AudioInput);
239 for(int i = 0; i < devices.size(); ++i)
240 m_deviceBox->addItem(devices.at(i).deviceName(), qVariantFromValue(devices.at(i)));
241
242 connect(m_deviceBox, SIGNAL(activated(int)), SLOT(deviceChanged(int)));
243 layout->addWidget(m_deviceBox);
244
245 m_modeButton = new QPushButton(this);
246 m_modeButton->setText(PushModeLabel);
247 connect(m_modeButton, SIGNAL(clicked()), SLOT(toggleMode()));
248 layout->addWidget(m_modeButton);
249
250 m_suspendResumeButton = new QPushButton(this);
251 m_suspendResumeButton->setText(SuspendLabel);
252 connect(m_suspendResumeButton, SIGNAL(clicked()), SLOT(toggleSuspend()));
253 layout->addWidget(m_suspendResumeButton);
254
255 window->setLayout(layout.data());
256 layout.take(); // ownership transferred
257
258 setCentralWidget(window.data());
259 QWidget *const windowPtr = window.take(); // ownership transferred
260 windowPtr->show();
261}
262
263void InputTest::initializeAudio()
264{
265 m_pullMode = true;
266
267 m_format.setFrequency(8000);
268 m_format.setChannels(1);
269 m_format.setSampleSize(16);
270 m_format.setSampleType(QAudioFormat::SignedInt);
271 m_format.setByteOrder(QAudioFormat::LittleEndian);
272 m_format.setCodec("audio/pcm");
273
274 QAudioDeviceInfo info(QAudioDeviceInfo::defaultInputDevice());
275 if (!info.isFormatSupported(m_format)) {
276 qWarning() << "Default format not supported - trying to use nearest";
277 m_format = info.nearestFormat(m_format);
278 }
279
280 m_audioInfo = new AudioInfo(m_format, this);
281 connect(m_audioInfo, SIGNAL(update()), SLOT(refreshDisplay()));
282
283 createAudioInput();
284}
285
286void InputTest::createAudioInput()
287{
288 m_audioInput = new QAudioInput(m_device, m_format, this);
289 connect(m_audioInput, SIGNAL(notify()), SLOT(notified()));
290 connect(m_audioInput, SIGNAL(stateChanged(QAudio::State)), SLOT(stateChanged(QAudio::State)));
291 m_audioInfo->start();
292 m_audioInput->start(m_audioInfo);
293}
294
295void InputTest::notified()
296{
297 qWarning() << "bytesReady = " << m_audioInput->bytesReady()
298 << ", " << "elapsedUSecs = " <<m_audioInput->elapsedUSecs()
299 << ", " << "processedUSecs = "<<m_audioInput->processedUSecs();
300}
301
302void InputTest::readMore()
303{
304 if(!m_audioInput)
305 return;
306 qint64 len = m_audioInput->bytesReady();
307 if(len > 4096)
308 len = 4096;
309 qint64 l = m_input->read(m_buffer.data(), len);
310 if(l > 0) {
311 m_audioInfo->write(m_buffer.constData(), l);
312 }
313}
314
315void InputTest::toggleMode()
316{
317 // Change bewteen pull and push modes
318 if (m_input != 0) {
319 disconnect(m_input, 0, this, 0);
320 m_input = 0;
321 }
322
323 m_audioInput->stop();
324
325 if (m_pullMode) {
326 m_modeButton->setText(PullModeLabel);
327 m_input = m_audioInput->start();
328 connect(m_input, SIGNAL(readyRead()), SLOT(readMore()));
329 m_pullMode = false;
330 } else {
331 m_modeButton->setText(PushModeLabel);
332 m_pullMode = true;
333 m_audioInput->start(m_audioInfo);
334 }
335
336 m_suspendResumeButton->setText(SuspendLabel);
337}
338
339void InputTest::toggleSuspend()
340{
341 // toggle suspend/resume
342 if(m_audioInput->state() == QAudio::SuspendedState) {
343 qWarning() << "status: Suspended, resume()";
344 m_audioInput->resume();
345 m_suspendResumeButton->setText(SuspendLabel);
346 } else if (m_audioInput->state() == QAudio::ActiveState) {
347 qWarning() << "status: Active, suspend()";
348 m_audioInput->suspend();
349 m_suspendResumeButton->setText(ResumeLabel);
350 } else if (m_audioInput->state() == QAudio::StoppedState) {
351 qWarning() << "status: Stopped, resume()";
352 m_audioInput->resume();
353 m_suspendResumeButton->setText(SuspendLabel);
354 } else if (m_audioInput->state() == QAudio::IdleState) {
355 qWarning() << "status: IdleState";
356 }
357}
358
359void InputTest::stateChanged(QAudio::State state)
360{
361 qWarning() << "state = " << state;
362}
363
364void InputTest::refreshDisplay()
365{
366 m_canvas->setLevel(m_audioInfo->level());
367 m_canvas->repaint();
368}
369
370void InputTest::deviceChanged(int index)
371{
372 m_audioInfo->stop();
373 m_audioInput->stop();
374 m_audioInput->disconnect(this);
375 delete m_audioInput;
376
377 m_device = m_deviceBox->itemData(index).value<QAudioDeviceInfo>();
378 createAudioInput();
379}
Note: See TracBrowser for help on using the repository browser.