qtmobility/src/multimedia/audio/qaudiooutput_symbian_p.cpp
changeset 14 6fbed849b4f4
equal deleted inserted replaced
11:06b8e2af4411 14:6fbed849b4f4
       
     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 Qt Mobility Components.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    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 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 #include "qaudiooutput_symbian_p.h"
       
    43 
       
    44 QT_BEGIN_NAMESPACE
       
    45 
       
    46 //-----------------------------------------------------------------------------
       
    47 // Constants
       
    48 //-----------------------------------------------------------------------------
       
    49 
       
    50 const int UnderflowTimerInterval = 50; // ms
       
    51 
       
    52 
       
    53 //-----------------------------------------------------------------------------
       
    54 // Private class
       
    55 //-----------------------------------------------------------------------------
       
    56 
       
    57 SymbianAudioOutputPrivate::SymbianAudioOutputPrivate(
       
    58                                QAudioOutputPrivate *audioDevice)
       
    59     :   m_audioDevice(audioDevice)
       
    60 {
       
    61 
       
    62 }
       
    63 
       
    64 SymbianAudioOutputPrivate::~SymbianAudioOutputPrivate()
       
    65 {
       
    66 
       
    67 }
       
    68 
       
    69 qint64 SymbianAudioOutputPrivate::readData(char *data, qint64 len)
       
    70 {
       
    71     Q_UNUSED(data)
       
    72     Q_UNUSED(len)
       
    73     return 0;
       
    74 }
       
    75 
       
    76 qint64 SymbianAudioOutputPrivate::writeData(const char *data, qint64 len)
       
    77 {
       
    78     qint64 totalWritten = 0;
       
    79 
       
    80     if (m_audioDevice->state() == QAudio::ActiveState ||
       
    81         m_audioDevice->state() == QAudio::IdleState) {
       
    82 
       
    83         while (totalWritten < len) {
       
    84             const qint64 written = m_audioDevice->pushData(data + totalWritten,
       
    85                                                            len - totalWritten);
       
    86             if (written > 0)
       
    87                 totalWritten += written;
       
    88             else
       
    89                 break;
       
    90         }
       
    91     }
       
    92 
       
    93     return totalWritten;
       
    94 }
       
    95 
       
    96 
       
    97 //-----------------------------------------------------------------------------
       
    98 // Public functions
       
    99 //-----------------------------------------------------------------------------
       
   100 
       
   101 QAudioOutputPrivate::QAudioOutputPrivate(const QByteArray &device)
       
   102     :   m_device(device)
       
   103     ,   m_clientBufferSize(SymbianAudio::DefaultBufferSize)
       
   104     ,   m_notifyInterval(SymbianAudio::DefaultNotifyInterval)
       
   105     ,   m_notifyTimer(new QTimer(this))
       
   106     ,   m_error(QAudio::NoError)
       
   107     ,   m_internalState(SymbianAudio::ClosedState)
       
   108     ,   m_externalState(QAudio::StoppedState)
       
   109     ,   m_pullMode(false)
       
   110     ,   m_source(0)
       
   111     ,   m_devSoundBuffer(0)
       
   112     ,   m_devSoundBufferSize(0)
       
   113     ,   m_bytesWritten(0)
       
   114     ,   m_pushDataReady(false)
       
   115     ,   m_bytesPadding(0)
       
   116     ,   m_underflow(false)
       
   117     ,   m_lastBuffer(false)
       
   118     ,   m_underflowTimer(new QTimer(this))
       
   119     ,   m_samplesPlayed(0)
       
   120     ,   m_totalSamplesPlayed(0)
       
   121 {
       
   122     connect(m_notifyTimer.data(), SIGNAL(timeout()), this, SIGNAL(notify()));
       
   123 
       
   124     //SymbianAudio::Utils::formatQtToNative(m_format, m_nativeFourCC,
       
   125     //                                      m_nativeFormat);
       
   126 
       
   127     m_underflowTimer->setInterval(UnderflowTimerInterval);
       
   128     connect(m_underflowTimer.data(), SIGNAL(timeout()), this,
       
   129             SLOT(underflowTimerExpired()));
       
   130 }
       
   131 
       
   132 QAudioOutputPrivate::~QAudioOutputPrivate()
       
   133 {
       
   134     close();
       
   135 }
       
   136 
       
   137 void QAudioOutputPrivate::setFormat(const QAudioFormat& fmt)
       
   138 {
       
   139     m_format = const_cast<QAudioFormat&>(fmt);
       
   140     SymbianAudio::Utils::formatQtToNative(m_format, m_nativeFourCC,
       
   141             m_nativeFormat);
       
   142 }
       
   143 
       
   144 void QAudioOutputPrivate::start(QIODevice *device)
       
   145 {
       
   146     stop();
       
   147 
       
   148     // We have to set these before the call to open() because of the
       
   149     // logic in initializingState()
       
   150     m_pullMode = true;
       
   151     m_source = device;
       
   152 
       
   153     open();
       
   154 
       
   155     if (SymbianAudio::ClosedState != m_internalState) {
       
   156         connect(m_source, SIGNAL(readyRead()), this, SLOT(dataReady()));
       
   157         m_elapsed.restart();
       
   158     }
       
   159 }
       
   160 
       
   161 QIODevice* QAudioOutputPrivate::start()
       
   162 {
       
   163     stop();
       
   164 
       
   165     open();
       
   166 
       
   167     if (SymbianAudio::ClosedState != m_internalState) {
       
   168         m_source = new SymbianAudioOutputPrivate(this);
       
   169         m_source->open(QIODevice::WriteOnly | QIODevice::Unbuffered);
       
   170         m_elapsed.restart();
       
   171     }
       
   172 
       
   173     return m_source;
       
   174 }
       
   175 
       
   176 void QAudioOutputPrivate::stop()
       
   177 {
       
   178     close();
       
   179 }
       
   180 
       
   181 void QAudioOutputPrivate::reset()
       
   182 {
       
   183     m_totalSamplesPlayed += getSamplesPlayed();
       
   184     m_devSound->Stop();
       
   185     m_bytesPadding = 0;
       
   186     startPlayback();
       
   187 }
       
   188 
       
   189 void QAudioOutputPrivate::suspend()
       
   190 {
       
   191     if (SymbianAudio::ActiveState == m_internalState
       
   192         || SymbianAudio::IdleState == m_internalState) {
       
   193         m_notifyTimer->stop();
       
   194         m_underflowTimer->stop();
       
   195 
       
   196         const qint64 samplesWritten = SymbianAudio::Utils::bytesToSamples(
       
   197                                           m_format, m_bytesWritten);
       
   198 
       
   199         const qint64 samplesPlayed = getSamplesPlayed();
       
   200 
       
   201         m_bytesWritten = 0;
       
   202 
       
   203         // CMMFDevSound::Pause() is not guaranteed to work correctly in all
       
   204         // implementations, for play-mode DevSound sessions.  We therefore
       
   205         // have to implement suspend() by calling CMMFDevSound::Stop().
       
   206         // Because this causes buffered data to be dropped, we replace the
       
   207         // lost data with silence following a call to resume(), in order to
       
   208         // ensure that processedUSecs() returns the correct value.
       
   209         m_devSound->Stop();
       
   210         m_totalSamplesPlayed += samplesPlayed;
       
   211 
       
   212         // Calculate the amount of data dropped
       
   213         const qint64 paddingSamples = samplesWritten - samplesPlayed;
       
   214         m_bytesPadding = SymbianAudio::Utils::samplesToBytes(m_format,
       
   215                                                              paddingSamples);
       
   216 
       
   217         setState(SymbianAudio::SuspendedState);
       
   218     }
       
   219 }
       
   220 
       
   221 void QAudioOutputPrivate::resume()
       
   222 {
       
   223     if (SymbianAudio::SuspendedState == m_internalState)
       
   224         startPlayback();
       
   225 }
       
   226 
       
   227 int QAudioOutputPrivate::bytesFree() const
       
   228 {
       
   229     int result = 0;
       
   230     if (m_devSoundBuffer) {
       
   231         const TDes8 &outputBuffer = m_devSoundBuffer->Data();
       
   232         result = outputBuffer.MaxLength() - outputBuffer.Length();
       
   233     }
       
   234     return result;
       
   235 }
       
   236 
       
   237 int QAudioOutputPrivate::periodSize() const
       
   238 {
       
   239     return bufferSize();
       
   240 }
       
   241 
       
   242 void QAudioOutputPrivate::setBufferSize(int value)
       
   243 {
       
   244     // Note that DevSound does not allow its client to specify the buffer size.
       
   245     // This functionality is available via custom interfaces, but since these
       
   246     // cannot be guaranteed to work across all DevSound implementations, we
       
   247     // do not use them here.
       
   248     // In order to comply with the expected bevahiour of QAudioOutput, we store
       
   249     // the value and return it from bufferSize(), but the underlying DevSound
       
   250     // buffer size remains unchanged.
       
   251     if (value > 0)
       
   252         m_clientBufferSize = value;
       
   253 }
       
   254 
       
   255 int QAudioOutputPrivate::bufferSize() const
       
   256 {
       
   257     return m_devSoundBufferSize ? m_devSoundBufferSize : m_clientBufferSize;
       
   258 }
       
   259 
       
   260 void QAudioOutputPrivate::setNotifyInterval(int ms)
       
   261 {
       
   262     if (ms > 0) {
       
   263         const int oldNotifyInterval = m_notifyInterval;
       
   264         m_notifyInterval = ms;
       
   265         if (m_notifyTimer->isActive() && ms != oldNotifyInterval)
       
   266             m_notifyTimer->start(m_notifyInterval);
       
   267     }
       
   268 }
       
   269 
       
   270 int QAudioOutputPrivate::notifyInterval() const
       
   271 {
       
   272     return m_notifyInterval;
       
   273 }
       
   274 
       
   275 qint64 QAudioOutputPrivate::processedUSecs() const
       
   276 {
       
   277     int samplesPlayed = 0;
       
   278     if (m_devSound && SymbianAudio::SuspendedState != m_internalState)
       
   279         samplesPlayed = getSamplesPlayed();
       
   280 
       
   281     // Protect against division by zero
       
   282     Q_ASSERT_X(m_format.frequency() > 0, Q_FUNC_INFO, "Invalid frequency");
       
   283 
       
   284     const qint64 result = qint64(1000000) *
       
   285                           (samplesPlayed + m_totalSamplesPlayed)
       
   286                         / m_format.frequency();
       
   287 
       
   288     return result;
       
   289 }
       
   290 
       
   291 qint64 QAudioOutputPrivate::elapsedUSecs() const
       
   292 {
       
   293     const qint64 result = (QAudio::StoppedState == state()) ?
       
   294                               0 : m_elapsed.elapsed() * 1000;
       
   295     return result;
       
   296 }
       
   297 
       
   298 QAudio::Error QAudioOutputPrivate::error() const
       
   299 {
       
   300     return m_error;
       
   301 }
       
   302 
       
   303 QAudio::State QAudioOutputPrivate::state() const
       
   304 {
       
   305     return m_externalState;
       
   306 }
       
   307 
       
   308 QAudioFormat QAudioOutputPrivate::format() const
       
   309 {
       
   310     return m_format;
       
   311 }
       
   312 
       
   313 //-----------------------------------------------------------------------------
       
   314 // MDevSoundObserver implementation
       
   315 //-----------------------------------------------------------------------------
       
   316 
       
   317 void QAudioOutputPrivate::InitializeComplete(TInt aError)
       
   318 {
       
   319     Q_ASSERT_X(SymbianAudio::InitializingState == m_internalState,
       
   320         Q_FUNC_INFO, "Invalid state");
       
   321 
       
   322     if (KErrNone == aError)
       
   323         startPlayback();
       
   324 }
       
   325 
       
   326 void QAudioOutputPrivate::ToneFinished(TInt aError)
       
   327 {
       
   328     Q_UNUSED(aError)
       
   329     // This class doesn't use DevSound's tone playback functions, so should
       
   330     // never receive this callback.
       
   331     Q_ASSERT_X(false, Q_FUNC_INFO, "Unexpected callback");
       
   332 }
       
   333 
       
   334 void QAudioOutputPrivate::BufferToBeFilled(CMMFBuffer *aBuffer)
       
   335 {
       
   336     // Following receipt of this callback, DevSound should not provide another
       
   337     // buffer until we have returned the current one.
       
   338     Q_ASSERT_X(!m_devSoundBuffer, Q_FUNC_INFO, "Buffer already held");
       
   339 
       
   340     // Will be returned to DevSound by bufferFilled().
       
   341     m_devSoundBuffer = static_cast<CMMFDataBuffer*>(aBuffer);
       
   342 
       
   343     if (!m_devSoundBufferSize)
       
   344         m_devSoundBufferSize = m_devSoundBuffer->Data().MaxLength();
       
   345 
       
   346     writePaddingData();
       
   347 
       
   348     if (m_pullMode && isDataReady() && !m_bytesPadding)
       
   349         pullData();
       
   350 }
       
   351 
       
   352 void QAudioOutputPrivate::PlayError(TInt aError)
       
   353 {
       
   354     switch (aError) {
       
   355     case KErrUnderflow:
       
   356         m_underflow = true;
       
   357         if (m_pullMode && !m_lastBuffer)
       
   358             setError(QAudio::UnderrunError);
       
   359         else
       
   360             setState(SymbianAudio::IdleState);
       
   361         break;
       
   362     default:
       
   363         setError(QAudio::IOError);
       
   364         break;
       
   365     }
       
   366 }
       
   367 
       
   368 void QAudioOutputPrivate::BufferToBeEmptied(CMMFBuffer *aBuffer)
       
   369 {
       
   370     Q_UNUSED(aBuffer)
       
   371     // This class doesn't use DevSound in record mode, so should never receive
       
   372     // this callback.
       
   373     Q_ASSERT_X(false, Q_FUNC_INFO, "Unexpected callback");
       
   374 }
       
   375 
       
   376 void QAudioOutputPrivate::RecordError(TInt aError)
       
   377 {
       
   378     Q_UNUSED(aError)
       
   379     // This class doesn't use DevSound in record mode, so should never receive
       
   380     // this callback.
       
   381     Q_ASSERT_X(false, Q_FUNC_INFO, "Unexpected callback");
       
   382 }
       
   383 
       
   384 void QAudioOutputPrivate::ConvertError(TInt aError)
       
   385 {
       
   386     Q_UNUSED(aError)
       
   387     // This class doesn't use DevSound's format conversion functions, so
       
   388     // should never receive this callback.
       
   389     Q_ASSERT_X(false, Q_FUNC_INFO, "Unexpected callback");
       
   390 }
       
   391 
       
   392 void QAudioOutputPrivate::DeviceMessage(TUid aMessageType, const TDesC8 &aMsg)
       
   393 {
       
   394     Q_UNUSED(aMessageType)
       
   395     Q_UNUSED(aMsg)
       
   396     // Ignore this callback.
       
   397 }
       
   398 
       
   399 //-----------------------------------------------------------------------------
       
   400 // Private functions
       
   401 //-----------------------------------------------------------------------------
       
   402 
       
   403 void QAudioOutputPrivate::dataReady()
       
   404 {
       
   405     // Client-provided QIODevice has data ready to read.
       
   406 
       
   407     Q_ASSERT_X(m_source->bytesAvailable(), Q_FUNC_INFO,
       
   408         "readyRead signal received, but no data available");
       
   409 
       
   410     if (!m_bytesPadding)
       
   411         pullData();
       
   412 }
       
   413 
       
   414 void QAudioOutputPrivate::underflowTimerExpired()
       
   415 {
       
   416     const TInt samplesPlayed = getSamplesPlayed();
       
   417     if (m_samplesPlayed && (samplesPlayed == m_samplesPlayed)) {
       
   418         setError(QAudio::UnderrunError);
       
   419     } else {
       
   420         m_samplesPlayed = samplesPlayed;
       
   421         m_underflowTimer->start();
       
   422     }
       
   423 }
       
   424 
       
   425 void QAudioOutputPrivate::open()
       
   426 {
       
   427     Q_ASSERT_X(SymbianAudio::ClosedState == m_internalState,
       
   428         Q_FUNC_INFO, "DevSound already opened");
       
   429 
       
   430     QT_TRAP_THROWING( m_devSound.reset(CMMFDevSound::NewL()) )
       
   431 
       
   432     QScopedPointer<SymbianAudio::DevSoundCapabilities> caps(
       
   433         new SymbianAudio::DevSoundCapabilities(*m_devSound,
       
   434                                                QAudio::AudioOutput));
       
   435 
       
   436     int err = SymbianAudio::Utils::isFormatSupported(m_format, *caps) ?
       
   437                   KErrNone : KErrNotSupported;
       
   438 
       
   439     if (KErrNone == err) {
       
   440         setState(SymbianAudio::InitializingState);
       
   441         TRAP(err, m_devSound->InitializeL(*this, m_nativeFourCC,
       
   442                                           EMMFStatePlaying));
       
   443     }
       
   444 
       
   445     if (KErrNone != err) {
       
   446         setError(QAudio::OpenError);
       
   447         m_devSound.reset();
       
   448     }
       
   449 }
       
   450 
       
   451 void QAudioOutputPrivate::startPlayback()
       
   452 {
       
   453     TRAPD(err, startDevSoundL());
       
   454     if (KErrNone == err) {
       
   455         if (isDataReady())
       
   456             setState(SymbianAudio::ActiveState);
       
   457         else
       
   458             setState(SymbianAudio::IdleState);
       
   459 
       
   460         m_notifyTimer->start(m_notifyInterval);
       
   461         m_underflow = false;
       
   462 
       
   463         Q_ASSERT(m_devSound->SamplesPlayed() == 0);
       
   464 
       
   465         writePaddingData();
       
   466 
       
   467         if (m_pullMode && m_source->bytesAvailable() && !m_bytesPadding)
       
   468             dataReady();
       
   469     } else {
       
   470         setError(QAudio::OpenError);
       
   471         close();
       
   472     }
       
   473 }
       
   474 
       
   475 void QAudioOutputPrivate::startDevSoundL()
       
   476 {
       
   477     TMMFCapabilities nativeFormat = m_devSound->Config();
       
   478     m_nativeFormat.iBufferSize = nativeFormat.iBufferSize;
       
   479     m_devSound->SetConfigL(m_nativeFormat);
       
   480     m_devSound->PlayInitL();
       
   481 }
       
   482 
       
   483 void QAudioOutputPrivate::writePaddingData()
       
   484 {
       
   485     // See comments in suspend()
       
   486 
       
   487     while (m_devSoundBuffer && m_bytesPadding) {
       
   488         if (SymbianAudio::IdleState == m_internalState)
       
   489             setState(SymbianAudio::ActiveState);
       
   490 
       
   491         TDes8 &outputBuffer = m_devSoundBuffer->Data();
       
   492         const qint64 outputBytes = bytesFree();
       
   493         const qint64 paddingBytes = outputBytes < m_bytesPadding ?
       
   494                                         outputBytes : m_bytesPadding;
       
   495         unsigned char *ptr = const_cast<unsigned char*>(outputBuffer.Ptr());
       
   496         Mem::FillZ(ptr, paddingBytes);
       
   497         outputBuffer.SetLength(outputBuffer.Length() + paddingBytes);
       
   498         m_bytesPadding -= paddingBytes;
       
   499 
       
   500         if (m_pullMode && m_source->atEnd())
       
   501             lastBufferFilled();
       
   502         if (paddingBytes == outputBytes)
       
   503             bufferFilled();
       
   504     }
       
   505 }
       
   506 
       
   507 qint64 QAudioOutputPrivate::pushData(const char *data, qint64 len)
       
   508 {
       
   509     // Data has been written to SymbianAudioOutputPrivate
       
   510 
       
   511     Q_ASSERT_X(!m_pullMode, Q_FUNC_INFO,
       
   512         "pushData called when in pull mode");
       
   513 
       
   514     const unsigned char *const inputPtr =
       
   515         reinterpret_cast<const unsigned char*>(data);
       
   516     qint64 bytesWritten = 0;
       
   517 
       
   518     if (SymbianAudio::IdleState == m_internalState)
       
   519         setState(SymbianAudio::ActiveState);
       
   520 
       
   521     while (m_devSoundBuffer && (bytesWritten < len)) {
       
   522         // writePaddingData() is called from BufferToBeFilled(), so we should
       
   523         // never have any padding data left at this point.
       
   524         Q_ASSERT_X(0 == m_bytesPadding, Q_FUNC_INFO,
       
   525             "Padding bytes remaining in pushData");
       
   526 
       
   527         TDes8 &outputBuffer = m_devSoundBuffer->Data();
       
   528 
       
   529         const qint64 outputBytes = bytesFree();
       
   530         const qint64 inputBytes = len - bytesWritten;
       
   531         const qint64 copyBytes = outputBytes < inputBytes ?
       
   532                                      outputBytes : inputBytes;
       
   533 
       
   534         outputBuffer.Append(inputPtr + bytesWritten, copyBytes);
       
   535         bytesWritten += copyBytes;
       
   536 
       
   537         bufferFilled();
       
   538     }
       
   539 
       
   540     m_pushDataReady = (bytesWritten < len);
       
   541 
       
   542     // If DevSound is still initializing (m_internalState == InitializingState),
       
   543     // we cannot transition m_internalState to ActiveState, but we must emit
       
   544     // an (external) state change from IdleState to ActiveState.  The following
       
   545     // call triggers this signal.
       
   546     setState(m_internalState);
       
   547 
       
   548     return bytesWritten;
       
   549 }
       
   550 
       
   551 void QAudioOutputPrivate::pullData()
       
   552 {
       
   553     Q_ASSERT_X(m_pullMode, Q_FUNC_INFO,
       
   554         "pullData called when in push mode");
       
   555 
       
   556     if (m_bytesPadding)
       
   557         m_bytesPadding = 1;
       
   558 
       
   559     // writePaddingData() is called by BufferToBeFilled() before pullData(),
       
   560     // so we should never have any padding data left at this point.
       
   561     Q_ASSERT_X(0 == m_bytesPadding, Q_FUNC_INFO,
       
   562         "Padding bytes remaining in pullData");
       
   563 
       
   564     qint64 inputBytes = m_source->bytesAvailable();
       
   565     while (m_devSoundBuffer && inputBytes) {
       
   566         if (SymbianAudio::IdleState == m_internalState)
       
   567             setState(SymbianAudio::ActiveState);
       
   568 
       
   569         TDes8 &outputBuffer = m_devSoundBuffer->Data();
       
   570 
       
   571         const qint64 outputBytes = bytesFree();
       
   572         const qint64 copyBytes = outputBytes < inputBytes ?
       
   573                                      outputBytes : inputBytes;
       
   574 
       
   575         char *outputPtr = (char*)(outputBuffer.Ptr() + outputBuffer.Length());
       
   576         const qint64 bytesCopied = m_source->read(outputPtr, copyBytes);
       
   577         Q_ASSERT(bytesCopied == copyBytes);
       
   578         outputBuffer.SetLength(outputBuffer.Length() + bytesCopied);
       
   579         inputBytes -= bytesCopied;
       
   580 
       
   581         if (m_source->atEnd())
       
   582             lastBufferFilled();
       
   583         else if (copyBytes == outputBytes)
       
   584             bufferFilled();
       
   585     }
       
   586 }
       
   587 
       
   588 void QAudioOutputPrivate::bufferFilled()
       
   589 {
       
   590     Q_ASSERT_X(m_devSoundBuffer, Q_FUNC_INFO, "No buffer to return");
       
   591 
       
   592     const TDes8 &outputBuffer = m_devSoundBuffer->Data();
       
   593     m_bytesWritten += outputBuffer.Length();
       
   594 
       
   595     m_devSoundBuffer = 0;
       
   596 
       
   597     m_samplesPlayed = getSamplesPlayed();
       
   598     m_underflowTimer->start();
       
   599 
       
   600     if (QAudio::UnderrunError == m_error)
       
   601         m_error = QAudio::NoError;
       
   602 
       
   603     m_devSound->PlayData();
       
   604 }
       
   605 
       
   606 void QAudioOutputPrivate::lastBufferFilled()
       
   607 {
       
   608     Q_ASSERT_X(m_devSoundBuffer, Q_FUNC_INFO, "No buffer to fill");
       
   609     Q_ASSERT_X(!m_lastBuffer, Q_FUNC_INFO, "Last buffer already sent");
       
   610     m_lastBuffer = true;
       
   611     m_devSoundBuffer->SetLastBuffer(ETrue);
       
   612     bufferFilled();
       
   613 }
       
   614 
       
   615 void QAudioOutputPrivate::close()
       
   616 {
       
   617     m_notifyTimer->stop();
       
   618     m_underflowTimer->stop();
       
   619 
       
   620     m_error = QAudio::NoError;
       
   621 
       
   622     if (m_devSound)
       
   623         m_devSound->Stop();
       
   624     m_devSound.reset();
       
   625     m_devSoundBuffer = 0;
       
   626     m_devSoundBufferSize = 0;
       
   627 
       
   628     if (!m_pullMode) // m_source is owned
       
   629         delete m_source;
       
   630     m_pullMode = false;
       
   631     m_source = 0;
       
   632 
       
   633     m_bytesWritten = 0;
       
   634     m_pushDataReady = false;
       
   635     m_bytesPadding = 0;
       
   636     m_underflow = false;
       
   637     m_lastBuffer = false;
       
   638     m_samplesPlayed = 0;
       
   639     m_totalSamplesPlayed = 0;
       
   640 
       
   641     setState(SymbianAudio::ClosedState);
       
   642 }
       
   643 
       
   644 qint64 QAudioOutputPrivate::getSamplesPlayed() const
       
   645 {
       
   646     qint64 result = 0;
       
   647     if (m_devSound) {
       
   648         const qint64 samplesWritten = SymbianAudio::Utils::bytesToSamples(
       
   649                                           m_format, m_bytesWritten);
       
   650 
       
   651         if (m_underflow) {
       
   652             result = samplesWritten;
       
   653         } else {
       
   654             // This is necessary because some DevSound implementations report
       
   655             // that they have played more data than has actually been provided to them
       
   656             // by the client.
       
   657             const qint64 devSoundSamplesPlayed(m_devSound->SamplesPlayed());
       
   658             result = qMin(devSoundSamplesPlayed, samplesWritten);
       
   659         }
       
   660     }
       
   661     return result;
       
   662 }
       
   663 
       
   664 void QAudioOutputPrivate::setError(QAudio::Error error)
       
   665 {
       
   666     m_error = error;
       
   667 
       
   668     // Although no state transition actually occurs here, a stateChanged event
       
   669     // must be emitted to inform the client that the call to start() was
       
   670     // unsuccessful.
       
   671     if (QAudio::OpenError == error)
       
   672         emit stateChanged(QAudio::StoppedState);
       
   673 
       
   674     if (QAudio::UnderrunError == error)
       
   675         setState(SymbianAudio::IdleState);
       
   676     else
       
   677         // Close the DevSound instance.  This causes a transition to
       
   678         // StoppedState.  This must be done asynchronously in case the
       
   679         // current function was called from a DevSound event handler, in which
       
   680         // case deleting the DevSound instance may cause an exception.
       
   681         QMetaObject::invokeMethod(this, "close", Qt::QueuedConnection);
       
   682 }
       
   683 
       
   684 void QAudioOutputPrivate::setState(SymbianAudio::State newInternalState)
       
   685 {
       
   686     const QAudio::State oldExternalState = m_externalState;
       
   687     m_internalState = newInternalState;
       
   688     m_externalState = SymbianAudio::Utils::stateNativeToQt(
       
   689                             m_internalState, initializingState());
       
   690 
       
   691     if (m_externalState != oldExternalState)
       
   692         emit stateChanged(m_externalState);
       
   693 }
       
   694 
       
   695 bool QAudioOutputPrivate::isDataReady() const
       
   696 {
       
   697     return (m_source && m_source->bytesAvailable())
       
   698         ||  m_bytesPadding
       
   699         ||  m_pushDataReady;
       
   700 }
       
   701 
       
   702 QAudio::State QAudioOutputPrivate::initializingState() const
       
   703 {
       
   704     return isDataReady() ? QAudio::ActiveState : QAudio::IdleState;
       
   705 }
       
   706 
       
   707 QT_END_NAMESPACE