/***************************************************************************
 *
 * 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 <sal.h>        // Markers for documenting API semantics
#include <winerror.h>   // For S_OK, E_FAIL
#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;
//      see Multichannel Audio.
//
// The 'data' chunk in an XMA2 file is an array of XMA2WAVEFORMAT.BlockCount XMA
// blocks, all 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. Frame F has 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 the first frame's start position within the packet.)
//
// 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

// LoopCount<255 means finite repetitions; LoopCount=255 means infinite looping
#define XMA_MAX_LOOPCOUNT               254u
#define XMA_INFINITE_LOOP               255u



/***************************************************************************
 *  XMA format structures
 ***************************************************************************/

// The currently recommended way to express format information for XMA2 files
// is the XMA2WAVEFORMATEX 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,
                             // stored as SPEAKER_xxx values (see audiodefs.h)
    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 loop repetitions; 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 defined in audiodefs.h, 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.
                             //   SubframeEnd: Subframe of the loop end frame where
                             //                the loop ends.  Ranges from 0 to 3.
                             //   SubframeSkip: Subframes to skip in the start frame to
                             //                 reach the loop.  Ranges from 0 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;         // Number of loop repetitions; 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(__in_bcount(1) const BYTE* pPacket)
{
    return (DWORD)(pPacket[0] >> 2);
}

__inline DWORD GetXmaPacketFirstFrameOffsetInBits(__in_bcount(3) const BYTE* pPacket)
{
    return ((DWORD)(pPacket[0] & 0x3) << 13) |
           ((DWORD)(pPacket[1]) << 5) |
           ((DWORD)(pPacket[2]) >> 3);
}

__inline DWORD GetXmaPacketMetadata(__in_bcount(3) const BYTE* pPacket)
{
    return (DWORD)(pPacket[2] & 0x7);
}

__inline DWORD GetXmaPacketSkipCount(__in_bcount(4) 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).
// }

// Size in bits of the frame's initial LengthInBits field
#define XMA_BITS_IN_FRAME_LENGTH_FIELD 15

// Special LengthInBits value that marks an invalid final frame
#define XMA_FINAL_FRAME_MARKER 0x7FFF



/***************************************************************************
 *  XMA helper functions
 ***************************************************************************/

// We define a local ASSERT macro to equal the global one if it exists.
// You can define XMA2DEFS_ASSERT in advance to override this default.
#ifndef XMA2DEFS_ASSERT
    #ifdef ASSERT
        #define XMA2DEFS_ASSERT ASSERT
    #else
        #define XMA2DEFS_ASSERT(a) /* No-op by default */
    #endif
#endif


// GetXmaBlockContainingSample: Use a given seek table to find the XMA block
// containing a given decoded sample.  Note that the seek table entries in an
// XMA file are stored in big-endian form and may need to be converted prior
// to calling this function.

__inline HRESULT GetXmaBlockContainingSample
(
    DWORD nBlockCount,                      // Blocks in the file (= seek table entries)
    __in_ecount(nBlockCount) const DWORD* pSeekTable,  // Pointer to the seek table data
    DWORD nDesiredSample,                   // Decoded sample to locate
    __out DWORD* pnBlockContainingSample,   // Index of the block containing the sample
    __out 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)
    {
        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
(
    __in_bcount(nBitPosition / 8 + 3)
    __in const BYTE* pPacket,  // Pointer to XMA packet[s] containing the frame
    DWORD nBitPosition         // Bit offset of the frame within this packet
)
{
    DWORD nRegion;
    DWORD nBytePosition = nBitPosition / 8;
    DWORD nBitOffset = nBitPosition % 8;

    if (nBitOffset < 2) // Only need to read 2 bytes (and might not be safe to read more)
    {
        nRegion = (DWORD)(pPacket[nBytePosition+0]) << 8 |
                  (DWORD)(pPacket[nBytePosition+1]);
        return (nRegion >> (1 - nBitOffset)) & 0x7FFF;  // Last 15 bits
    }
    else // Need to read 3 bytes
    {
        nRegion = (DWORD)(pPacket[nBytePosition+0]) << 16 |
                  (DWORD)(pPacket[nBytePosition+1]) << 8 |
                  (DWORD)(pPacket[nBytePosition+2]);
        return (nRegion >> (9 - nBitOffset)) & 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
(
    __in_bcount(nXmaDataBytes) const BYTE* pXmaData,  // Pointer to 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 complete
// frame in an XMA block or set of blocks.

__inline DWORD GetLastXmaFrameBitPosition
(
    __in_bcount(nXmaDataBytes) const BYTE* pXmaData,  // Pointer to XMA block[s]
    DWORD nXmaDataBytes,                              // Size of pXmaData in bytes
    DWORD nStreamIndex                                // Stream within which to seek
)
{
    const BYTE* pLastPacket;
    DWORD nBytesToNextPacket;
    DWORD nFrameBitOffset;
    DWORD nFramesInLastPacket;

    XMA2DEFS_ASSERT(pXmaData);
    XMA2DEFS_ASSERT(nXmaDataBytes % XMA_BYTES_PER_PACKET == 0);
    XMA2DEFS_ASSERT(nXmaDataBytes >= XMA_BYTES_PER_PACKET * (nStreamIndex + 1));

    // 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.
    pLastPacket = pXmaData + nStreamIndex * XMA_BYTES_PER_PACKET;

    // Search for the last packet belonging to the desired stream
    for (;;)
    {
        nBytesToNextPacket = XMA_BYTES_PER_PACKET * (GetXmaPacketSkipCount(pLastPacket) + 1);
        XMA2DEFS_ASSERT(nBytesToNextPacket);
        if (pLastPacket + nBytesToNextPacket + XMA_BYTES_PER_PACKET > pXmaData + nXmaDataBytes)
        {
            break;  // The next packet would extend beyond the end of pXmaData
        }
        pLastPacket += nBytesToNextPacket;
    }

    // The last packet can sometimes have no seekable frames, in which case we
    // have to use the previous one
    if (GetXmaPacketFrameCount(pLastPacket) == 0)
    {
        pLastPacket -= nBytesToNextPacket;
    }

    // Found the last packet.  Get the bit offset of its first frame.
    nFrameBitOffset = XMA_PACKET_HEADER_BITS + GetXmaPacketFirstFrameOffsetInBits(pLastPacket);

    // Traverse frames until we reach the last one
    nFramesInLastPacket = GetXmaPacketFrameCount(pLastPacket);
    while (--nFramesInLastPacket)
    {
        nFrameBitOffset += GetXmaFrameLengthInBits(pLastPacket, nFrameBitOffset);
    }

    // The bit offset to return is the number of bits from pXmaData to
    // pLastPacket plus the offset of the last frame in this packet.
    return (DWORD)(pLastPacket - pXmaData) * 8 + nFrameBitOffset;
}


// 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
(
    __in_bcount(nXmaDataBytes) const BYTE* pXmaData,  // Pointer to XMA block[s]
    DWORD nXmaDataBytes,                              // Size of pXmaData in bytes
    DWORD nStreamIndex,                               // Stream within which to seek
    DWORD nDesiredSample,                             // Sample sought
    __out DWORD* pnBitOffset,                         // Returns the bit offset within pXmaData of
                                                      // the frame containing the sample sought
    __out 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 audiodefs.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).

__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(__inout 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__