diff options
author | Gareth Stockwell <ext-gareth.stockwell@nokia.com> | 2010-05-10 07:40:05 (GMT) |
---|---|---|
committer | Gareth Stockwell <ext-gareth.stockwell@nokia.com> | 2010-05-10 07:43:32 (GMT) |
commit | a4bd18a3640fcad545fbd5a85374eb0056687dee (patch) | |
tree | 9376302c6027ebfdaabc069a4a488bc6a616e4fc | |
parent | 23f3c92081a8afd6ff4be352fee407817950e72f (diff) | |
download | Qt-a4bd18a3640fcad545fbd5a85374eb0056687dee.zip Qt-a4bd18a3640fcad545fbd5a85374eb0056687dee.tar.gz Qt-a4bd18a3640fcad545fbd5a85374eb0056687dee.tar.bz2 |
Return correct formats supported lists from Symbian audio backend
* QAudioDeviceInfoInternal functions now call CMMFDevSound::InitializeL()
The root cause of QTBUG-10205 was that CMMFDevSound::IntitializeL()
was not called before CMMFDevSound::Capabilities(), resulting in
incorrect values being returned by QAudioDeviceInfo 'supported format'
functions. Resolving this was not straightforward because, while
QAudioDeviceInfo is an entirely synchronous API,
CMMFDevSound::InitializeL() is an asynchronous function. The changes
therefore include a while(QApplication::instance()->processEvents(...))
loop, which is not a particularly elegant solution.
* Encapsulated all interaction with CMMFDevSound API in DevSoundWrapper
class
Because the original bug fix required QAudioDeviceInfoInternal to call
CMMFDevSound::InitializeL(), MDevSoundObserver callback functions had
to be implemented in QAudioDeviceInfoInternal. Rather than pollute
QAudioDeviceInfoInternal, a new class, DevSoundWrapper, was created
which encapsulates all interaction with CMMFDevSound, and therefore
implements MDevSoundObserver. QAudioInputPrivate and
QAudioOutputPrivate now call CMMFDevSound via DevSoundWrapper, with
only the required MDevSoundObserver callbacks forwarded on via signals.
After fixing this bug, running the auto test suites exposed a number
of regressions which had been introduced by the necessary refactoring
described above; this commit therefore also includes fixes for the
following:
* No stateChanged() signal emitted during DevSound initialization
Previously, QAudioInput / QAudioOutput would emit a state change from
StoppedState to IdleState or ActiveState, as soon as start() was called.
In the case where the requested format is subsequently found to be
unsupported, in addition to the error() value being set to OpenError,
a second state change would be emitted, back to StoppedState. This is
not the behaviour exhibited by the other platforms' backends,
and therefore caused an auto test failure. Now, the backend only
transitions to IdleState / ActiveState on successful initialization.
* Stop emitting notify() signal when notifyInterval is set to zero
* Call CMMFDevSound::RecordInitL() from QAudioInput::resume() if no
buffer is currently held
This is necessary in order to restart the data flow.
* Call CMMFDevSound::BufferFilled() from QAudioOutput::resume() in push
mode
The auto test does not resume pushing data to QAudioOutput until
'bytesFree() >= periodSize()' becomes true. Because, for the Symbian
backend, periodSize() == bufferSize(), this condition is only met when a
new buffer is provided by DevSound via BufferToBeFilled().
Task-number: QTBUG-10205
-rw-r--r-- | src/multimedia/audio/qaudio_symbian_p.cpp | 357 | ||||
-rw-r--r-- | src/multimedia/audio/qaudio_symbian_p.h | 113 | ||||
-rw-r--r-- | src/multimedia/audio/qaudiodeviceinfo_symbian_p.cpp | 106 | ||||
-rw-r--r-- | src/multimedia/audio/qaudiodeviceinfo_symbian_p.h | 28 | ||||
-rw-r--r-- | src/multimedia/audio/qaudioinput_symbian_p.cpp | 231 | ||||
-rw-r--r-- | src/multimedia/audio/qaudioinput_symbian_p.h | 22 | ||||
-rw-r--r-- | src/multimedia/audio/qaudiooutput_symbian_p.cpp | 225 | ||||
-rw-r--r-- | src/multimedia/audio/qaudiooutput_symbian_p.h | 20 |
8 files changed, 649 insertions, 453 deletions
diff --git a/src/multimedia/audio/qaudio_symbian_p.cpp b/src/multimedia/audio/qaudio_symbian_p.cpp index afe98f5..4522c5c 100644 --- a/src/multimedia/audio/qaudio_symbian_p.cpp +++ b/src/multimedia/audio/qaudio_symbian_p.cpp @@ -45,41 +45,6 @@ QT_BEGIN_NAMESPACE namespace SymbianAudio { - -DevSoundCapabilities::DevSoundCapabilities(CMMFDevSound &devsound, - QAudio::Mode mode) -{ - QT_TRAP_THROWING(constructL(devsound, mode)); -} - -DevSoundCapabilities::~DevSoundCapabilities() -{ - m_fourCC.Close(); -} - -void DevSoundCapabilities::constructL(CMMFDevSound &devsound, - QAudio::Mode mode) -{ - m_caps = devsound.Capabilities(); - - TMMFPrioritySettings settings; - - switch (mode) { - case QAudio::AudioOutput: - settings.iState = EMMFStatePlaying; - devsound.GetSupportedInputDataTypesL(m_fourCC, settings); - break; - - case QAudio::AudioInput: - settings.iState = EMMFStateRecording; - devsound.GetSupportedInputDataTypesL(m_fourCC, settings); - break; - - default: - Q_ASSERT_X(false, Q_FUNC_INFO, "Invalid mode"); - } -} - namespace Utils { //----------------------------------------------------------------------------- @@ -274,11 +239,8 @@ bool sampleInfoQtToNative(int inputSampleSize, return found; } -//----------------------------------------------------------------------------- -// Public functions -//----------------------------------------------------------------------------- - -void capabilitiesNativeToQt(const DevSoundCapabilities &caps, +void capabilitiesNativeToQt(const TMMFCapabilities &caps, + const TFourCC &fourcc, QList<int> &frequencies, QList<int> &channels, QList<int> &sampleSizes, @@ -292,37 +254,20 @@ void capabilitiesNativeToQt(const DevSoundCapabilities &caps, channels.clear(); for (int i=0; i<SampleRateCount; ++i) - if (caps.caps().iRate & SampleRateListNative[i]) + if (caps.iRate & SampleRateListNative[i]) frequencies += SampleRateListQt[i]; for (int i=0; i<ChannelsCount; ++i) - if (caps.caps().iChannels & ChannelsListNative[i]) + if (caps.iChannels & ChannelsListNative[i]) channels += ChannelsListQt[i]; for (int i=0; i<EncodingCount; ++i) { - if (caps.fourCC().Find(EncodingFourCC[i]) != KErrNotFound) { + if (fourcc == EncodingFourCC[i]) { sampleSizes += EncodingSampleSize[i]; byteOrders += EncodingByteOrder[i]; sampleTypes += EncodingSampleType[i]; } } - -} - -bool isFormatSupported(const QAudioFormat &formatQt, - const DevSoundCapabilities &caps) { - TMMFCapabilities formatNative; - TUint32 fourCC; - - bool result = false; - if (formatQt.codec() == QString::fromAscii("audio/pcm") && - formatQtToNative(formatQt, fourCC, formatNative)) { - result = - (formatNative.iRate & caps.caps().iRate) - && (formatNative.iChannels & caps.caps().iChannels) - && (caps.fourCC().Find(fourCC) != KErrNotFound); - } - return result; } bool formatQtToNative(const QAudioFormat &inputFormat, @@ -337,7 +282,7 @@ bool formatQtToNative(const QAudioFormat &inputFormat, TMMFMonoStereo outputChannels; TMMFSoundEncoding outputEncoding; - if (inputFormat.codec() == QString::fromAscii("audio/pcm")) { + if (inputFormat.codec() == QLatin1String("audio/pcm")) { result = sampleRateQtToNative(inputFormat.frequency(), outputSampleRate) && channelsQtToNative(inputFormat.channels(), outputChannels) @@ -357,14 +302,13 @@ bool formatQtToNative(const QAudioFormat &inputFormat, return result; } -QAudio::State stateNativeToQt(State nativeState, - QAudio::State initializingState) +QAudio::State stateNativeToQt(State nativeState) { switch (nativeState) { case ClosedState: return QAudio::StoppedState; case InitializingState: - return initializingState; + return QAudio::StoppedState; case ActiveState: return QAudio::ActiveState; case IdleState: @@ -388,6 +332,291 @@ qint64 samplesToBytes(const QAudioFormat &format, qint64 samples) } } // namespace Utils + + +//----------------------------------------------------------------------------- +// DevSoundWrapper +//----------------------------------------------------------------------------- + +DevSoundWrapper::DevSoundWrapper(QAudio::Mode mode, QObject *parent) + : QObject(parent) + , m_mode(mode) + , m_state(StateIdle) + , m_devsound(0) + , m_fourcc(0) +{ + QT_TRAP_THROWING(m_devsound = CMMFDevSound::NewL()); + + switch (mode) { + case QAudio::AudioOutput: + m_nativeMode = EMMFStatePlaying; + break; + + case QAudio::AudioInput: + m_nativeMode = EMMFStateRecording; + break; + + default: + Q_ASSERT_X(false, Q_FUNC_INFO, "Invalid mode"); + } + + getSupportedCodecs(); +} + +DevSoundWrapper::~DevSoundWrapper() +{ + delete m_devsound; +} + +const QList<QString>& DevSoundWrapper::supportedCodecs() const +{ + return m_supportedCodecs; +} + +void DevSoundWrapper::initialize(const QString& codec) +{ + Q_ASSERT(StateInitializing != m_state); + m_state = StateInitializing; + if (QLatin1String("audio/pcm") == codec) { + m_fourcc = KMMFFourCCCodePCM16; + TRAPD(err, m_devsound->InitializeL(*this, m_fourcc, m_nativeMode)); + if (KErrNone != err) { + m_state = StateIdle; + emit initializeComplete(err); + } + } else { + emit initializeComplete(KErrNotSupported); + } +} + +const QList<int>& DevSoundWrapper::supportedFrequencies() const +{ + Q_ASSERT(StateInitialized == m_state); + return m_supportedFrequencies; +} + +const QList<int>& DevSoundWrapper::supportedChannels() const +{ + Q_ASSERT(StateInitialized == m_state); + return m_supportedChannels; +} + +const QList<int>& DevSoundWrapper::supportedSampleSizes() const +{ + Q_ASSERT(StateInitialized == m_state); + return m_supportedSampleSizes; +} + +const QList<QAudioFormat::Endian>& DevSoundWrapper::supportedByteOrders() const +{ + Q_ASSERT(StateInitialized == m_state); + return m_supportedByteOrders; +} + +const QList<QAudioFormat::SampleType>& DevSoundWrapper::supportedSampleTypes() const +{ + Q_ASSERT(StateInitialized == m_state); + return m_supportedSampleTypes; +} + +bool DevSoundWrapper::isFormatSupported(const QAudioFormat &format) const +{ + Q_ASSERT(StateInitialized == m_state); + return m_supportedCodecs.contains(format.codec()) + && m_supportedFrequencies.contains(format.frequency()) + && m_supportedChannels.contains(format.channels()) + && m_supportedSampleSizes.contains(format.sampleSize()) + && m_supportedSampleTypes.contains(format.sampleType()) + && m_supportedByteOrders.contains(format.byteOrder()); +} + +int DevSoundWrapper::samplesProcessed() const +{ + Q_ASSERT(StateInitialized == m_state); + int result = 0; + switch (m_mode) { + case QAudio::AudioInput: + result = m_devsound->SamplesRecorded(); + break; + case QAudio::AudioOutput: + result = m_devsound->SamplesPlayed(); + break; + } + return result; +} + +bool DevSoundWrapper::setFormat(const QAudioFormat &format) +{ + Q_ASSERT(StateInitialized == m_state); + bool result = false; + TUint32 fourcc; + TMMFCapabilities nativeFormat; + if (Utils::formatQtToNative(format, fourcc, nativeFormat)) { + TMMFCapabilities currentNativeFormat = m_devsound->Config(); + nativeFormat.iBufferSize = currentNativeFormat.iBufferSize; + TRAPD(err, m_devsound->SetConfigL(nativeFormat)); + result = (KErrNone == err); + } + return result; +} + +bool DevSoundWrapper::start() +{ + Q_ASSERT(StateInitialized == m_state); + int err = KErrArgument; + switch (m_mode) { + case QAudio::AudioInput: + TRAP(err, m_devsound->RecordInitL()); + break; + case QAudio::AudioOutput: + TRAP(err, m_devsound->PlayInitL()); + break; + } + return (KErrNone == err); +} + +void DevSoundWrapper::pause() +{ + Q_ASSERT(StateInitialized == m_state); + m_devsound->Pause(); +} + +void DevSoundWrapper::stop() +{ + m_devsound->Stop(); +} + +void DevSoundWrapper::bufferProcessed() +{ + Q_ASSERT(StateInitialized == m_state); + switch (m_mode) { + case QAudio::AudioInput: + m_devsound->RecordData(); + break; + case QAudio::AudioOutput: + m_devsound->PlayData(); + break; + } +} + +void DevSoundWrapper::getSupportedCodecs() +{ +/* + * TODO: once we support formats other than PCM, this function should + * convert the array of FourCC codes into MIME types for each codec. + * + RArray<TFourCC> fourcc; + QT_TRAP_THROWING(CleanupClosePushL(&fourcc)); + + TMMFPrioritySettings settings; + switch (mode) { + case QAudio::AudioOutput: + settings.iState = EMMFStatePlaying; + m_devsound->GetSupportedInputDataTypesL(fourcc, settings); + break; + + case QAudio::AudioInput: + settings.iState = EMMFStateRecording; + m_devsound->GetSupportedInputDataTypesL(fourcc, settings); + break; + + default: + Q_ASSERT_X(false, Q_FUNC_INFO, "Invalid mode"); + } + + CleanupStack::PopAndDestroy(); // fourcc +*/ + + m_supportedCodecs.append(QLatin1String("audio/pcm")); +} + +void DevSoundWrapper::populateCapabilities() +{ + m_supportedFrequencies.clear(); + m_supportedChannels.clear(); + m_supportedSampleSizes.clear(); + m_supportedByteOrders.clear(); + m_supportedSampleTypes.clear(); + + const TMMFCapabilities caps = m_devsound->Capabilities(); + + for (int i=0; i<Utils::SampleRateCount; ++i) + if (caps.iRate & Utils::SampleRateListNative[i]) + m_supportedFrequencies += Utils::SampleRateListQt[i]; + + for (int i=0; i<Utils::ChannelsCount; ++i) + if (caps.iChannels & Utils::ChannelsListNative[i]) + m_supportedChannels += Utils::ChannelsListQt[i]; + + for (int i=0; i<Utils::EncodingCount; ++i) { + if (m_fourcc == Utils::EncodingFourCC[i]) { + m_supportedSampleSizes += Utils::EncodingSampleSize[i]; + m_supportedByteOrders += Utils::EncodingByteOrder[i]; + m_supportedSampleTypes += Utils::EncodingSampleType[i]; + } + } +} + +void DevSoundWrapper::InitializeComplete(TInt aError) +{ + Q_ASSERT(StateInitializing == m_state); + if (KErrNone == aError) { + m_state = StateInitialized; + populateCapabilities(); + } else { + m_state = StateIdle; + } + emit initializeComplete(aError); +} + +void DevSoundWrapper::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 DevSoundWrapper::BufferToBeFilled(CMMFBuffer *aBuffer) +{ + Q_ASSERT(QAudio::AudioOutput == m_mode); + emit bufferToBeProcessed(aBuffer); +} + +void DevSoundWrapper::PlayError(TInt aError) +{ + Q_ASSERT(QAudio::AudioOutput == m_mode); + emit processingError(aError); +} + +void DevSoundWrapper::BufferToBeEmptied(CMMFBuffer *aBuffer) +{ + Q_ASSERT(QAudio::AudioInput == m_mode); + emit bufferToBeProcessed(aBuffer); +} + +void DevSoundWrapper::RecordError(TInt aError) +{ + Q_ASSERT(QAudio::AudioInput == m_mode); + emit processingError(aError); +} + +void DevSoundWrapper::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 DevSoundWrapper::DeviceMessage(TUid aMessageType, const TDesC8 &aMsg) +{ + Q_UNUSED(aMessageType) + Q_UNUSED(aMsg) + // Ignore this callback. +} + + } // namespace SymbianAudio QT_END_NAMESPACE diff --git a/src/multimedia/audio/qaudio_symbian_p.h b/src/multimedia/audio/qaudio_symbian_p.h index d5238b4..58ef192 100644 --- a/src/multimedia/audio/qaudio_symbian_p.h +++ b/src/multimedia/audio/qaudio_symbian_p.h @@ -53,7 +53,8 @@ #ifndef QAUDIO_SYMBIAN_P_H #define QAUDIO_SYMBIAN_P_H -#include <QtCore/qnamespace.h> +#include <QtCore/QList> +#include <QtCore/QString> #include <QtMultimedia/qaudioformat.h> #include <QtMultimedia/qaudio.h> #include <sounddevice.h> @@ -83,60 +84,92 @@ enum State { , SuspendedState }; -/* - * Helper class for querying DevSound codec / format support +/** + * Wrapper around DevSound instance */ -class DevSoundCapabilities { +class DevSoundWrapper + : public QObject + , public MDevSoundObserver +{ + Q_OBJECT + +public: + DevSoundWrapper(QAudio::Mode mode, QObject *parent = 0); + ~DevSoundWrapper(); + public: - DevSoundCapabilities(CMMFDevSound &devsound, QAudio::Mode mode); - ~DevSoundCapabilities(); + // List of supported codecs; can be called once object is constructed + const QList<QString>& supportedCodecs() const; + + // Asynchronous initialization function; emits devsoundInitializeComplete + void initialize(const QString& codec); + + // Capabilities, for selected codec. Can be called once initialize has returned + // successfully. + const QList<int>& supportedFrequencies() const; + const QList<int>& supportedChannels() const; + const QList<int>& supportedSampleSizes() const; + const QList<QAudioFormat::Endian>& supportedByteOrders() const; + const QList<QAudioFormat::SampleType>& supportedSampleTypes() const; - const RArray<TFourCC>& fourCC() const { return m_fourCC; } - const TMMFCapabilities& caps() const { return m_caps; } + bool isFormatSupported(const QAudioFormat &format) const; + + int samplesProcessed() const; + bool setFormat(const QAudioFormat &format); + bool start(); + void pause(); + void stop(); + void bufferProcessed(); + +public: + // MDevSoundObserver + void InitializeComplete(TInt aError); + void ToneFinished(TInt aError); + void BufferToBeFilled(CMMFBuffer *aBuffer); + void PlayError(TInt aError); + void BufferToBeEmptied(CMMFBuffer *aBuffer); + void RecordError(TInt aError); + void ConvertError(TInt aError); + void DeviceMessage(TUid aMessageType, const TDesC8 &aMsg); + +signals: + void initializeComplete(int error); + void bufferToBeProcessed(CMMFBuffer *buffer); + void processingError(int error); private: - void constructL(CMMFDevSound &devsound, QAudio::Mode mode); + void getSupportedCodecs(); + void populateCapabilities(); private: - RArray<TFourCC> m_fourCC; - TMMFCapabilities m_caps; -}; + const QAudio::Mode m_mode; + TMMFState m_nativeMode; -namespace Utils { + enum State { + StateIdle, + StateInitializing, + StateInitialized + } m_state; -/** - * Convert native audio capabilities to QAudio lists. - */ -void capabilitiesNativeToQt(const DevSoundCapabilities &caps, - QList<int> &frequencies, - QList<int> &channels, - QList<int> &sampleSizes, - QList<QAudioFormat::Endian> &byteOrders, - QList<QAudioFormat::SampleType> &sampleTypes); + CMMFDevSound* m_devsound; + TFourCC m_fourcc; -/** - * Check whether format is supported. - */ -bool isFormatSupported(const QAudioFormat &format, - const DevSoundCapabilities &caps); + QList<QString> m_supportedCodecs; + QList<int> m_supportedFrequencies; + QList<int> m_supportedChannels; + QList<int> m_supportedSampleSizes; + QList<QAudioFormat::Endian> m_supportedByteOrders; + QList<QAudioFormat::SampleType> m_supportedSampleTypes; -/** - * Convert QAudioFormat to native format types. - * - * Note that, despite the name, DevSound uses TMMFCapabilities to specify - * single formats as well as capabilities. - * - * Note that this function does not modify outputFormat.iBufferSize. - */ -bool formatQtToNative(const QAudioFormat &inputFormat, - TUint32 &outputFourCC, - TMMFCapabilities &outputFormat); +}; + + +namespace Utils { /** * Convert internal states to QAudio states. */ -QAudio::State stateNativeToQt(State nativeState, - QAudio::State initializingState); +QAudio::State stateNativeToQt(State nativeState); /** * Convert data length to number of samples. diff --git a/src/multimedia/audio/qaudiodeviceinfo_symbian_p.cpp b/src/multimedia/audio/qaudiodeviceinfo_symbian_p.cpp index 36284d3..4be116f 100644 --- a/src/multimedia/audio/qaudiodeviceinfo_symbian_p.cpp +++ b/src/multimedia/audio/qaudiodeviceinfo_symbian_p.cpp @@ -39,6 +39,7 @@ ** ****************************************************************************/ +#include <QtCore/QCoreApplication> #include "qaudiodeviceinfo_symbian_p.h" #include "qaudio_symbian_p.h" @@ -50,7 +51,7 @@ QAudioDeviceInfoInternal::QAudioDeviceInfoInternal(QByteArray device, , m_mode(mode) , m_updated(false) { - QT_TRAP_THROWING(m_devsound.reset(CMMFDevSound::NewL())); + } QAudioDeviceInfoInternal::~QAudioDeviceInfoInternal() @@ -85,16 +86,21 @@ QAudioFormat QAudioDeviceInfoInternal::preferredFormat() const } if (!isFormatSupported(format)) { - if (m_frequencies.size()) - format.setFrequency(m_frequencies[0]); - if (m_channels.size()) - format.setChannels(m_channels[0]); - if (m_sampleSizes.size()) - format.setSampleSize(m_sampleSizes[0]); - if (m_byteOrders.size()) - format.setByteOrder(m_byteOrders[0]); - if (m_sampleTypes.size()) - format.setSampleType(m_sampleTypes[0]); + format = QAudioFormat(); + format.setCodec(QLatin1String("audio/pcm")); + if (m_capabilities.contains(format.codec())) { + const Capabilities &codecCaps = m_capabilities[format.codec()]; + if (codecCaps.m_frequencies.size()) + format.setFrequency(codecCaps.m_frequencies[0]); + if (codecCaps.m_channels.size()) + format.setChannels(codecCaps.m_channels[0]); + if (codecCaps.m_sampleSizes.size()) + format.setSampleSize(codecCaps.m_sampleSizes[0]); + if (codecCaps.m_byteOrders.size()) + format.setByteOrder(codecCaps.m_byteOrders[0]); + if (codecCaps.m_sampleTypes.size()) + format.setSampleType(codecCaps.m_sampleTypes[0]); + } } return format; @@ -104,14 +110,15 @@ bool QAudioDeviceInfoInternal::isFormatSupported( const QAudioFormat &format) const { getSupportedFormats(); - const bool supported = - m_codecs.contains(format.codec()) - && m_frequencies.contains(format.frequency()) - && m_channels.contains(format.channels()) - && m_sampleSizes.contains(format.sampleSize()) - && m_byteOrders.contains(format.byteOrder()) - && m_sampleTypes.contains(format.sampleType()); - + bool supported = false; + if (m_capabilities.contains(format.codec())) { + const Capabilities &codecCaps = m_capabilities[format.codec()]; + supported = codecCaps.m_frequencies.contains(format.frequency()) + && codecCaps.m_channels.contains(format.channels()) + && codecCaps.m_sampleSizes.contains(format.sampleSize()) + && codecCaps.m_byteOrders.contains(format.byteOrder()) + && codecCaps.m_sampleTypes.contains(format.sampleType()); + } return supported; } @@ -131,37 +138,37 @@ QString QAudioDeviceInfoInternal::deviceName() const QStringList QAudioDeviceInfoInternal::codecList() { getSupportedFormats(); - return m_codecs; + return m_capabilities.keys(); } QList<int> QAudioDeviceInfoInternal::frequencyList() { getSupportedFormats(); - return m_frequencies; + return m_unionCapabilities.m_frequencies; } QList<int> QAudioDeviceInfoInternal::channelsList() { getSupportedFormats(); - return m_channels; + return m_unionCapabilities.m_channels; } QList<int> QAudioDeviceInfoInternal::sampleSizeList() { getSupportedFormats(); - return m_sampleSizes; + return m_unionCapabilities.m_sampleSizes; } QList<QAudioFormat::Endian> QAudioDeviceInfoInternal::byteOrderList() { getSupportedFormats(); - return m_byteOrders; + return m_unionCapabilities.m_byteOrders; } QList<QAudioFormat::SampleType> QAudioDeviceInfoInternal::sampleTypeList() { getSupportedFormats(); - return m_sampleTypes; + return m_unionCapabilities.m_sampleTypes; } QByteArray QAudioDeviceInfoInternal::defaultInputDevice() @@ -181,17 +188,50 @@ QList<QByteArray> QAudioDeviceInfoInternal::availableDevices(QAudio::Mode) return result; } -void QAudioDeviceInfoInternal::getSupportedFormats() const +void QAudioDeviceInfoInternal::devsoundInitializeComplete(int err) { - if (!m_updated) { - QScopedPointer<SymbianAudio::DevSoundCapabilities> caps( - new SymbianAudio::DevSoundCapabilities(*m_devsound, m_mode)); + m_intializationResult = err; + m_initializing = false; +} - SymbianAudio::Utils::capabilitiesNativeToQt(*caps, - m_frequencies, m_channels, m_sampleSizes, - m_byteOrders, m_sampleTypes); +// Helper function +template<typename T> +void appendUnique(QList<T> &left, const QList<T> &right) +{ + foreach (const T &value, right) + if (!left.contains(value)) + left += value; +} - m_codecs.append(QLatin1String("audio/pcm")); +void QAudioDeviceInfoInternal::getSupportedFormats() const +{ + if (!m_updated) { + QScopedPointer<SymbianAudio::DevSoundWrapper> devsound(new SymbianAudio::DevSoundWrapper(m_mode)); + connect(devsound.data(), SIGNAL(initializeComplete(int)), + this, SLOT(devsoundInitializeComplete(int))); + + foreach (const QString& codec, devsound->supportedCodecs()) { + m_initializing = true; + devsound->initialize(codec); + while (m_initializing) + QCoreApplication::instance()->processEvents(QEventLoop::WaitForMoreEvents); + if (KErrNone == m_intializationResult) { + m_capabilities[codec].m_frequencies = devsound->supportedFrequencies(); + appendUnique(m_unionCapabilities.m_frequencies, devsound->supportedFrequencies()); + + m_capabilities[codec].m_channels = devsound->supportedChannels(); + appendUnique(m_unionCapabilities.m_channels, devsound->supportedChannels()); + + m_capabilities[codec].m_sampleSizes = devsound->supportedSampleSizes(); + appendUnique(m_unionCapabilities.m_sampleSizes, devsound->supportedSampleSizes()); + + m_capabilities[codec].m_byteOrders = devsound->supportedByteOrders(); + appendUnique(m_unionCapabilities.m_byteOrders, devsound->supportedByteOrders()); + + m_capabilities[codec].m_sampleTypes = devsound->supportedSampleTypes(); + appendUnique(m_unionCapabilities.m_sampleTypes, devsound->supportedSampleTypes()); + } + } m_updated = true; } diff --git a/src/multimedia/audio/qaudiodeviceinfo_symbian_p.h b/src/multimedia/audio/qaudiodeviceinfo_symbian_p.h index 89e539f..79b23cc 100644 --- a/src/multimedia/audio/qaudiodeviceinfo_symbian_p.h +++ b/src/multimedia/audio/qaudiodeviceinfo_symbian_p.h @@ -53,11 +53,16 @@ #ifndef QAUDIODEVICEINFO_SYMBIAN_P_H #define QAUDIODEVICEINFO_SYMBIAN_P_H +#include <QtCore/QMap> #include <QtMultimedia/qaudioengine.h> #include <sounddevice.h> QT_BEGIN_NAMESPACE +namespace SymbianAudio { +class DevSoundWrapper; +} + class QAudioDeviceInfoInternal : public QAbstractAudioDeviceInfo { @@ -82,24 +87,33 @@ public: static QByteArray defaultOutputDevice(); static QList<QByteArray> availableDevices(QAudio::Mode); +private slots: + void devsoundInitializeComplete(int err); + private: void getSupportedFormats() const; private: - QScopedPointer<CMMFDevSound> m_devsound; + mutable bool m_initializing; + int m_intializationResult; QString m_deviceName; QAudio::Mode m_mode; + struct Capabilities + { + QList<int> m_frequencies; + QList<int> m_channels; + QList<int> m_sampleSizes; + QList<QAudioFormat::Endian> m_byteOrders; + QList<QAudioFormat::SampleType> m_sampleTypes; + }; + // Mutable to allow lazy initialization when called from const-qualified // public functions (isFormatSupported, nearestFormat) mutable bool m_updated; - mutable QStringList m_codecs; - mutable QList<int> m_frequencies; - mutable QList<int> m_channels; - mutable QList<int> m_sampleSizes; - mutable QList<QAudioFormat::Endian> m_byteOrders; - mutable QList<QAudioFormat::SampleType> m_sampleTypes; + mutable QMap<QString, Capabilities> m_capabilities; + mutable Capabilities m_unionCapabilities; }; QT_END_NAMESPACE diff --git a/src/multimedia/audio/qaudioinput_symbian_p.cpp b/src/multimedia/audio/qaudioinput_symbian_p.cpp index 52daa88..9d240ca 100644 --- a/src/multimedia/audio/qaudioinput_symbian_p.cpp +++ b/src/multimedia/audio/qaudioinput_symbian_p.cpp @@ -116,16 +116,16 @@ QAudioInputPrivate::QAudioInputPrivate(const QByteArray &device, , m_pullMode(false) , m_sink(0) , m_pullTimer(new QTimer(this)) + , m_devSound(0) , m_devSoundBuffer(0) , m_devSoundBufferSize(0) , m_totalBytesReady(0) , m_devSoundBufferPos(0) , m_totalSamplesRecorded(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_pullTimer->setInterval(PushInterval); connect(m_pullTimer.data(), SIGNAL(timeout()), this, SLOT(pullData())); @@ -164,7 +164,7 @@ void QAudioInputPrivate::stop() void QAudioInputPrivate::reset() { m_totalSamplesRecorded += getSamplesRecorded(); - m_devSound->Stop(); + m_devSound->stop(); startRecording(); } @@ -174,7 +174,7 @@ void QAudioInputPrivate::suspend() || SymbianAudio::IdleState == m_internalState) { m_notifyTimer->stop(); m_pullTimer->stop(); - m_devSound->Pause(); + m_devSound->pause(); const qint64 samplesRecorded = getSamplesRecorded(); m_totalSamplesRecorded += samplesRecorded; @@ -189,8 +189,11 @@ void QAudioInputPrivate::suspend() void QAudioInputPrivate::resume() { - if (SymbianAudio::SuspendedState == m_internalState) + if (SymbianAudio::SuspendedState == m_internalState) { + if (!m_pullMode && !bytesReady()) + m_devSound->start(); startDataTransfer(); + } } int QAudioInputPrivate::bytesReady() const @@ -224,11 +227,14 @@ int QAudioInputPrivate::bufferSize() const void QAudioInputPrivate::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(); } } @@ -275,88 +281,6 @@ QAudioFormat QAudioInputPrivate::format() const return m_format; } -//----------------------------------------------------------------------------- -// MDevSoundObserver implementation -//----------------------------------------------------------------------------- - -void QAudioInputPrivate::InitializeComplete(TInt aError) -{ - Q_ASSERT_X(SymbianAudio::InitializingState == m_internalState, - Q_FUNC_INFO, "Invalid state"); - - if (KErrNone == aError) - startRecording(); -} - -void QAudioInputPrivate::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 QAudioInputPrivate::BufferToBeFilled(CMMFBuffer *aBuffer) -{ - Q_UNUSED(aBuffer) - // This class doesn't use DevSound in play mode, so should never receive - // this callback. - Q_ASSERT_X(false, Q_FUNC_INFO, "Unexpected callback"); -} - -void QAudioInputPrivate::PlayError(TInt aError) -{ - Q_UNUSED(aError) - // This class doesn't use DevSound in play mode, so should never receive - // this callback. - Q_ASSERT_X(false, Q_FUNC_INFO, "Unexpected callback"); -} - -void QAudioInputPrivate::BufferToBeEmptied(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"); - - CMMFDataBuffer *const buffer = static_cast<CMMFDataBuffer*>(aBuffer); - - if (!m_devSoundBufferSize) - m_devSoundBufferSize = buffer->Data().MaxLength(); - - m_totalBytesReady += buffer->Data().Length(); - - if (SymbianAudio::SuspendedState == m_internalState) { - m_devSoundBufferQ.append(buffer); - } else { - // Will be returned to DevSound by bufferEmptied(). - m_devSoundBuffer = buffer; - m_devSoundBufferPos = 0; - - if (bytesReady() && !m_pullMode) - pushData(); - } -} - -void QAudioInputPrivate::RecordError(TInt aError) -{ - Q_UNUSED(aError) - setError(QAudio::IOError); -} - -void QAudioInputPrivate::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 QAudioInputPrivate::DeviceMessage(TUid aMessageType, const TDesC8 &aMsg) -{ - Q_UNUSED(aMessageType) - Q_UNUSED(aMsg) - // Ignore this callback. -} //----------------------------------------------------------------------------- // Private functions @@ -367,33 +291,30 @@ void QAudioInputPrivate::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::AudioInput)); + Q_ASSERT(!m_devSound); + m_devSound = new SymbianAudio::DevSoundWrapper(QAudio::AudioInput, this); - int err = SymbianAudio::Utils::isFormatSupported(m_format, *caps) ? - KErrNone : KErrNotSupported; - - if (KErrNone == err) { - setState(SymbianAudio::InitializingState); - TRAP(err, m_devSound->InitializeL(*this, m_nativeFourCC, - EMMFStateRecording)); - } + connect(m_devSound, SIGNAL(initializeComplete(int)), + this, SLOT(devsoundInitializeComplete(int))); + connect(m_devSound, SIGNAL(bufferToBeProcessed(CMMFBuffer *)), + this, SLOT(devsoundBufferToBeEmptied(CMMFBuffer *))); + connect(m_devSound, SIGNAL(processingError(int)), + this, SLOT(devsoundRecordError(int))); - if (KErrNone != err) { - setError(QAudio::OpenError); - m_devSound.reset(); - } + setState(SymbianAudio::InitializingState); + m_devSound->initialize(m_format.codec()); } void QAudioInputPrivate::startRecording() { - const int samplesRecorded = m_devSound->SamplesRecorded(); + const int samplesRecorded = m_devSound->samplesProcessed(); Q_ASSERT(samplesRecorded == 0); - TRAPD(err, startDevSoundL()); - if (KErrNone == err) { + bool ok = m_devSound->setFormat(m_format); + if (ok) + ok = m_devSound->start(); + + if (ok) { startDataTransfer(); } else { setError(QAudio::OpenError); @@ -401,17 +322,10 @@ void QAudioInputPrivate::startRecording() } } -void QAudioInputPrivate::startDevSoundL() -{ - TMMFCapabilities nativeFormat = m_devSound->Config(); - m_nativeFormat.iBufferSize = nativeFormat.iBufferSize; - m_devSound->SetConfigL(m_nativeFormat); - m_devSound->RecordInitL(); -} - void QAudioInputPrivate::startDataTransfer() { - m_notifyTimer->start(m_notifyInterval); + if (m_notifyInterval) + m_notifyTimer->start(m_notifyInterval); if (m_pullMode) m_pullTimer->start(); @@ -503,6 +417,48 @@ void QAudioInputPrivate::pullData() } } +void QAudioInputPrivate::devsoundInitializeComplete(int err) +{ + Q_ASSERT_X(SymbianAudio::InitializingState == m_internalState, + Q_FUNC_INFO, "Invalid state"); + + if (!err && m_devSound->isFormatSupported(m_format)) + startRecording(); + else + setError(QAudio::OpenError); +} + +void QAudioInputPrivate::devsoundBufferToBeEmptied(CMMFBuffer *baseBuffer) +{ + // 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"); + + CMMFDataBuffer *const buffer = static_cast<CMMFDataBuffer*>(baseBuffer); + + if (!m_devSoundBufferSize) + m_devSoundBufferSize = buffer->Data().MaxLength(); + + m_totalBytesReady += buffer->Data().Length(); + + if (SymbianAudio::SuspendedState == m_internalState) { + m_devSoundBufferQ.append(buffer); + } else { + // Will be returned to DevSoundWrapper by bufferProcessed(). + m_devSoundBuffer = buffer; + m_devSoundBufferPos = 0; + + if (bytesReady() && !m_pullMode) + pushData(); + } +} + +void QAudioInputPrivate::devsoundRecordError(int err) +{ + Q_UNUSED(err) + setError(QAudio::IOError); +} + void QAudioInputPrivate::bufferEmptied() { m_devSoundBufferPos = 0; @@ -510,7 +466,7 @@ void QAudioInputPrivate::bufferEmptied() if (m_devSoundBuffer) { m_totalBytesReady -= m_devSoundBuffer->Data().Length(); m_devSoundBuffer = 0; - m_devSound->RecordData(); + m_devSound->bufferProcessed(); } else { Q_ASSERT(!m_devSoundBufferQ.empty()); m_totalBytesReady -= m_devSoundBufferQ.front()->Data().Length(); @@ -518,7 +474,8 @@ void QAudioInputPrivate::bufferEmptied() // If the queue has been emptied, resume transfer from the hardware if (m_devSoundBufferQ.empty()) - m_devSound->RecordInitL(); + if (!m_devSound->start()) + setError(QAudio::IOError); } Q_ASSERT(m_totalBytesReady >= 0); @@ -532,8 +489,10 @@ void QAudioInputPrivate::close() 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; m_totalBytesReady = 0; @@ -554,7 +513,7 @@ qint64 QAudioInputPrivate::getSamplesRecorded() const { qint64 result = 0; if (m_devSound) - result = qint64(m_devSound->SamplesRecorded()); + result = qint64(m_devSound->samplesProcessed()); return result; } @@ -565,30 +524,28 @@ void QAudioInputPrivate::setError(QAudio::Error error) // 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); - - // 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 QAudioInputPrivate::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); } -QAudio::State QAudioInputPrivate::initializingState() const -{ - return QAudio::IdleState; -} - QT_END_NAMESPACE diff --git a/src/multimedia/audio/qaudioinput_symbian_p.h b/src/multimedia/audio/qaudioinput_symbian_p.h index ca3ccf7..7417655 100644 --- a/src/multimedia/audio/qaudioinput_symbian_p.h +++ b/src/multimedia/audio/qaudioinput_symbian_p.h @@ -56,7 +56,6 @@ #include <QtMultimedia/qaudioengine.h> #include <QTime> #include <QTimer> -#include <sounddevice.h> #include "qaudio_symbian_p.h" QT_BEGIN_NAMESPACE @@ -82,7 +81,6 @@ private: class QAudioInputPrivate : public QAbstractAudioInput - , public MDevSoundObserver { friend class SymbianAudioInputPrivate; Q_OBJECT @@ -109,23 +107,15 @@ public: QAudio::State state() const; QAudioFormat format() const; - // MDevSoundObserver - void InitializeComplete(TInt aError); - void ToneFinished(TInt aError); - void BufferToBeFilled(CMMFBuffer *aBuffer); - void PlayError(TInt aError); - void BufferToBeEmptied(CMMFBuffer *aBuffer); - void RecordError(TInt aError); - void ConvertError(TInt aError); - void DeviceMessage(TUid aMessageType, const TDesC8 &aMsg); - private slots: void pullData(); + void devsoundInitializeComplete(int err); + void devsoundBufferToBeEmptied(CMMFBuffer *); + void devsoundRecordError(int err); private: void open(); void startRecording(); - void startDevSoundL(); void startDataTransfer(); CMMFDataBuffer* currentBuffer() const; void pushData(); @@ -138,8 +128,6 @@ private: void setError(QAudio::Error error); void setState(SymbianAudio::State state); - QAudio::State initializingState() const; - private: const QByteArray m_device; const QAudioFormat m_format; @@ -158,9 +146,7 @@ private: QScopedPointer<QTimer> m_pullTimer; - QScopedPointer<CMMFDevSound> m_devSound; - TUint32 m_nativeFourCC; - TMMFCapabilities m_nativeFormat; + SymbianAudio::DevSoundWrapper* m_devSound; // Latest buffer provided by DevSound, to be empied of data. CMMFDataBuffer *m_devSoundBuffer; diff --git a/src/multimedia/audio/qaudiooutput_symbian_p.cpp b/src/multimedia/audio/qaudiooutput_symbian_p.cpp index 3f8e933..5098469 100644 --- a/src/multimedia/audio/qaudiooutput_symbian_p.cpp +++ b/src/multimedia/audio/qaudiooutput_symbian_p.cpp @@ -110,6 +110,7 @@ QAudioOutputPrivate::QAudioOutputPrivate(const QByteArray &device, , 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 @@ QAudioOutputPrivate::QAudioOutputPrivate(const QByteArray &device, , 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 @@ QIODevice* QAudioOutputPrivate::start(QIODevice *device) { 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::stop() void QAudioOutputPrivate::reset() { m_totalSamplesPlayed += getSamplesPlayed(); - m_devSound->Stop(); + m_devSound->stop(); m_bytesPadding = 0; startPlayback(); } @@ -196,11 +194,12 @@ void QAudioOutputPrivate::suspend() // 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::suspend() 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 @@ int QAudioOutputPrivate::bufferSize() const 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,35 +305,52 @@ QAudioFormat QAudioOutputPrivate::format() const return m_format; } + //----------------------------------------------------------------------------- -// MDevSoundObserver implementation +// Private functions //----------------------------------------------------------------------------- -void QAudioOutputPrivate::InitializeComplete(TInt aError) +void QAudioOutputPrivate::dataReady() { - Q_ASSERT_X(SymbianAudio::InitializingState == m_internalState, - Q_FUNC_INFO, "Invalid state"); + // Client-provided QIODevice has data ready to read. - if (KErrNone == aError) - startPlayback(); + Q_ASSERT_X(m_source->bytesAvailable(), Q_FUNC_INFO, + "readyRead signal received, but no data available"); + + if (!m_bytesPadding) + pullData(); } -void QAudioOutputPrivate::ToneFinished(TInt aError) +void QAudioOutputPrivate::underflowTimerExpired() { - 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"); + const TInt samplesPlayed = getSamplesPlayed(); + if (m_samplesPlayed && (samplesPlayed == m_samplesPlayed)) { + setError(QAudio::UnderrunError); + } else { + m_samplesPlayed = samplesPlayed; + m_underflowTimer->start(); + } +} + +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::BufferToBeFilled(CMMFBuffer *aBuffer) +void QAudioOutputPrivate::devsoundBufferToBeFilled(CMMFBuffer *bufferBase) { - // Following receipt of this callback, DevSound should not provide another + // 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 DevSound by bufferFilled(). - m_devSoundBuffer = static_cast<CMMFDataBuffer*>(aBuffer); + // Will be returned to DevSoundWrapper by bufferProcessed(). + m_devSoundBuffer = static_cast<CMMFDataBuffer*>(bufferBase); if (!m_devSoundBufferSize) m_devSoundBufferSize = m_devSoundBuffer->Data().MaxLength(); @@ -339,9 +361,9 @@ void QAudioOutputPrivate::BufferToBeFilled(CMMFBuffer *aBuffer) pullData(); } -void QAudioOutputPrivate::PlayError(TInt aError) +void QAudioOutputPrivate::devsoundPlayError(int err) { - switch (aError) { + switch (err) { case KErrUnderflow: m_underflow = true; if (m_pullMode && !m_lastBuffer) @@ -355,102 +377,42 @@ void QAudioOutputPrivate::PlayError(TInt aError) } } -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 -//----------------------------------------------------------------------------- - -void QAudioOutputPrivate::dataReady() -{ - // Client-provided QIODevice has data ready to read. - - Q_ASSERT_X(m_source->bytesAvailable(), Q_FUNC_INFO, - "readyRead signal received, but no data available"); - - if (!m_bytesPadding) - pullData(); -} - -void QAudioOutputPrivate::underflowTimerExpired() -{ - const TInt samplesPlayed = getSamplesPlayed(); - if (m_samplesPlayed && (samplesPlayed == m_samplesPlayed)) { - setError(QAudio::UnderrunError); - } else { - m_samplesPlayed = samplesPlayed; - m_underflowTimer->start(); - } -} - 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)); + Q_ASSERT(!m_devSound); + m_devSound = new SymbianAudio::DevSoundWrapper(QAudio::AudioOutput, this); - int err = SymbianAudio::Utils::isFormatSupported(m_format, *caps) ? - KErrNone : KErrNotSupported; + 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) { - setState(SymbianAudio::InitializingState); - TRAP(err, m_devSound->InitializeL(*this, m_nativeFourCC, - EMMFStatePlaying)); - } - - 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::startPlayback() } } -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 @@ void QAudioOutputPrivate::writePaddingData() 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 @@ void QAudioOutputPrivate::pullData() 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 @@ void QAudioOutputPrivate::bufferFilled() if (QAudio::UnderrunError == m_error) m_error = QAudio::NoError; - m_devSound->PlayData(); + m_devSound->bufferProcessed(); } void QAudioOutputPrivate::lastBufferFilled() @@ -610,8 +562,10 @@ void QAudioOutputPrivate::close() 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 @@ qint64 QAudioOutputPrivate::getSamplesPlayed() const // 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 @@ void QAudioOutputPrivate::setError(QAudio::Error error) // 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 @@ bool QAudioOutputPrivate::isDataReady() const || m_pushDataReady; } -QAudio::State QAudioOutputPrivate::initializingState() const -{ - return isDataReady() ? QAudio::ActiveState : QAudio::IdleState; -} - QT_END_NAMESPACE diff --git a/src/multimedia/audio/qaudiooutput_symbian_p.h b/src/multimedia/audio/qaudiooutput_symbian_p.h index 00ccb24..c0acb07 100644 --- a/src/multimedia/audio/qaudiooutput_symbian_p.h +++ b/src/multimedia/audio/qaudiooutput_symbian_p.h @@ -80,7 +80,6 @@ private: class QAudioOutputPrivate : public QAbstractAudioOutput - , public MDevSoundObserver { friend class SymbianAudioOutputPrivate; Q_OBJECT @@ -107,24 +106,16 @@ public: QAudio::State state() const; QAudioFormat format() const; - // MDevSoundObserver - void InitializeComplete(TInt aError); - void ToneFinished(TInt aError); - void BufferToBeFilled(CMMFBuffer *aBuffer); - void PlayError(TInt aError); - void BufferToBeEmptied(CMMFBuffer *aBuffer); - void RecordError(TInt aError); - void ConvertError(TInt aError); - void DeviceMessage(TUid aMessageType, const TDesC8 &aMsg); - private slots: void dataReady(); void underflowTimerExpired(); + void devsoundInitializeComplete(int err); + void devsoundBufferToBeFilled(CMMFBuffer *); + void devsoundPlayError(int err); private: void open(); void startPlayback(); - void startDevSoundL(); void writePaddingData(); qint64 pushData(const char *data, qint64 len); void pullData(); @@ -138,7 +129,6 @@ private: void setState(SymbianAudio::State state); bool isDataReady() const; - QAudio::State initializingState() const; private: const QByteArray m_device; @@ -156,9 +146,7 @@ private: bool m_pullMode; QIODevice *m_source; - QScopedPointer<CMMFDevSound> m_devSound; - TUint32 m_nativeFourCC; - TMMFCapabilities m_nativeFormat; + SymbianAudio::DevSoundWrapper* m_devSound; // Buffer provided by DevSound, to be filled with data. CMMFDataBuffer *m_devSoundBuffer; |