/***************************************************************************

 *

 * Copyright (c) Microsoft Corporation.  All rights reserved.

 *

 * File:     xma2defs.h

 * Content:  Constants, data types and functions for XMA2 compressed audio.

 *

 ***************************************************************************/



#ifndef __XMA2DEFS_INCLUDED__

#define __XMA2DEFS_INCLUDED__



#include "audiodefs.h"  // Basic data types and constants for audio work





/***************************************************************************

 *  Overview

 ***************************************************************************/



// A typical XMA2 file contains these RIFF chunks:

//

// 'fmt' or 'XMA2' chunk (or both): A description of the XMA data's structure

// and characteristics (length, channels, sample rate, loops, block size, etc).

//

// 'seek' chunk: A seek table to help navigate the XMA data.

//

// 'data' chunk: The encoded XMA2 data.

//

// The encoded XMA2 data is structured as a set of BLOCKS, which contain PACKETS,

// which contain FRAMES, which contain SUBFRAMES (roughly speaking).  The frames

// in a file may also be divided into several subsets, called STREAMS.

//

// FRAME: A variable-sized segment of XMA data that decodes to exactly 512 mono

//      or stereo PCM samples.  This is the smallest unit of XMA data that can

//      be decoded in isolation.  Frames are an arbitrary number of bits in

//      length, and need not be byte-aligned.  See "XMA frame structure" below.

//

// SUBFRAME: A region of bits in an XMA frame that decodes to 128 mono or stereo

//      samples.  The XMA decoder cannot decode a subframe in isolation; it needs

//      a whole frame to work with.  However, it can begin emitting the frame's

//      decoded samples at any one of the four subframe boundaries.  Subframes

//      can be addressed for seeking and looping purposes.

//

// PACKET: A 2Kb region containing a 32-bit header and some XMA frames.  Frames

//      can (and usually do) span packets.  A packet's header includes the offset

//      in bits of the first frame that begins within that packet.  All of the

//      frames that begin in a given packet belong to the same "stream" (see the

//      Multichannel Audio section below).

//

// STREAM: A set of packets within an XMA file that all contain data for the

//      same mono or stereo component of a PCM file with more than two channels.

//      The packets comprising a given stream may be interleaved with each other

//      more or less arbitrarily; see Multichannel Audio.

//

// BLOCK: An array of XMA packets; or, to break it down differently, a series of

//      consecutive XMA frames, padded at the end with reserved data.  A block

//      must contain at least one 2Kb packet per stream, and it can hold up to

//      4095 packets (8190Kb), but its size is typically in the 32Kb-128Kb range.

//      (The size chosen involves a trade-off between memory use and efficiency

//      of reading from permanent storage.)

//

//      XMA frames do not span blocks, so a block is guaranteed to begin with a

//      set of complete frames, one per stream.  Also, a block in a multi-stream

//      XMA2 file always contains the same number of samples for each stream in

//      the file; see Multichannel Audio.

//

// The 'data' chunk in an XMA2 file is an array of XMA2WAVEFORMAT.BlockCount XMA

// blocks, all of the same size (as specified in XMA2WAVEFORMAT.BlockSizeInBytes)

// except for the last one, which may be shorter.





// MULTICHANNEL AUDIO: the XMA decoder can only decode raw XMA data into either

// mono or stereo PCM data.  In order to encode a 6-channel file (say), the file

// must be deinterleaved into 3 stereo streams that are encoded independently,

// producing 3 encoded XMA data streams.  Then the packets in these 3 streams

// are interleaved to produce a single XMA2 file, and some information is added

// to the file so that the original 6-channel audio can be reconstructed at

// decode time.  This works using the concept of an XMA stream (see above).

//

// The frames for all the streams in an XMA file are interleaved in an arbitrary

// order.  To locate a frame that belongs to a given stream in a given XMA block,

// you must examine the first few packets in the block.  Here (and only here) the

// packets are guaranteed to be presented in stream order, so that all frames

// beginning in packet 0 belong to stream 0 (the first stereo pair), etc.

//

// (This means that when decoding multi-stream XMA files, only entire XMA blocks

// should be submitted to the decoder; otherwise it cannot know which frames

// belong to which stream.)

