/*************************************************************************** * * 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 // Markers for documenting API semantics #include // 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__