719 lines
31 KiB
C
719 lines
31 KiB
C
/***************************************************************************
|
|
*
|
|
* 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__
|