//

// Once you have one frame that belongs to a given stream, you can find the next

// one by looking at the frame's 'NextFrameOffsetBits' value (which is stored in

// its first 15 bits; see XMAFRAME below).  The GetXmaFrameBitPosition function

// uses this technique.





// SEEKING IN XMA2 FILES: Here is some pseudocode to find the byte position and

// subframe in an XMA2 file which will contain sample S when decoded.

//

// 1. Traverse the seek table to find the XMA2 block containing sample S. The

//    seek table is an array of big-endian DWORDs, one per block in the file.

//    The Nth DWORD is the total number of PCM samples that would be obtained

//    by decoding the entire XMA file up to the end of block N.  Hence, the

//    block we want is the first one whose seek table entry is greater than S.

//    (See the GetXmaBlockContainingSample helper function.)

//

// 2. Calculate which frame F within the block found above contains sample S.

//    Since each frame decodes to 512 samples, this is straightforward.  The

//    first frame in the block produces samples X to X + 512, where X is the

//    seek table entry for the prior block.  So F is (S - X) / 512.

//

// 3. Find the bit offset within the block where frame F starts.  Since frames

//    are variable-sized, this can only be done by traversing all the frames in

//    the block until we reach frame F.  (See GetXmaFrameBitPosition.)

//

// 4. This frame is made up of four 128-sample subframes.  To find the subframe

//    containing S, we can use the formula (S % 512) / 128.

//

// In the case of multi-stream XMA files, sample S is a multichannel sample with

// parts coming from several frames, one per stream.  To find all these frames,

// steps 2-4 need to be repeated for each stream N, using the knowledge that the

// first packets in a block are presented in stream order.  The frame traversal

// in step 3 must be started at the first frame in the Nth packet of the block,

// which will be the first frame for stream N.  (And the packet header will tell

// you where within the packet's first frame begins.)

//

// Step 1 can be performed using the GetXmaBlockContainingSample function below,

// and steps 2-4 by calling GetXmaDecodePositionForSample once for each stream.







/***************************************************************************

 *  XMA constants

 ***************************************************************************/



// Size of the PCM samples produced by the XMA decoder

#define XMA_OUTPUT_SAMPLE_BYTES         2u

#define XMA_OUTPUT_SAMPLE_BITS          (XMA_OUTPUT_SAMPLE_BYTES * 8u)



// Size of an XMA packet

#define XMA_BYTES_PER_PACKET            2048u

#define XMA_BITS_PER_PACKET             (XMA_BYTES_PER_PACKET * 8u)



// Size of an XMA packet header

#define XMA_PACKET_HEADER_BYTES         4u

#define XMA_PACKET_HEADER_BITS          (XMA_PACKET_HEADER_BYTES * 8u)



// Sample blocks in a decoded XMA frame

#define XMA_SAMPLES_PER_FRAME           512u



// Sample blocks in a decoded XMA subframe

#define XMA_SAMPLES_PER_SUBFRAME        128u



// Maximum encoded data that can be submitted to the XMA decoder at a time

#define XMA_READBUFFER_MAX_PACKETS      4095u

#define XMA_READBUFFER_MAX_BYTES        (XMA_READBUFFER_MAX_PACKETS * XMA_BYTES_PER_PACKET)



// Maximum size allowed for the XMA decoder's output buffers

#define XMA_WRITEBUFFER_MAX_BYTES       (31u * 256u)



// Required byte alignment of the XMA decoder's output buffers

#define XMA_WRITEBUFFER_BYTE_ALIGNMENT  256u



// Decode chunk sizes for the XMA_PLAYBACK_INIT.subframesToDecode field

#define XMA_MIN_SUBFRAMES_TO_DECODE     1u

#define XMA_MAX_SUBFRAMES_TO_DECODE     8u

#define XMA_OPTIMAL_SUBFRAMES_TO_DECODE 4u



// Maximum legal value for LoopCount; represent an infinite number of loops

#define XMA_MAX_LOOPCOUNT               255u







/***************************************************************************

 *  XMA format structures

 ***************************************************************************/



// The currently recommended way to express format information for XMA2 files

// is the XMAWAVEFORMATEX structure.  This structure is fully compliant with

