src/multimedia/audio/qaudiooutput_symbian_p.cpp
changeset 25 e24348a560a6
parent 19 fcece45ef507
--- a/src/multimedia/audio/qaudiooutput_symbian_p.cpp	Thu May 27 13:40:48 2010 +0300
+++ b/src/multimedia/audio/qaudiooutput_symbian_p.cpp	Fri Jun 11 14:24:45 2010 +0300
@@ -110,6 +110,7 @@
     ,   m_externalState(QAudio::StoppedState)
     ,   m_pullMode(false)
     ,   m_source(0)
+    ,   m_devSound(0)
     ,   m_devSoundBuffer(0)
     ,   m_devSoundBufferSize(0)
     ,   m_bytesWritten(0)
@@ -121,10 +122,9 @@
     ,   m_samplesPlayed(0)
     ,   m_totalSamplesPlayed(0)
 {
-    connect(m_notifyTimer.data(), SIGNAL(timeout()), this, SIGNAL(notify()));
+    qRegisterMetaType<CMMFBuffer *>("CMMFBuffer *");
 
-    SymbianAudio::Utils::formatQtToNative(m_format, m_nativeFourCC,
-                                          m_nativeFormat);
+    connect(m_notifyTimer.data(), SIGNAL(timeout()), this, SIGNAL(notify()));
 
     m_underflowTimer->setInterval(UnderflowTimerInterval);
     connect(m_underflowTimer.data(), SIGNAL(timeout()), this,
@@ -140,8 +140,6 @@
 {
     stop();
 
-    // We have to set these before the call to open() because of the
-    // logic in initializingState()
     if (device) {
         m_pullMode = true;
         m_source = device;
@@ -171,7 +169,7 @@
 void QAudioOutputPrivate::reset()
 {
     m_totalSamplesPlayed += getSamplesPlayed();
-    m_devSound->Stop();
+    m_devSound->stop();
     m_bytesPadding = 0;
     startPlayback();
 }
@@ -196,11 +194,12 @@
         // Because this causes buffered data to be dropped, we replace the
         // lost data with silence following a call to resume(), in order to
         // ensure that processedUSecs() returns the correct value.
-        m_devSound->Stop();
+        m_devSound->stop();
         m_totalSamplesPlayed += samplesPlayed;
 
         // Calculate the amount of data dropped
         const qint64 paddingSamples = samplesWritten - samplesPlayed;
+        Q_ASSERT(paddingSamples >= 0);
         m_bytesPadding = SymbianAudio::Utils::samplesToBytes(m_format,
                                                              paddingSamples);
 
@@ -210,8 +209,11 @@
 
 void QAudioOutputPrivate::resume()
 {
-    if (SymbianAudio::SuspendedState == m_internalState)
+    if (SymbianAudio::SuspendedState == m_internalState) {
+        if (!m_pullMode && m_devSoundBuffer && m_devSoundBuffer->Data().Length())
+            bufferFilled();
         startPlayback();
+    }
 }
 
 int QAudioOutputPrivate::bytesFree() const
@@ -249,11 +251,14 @@
 
 void QAudioOutputPrivate::setNotifyInterval(int ms)
 {
-    if (ms > 0) {
+    if (ms >= 0) {
         const int oldNotifyInterval = m_notifyInterval;
         m_notifyInterval = ms;
-        if (m_notifyTimer->isActive() && ms != oldNotifyInterval)
+        if (m_notifyInterval && (SymbianAudio::ActiveState == m_internalState ||
+                                 SymbianAudio::IdleState == m_internalState))
             m_notifyTimer->start(m_notifyInterval);
+        else
+            m_notifyTimer->stop();
     }
 }
 
@@ -300,91 +305,6 @@
     return m_format;
 }
 
-//-----------------------------------------------------------------------------
-// MDevSoundObserver implementation
-//-----------------------------------------------------------------------------
-
-void QAudioOutputPrivate::InitializeComplete(TInt aError)
-{
-    Q_ASSERT_X(SymbianAudio::InitializingState == m_internalState,
-        Q_FUNC_INFO, "Invalid state");
-
-    if (KErrNone == aError)
-        startPlayback();
-}
-
-void QAudioOutputPrivate::ToneFinished(TInt aError)
-{
-    Q_UNUSED(aError)
-    // This class doesn't use DevSound's tone playback functions, so should
-    // never receive this callback.
-    Q_ASSERT_X(false, Q_FUNC_INFO, "Unexpected callback");
-}
-
-void QAudioOutputPrivate::BufferToBeFilled(CMMFBuffer *aBuffer)
-{
-    // Following receipt of this callback, DevSound should not provide another
-    // buffer until we have returned the current one.
-    Q_ASSERT_X(!m_devSoundBuffer, Q_FUNC_INFO, "Buffer already held");
-
-    // Will be returned to DevSound by bufferFilled().
-    m_devSoundBuffer = static_cast<CMMFDataBuffer*>(aBuffer);
-
-    if (!m_devSoundBufferSize)
-        m_devSoundBufferSize = m_devSoundBuffer->Data().MaxLength();
-
-    writePaddingData();
-
-    if (m_pullMode && isDataReady() && !m_bytesPadding)
-        pullData();
-}
-
-void QAudioOutputPrivate::PlayError(TInt aError)
-{
-    switch (aError) {
-    case KErrUnderflow:
-        m_underflow = true;
-        if (m_pullMode && !m_lastBuffer)
-            setError(QAudio::UnderrunError);
-        else
-            setState(SymbianAudio::IdleState);
-        break;
-    default:
-        setError(QAudio::IOError);
-        break;
-    }
-}
-
-void QAudioOutputPrivate::BufferToBeEmptied(CMMFBuffer *aBuffer)
-{
-    Q_UNUSED(aBuffer)
-    // This class doesn't use DevSound in record mode, so should never receive
-    // this callback.
-    Q_ASSERT_X(false, Q_FUNC_INFO, "Unexpected callback");
-}
-
-void QAudioOutputPrivate::RecordError(TInt aError)
-{
-    Q_UNUSED(aError)
-    // This class doesn't use DevSound in record mode, so should never receive
-    // this callback.
-    Q_ASSERT_X(false, Q_FUNC_INFO, "Unexpected callback");
-}
-
-void QAudioOutputPrivate::ConvertError(TInt aError)
-{
-    Q_UNUSED(aError)
-    // This class doesn't use DevSound's format conversion functions, so
-    // should never receive this callback.
-    Q_ASSERT_X(false, Q_FUNC_INFO, "Unexpected callback");
-}
-
-void QAudioOutputPrivate::DeviceMessage(TUid aMessageType, const TDesC8 &aMsg)
-{
-    Q_UNUSED(aMessageType)
-    Q_UNUSED(aMsg)
-    // Ignore this callback.
-}
 
 //-----------------------------------------------------------------------------
 // Private functions
@@ -412,45 +332,87 @@
     }
 }
 
+void QAudioOutputPrivate::devsoundInitializeComplete(int err)
+{
+    Q_ASSERT_X(SymbianAudio::InitializingState == m_internalState,
+        Q_FUNC_INFO, "Invalid state");
+
+    if (!err && m_devSound->isFormatSupported(m_format))
+        startPlayback();
+    else
+        setError(QAudio::OpenError);
+}
+
+void QAudioOutputPrivate::devsoundBufferToBeFilled(CMMFBuffer *bufferBase)
+{
+    // Following receipt of this signal, DevSound should not provide another
+    // buffer until we have returned the current one.
+    Q_ASSERT_X(!m_devSoundBuffer, Q_FUNC_INFO, "Buffer already held");
+
+    // Will be returned to DevSoundWrapper by bufferProcessed().
+    m_devSoundBuffer = static_cast<CMMFDataBuffer*>(bufferBase);
+
+    if (!m_devSoundBufferSize)
+        m_devSoundBufferSize = m_devSoundBuffer->Data().MaxLength();
+
+    writePaddingData();
+
+    if (m_pullMode && isDataReady() && !m_bytesPadding)
+        pullData();
+}
+
+void QAudioOutputPrivate::devsoundPlayError(int err)
+{
+    switch (err) {
+    case KErrUnderflow:
+        m_underflow = true;
+        if (m_pullMode && !m_lastBuffer)
+            setError(QAudio::UnderrunError);
+        else
+            setState(SymbianAudio::IdleState);
+        break;
+    default:
+        setError(QAudio::IOError);
+        break;
+    }
+}
+
 void QAudioOutputPrivate::open()
 {
     Q_ASSERT_X(SymbianAudio::ClosedState == m_internalState,
         Q_FUNC_INFO, "DevSound already opened");
 
-    QT_TRAP_THROWING( m_devSound.reset(CMMFDevSound::NewL()) )
-
-    QScopedPointer<SymbianAudio::DevSoundCapabilities> caps(
-        new SymbianAudio::DevSoundCapabilities(*m_devSound,
-                                               QAudio::AudioOutput));
-
-    int err = SymbianAudio::Utils::isFormatSupported(m_format, *caps) ?
-                  KErrNone : KErrNotSupported;
+    Q_ASSERT(!m_devSound);
+    m_devSound = new SymbianAudio::DevSoundWrapper(QAudio::AudioOutput, this);
 
-    if (KErrNone == err) {
-        setState(SymbianAudio::InitializingState);
-        TRAP(err, m_devSound->InitializeL(*this, m_nativeFourCC,
-                                          EMMFStatePlaying));
-    }
+    connect(m_devSound, SIGNAL(initializeComplete(int)),
+            this, SLOT(devsoundInitializeComplete(int)));
+    connect(m_devSound, SIGNAL(bufferToBeProcessed(CMMFBuffer *)),
+            this, SLOT(devsoundBufferToBeFilled(CMMFBuffer *)));
+    connect(m_devSound, SIGNAL(processingError(int)),
+            this, SLOT(devsoundPlayError(int)));
 
-    if (KErrNone != err) {
-        setError(QAudio::OpenError);
-        m_devSound.reset();
-    }
+    setState(SymbianAudio::InitializingState);
+    m_devSound->initialize(m_format.codec());
 }
 
 void QAudioOutputPrivate::startPlayback()
 {
-    TRAPD(err, startDevSoundL());
-    if (KErrNone == err) {
+    bool ok = m_devSound->setFormat(m_format);
+    if (ok)
+        ok = m_devSound->start();
+
+    if (ok) {
         if (isDataReady())
             setState(SymbianAudio::ActiveState);
         else
             setState(SymbianAudio::IdleState);
 
-        m_notifyTimer->start(m_notifyInterval);
+        if (m_notifyInterval)
+            m_notifyTimer->start(m_notifyInterval);
         m_underflow = false;
 
-        Q_ASSERT(m_devSound->SamplesPlayed() == 0);
+        Q_ASSERT(m_devSound->samplesProcessed() == 0);
 
         writePaddingData();
 
@@ -462,14 +424,6 @@
     }
 }
 
-void QAudioOutputPrivate::startDevSoundL()
-{
-    TMMFCapabilities nativeFormat = m_devSound->Config();
-    m_nativeFormat.iBufferSize = nativeFormat.iBufferSize;
-    m_devSound->SetConfigL(m_nativeFormat);
-    m_devSound->PlayInitL();
-}
-
 void QAudioOutputPrivate::writePaddingData()
 {
     // See comments in suspend()
@@ -486,10 +440,11 @@
         Mem::FillZ(ptr, paddingBytes);
         outputBuffer.SetLength(outputBuffer.Length() + paddingBytes);
         m_bytesPadding -= paddingBytes;
+        Q_ASSERT(m_bytesPadding >= 0);
 
         if (m_pullMode && m_source->atEnd())
             lastBufferFilled();
-        if (paddingBytes == outputBytes)
+        if ((paddingBytes == outputBytes) || !m_bytesPadding)
             bufferFilled();
     }
 }
@@ -543,9 +498,6 @@
     Q_ASSERT_X(m_pullMode, Q_FUNC_INFO,
         "pullData called when in push mode");
 
-    if (m_bytesPadding)
-        m_bytesPadding = 1;
-
     // writePaddingData() is called by BufferToBeFilled() before pullData(),
     // so we should never have any padding data left at this point.
     Q_ASSERT_X(0 == m_bytesPadding, Q_FUNC_INFO,
@@ -590,7 +542,7 @@
     if (QAudio::UnderrunError == m_error)
         m_error = QAudio::NoError;
 
-    m_devSound->PlayData();
+    m_devSound->bufferProcessed();
 }
 
 void QAudioOutputPrivate::lastBufferFilled()
@@ -610,8 +562,10 @@
     m_error = QAudio::NoError;
 
     if (m_devSound)
-        m_devSound->Stop();
-    m_devSound.reset();
+        m_devSound->stop();
+    delete m_devSound;
+    m_devSound = 0;
+
     m_devSoundBuffer = 0;
     m_devSoundBufferSize = 0;
 
@@ -644,7 +598,7 @@
             // This is necessary because some DevSound implementations report
             // that they have played more data than has actually been provided to them
             // by the client.
-            const qint64 devSoundSamplesPlayed(m_devSound->SamplesPlayed());
+            const qint64 devSoundSamplesPlayed(m_devSound->samplesProcessed());
             result = qMin(devSoundSamplesPlayed, samplesWritten);
         }
     }
