/****************************************************************************
**
** 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 {

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 {

//-----------------------------------------------------------------------------
// 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;
}

//-----------------------------------------------------------------------------
// Public functions
//-----------------------------------------------------------------------------

void capabilitiesNativeToQt(const DevSoundCapabilities &caps,
                            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.caps().iRate & SampleRateListNative[i])
            frequencies += SampleRateListQt[i];

    for (int i=0; i<ChannelsCount; ++i)
        if (caps.caps().iChannels & ChannelsListNative[i])
            channels += ChannelsListQt[i];

    for (int i=0; i<EncodingCount; ++i) {
        if (caps.fourCC().Find(EncodingFourCC[i]) != KErrNotFound) {
            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() == "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,
                      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() == "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,
                              QAudio::State initializingState)
{
    switch (nativeState) {
    case ClosedState:
        return QAudio::StoppedState;
    case InitializingState:
        return initializingState;
    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
} // namespace SymbianAudio

QT_END_NAMESPACE