// the WAVEFORMATEX standard and contains all the information needed to parse

// and manage XMA2 files in a compact way.



#define WAVE_FORMAT_XMA2 0x166



typedef struct XMA2WAVEFORMATEX

{

    WAVEFORMATEX wfx;

    // Meaning of the WAVEFORMATEX fields here:

    //    wFormatTag;        // Audio format type; always WAVE_FORMAT_XMA2

    //    nChannels;         // Channel count of the decoded audio

    //    nSamplesPerSec;    // Sample rate of the decoded audio

    //    nAvgBytesPerSec;   // Used internally by the XMA encoder

    //    nBlockAlign;       // Decoded sample size; channels * wBitsPerSample / 8

    //    wBitsPerSample;    // Bits per decoded mono sample; always 16 for XMA

    //    cbSize;            // Size in bytes of the rest of this structure (34)



    WORD  NumStreams;        // Number of audio streams (1 or 2 channels each)

    DWORD ChannelMask;       // Spatial positions of the channels in this file

    DWORD SamplesEncoded;    // Total number of PCM samples the file decodes to

    DWORD BytesPerBlock;     // XMA block size (but the last one may be shorter)

    DWORD PlayBegin;         // First valid sample in the decoded audio

    DWORD PlayLength;        // Length of the valid part of the decoded audio

    DWORD LoopBegin;         // Beginning of the loop region in decoded sample terms

    DWORD LoopLength;        // Length of the loop region in decoded sample terms

    BYTE  LoopCount;         // Number of loops; 0 = no looping, 255 == infinite

    BYTE  EncoderVersion;    // Version of XMA encoder that generated the file

    WORD  BlockCount;        // XMA blocks in file (and entries in its seek table)

} XMA2WAVEFORMATEX, *PXMA2WAVEFORMATEX;





// The legacy XMA format structures are described here for reference, but they

// should not be used in new content.  XMAWAVEFORMAT was the structure used in

// XMA version 1 files.  XMA2WAVEFORMAT was used in early XMA2 files; it is not

// placed in the usual 'fmt' RIFF chunk but in its own 'XMA2' chunk.



#ifndef WAVE_FORMAT_XMA

#define WAVE_FORMAT_XMA 0x0165



// Values used in the ChannelMask fields below.  Similar to the SPEAKER_xxx

// values used in WAVEFORMATEXTENSIBLE, but modified to fit in a single byte.

#ifndef XMA_SPEAKER_LEFT

    #define XMA_SPEAKER_LEFT            0x01

    #define XMA_SPEAKER_RIGHT           0x02

    #define XMA_SPEAKER_CENTER          0x04

    #define XMA_SPEAKER_LFE             0x08

    #define XMA_SPEAKER_LEFT_SURROUND   0x10

    #define XMA_SPEAKER_RIGHT_SURROUND  0x20

    #define XMA_SPEAKER_LEFT_BACK       0x40

    #define XMA_SPEAKER_RIGHT_BACK      0x80

#endif





// Used in XMAWAVEFORMAT for per-stream data

typedef struct XMASTREAMFORMAT

{

    DWORD PsuedoBytesPerSec; // Used by the XMA encoder (typo preserved for legacy reasons)

    DWORD SampleRate;        // The stream's decoded sample rate (in XMA2 files,

                             // this is the same for all streams in the file).

    DWORD LoopStart;         // Bit offset of the frame containing the loop start

                             // point, relative to the beginning of the stream.

    DWORD LoopEnd;           // Bit offset of the frame containing the loop end.

    BYTE  SubframeData;      // Two 4-bit numbers specifying the exact location of

                             // the loop points within the frames that contain them.

                             //  1st 4 bits: Which subframe of the end frame does

                             //              the loop end at.  Ranges from 0 to 3.

                             //  2nd 4 bits: Which subframe of the start frame does

                             //              the loop begin at.  Ranges from 1 to 4.

    BYTE  Channels;          // Number of channels in the stream (1 or 2)

    WORD  ChannelMask;       // Spatial positions of the channels in the stream

} XMASTREAMFORMAT;



// Legacy XMA1 format structure

typedef struct XMAWAVEFORMAT

