source: trunk/src/multimedia/audio/qaudioinput_symbian_p.cpp

Last change on this file was 846, checked in by Dmitry A. Kuminov, 14 years ago

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

File size: 16.1 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 QtMultimedia module 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 "qaudioinput_symbian_p.h"
43
44QT_BEGIN_NAMESPACE
45
46//-----------------------------------------------------------------------------
47// Constants
48//-----------------------------------------------------------------------------
49
50const int PushInterval = 50; // ms
51
52
53//-----------------------------------------------------------------------------
54// Private class
55//-----------------------------------------------------------------------------
56
57SymbianAudioInputPrivate::SymbianAudioInputPrivate(
58 QAudioInputPrivate *audioDevice)
59 : m_audioDevice(audioDevice)
60{
61
62}
63
64SymbianAudioInputPrivate::~SymbianAudioInputPrivate()
65{
66
67}
68
69qint64 SymbianAudioInputPrivate::readData(char *data, qint64 len)
70{
71 qint64 totalRead = 0;
72
73 if (m_audioDevice->state() == QAudio::ActiveState ||
74 m_audioDevice->state() == QAudio::IdleState) {
75
76 while (totalRead < len) {
77 const qint64 read = m_audioDevice->read(data + totalRead,
78 len - totalRead);
79 if (read > 0)
80 totalRead += read;
81 else
82 break;
83 }
84 }
85
86 return totalRead;
87}
88
89qint64 SymbianAudioInputPrivate::writeData(const char *data, qint64 len)
90{
91 Q_UNUSED(data)
92 Q_UNUSED(len)
93 return 0;
94}
95
96void SymbianAudioInputPrivate::dataReady()
97{
98 emit readyRead();
99}
100
101
102//-----------------------------------------------------------------------------
103// Public functions
104//-----------------------------------------------------------------------------
105
106QAudioInputPrivate::QAudioInputPrivate(const QByteArray &device,
107 const QAudioFormat &format)
108 : m_device(device)
109 , m_format(format)
110 , m_clientBufferSize(SymbianAudio::DefaultBufferSize)
111 , m_notifyInterval(SymbianAudio::DefaultNotifyInterval)
112 , m_notifyTimer(new QTimer(this))
113 , m_error(QAudio::NoError)
114 , m_internalState(SymbianAudio::ClosedState)
115 , m_externalState(QAudio::StoppedState)
116 , m_pullMode(false)
117 , m_sink(0)
118 , m_pullTimer(new QTimer(this))
119 , m_devSound(0)
120 , m_devSoundBuffer(0)
121 , m_devSoundBufferSize(0)
122 , m_totalBytesReady(0)
123 , m_devSoundBufferPos(0)
124 , m_totalSamplesRecorded(0)
125{
126 qRegisterMetaType<CMMFBuffer *>("CMMFBuffer *");
127
128 connect(m_notifyTimer.data(), SIGNAL(timeout()), this, SIGNAL(notify()));
129
130 m_pullTimer->setInterval(PushInterval);
131 connect(m_pullTimer.data(), SIGNAL(timeout()), this, SLOT(pullData()));
132}
133
134QAudioInputPrivate::~QAudioInputPrivate()
135{
136 close();
137}
138
139QIODevice* QAudioInputPrivate::start(QIODevice *device)
140{
141 stop();
142
143 open();
144 if (SymbianAudio::ClosedState != m_internalState) {
145 if (device) {
146 m_pullMode = true;
147 m_sink = device;
148 } else {
149 m_sink = new SymbianAudioInputPrivate(this);
150 m_sink->open(QIODevice::ReadOnly | QIODevice::Unbuffered);
151 }
152
153 m_elapsed.restart();
154 }
155
156 return m_sink;
157}
158
159void QAudioInputPrivate::stop()
160{
161 close();
162}
163
164void QAudioInputPrivate::reset()
165{
166 m_totalSamplesRecorded += getSamplesRecorded();
167 m_devSound->stop();
168 startRecording();
169}
170
171void QAudioInputPrivate::suspend()
172{
173 if (SymbianAudio::ActiveState == m_internalState
174 || SymbianAudio::IdleState == m_internalState) {
175 m_notifyTimer->stop();
176 m_pullTimer->stop();
177 const qint64 samplesRecorded = getSamplesRecorded();
178 m_totalSamplesRecorded += samplesRecorded;
179
180 const bool paused = m_devSound->pause();
181 if (paused) {
182 if (m_devSoundBuffer)
183 m_devSoundBufferQ.append(m_devSoundBuffer);
184 m_devSoundBuffer = 0;
185 setState(SymbianAudio::SuspendedPausedState);
186 } else {
187 m_devSoundBuffer = 0;
188 m_devSoundBufferQ.clear();
189 m_devSoundBufferPos = 0;
190 setState(SymbianAudio::SuspendedStoppedState);
191 }
192 }
193}
194
195void QAudioInputPrivate::resume()
196{
197 if (QAudio::SuspendedState == m_externalState) {
198 if (SymbianAudio::SuspendedPausedState == m_internalState)
199 m_devSound->resume();
200 else
201 m_devSound->start();
202 startDataTransfer();
203 }
204}
205
206int QAudioInputPrivate::bytesReady() const
207{
208 Q_ASSERT(m_devSoundBufferPos <= m_totalBytesReady);
209 return m_totalBytesReady - m_devSoundBufferPos;
210}
211
212int QAudioInputPrivate::periodSize() const
213{
214 return bufferSize();
215}
216
217void QAudioInputPrivate::setBufferSize(int value)
218{
219 // Note that DevSound does not allow its client to specify the buffer size.
220 // This functionality is available via custom interfaces, but since these
221 // cannot be guaranteed to work across all DevSound implementations, we
222 // do not use them here.
223 // In order to comply with the expected bevahiour of QAudioInput, we store
224 // the value and return it from bufferSize(), but the underlying DevSound
225 // buffer size remains unchanged.
226 if (value > 0)
227 m_clientBufferSize = value;
228}
229
230int QAudioInputPrivate::bufferSize() const
231{
232 return m_devSoundBufferSize ? m_devSoundBufferSize : m_clientBufferSize;
233}
234
235void QAudioInputPrivate::setNotifyInterval(int ms)
236{
237 if (ms >= 0) {
238 const int oldNotifyInterval = m_notifyInterval;
239 m_notifyInterval = ms;
240 if (m_notifyInterval && (SymbianAudio::ActiveState == m_internalState ||
241 SymbianAudio::IdleState == m_internalState))
242 m_notifyTimer->start(m_notifyInterval);
243 else
244 m_notifyTimer->stop();
245 }
246}
247
248int QAudioInputPrivate::notifyInterval() const
249{
250 return m_notifyInterval;
251}
252
253qint64 QAudioInputPrivate::processedUSecs() const
254{
255 int samplesPlayed = 0;
256 if (m_devSound && QAudio::SuspendedState != m_externalState)
257 samplesPlayed = getSamplesRecorded();
258
259 // Protect against division by zero
260 Q_ASSERT_X(m_format.frequency() > 0, Q_FUNC_INFO, "Invalid frequency");
261
262 const qint64 result = qint64(1000000) *
263 (samplesPlayed + m_totalSamplesRecorded)
264 / m_format.frequency();
265
266 return result;
267}
268
269qint64 QAudioInputPrivate::elapsedUSecs() const
270{
271 const qint64 result = (QAudio::StoppedState == state()) ?
272 0 : m_elapsed.elapsed() * 1000;
273 return result;
274}
275
276QAudio::Error QAudioInputPrivate::error() const
277{
278 return m_error;
279}
280
281QAudio::State QAudioInputPrivate::state() const
282{
283 return m_externalState;
284}
285
286QAudioFormat QAudioInputPrivate::format() const
287{
288 return m_format;
289}
290
291
292//-----------------------------------------------------------------------------
293// Private functions
294//-----------------------------------------------------------------------------
295
296void QAudioInputPrivate::open()
297{
298 Q_ASSERT_X(SymbianAudio::ClosedState == m_internalState,
299 Q_FUNC_INFO, "DevSound already opened");
300
301 Q_ASSERT(!m_devSound);
302 m_devSound = new SymbianAudio::DevSoundWrapper(QAudio::AudioInput, this);
303
304 connect(m_devSound, SIGNAL(initializeComplete(int)),
305 this, SLOT(devsoundInitializeComplete(int)));
306 connect(m_devSound, SIGNAL(bufferToBeProcessed(CMMFBuffer *)),
307 this, SLOT(devsoundBufferToBeEmptied(CMMFBuffer *)));
308 connect(m_devSound, SIGNAL(processingError(int)),
309 this, SLOT(devsoundRecordError(int)));
310
311 setState(SymbianAudio::InitializingState);
312 m_devSound->initialize(m_format.codec());
313}
314
315void QAudioInputPrivate::startRecording()
316{
317 const int samplesRecorded = m_devSound->samplesProcessed();
318 Q_ASSERT(samplesRecorded == 0);
319
320 bool ok = m_devSound->setFormat(m_format);
321 if (ok)
322 ok = m_devSound->start();
323
324 if (ok) {
325 startDataTransfer();
326 } else {
327 setError(QAudio::OpenError);
328 close();
329 }
330}
331
332void QAudioInputPrivate::startDataTransfer()
333{
334 if (m_notifyInterval)
335 m_notifyTimer->start(m_notifyInterval);
336
337 if (m_pullMode)
338 m_pullTimer->start();
339
340 if (bytesReady()) {
341 setState(SymbianAudio::ActiveState);
342 if (!m_pullMode)
343 pushData();
344 } else {
345 if (QAudio::SuspendedState == m_externalState)
346 setState(SymbianAudio::ActiveState);
347 else
348 setState(SymbianAudio::IdleState);
349 }
350}
351
352CMMFDataBuffer* QAudioInputPrivate::currentBuffer() const
353{
354 CMMFDataBuffer *result = m_devSoundBuffer;
355 if (!result && !m_devSoundBufferQ.empty())
356 result = m_devSoundBufferQ.front();
357 return result;
358}
359
360void QAudioInputPrivate::pushData()
361{
362 Q_ASSERT_X(bytesReady(), Q_FUNC_INFO, "No data available");
363 Q_ASSERT_X(!m_pullMode, Q_FUNC_INFO, "pushData called when in pull mode");
364 qobject_cast<SymbianAudioInputPrivate *>(m_sink)->dataReady();
365}
366
367qint64 QAudioInputPrivate::read(char *data, qint64 len)
368{
369 // SymbianAudioInputPrivate is ready to read data
370
371 Q_ASSERT_X(!m_pullMode, Q_FUNC_INFO,
372 "read called when in pull mode");
373
374 qint64 bytesRead = 0;
375
376 CMMFDataBuffer *buffer = 0;
377 while ((buffer = currentBuffer()) && (bytesRead < len)) {
378 if (SymbianAudio::IdleState == m_internalState)
379 setState(SymbianAudio::ActiveState);
380
381 TDesC8 &inputBuffer = buffer->Data();
382
383 Q_ASSERT(inputBuffer.Length() >= m_devSoundBufferPos);
384 const qint64 inputBytes = inputBuffer.Length() - m_devSoundBufferPos;
385 const qint64 outputBytes = len - bytesRead;
386 const qint64 copyBytes = outputBytes < inputBytes ?
387 outputBytes : inputBytes;
388
389 memcpy(data, inputBuffer.Ptr() + m_devSoundBufferPos, copyBytes);
390
391 m_devSoundBufferPos += copyBytes;
392 data += copyBytes;
393 bytesRead += copyBytes;
394
395 if (inputBytes == copyBytes)
396 bufferEmptied();
397 }
398
399 return bytesRead;
400}
401
402void QAudioInputPrivate::pullData()
403{
404 Q_ASSERT_X(m_pullMode, Q_FUNC_INFO,
405 "pullData called when in push mode");
406
407 CMMFDataBuffer *buffer = 0;
408 while (buffer = currentBuffer()) {
409 if (SymbianAudio::IdleState == m_internalState)
410 setState(SymbianAudio::ActiveState);
411
412 TDesC8 &inputBuffer = buffer->Data();
413
414 Q_ASSERT(inputBuffer.Length() >= m_devSoundBufferPos);
415 const qint64 inputBytes = inputBuffer.Length() - m_devSoundBufferPos;
416 const qint64 bytesPushed = m_sink->write(
417 (char*)inputBuffer.Ptr() + m_devSoundBufferPos, inputBytes);
418
419 m_devSoundBufferPos += bytesPushed;
420
421 if (inputBytes == bytesPushed)
422 bufferEmptied();
423
424 if (!bytesPushed)
425 break;
426 }
427}
428
429void QAudioInputPrivate::devsoundInitializeComplete(int err)
430{
431 Q_ASSERT_X(SymbianAudio::InitializingState == m_internalState,
432 Q_FUNC_INFO, "Invalid state");
433
434 if (!err && m_devSound->isFormatSupported(m_format))
435 startRecording();
436 else
437 setError(QAudio::OpenError);
438}
439
440void QAudioInputPrivate::devsoundBufferToBeEmptied(CMMFBuffer *baseBuffer)
441{
442 // Following receipt of this signal, DevSound should not provide another
443 // buffer until we have returned the current one.
444 Q_ASSERT_X(!m_devSoundBuffer, Q_FUNC_INFO, "Buffer already held");
445
446 CMMFDataBuffer *const buffer = static_cast<CMMFDataBuffer*>(baseBuffer);
447
448 if (!m_devSoundBufferSize)
449 m_devSoundBufferSize = buffer->Data().MaxLength();
450
451 m_totalBytesReady += buffer->Data().Length();
452
453 if (SymbianAudio::SuspendedPausedState == m_internalState) {
454 m_devSoundBufferQ.append(buffer);
455 } else {
456 // Will be returned to DevSoundWrapper by bufferProcessed().
457 m_devSoundBuffer = buffer;
458 m_devSoundBufferPos = 0;
459
460 if (bytesReady() && !m_pullMode)
461 pushData();
462 }
463}
464
465void QAudioInputPrivate::devsoundRecordError(int err)
466{
467 Q_UNUSED(err)
468 setError(QAudio::IOError);
469}
470
471void QAudioInputPrivate::bufferEmptied()
472{
473 m_devSoundBufferPos = 0;
474
475 if (m_devSoundBuffer) {
476 m_totalBytesReady -= m_devSoundBuffer->Data().Length();
477 m_devSoundBuffer = 0;
478 m_devSound->bufferProcessed();
479 } else {
480 Q_ASSERT(!m_devSoundBufferQ.empty());
481 m_totalBytesReady -= m_devSoundBufferQ.front()->Data().Length();
482 m_devSoundBufferQ.erase(m_devSoundBufferQ.begin());
483
484 // If the queue has been emptied, resume transfer from the hardware
485 if (m_devSoundBufferQ.empty())
486 if (!m_devSound->start())
487 setError(QAudio::IOError);
488 }
489
490 Q_ASSERT(m_totalBytesReady >= 0);
491}
492
493void QAudioInputPrivate::close()
494{
495 m_notifyTimer->stop();
496 m_pullTimer->stop();
497
498 m_error = QAudio::NoError;
499
500 if (m_devSound)
501 m_devSound->stop();
502 delete m_devSound;
503 m_devSound = 0;
504
505 m_devSoundBuffer = 0;
506 m_devSoundBufferSize = 0;
507 m_totalBytesReady = 0;
508
509 if (!m_pullMode) // m_sink is owned
510 delete m_sink;
511 m_pullMode = false;
512 m_sink = 0;
513
514 m_devSoundBufferQ.clear();
515 m_devSoundBufferPos = 0;
516 m_totalSamplesRecorded = 0;
517
518 setState(SymbianAudio::ClosedState);
519}
520
521qint64 QAudioInputPrivate::getSamplesRecorded() const
522{
523 qint64 result = 0;
524 if (m_devSound)
525 result = qint64(m_devSound->samplesProcessed());
526 return result;
527}
528
529void QAudioInputPrivate::setError(QAudio::Error error)
530{
531 m_error = error;
532
533 // Although no state transition actually occurs here, a stateChanged event
534 // must be emitted to inform the client that the call to start() was
535 // unsuccessful.
536 if (QAudio::OpenError == error) {
537 emit stateChanged(QAudio::StoppedState);
538 } else {
539 if (QAudio::UnderrunError == error)
540 setState(SymbianAudio::IdleState);
541 else
542 // Close the DevSound instance. This causes a transition to
543 // StoppedState. This must be done asynchronously in case the
544 // current function was called from a DevSound event handler, in which
545 // case deleting the DevSound instance may cause an exception.
546 QMetaObject::invokeMethod(this, "close", Qt::QueuedConnection);
547 }
548}
549
550void QAudioInputPrivate::setState(SymbianAudio::State newInternalState)
551{
552 const QAudio::State oldExternalState = m_externalState;
553 m_internalState = newInternalState;
554 m_externalState = SymbianAudio::Utils::stateNativeToQt(m_internalState);
555
556 if (m_externalState != oldExternalState)
557 emit stateChanged(m_externalState);
558}
559
560QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.