@@ -658,25 +612,25 @@
     // Although no state transition actually occurs here, a stateChanged event
     // must be emitted to inform the client that the call to start() was
     // unsuccessful.
-    if (QAudio::OpenError == error)
+    if (QAudio::OpenError == error) {
         emit stateChanged(QAudio::StoppedState);
-
-    if (QAudio::UnderrunError == error)
-        setState(SymbianAudio::IdleState);
-    else
-        // Close the DevSound instance.  This causes a transition to
-        // StoppedState.  This must be done asynchronously in case the
-        // current function was called from a DevSound event handler, in which
-        // case deleting the DevSound instance may cause an exception.
-        QMetaObject::invokeMethod(this, "close", Qt::QueuedConnection);
+    } else {
+        if (QAudio::UnderrunError == error)
+            setState(SymbianAudio::IdleState);
+        else
+            // Close the DevSound instance.  This causes a transition to
+            // StoppedState.  This must be done asynchronously in case the
+            // current function was called from a DevSound event handler, in which
+            // case deleting the DevSound instance may cause an exception.
+            QMetaObject::invokeMethod(this, "close", Qt::QueuedConnection);
+    }
 }
 
 void QAudioOutputPrivate::setState(SymbianAudio::State newInternalState)
 {
     const QAudio::State oldExternalState = m_externalState;
     m_internalState = newInternalState;
-    m_externalState = SymbianAudio::Utils::stateNativeToQt(
-                            m_internalState, initializingState());
+    m_externalState = SymbianAudio::Utils::stateNativeToQt(m_internalState);
 
     if (m_externalState != oldExternalState)
         emit stateChanged(m_externalState);
@@ -689,9 +643,4 @@
         ||  m_pushDataReady;
 }
 
-QAudio::State QAudioOutputPrivate::initializingState() const
-{
-    return isDataReady() ? QAudio::ActiveState : QAudio::IdleState;
-}
-
 QT_END_NAMESPACE