{

    WORD FormatTag;          // Audio format type (always WAVE_FORMAT_XMA)

    WORD BitsPerSample;      // Bit depth (currently required to be 16)

    WORD EncodeOptions;      // Options for XMA encoder/decoder

    WORD LargestSkip;        // Largest skip used in interleaving streams

    WORD NumStreams;         // Number of interleaved audio streams

    BYTE LoopCount;          // Number of loop repetitions; 255 == infinite

    BYTE Version;            // XMA encoder version that generated the file.

                             // Always 3 or higher for XMA2 files.

    XMASTREAMFORMAT XmaStreams[1]; // Per-stream format information; the actual

                                   // array length is in the NumStreams field.

} XMAWAVEFORMAT;





// Used in XMA2WAVEFORMAT for per-stream data

typedef struct XMA2STREAMFORMAT

{

    BYTE Channels;           // Number of channels in the stream (1 or 2)

    BYTE RESERVED;           // Reserved for future use

    WORD ChannelMask;        // Spatial positions of the channels in the stream

} XMA2STREAMFORMAT;



// Legacy XMA2 format structure (big-endian byte ordering)

typedef struct XMA2WAVEFORMAT

{

    BYTE  Version;           // XMA encoder version that generated the file.

                             // Always 3 or higher for XMA2 files.

    BYTE  NumStreams;        // Number of interleaved audio streams

    BYTE  RESERVED;          // Reserved for future use

    BYTE  LoopCount;         // Loop count; 255 == infinite

    DWORD LoopBegin;         // Loop begin point, in samples

    DWORD LoopEnd;           // Loop end point, in samples

    DWORD SampleRate;        // The file's decoded sample rate

    DWORD EncodeOptions;     // Options for the XMA encoder/decoder

    DWORD PsuedoBytesPerSec; // Used internally by the XMA encoder

    DWORD BlockSizeInBytes;  // Size in bytes of this file's XMA blocks (except

                             // possibly the last one).  Always a multiple of

                             // 2Kb, since XMA blocks are arrays of 2Kb packets.

    DWORD SamplesEncoded;    // Total number of PCM samples encoded in this file

    DWORD SamplesInSource;   // Actual number of PCM samples in the source

                             // material used to generate this file

    DWORD BlockCount;        // Number of XMA blocks in this file (and hence

                             // also the number of entries in its seek table)

    XMA2STREAMFORMAT Streams[1]; // Per-stream format information; the actual

                                 // array length is in the NumStreams field.

} XMA2WAVEFORMAT;



#endif // #ifndef WAVE_FORMAT_XMA







/***************************************************************************

 *  XMA packet structure (in big-endian form)

 ***************************************************************************/



typedef struct XMA2PACKET

{

    int FrameCount        :  6;  // Number of XMA frames that begin in this packet

    int FrameOffsetInBits : 15;  // Bit of XmaData where the first complete frame begins

    int PacketMetaData    :  3;  // Metadata stored in the packet (always 1 for XMA2)

    int PacketSkipCount   :  8;  // How many packets belonging to other streams must be

                                 // skipped to find the next packet belonging to this one

    BYTE XmaData[XMA_BYTES_PER_PACKET - sizeof(DWORD)];  // XMA encoded data

} XMA2PACKET;



// E.g. if the first DWORD of a packet is 0x30107902:

//

// 001100 000001000001111 001 00000010

//    |          |         |      |____ Skip 2 packets to find the next one for this stream

//    |          |         |___________ XMA2 signature (always 001)

//    |          |_____________________ First frame starts 527 bits into packet

//    |________________________________ Packet contains 12 frames





// Helper functions to extract the fields above from an XMA packet.  (Note that

// the bitfields cannot be read directly on little-endian architectures such as

// the Intel x86, as they are laid out in big-endian form.)



__inline DWORD GetXmaPacketFrameCount(const BYTE* pPacket)

{

    return (DWORD)(pPacket[0] >> 2);

}



__inline DWORD GetXmaPacketFirstFrameOffsetInBits(const BYTE* pPacket)

{

    return ((DWORD)(pPacket[0] & 0x3) << 13) |

           ((DWORD)(pPacket[1]) << 5) |

           ((DWORD)(pPacket[2]) >> 3);

}



