diff options
Diffstat (limited to 'src/multimedia/audio/qaudio_symbian_p.cpp')
-rw-r--r-- | src/multimedia/audio/qaudio_symbian_p.cpp | 624 |
1 files changed, 624 insertions, 0 deletions
diff --git a/src/multimedia/audio/qaudio_symbian_p.cpp b/src/multimedia/audio/qaudio_symbian_p.cpp new file mode 100644 index 0000000000..4522c5c7e8 --- /dev/null +++ b/src/multimedia/audio/qaudio_symbian_p.cpp @@ -0,0 +1,624 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtMultimedia module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qaudio_symbian_p.h" +#include <mmffourcc.h> + +QT_BEGIN_NAMESPACE + +namespace SymbianAudio { +namespace Utils { + +//----------------------------------------------------------------------------- +// Static data +//----------------------------------------------------------------------------- + +// Sample rate / frequency + +typedef TMMFSampleRate SampleRateNative; +typedef int SampleRateQt; + +const int SampleRateCount = 12; + +const SampleRateNative SampleRateListNative[SampleRateCount] = { + EMMFSampleRate8000Hz + , EMMFSampleRate11025Hz + , EMMFSampleRate12000Hz + , EMMFSampleRate16000Hz + , EMMFSampleRate22050Hz + , EMMFSampleRate24000Hz + , EMMFSampleRate32000Hz + , EMMFSampleRate44100Hz + , EMMFSampleRate48000Hz + , EMMFSampleRate64000Hz + , EMMFSampleRate88200Hz + , EMMFSampleRate96000Hz +}; + +const SampleRateQt SampleRateListQt[SampleRateCount] = { + 8000 + , 11025 + , 12000 + , 16000 + , 22050 + , 24000 + , 32000 + , 44100 + , 48000 + , 64000 + , 88200 + , 96000 +}; + +// Channels + +typedef TMMFMonoStereo ChannelsNative; +typedef int ChannelsQt; + +const int ChannelsCount = 2; + +const ChannelsNative ChannelsListNative[ChannelsCount] = { + EMMFMono + , EMMFStereo +}; + +const ChannelsQt ChannelsListQt[ChannelsCount] = { + 1 + , 2 +}; + +// Encoding + +const int EncodingCount = 6; + +const TUint32 EncodingFourCC[EncodingCount] = { + KMMFFourCCCodePCM8 // 0 + , KMMFFourCCCodePCMU8 // 1 + , KMMFFourCCCodePCM16 // 2 + , KMMFFourCCCodePCMU16 // 3 + , KMMFFourCCCodePCM16B // 4 + , KMMFFourCCCodePCMU16B // 5 +}; + +// The characterised DevSound API specification states that the iEncoding +// field in TMMFCapabilities is ignored, and that the FourCC should be used +// to specify the PCM encoding. +// See "SGL.GT0287.102 Multimedia DevSound Baseline Compatibility.doc" in the +// mm_info/mm_docs repository. +const TMMFSoundEncoding EncodingNative[EncodingCount] = { + EMMFSoundEncoding16BitPCM // 0 + , EMMFSoundEncoding16BitPCM // 1 + , EMMFSoundEncoding16BitPCM // 2 + , EMMFSoundEncoding16BitPCM // 3 + , EMMFSoundEncoding16BitPCM // 4 + , EMMFSoundEncoding16BitPCM // 5 +}; + + +const int EncodingSampleSize[EncodingCount] = { + 8 // 0 + , 8 // 1 + , 16 // 2 + , 16 // 3 + , 16 // 4 + , 16 // 5 +}; + +const QAudioFormat::Endian EncodingByteOrder[EncodingCount] = { + QAudioFormat::LittleEndian // 0 + , QAudioFormat::LittleEndian // 1 + , QAudioFormat::LittleEndian // 2 + , QAudioFormat::LittleEndian // 3 + , QAudioFormat::BigEndian // 4 + , QAudioFormat::BigEndian // 5 +}; + +const QAudioFormat::SampleType EncodingSampleType[EncodingCount] = { + QAudioFormat::SignedInt // 0 + , QAudioFormat::UnSignedInt // 1 + , QAudioFormat::SignedInt // 2 + , QAudioFormat::UnSignedInt // 3 + , QAudioFormat::SignedInt // 4 + , QAudioFormat::UnSignedInt // 5 +}; + + +//----------------------------------------------------------------------------- +// Private functions +//----------------------------------------------------------------------------- + +// Helper functions for implementing parameter conversions + +template<typename Input> +bool findValue(const Input *inputArray, int length, Input input, int &index) { + bool result = false; + for (int i=0; !result && i<length; ++i) + if (inputArray[i] == input) { + index = i; + result = true; + } + return result; +} + +template<typename Input, typename Output> +bool convertValue(const Input *inputArray, const Output *outputArray, + int length, Input input, Output &output) { + int index; + const bool result = findValue<Input>(inputArray, length, input, index); + if (result) + output = outputArray[index]; + return result; +} + +/** + * Macro which is used to generate the implementation of the conversion + * functions. The implementation is just a wrapper around the templated + * convertValue function, e.g. + * + * CONVERSION_FUNCTION_IMPL(SampleRate, Qt, Native) + * + * expands to + * + * bool SampleRateQtToNative(int input, TMMFSampleRate &output) { + * return convertValue<SampleRateQt, SampleRateNative> + * (SampleRateListQt, SampleRateListNative, SampleRateCount, + * input, output); + * } + */ +#define CONVERSION_FUNCTION_IMPL(FieldLc, Field, Input, Output) \ +bool FieldLc##Input##To##Output(Field##Input input, Field##Output &output) { \ + return convertValue<Field##Input, Field##Output>(Field##List##Input, \ + Field##List##Output, Field##Count, input, output); \ +} + +//----------------------------------------------------------------------------- +// Local helper functions +//----------------------------------------------------------------------------- + +CONVERSION_FUNCTION_IMPL(sampleRate, SampleRate, Qt, Native) +CONVERSION_FUNCTION_IMPL(sampleRate, SampleRate, Native, Qt) +CONVERSION_FUNCTION_IMPL(channels, Channels, Qt, Native) +CONVERSION_FUNCTION_IMPL(channels, Channels, Native, Qt) + +bool sampleInfoQtToNative(int inputSampleSize, + QAudioFormat::Endian inputByteOrder, + QAudioFormat::SampleType inputSampleType, + TUint32 &outputFourCC, + TMMFSoundEncoding &outputEncoding) { + + bool found = false; + + for (int i=0; i<EncodingCount && !found; ++i) { + if ( EncodingSampleSize[i] == inputSampleSize + && EncodingByteOrder[i] == inputByteOrder + && EncodingSampleType[i] == inputSampleType) { + outputFourCC = EncodingFourCC[i]; + outputEncoding = EncodingNative[i]; // EMMFSoundEncoding16BitPCM + found = true; + } + } + + return found; +} + +void capabilitiesNativeToQt(const TMMFCapabilities &caps, + const TFourCC &fourcc, + QList<int> &frequencies, + QList<int> &channels, + QList<int> &sampleSizes, + QList<QAudioFormat::Endian> &byteOrders, + QList<QAudioFormat::SampleType> &sampleTypes) { + + frequencies.clear(); + sampleSizes.clear(); + byteOrders.clear(); + sampleTypes.clear(); + channels.clear(); + + for (int i=0; i<SampleRateCount; ++i) + if (caps.iRate & SampleRateListNative[i]) + frequencies += SampleRateListQt[i]; + + for (int i=0; i<ChannelsCount; ++i) + if (caps.iChannels & ChannelsListNative[i]) + channels += ChannelsListQt[i]; + + for (int i=0; i<EncodingCount; ++i) { + if (fourcc == EncodingFourCC[i]) { + sampleSizes += EncodingSampleSize[i]; + byteOrders += EncodingByteOrder[i]; + sampleTypes += EncodingSampleType[i]; + } + } +} + +bool formatQtToNative(const QAudioFormat &inputFormat, + TUint32 &outputFourCC, + TMMFCapabilities &outputFormat) { + + bool result = false; + + // Need to use temporary variables because TMMFCapabilities fields are all + // TInt, rather than MMF enumerated types. + TMMFSampleRate outputSampleRate; + TMMFMonoStereo outputChannels; + TMMFSoundEncoding outputEncoding; + + if (inputFormat.codec() == QLatin1String("audio/pcm")) { + result = + sampleRateQtToNative(inputFormat.frequency(), outputSampleRate) + && channelsQtToNative(inputFormat.channels(), outputChannels) + && sampleInfoQtToNative(inputFormat.sampleSize(), + inputFormat.byteOrder(), + inputFormat.sampleType(), + outputFourCC, + outputEncoding); + } + + if (result) { + outputFormat.iRate = outputSampleRate; + outputFormat.iChannels = outputChannels; + outputFormat.iEncoding = outputEncoding; + } + + return result; +} + +QAudio::State stateNativeToQt(State nativeState) +{ + switch (nativeState) { + case ClosedState: + return QAudio::StoppedState; + case InitializingState: + return QAudio::StoppedState; + case ActiveState: + return QAudio::ActiveState; + case IdleState: + return QAudio::IdleState; + case SuspendedState: + return QAudio::SuspendedState; + default: + Q_ASSERT_X(false, Q_FUNC_INFO, "Invalid state"); + return QAudio::StoppedState; // suppress compiler warning + } +} + +qint64 bytesToSamples(const QAudioFormat &format, qint64 length) +{ + return length / ((format.sampleSize() / 8) * format.channels()); +} + +qint64 samplesToBytes(const QAudioFormat &format, qint64 samples) +{ + return samples * (format.sampleSize() / 8) * format.channels(); +} + +} // 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 + + |