__inline DWORD GetXmaPacketMetadata(const BYTE* pPacket)

{

    return (DWORD)(pPacket[2] & 0x7);

}



__inline DWORD GetXmaPacketSkipCount(const BYTE* pPacket)

{

    return (DWORD)(pPacket[3]);

}







/***************************************************************************

 *  XMA frame structure

 ***************************************************************************/



// There is no way to represent the XMA frame as a C struct, since it is a

// variable-sized string of bits that need not be stored at a byte-aligned

// position in memory.  This is the layout:

//

// XMAFRAME

// {

//    LengthInBits: A 15-bit number representing the length of this frame.

//    XmaData: Encoded XMA data; its size in bits is (LengthInBits - 15).

// }







/***************************************************************************

 *  XMA helper functions

 ***************************************************************************/



// Try to use ASSERT and TRACE macros if available

#ifdef ASSERT

    #define XMA2DEFS_ASSERT ASSERT

#else

    #define XMA2DEFS_ASSERT(a)

#endif



// GetXmaBlockContainingSample: Use a given seek table to find the XMA block

// containing a given decoded sample.



__inline HRESULT GetXmaBlockContainingSample

(

    DWORD nBlockCount,               // Blocks in the file (= seek table entries)

    const DWORD* pSeekTable,         // Pointer to data from the seek table chunk

    DWORD nDesiredSample,            // Decoder sample to locate

    DWORD* pnBlockContainingSample,  // Index of the block containing the sample

    DWORD* pnSampleOffsetWithinBlock // Position of the sample in this block

)

{

    DWORD nPreviousTotalSamples = 0;

    DWORD nBlock;

    DWORD nTotalSamplesSoFar;



    XMA2DEFS_ASSERT(pSeekTable);

    XMA2DEFS_ASSERT(pnBlockContainingSample);

    XMA2DEFS_ASSERT(pnSampleOffsetWithinBlock);



    for (nBlock = 0; nBlock < nBlockCount; ++nBlock)

    {

        // REVISE: Is byte-swapping required?

        nTotalSamplesSoFar = pSeekTable[nBlock];

        if (nTotalSamplesSoFar > nDesiredSample)

        {

            *pnBlockContainingSample = nBlock;

            *pnSampleOffsetWithinBlock = nDesiredSample - nPreviousTotalSamples;

            return S_OK;

        }

        nPreviousTotalSamples = nTotalSamplesSoFar;

    }



    return E_FAIL;

}



// GetXmaFrameLengthInBits: Reads a given frame's LengthInBits field.



__inline DWORD GetXmaFrameLengthInBits

(

    const BYTE* pPacket, // Beginning of the XMA packet containing the frame

    DWORD nBitPosition   // Bit offset of the frame within this packet

)

{

    DWORD nBytePosition = nBitPosition / 8;



    DWORD nRegion = (DWORD)(pPacket[nBytePosition+0]) << 16 |

                    (DWORD)(pPacket[nBytePosition+1]) << 8 |

                    (DWORD)(pPacket[nBytePosition+2]);



    return (nRegion >> (9 - nBitPosition % 8)) & 0x7FFF;  // Last 15 bits

}



// GetXmaFrameBitPosition: Calculates the bit offset of a given frame within

// an XMA block or set of blocks.  Returns 0 on failure.



__inline DWORD GetXmaFrameBitPosition

(

    const BYTE* pXmaData,  // Pointer to beginning of the XMA block[s]

    DWORD nXmaDataBytes,   // Size of pXmaData in bytes

    DWORD nStreamIndex,    // Stream within which to seek

    DWORD nDesiredFrame    // Frame sought

)

{

    const BYTE* pCurrentPacket;

    DWORD nPacketsExamined = 0;

    DWORD nFrameCountSoFar = 0;

    DWORD nFramesToSkip;

    DWORD nFrameBitOffset;



    XMA2DEFS_ASSERT(pXmaData);

    XMA2DEFS_ASSERT(nXmaDataBytes % XMA_BYTES_PER_PACKET == 0);



    // Get the first XMA packet belonging to the desired stream, relying on the

    // fact that the first packets for each stream are in consecutive order at

    // the beginning of an XMA block.



    pCurrentPacket = pXmaData + nStreamIndex * XMA_BYTES_PER_PACKET;

    for (;;)

    {

        // If we have exceeded the size of the XMA data, return failure

        if (pCurrentPacket + XMA_BYTES_PER_PACKET > pXmaData + nXmaDataBytes)

        {

            return 0;

        }



        // If the current packet contains the frame we are looking for...

        if (nFrameCountSoFar + GetXmaPacketFrameCount(pCurrentPacket) > nDesiredFrame)

        {

            // See how many frames in this packet we need to skip to get to it

            XMA2DEFS_ASSERT(nDesiredFrame >= nFrameCountSoFar);

            nFramesToSkip = nDesiredFrame - nFrameCountSoFar;



            // Get the bit offset of the first frame in this packet

            nFrameBitOffset = XMA_PACKET_HEADER_BITS + GetXmaPacketFirstFrameOffsetInBits(pCurrentPacket);



            // Advance nFrameBitOffset to the frame of interest

            while (nFramesToSkip--)

            {

                nFrameBitOffset += GetXmaFrameLengthInBits(pCurrentPacket, nFrameBitOffset);

            }



            // The bit offset to return is the number of bits from pXmaData to

            // pCurrentPacket plus the bit offset of the frame of interest

            return (DWORD)(pCurrentPacket - pXmaData) * 8 + nFrameBitOffset;

        }



        // If we haven't found the right packet yet, advance our counters

        ++nPacketsExamined;

        nFrameCountSoFar += GetXmaPacketFrameCount(pCurrentPacket);



        // And skip to the next packet belonging to the same stream

        pCurrentPacket += XMA_BYTES_PER_PACKET * (GetXmaPacketSkipCount(pCurrentPacket) + 1);

    }

}



// GetLastXmaFrameBitPosition: Calculates the bit offset of the last frame in

// an XMA block or set of blocks.  Returns 0 on failure.



__inline DWORD GetLastXmaFrameBitPosition

(

    const BYTE* pXmaData,  // Pointer to beginning of the XMA block[s]

    DWORD nXmaDataBytes,   // Size of pXmaData in bytes

    DWORD nStreamIndex     // Stream within which to seek

)

{

    const BYTE* pCurrentPacket;

    DWORD nBytesToNextPacket;



    XMA2DEFS_ASSERT(pXmaData);

    XMA2DEFS_ASSERT(nXmaDataBytes % XMA_BYTES_PER_PACKET == 0);



    // Get the first XMA packet belonging to the desired stream, relying on the

    // fact that the first packets for each stream are in consecutive order at

    // the beginning of an XMA block.



    pCurrentPacket = pXmaData + nStreamIndex * XMA_BYTES_PER_PACKET;

    for (;;)

    {

        // If we have exceeded the size of the XMA data, return failure

        if (pCurrentPacket + XMA_BYTES_PER_PACKET > pXmaData + nXmaDataBytes)

        {

            return 0;

        }



        // See if the next packet begins beyond pXmaData + nXmaDataBytes

        nBytesToNextPacket = XMA_BYTES_PER_PACKET * (GetXmaPacketSkipCount(pCurrentPacket) + 1);

        if (pCurrentPacket + nBytesToNextPacket >= pXmaData + nXmaDataBytes)

        {

            // Found the last packet.  Get the bit offset of its first frame.

            DWORD nFrameBitOffset = XMA_PACKET_HEADER_BITS + GetXmaPacketFirstFrameOffsetInBits(pCurrentPacket);



            // Find the bit offset of the last complete frame in the packet

            DWORD nFrameCount = GetXmaPacketFrameCount(pCurrentPacket);

            while (--nFrameCount)

            {

                nFrameBitOffset += GetXmaFrameLengthInBits(pCurrentPacket, nFrameBitOffset);

            }



            // The bit offset to return is the number of bits from pXmaData to

            // pCurrentPacket plus the offset of the last frame in this packet

            return (DWORD)(pCurrentPacket - pXmaData) * 8 + nFrameBitOffset;

        }



        // Advance to the next packet and continue

        pCurrentPacket += nBytesToNextPacket;

    }

}



// GetXmaDecodePositionForSample: Obtains the information needed to make the

// decoder generate audio starting at a given sample position relative to the

// beginning of the given XMA block: the bit offset of the appropriate frame,

// and the right subframe within that frame.  This data can be passed directly

// to the XMAPlaybackSetDecodePosition function.



__inline HRESULT GetXmaDecodePositionForSample

(

    const BYTE* pXmaData,  // Pointer to beginning of the XMA block[s]

    DWORD nXmaDataBytes,   // Size of pXmaData in bytes

    DWORD nStreamIndex,    // Stream within which to seek

    DWORD nDesiredSample,  // Sample sought

    DWORD* pnBitOffset,    // Returns the bit offset within pXmaData of

                           // the frame containing the sample sought

    DWORD* pnSubFrame      // Returns the subframe containing the sample

)

{

    DWORD nDesiredFrame = nDesiredSample / XMA_SAMPLES_PER_FRAME;

    DWORD nSubFrame = (nDesiredSample % XMA_SAMPLES_PER_FRAME) / XMA_SAMPLES_PER_SUBFRAME;

    DWORD nBitOffset = GetXmaFrameBitPosition(pXmaData, nXmaDataBytes, nStreamIndex, nDesiredFrame);



    XMA2DEFS_ASSERT(pnBitOffset);

    XMA2DEFS_ASSERT(pnSubFrame);



    if (nBitOffset)

    {

        *pnBitOffset = nBitOffset;

        *pnSubFrame = nSubFrame;

        return S_OK;

    }

    else

    {

        return E_FAIL;

    }

}



// GetXmaSampleRate: Obtains the legal XMA sample rate (24, 32, 44.1 or 48Khz)

// corresponding to a generic sample rate.



__inline DWORD GetXmaSampleRate(DWORD dwGeneralRate)

{

    DWORD dwXmaRate = 48000; // Default XMA rate for all rates above 44100Hz



    if (dwGeneralRate <= 24000)      dwXmaRate = 24000;

    else if (dwGeneralRate <= 32000) dwXmaRate = 32000;

    else if (dwGeneralRate <= 44100) dwXmaRate = 44100;



    return dwXmaRate;

}



// Functions to convert between WAVEFORMATEXTENSIBLE channel masks (combinations

// of the SPEAKER_xxx flags defined in x3daudio.h) and XMA channel masks (which

// are limited to eight possible speaker positions: left, right, center, low

// frequency, side left, side right, back left and back right).



#ifndef SPEAKER_FRONT_LEFT

    #include <x3daudio.h>       // Definitions of the SPEAKER_xxx positions

    #define _SPEAKER_POSITIONS_ // Avoids macro redefinition errors in mmreg.h

#endif



__inline DWORD GetStandardChannelMaskFromXmaMask(BYTE bXmaMask)

{

    DWORD dwStandardMask = 0;



    if (bXmaMask & XMA_SPEAKER_LEFT)           dwStandardMask |= SPEAKER_FRONT_LEFT;

    if (bXmaMask & XMA_SPEAKER_RIGHT)          dwStandardMask |= SPEAKER_FRONT_RIGHT;

    if (bXmaMask & XMA_SPEAKER_CENTER)         dwStandardMask |= SPEAKER_FRONT_CENTER;

    if (bXmaMask & XMA_SPEAKER_LFE)            dwStandardMask |= SPEAKER_LOW_FREQUENCY;

    if (bXmaMask & XMA_SPEAKER_LEFT_SURROUND)  dwStandardMask |= SPEAKER_SIDE_LEFT;

    if (bXmaMask & XMA_SPEAKER_RIGHT_SURROUND) dwStandardMask |= SPEAKER_SIDE_RIGHT;

    if (bXmaMask & XMA_SPEAKER_LEFT_BACK)      dwStandardMask |= SPEAKER_BACK_LEFT;

    if (bXmaMask & XMA_SPEAKER_RIGHT_BACK)     dwStandardMask |= SPEAKER_BACK_RIGHT;



    return dwStandardMask;

}



__inline BYTE GetXmaChannelMaskFromStandardMask(DWORD dwStandardMask)

{

    BYTE bXmaMask = 0;



    if (dwStandardMask & SPEAKER_FRONT_LEFT)    bXmaMask |= XMA_SPEAKER_LEFT;

    if (dwStandardMask & SPEAKER_FRONT_RIGHT)   bXmaMask |= XMA_SPEAKER_RIGHT;

    if (dwStandardMask & SPEAKER_FRONT_CENTER)  bXmaMask |= XMA_SPEAKER_CENTER;

    if (dwStandardMask & SPEAKER_LOW_FREQUENCY) bXmaMask |= XMA_SPEAKER_LFE;

    if (dwStandardMask & SPEAKER_SIDE_LEFT)     bXmaMask |= XMA_SPEAKER_LEFT_SURROUND;

    if (dwStandardMask & SPEAKER_SIDE_RIGHT)    bXmaMask |= XMA_SPEAKER_RIGHT_SURROUND;

    if (dwStandardMask & SPEAKER_BACK_LEFT)     bXmaMask |= XMA_SPEAKER_LEFT_BACK;

    if (dwStandardMask & SPEAKER_BACK_RIGHT)    bXmaMask |= XMA_SPEAKER_RIGHT_BACK;



    return bXmaMask;

}



// LocalizeXma2Format: Modifies a XMA2WAVEFORMATEX structure in place to comply

// with the current platform's byte-ordering rules (little- or big-endian).



__inline HRESULT LocalizeXma2Format(XMA2WAVEFORMATEX* pXma2Format)

{

    #define XMASWAP2BYTES(n) ((WORD)(((n) >> 8) | (((n) & 0xff) << 8)))

    #define XMASWAP4BYTES(n) ((DWORD)((n) >> 24 | (n) << 24 | ((n) & 0xff00) << 8 | ((n) & 0xff0000) >> 8))



    if (pXma2Format->wfx.wFormatTag == WAVE_FORMAT_XMA2)

    {

        return S_OK;

    }

    else if (XMASWAP2BYTES(pXma2Format->wfx.wFormatTag) == WAVE_FORMAT_XMA2)

    {

        pXma2Format->wfx.wFormatTag      = XMASWAP2BYTES(pXma2Format->wfx.wFormatTag);

        pXma2Format->wfx.nChannels       = XMASWAP2BYTES(pXma2Format->wfx.nChannels);

        pXma2Format->wfx.nSamplesPerSec  = XMASWAP4BYTES(pXma2Format->wfx.nSamplesPerSec);

        pXma2Format->wfx.nAvgBytesPerSec = XMASWAP4BYTES(pXma2Format->wfx.nAvgBytesPerSec);

        pXma2Format->wfx.nBlockAlign     = XMASWAP2BYTES(pXma2Format->wfx.nBlockAlign);

        pXma2Format->wfx.wBitsPerSample  = XMASWAP2BYTES(pXma2Format->wfx.wBitsPerSample);

        pXma2Format->wfx.cbSize          = XMASWAP2BYTES(pXma2Format->wfx.cbSize);

        pXma2Format->NumStreams          = XMASWAP2BYTES(pXma2Format->NumStreams);

        pXma2Format->ChannelMask         = XMASWAP4BYTES(pXma2Format->ChannelMask);

        pXma2Format->SamplesEncoded      = XMASWAP4BYTES(pXma2Format->SamplesEncoded);

        pXma2Format->BytesPerBlock       = XMASWAP4BYTES(pXma2Format->BytesPerBlock);

        pXma2Format->PlayBegin           = XMASWAP4BYTES(pXma2Format->PlayBegin);

        pXma2Format->PlayLength          = XMASWAP4BYTES(pXma2Format->PlayLength);

        pXma2Format->LoopBegin           = XMASWAP4BYTES(pXma2Format->LoopBegin);

        pXma2Format->LoopLength          = XMASWAP4BYTES(pXma2Format->LoopLength);

        pXma2Format->BlockCount          = XMASWAP2BYTES(pXma2Format->BlockCount);

        return S_OK;

    }

    else

    {

        return E_FAIL; // Not a recognizable XMA2 format

    }



    #undef XMASWAP2BYTES

    #undef XMASWAP4BYTES

}





#endif // #ifndef __XMA2DEFS_INCLUDED__

