/***************************************************************************
 
 *
 
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 
 *
 
 *  File:       xact3wb.h
 
 *  Content:    XACT 3 wave bank definitions.
 
 *
 
 ****************************************************************************/
 
 
 
#ifndef __XACT3WB_H__
 
#define __XACT3WB_H__
 
 
 
#ifdef _XBOX
 
#   include <xtl.h>
 
#else
 
#   include <math.h>
 
#endif
 
 
 
#include <audiodefs.h>
 
#include <xma2defs.h>
 
 
 
#pragma warning(push)
 
#pragma warning(disable:4201)
 
#pragma warning(disable:4214)   // nonstandard extension used : bit field types other than int
 
 
 
#pragma pack(push, 1)
 
#if !defined(_X86_)
 
    #define XACTUNALIGNED __unaligned
 
#else
 
    #define XACTUNALIGNED
 
#endif
 
 
 
#ifdef _M_PPCBE
 
#pragma bitfield_order(push, lsb_to_msb)
 
#endif
 
 
 
#define WAVEBANK_HEADER_SIGNATURE               'DNBW'      // WaveBank  RIFF chunk signature
 
#define WAVEBANK_HEADER_VERSION                 44          // Current wavebank file version
 
 
 
#define WAVEBANK_BANKNAME_LENGTH                64          // Wave bank friendly name length, in characters
 
#define WAVEBANK_ENTRYNAME_LENGTH               64          // Wave bank entry friendly name length, in characters
 
 
 
#define WAVEBANK_MAX_DATA_SEGMENT_SIZE          0xFFFFFFFF  // Maximum wave bank data segment size, in bytes
 
#define WAVEBANK_MAX_COMPACT_DATA_SEGMENT_SIZE  0x001FFFFF  // Maximum compact wave bank data segment size, in bytes
 
 
 
typedef DWORD WAVEBANKOFFSET;
 
 
 
//
 
// Bank flags
 
//
 
 
 
#define WAVEBANK_TYPE_BUFFER         0x00000000      // In-memory buffer
 
#define WAVEBANK_TYPE_STREAMING      0x00000001      // Streaming
 
#define WAVEBANK_TYPE_MASK           0x00000001
 
 
 
#define WAVEBANK_FLAGS_ENTRYNAMES    0x00010000      // Bank includes entry names
 
#define WAVEBANK_FLAGS_COMPACT       0x00020000      // Bank uses compact format
 
#define WAVEBANK_FLAGS_SYNC_DISABLED 0x00040000      // Bank is disabled for audition sync
 
#define WAVEBANK_FLAGS_SEEKTABLES    0x00080000      // Bank includes seek tables.
 
#define WAVEBANK_FLAGS_MASK          0x000F0000
 
 
 
//
 
// Entry flags
 
//
 
 
 
#define WAVEBANKENTRY_FLAGS_READAHEAD       0x00000001  // Enable stream read-ahead
 
#define WAVEBANKENTRY_FLAGS_LOOPCACHE       0x00000002  // One or more looping sounds use this wave
 
#define WAVEBANKENTRY_FLAGS_REMOVELOOPTAIL  0x00000004  // Remove data after the end of the loop region
 
#define WAVEBANKENTRY_FLAGS_IGNORELOOP      0x00000008  // Used internally when the loop region can't be used
 
#define WAVEBANKENTRY_FLAGS_MASK            0x00000008
 
 
 
//
 
// Entry wave format identifiers
 
//
 
 
 
#define WAVEBANKMINIFORMAT_TAG_PCM      0x0     // PCM data
 
#define WAVEBANKMINIFORMAT_TAG_XMA      0x1     // XMA data
 
#define WAVEBANKMINIFORMAT_TAG_ADPCM    0x2     // ADPCM data
 
#define WAVEBANKMINIFORMAT_TAG_WMA      0x3     // WMA data
 
 
 
#define WAVEBANKMINIFORMAT_BITDEPTH_8   0x0     // 8-bit data (PCM only)
 
#define WAVEBANKMINIFORMAT_BITDEPTH_16  0x1     // 16-bit data (PCM only)
 
 
 
//
 
// Arbitrary fixed sizes
 
//
 
#define WAVEBANKENTRY_XMASTREAMS_MAX          3   // enough for 5.1 channel audio
 
#define WAVEBANKENTRY_XMACHANNELS_MAX         6   // enough for 5.1 channel audio (cf. XAUDIOCHANNEL_SOURCEMAX)
 
 
 
//
 
// DVD data sizes
 
//
 
 
 
#define WAVEBANK_DVD_SECTOR_SIZE    2048
 
#define WAVEBANK_DVD_BLOCK_SIZE     (WAVEBANK_DVD_SECTOR_SIZE * 16)
 
 
 
//
 
// Bank alignment presets
 
//
 
 
 
#define WAVEBANK_ALIGNMENT_MIN  4                           // Minimum alignment
 
#define WAVEBANK_ALIGNMENT_DVD  WAVEBANK_DVD_SECTOR_SIZE    // DVD-optimized alignment
 
 
 
//
 
// Wave bank segment identifiers
 
//
 
 
 
typedef enum WAVEBANKSEGIDX
 
{
 
    WAVEBANK_SEGIDX_BANKDATA = 0,       // Bank data
 
    WAVEBANK_SEGIDX_ENTRYMETADATA,      // Entry meta-data
 
    WAVEBANK_SEGIDX_SEEKTABLES,         // Storage for seek tables for the encoded waves.
 
    WAVEBANK_SEGIDX_ENTRYNAMES,         // Entry friendly names
 
    WAVEBANK_SEGIDX_ENTRYWAVEDATA,      // Entry wave data
 
    WAVEBANK_SEGIDX_COUNT
 
} WAVEBANKSEGIDX, *LPWAVEBANKSEGIDX;
 
 
 
typedef const WAVEBANKSEGIDX *LPCWAVEBANKSEGIDX;
 
 
 
//
 
// Endianness
 
//
 
 
 
#ifdef __cplusplus
 
 
 
namespace XACTWaveBank
 
{
 
    __inline void SwapBytes(XACTUNALIGNED DWORD &dw)
 
    {
 
 
 
#ifdef _X86_
 
 
 
        __asm
 
        {
 
            mov edi, dw
 
            mov eax, [edi]
 
            bswap eax
 
            mov [edi], eax
 
        }
 
 
 
#else // _X86_
 
 
 
        dw = _byteswap_ulong(dw);
 
 
 
#endif // _X86_
 
 
 
    }
 
 
 
    __inline void SwapBytes(XACTUNALIGNED WORD &w)
 
    {
 
 
 
#ifdef _X86_
 
 
 
        __asm
 
        {
 
            mov edi, w
 
            mov ax, [edi]
 
            xchg ah, al
 
            mov [edi], ax
 
        }
 
 
 
#else // _X86_
 
 
 
        w = _byteswap_ushort(w);
 
 
 
#endif // _X86_
 
 
 
    }
 
 
 
}
 
 
 
#endif // __cplusplus
 
 
 
//
 
// Wave bank region in bytes.
 
//
 
 
 
typedef struct WAVEBANKREGION
 
{
 
    DWORD       dwOffset;               // Region offset, in bytes.
 
    DWORD       dwLength;               // Region length, in bytes.
 
 
 
#ifdef __cplusplus
 
 
 
    void SwapBytes(void)
 
    {
 
        XACTWaveBank::SwapBytes(dwOffset);
 
        XACTWaveBank::SwapBytes(dwLength);
 
    }
 
 
 
#endif // __cplusplus
 
 
 
} WAVEBANKREGION, *LPWAVEBANKREGION;
 
 
 
typedef const WAVEBANKREGION *LPCWAVEBANKREGION;
 
 
 
 
 
//
 
// Wave bank region in samples.
 
//
 
 
 
typedef struct WAVEBANKSAMPLEREGION
 
{
 
    DWORD       dwStartSample;          // Start sample for the region.
 
    DWORD       dwTotalSamples;         // Region length in samples.
 
 
 
#ifdef __cplusplus
 
 
 
    void SwapBytes(void)
 
    {
 
        XACTWaveBank::SwapBytes(dwStartSample);
 
        XACTWaveBank::SwapBytes(dwTotalSamples);
 
    }
 
 
 
#endif // __cplusplus
 
 
 
} WAVEBANKSAMPLEREGION, *LPWAVEBANKSAMPLEREGION;
 
 
 
typedef const WAVEBANKSAMPLEREGION *LPCWAVEBANKSAMPLEREGION;
 
 
 
 
 
//
 
// Wave bank file header
 
//
 
 
 
typedef struct WAVEBANKHEADER
 
{
 
    DWORD           dwSignature;                        // File signature
 
    DWORD           dwVersion;                          // Version of the tool that created the file
 
    DWORD           dwHeaderVersion;                    // Version of the file format
 
    WAVEBANKREGION  Segments[WAVEBANK_SEGIDX_COUNT];    // Segment lookup table
 
 
 
#ifdef __cplusplus
 
 
 
    void SwapBytes(void)
 
    {
 
        XACTWaveBank::SwapBytes(dwSignature);
 
        XACTWaveBank::SwapBytes(dwVersion);
 
        XACTWaveBank::SwapBytes(dwHeaderVersion);
 
 
 
        for(int i = 0; i < WAVEBANK_SEGIDX_COUNT; i++)
 
        {
 
            Segments[i].SwapBytes();
 
        }
 
    }
 
 
 
#endif // __cplusplus
 
 
 
} WAVEBANKHEADER, *LPWAVEBANKHEADER;
 
 
 
typedef const WAVEBANKHEADER *LPCWAVEBANKHEADER;
 
 
 
//
 
// Table for converting WMA Average Bytes per Second values to the WAVEBANKMINIWAVEFORMAT wBlockAlign field
 
// NOTE: There can be a max of 8 values in the table.
 
//
 
 
 
#define MAX_WMA_AVG_BYTES_PER_SEC_ENTRIES 7
 
 
 
static const DWORD aWMAAvgBytesPerSec[] =
 
{
 
    12000,
 
    24000,
 
    4000,
 
    6000,
 
    8000,
 
    20000,
 
    2500
 
};
 
// bitrate = entry * 8
 
 
 
//
 
// Table for converting WMA Block Align values to the WAVEBANKMINIWAVEFORMAT wBlockAlign field
 
// NOTE: There can be a max of 32 values in the table.
 
//
 
 
 
#define MAX_WMA_BLOCK_ALIGN_ENTRIES 17
 
 
 
static const DWORD aWMABlockAlign[] =
 
{
 
    929,
 
    1487,
 
    1280,
 
    2230,
 
    8917,
 
    8192,
 
    4459,
 
    5945,
 
    2304,
 
    1536,
 
    1485,
 
    1008,
 
    2731,
 
    4096,
 
    6827,
 
    5462,
 
    1280
 
};
 
 
 
struct WAVEBANKENTRY;
 
 
 
//
 
// Entry compressed data format
 
//
 
 
 
typedef union WAVEBANKMINIWAVEFORMAT
 
{
 
    struct
 
    {
 
        DWORD       wFormatTag      : 2;        // Format tag
 
        DWORD       nChannels       : 3;        // Channel count (1 - 6)
 
        DWORD       nSamplesPerSec  : 18;       // Sampling rate
 
        DWORD       wBlockAlign     : 8;        // Block alignment.  For WMA, lower 6 bits block alignment index, upper 2 bits bytes-per-second index.
 
        DWORD       wBitsPerSample  : 1;        // Bits per sample (8 vs. 16, PCM only); WMAudio2/WMAudio3 (for WMA)
 
    };
 
 
 
    DWORD           dwValue;
 
 
 
#ifdef __cplusplus
 
 
 
    void SwapBytes(void)
 
    {
 
        XACTWaveBank::SwapBytes(dwValue);
 
    }
 
 
 
    WORD BitsPerSample() const
 
    {
 
        if (wFormatTag == WAVEBANKMINIFORMAT_TAG_XMA)
 
            return XMA_OUTPUT_SAMPLE_BITS; // First, because most common on Xbox 360
 
        if (wFormatTag == WAVEBANKMINIFORMAT_TAG_WMA)
 
            return 16;
 
        if (wFormatTag == WAVEBANKMINIFORMAT_TAG_ADPCM)
 
            return 4; // MSADPCM_BITS_PER_SAMPLE == 4
 
 
 
        // wFormatTag must be WAVEBANKMINIFORMAT_TAG_PCM (2 bits can only represent 4 different values)
 
        return (wBitsPerSample == WAVEBANKMINIFORMAT_BITDEPTH_16) ? 16 : 8;
 
    }
 
 
 
    #define ADPCM_MINIWAVEFORMAT_BLOCKALIGN_CONVERSION_OFFSET 22
 
    DWORD BlockAlign() const
 
    {
 
        DWORD dwReturn = 0;
 
 
 
        switch (wFormatTag)
 
        {
 
        case WAVEBANKMINIFORMAT_TAG_PCM:
 
            dwReturn = wBlockAlign;
 
            break;
 
 
 
        case WAVEBANKMINIFORMAT_TAG_XMA:
 
            dwReturn = nChannels * XMA_OUTPUT_SAMPLE_BITS / 8;
 
            break;
 
 
 
        case WAVEBANKMINIFORMAT_TAG_ADPCM:
 
            dwReturn = (wBlockAlign + ADPCM_MINIWAVEFORMAT_BLOCKALIGN_CONVERSION_OFFSET) * nChannels;
 
            break;
 
 
 
        case WAVEBANKMINIFORMAT_TAG_WMA:
 
            {
 
                DWORD dwBlockAlignIndex = wBlockAlign & 0x1F;
 
                if (dwBlockAlignIndex < MAX_WMA_BLOCK_ALIGN_ENTRIES)
 
                        dwReturn = aWMABlockAlign[dwBlockAlignIndex];
 
            }
 
            break;
 
        }
 
 
 
        return dwReturn;
 
    }
 
 
 
    DWORD AvgBytesPerSec() const
 
    {
 
        DWORD dwReturn = 0;
 
 
 
        switch (wFormatTag)
 
        {
 
        case WAVEBANKMINIFORMAT_TAG_PCM:
 
        case WAVEBANKMINIFORMAT_TAG_XMA:
 
            dwReturn = nSamplesPerSec * wBlockAlign;
 
            break;
 
 
 
        case WAVEBANKMINIFORMAT_TAG_ADPCM:
 
            {
 
                DWORD blockAlign = BlockAlign();
 
                DWORD samplesPerAdpcmBlock = AdpcmSamplesPerBlock();
 
                dwReturn = blockAlign * nSamplesPerSec / samplesPerAdpcmBlock;
 
            }
 
            break;
 
 
 
        case WAVEBANKMINIFORMAT_TAG_WMA:
 
            {
 
                DWORD dwBytesPerSecIndex = wBlockAlign >> 5;
 
                if (dwBytesPerSecIndex < MAX_WMA_AVG_BYTES_PER_SEC_ENTRIES)
 
                    dwReturn = aWMAAvgBytesPerSec[dwBytesPerSecIndex];
 
            }
 
            break;
 
        }
 
 
 
        return dwReturn;
 
    }
 
 
 
    DWORD EncodeWMABlockAlign(DWORD dwBlockAlign, DWORD dwAvgBytesPerSec) const
 
    {
 
        DWORD dwReturn = 0;
 
        DWORD dwBlockAlignIndex = 0;
 
        DWORD dwBytesPerSecIndex = 0;
 
 
 
        for (; dwBlockAlignIndex < MAX_WMA_BLOCK_ALIGN_ENTRIES && dwBlockAlign != aWMABlockAlign[dwBlockAlignIndex]; dwBlockAlignIndex++);
 
 
 
        if (dwBlockAlignIndex < MAX_WMA_BLOCK_ALIGN_ENTRIES)
 
        {
 
            for (; dwBytesPerSecIndex < MAX_WMA_AVG_BYTES_PER_SEC_ENTRIES && dwAvgBytesPerSec != aWMAAvgBytesPerSec[dwBytesPerSecIndex]; dwBytesPerSecIndex++);
 
 
 
            if (dwBytesPerSecIndex < MAX_WMA_AVG_BYTES_PER_SEC_ENTRIES)
 
            {
 
                dwReturn = dwBlockAlignIndex | (dwBytesPerSecIndex << 5);
 
            }
 
        }
 
 
 
        return dwReturn;
 
    }
 
 
 
 
 
    void XMA2FillFormatEx(XMA2WAVEFORMATEX *fmt, WORD blockCount, const struct WAVEBANKENTRY* entry) const;
 
 
 
    DWORD AdpcmSamplesPerBlock() const
 
    {
 
        DWORD nBlockAlign = (wBlockAlign + ADPCM_MINIWAVEFORMAT_BLOCKALIGN_CONVERSION_OFFSET) * nChannels;
 
        return nBlockAlign * 2 / (DWORD)nChannels - 12;
 
    }
 
 
 
    void AdpcmFillCoefficientTable(ADPCMWAVEFORMAT *fmt) const
 
    {
 
        // These are fixed since we are always using MS ADPCM
 
        fmt->wNumCoef = 7; /* MSADPCM_NUM_COEFFICIENTS */
 
 
 
        static ADPCMCOEFSET aCoef[7] = { { 256, 0}, {512, -256}, {0,0}, {192,64}, {240,0}, {460, -208}, {392,-232} };
 
        memcpy( &fmt->aCoef, aCoef, sizeof(aCoef) );
 
    }
 
 
 
#endif // __cplusplus
 
 
 
} WAVEBANKMINIWAVEFORMAT, *LPWAVEBANKMINIWAVEFORMAT;
 
 
 
typedef const WAVEBANKMINIWAVEFORMAT *LPCWAVEBANKMINIWAVEFORMAT;
 
 
 
//
 
// Entry meta-data
 
//
 
 
 
typedef struct WAVEBANKENTRY
 
{
 
    union
 
    {
 
        struct
 
        {
 
            // Entry flags
 
            DWORD                   dwFlags  :  4;
 
 
 
            // Duration of the wave, in units of one sample.
 
            // For instance, a ten second long wave sampled
 
            // at 48KHz would have a duration of 480,000.
 
            // This value is not affected by the number of
 
            // channels, the number of bits per sample, or the
 
            // compression format of the wave.
 
            DWORD                   Duration : 28;
 
        };
 
        DWORD dwFlagsAndDuration;
 
    };
 
 
 
    WAVEBANKMINIWAVEFORMAT  Format;         // Entry format.
 
    WAVEBANKREGION          PlayRegion;     // Region within the wave data segment that contains this entry.
 
    WAVEBANKSAMPLEREGION    LoopRegion;     // Region within the wave data (in samples) that should loop.
 
 
 
#ifdef __cplusplus
 
 
 
    void SwapBytes(void)
 
    {
 
        XACTWaveBank::SwapBytes(dwFlagsAndDuration);
 
        Format.SwapBytes();
 
        PlayRegion.SwapBytes();
 
        LoopRegion.SwapBytes();
 
    }
 
 
 
#endif // __cplusplus
 
 
 
} WAVEBANKENTRY, *LPWAVEBANKENTRY;
 
 
 
typedef const WAVEBANKENTRY *LPCWAVEBANKENTRY;
 
 
 
//
 
// Compact entry meta-data
 
//
 
 
 
typedef struct WAVEBANKENTRYCOMPACT
 
{
 
    DWORD       dwOffset            : 21;       // Data offset, in sectors
 
    DWORD       dwLengthDeviation   : 11;       // Data length deviation, in bytes
 
 
 
#ifdef __cplusplus
 
 
 
    void SwapBytes(void)
 
    {
 
        XACTWaveBank::SwapBytes(*(LPDWORD)this);
 
    }
 
 
 
#endif // __cplusplus
 
 
 
} WAVEBANKENTRYCOMPACT, *LPWAVEBANKENTRYCOMPACT;
 
 
 
typedef const WAVEBANKENTRYCOMPACT *LPCWAVEBANKENTRYCOMPACT;
 
 
 
//
 
// Bank data segment
 
//
 
 
 
typedef struct WAVEBANKDATA
 
{
 
    DWORD                   dwFlags;                                // Bank flags
 
    DWORD                   dwEntryCount;                           // Number of entries in the bank
 
    CHAR                    szBankName[WAVEBANK_BANKNAME_LENGTH];   // Bank friendly name
 
    DWORD                   dwEntryMetaDataElementSize;             // Size of each entry meta-data element, in bytes
 
    DWORD                   dwEntryNameElementSize;                 // Size of each entry name element, in bytes
 
    DWORD                   dwAlignment;                            // Entry alignment, in bytes
 
    WAVEBANKMINIWAVEFORMAT  CompactFormat;                          // Format data for compact bank
 
    FILETIME                BuildTime;                              // Build timestamp
 
 
 
#ifdef __cplusplus
 
 
 
    void SwapBytes(void)
 
    {
 
        XACTWaveBank::SwapBytes(dwFlags);
 
        XACTWaveBank::SwapBytes(dwEntryCount);
 
        XACTWaveBank::SwapBytes(dwEntryMetaDataElementSize);
 
        XACTWaveBank::SwapBytes(dwEntryNameElementSize);
 
        XACTWaveBank::SwapBytes(dwAlignment);
 
        CompactFormat.SwapBytes();
 
        XACTWaveBank::SwapBytes(BuildTime.dwLowDateTime);
 
        XACTWaveBank::SwapBytes(BuildTime.dwHighDateTime);
 
    }
 
 
 
#endif // __cplusplus
 
 
 
} WAVEBANKDATA, *LPWAVEBANKDATA;
 
 
 
typedef const WAVEBANKDATA *LPCWAVEBANKDATA;
 
 
 
inline void WAVEBANKMINIWAVEFORMAT::XMA2FillFormatEx(XMA2WAVEFORMATEX *fmt, WORD blockCount, const WAVEBANKENTRY* entry) const
 
{
 
    // Note caller is responsbile for filling out fmt->wfx with other helper functions.
 
 
 
    fmt->NumStreams = (WORD)( (nChannels + 1) / 2 );
 
 
 
    switch (nChannels)
 
    {
 
        case 1: fmt->ChannelMask =  SPEAKER_MONO; break;
 
        case 2: fmt->ChannelMask =  SPEAKER_STEREO; break;
 
        case 3: fmt->ChannelMask =  SPEAKER_2POINT1; break;
 
        case 4: fmt->ChannelMask =  SPEAKER_QUAD; break;
 
        case 5: fmt->ChannelMask =  SPEAKER_4POINT1; break;
 
        case 6: fmt->ChannelMask =  SPEAKER_5POINT1; break;
 
        case 7: fmt->ChannelMask =  SPEAKER_5POINT1 | SPEAKER_BACK_CENTER; break;
 
        case 8: fmt->ChannelMask =  SPEAKER_7POINT1; break;
 
        default: fmt->ChannelMask = 0; break;
 
    }
 
 
 
    fmt->SamplesEncoded = entry->Duration;
 
    fmt->BytesPerBlock = 65536; /* XACT_FIXED_XMA_BLOCK_SIZE */
 
 
 
    fmt->PlayBegin = entry->PlayRegion.dwOffset;
 
    fmt->PlayLength = entry->PlayRegion.dwLength;
 
 
 
    if (entry->LoopRegion.dwTotalSamples > 0)
 
    {
 
        fmt->LoopBegin = entry->LoopRegion.dwStartSample;
 
        fmt->LoopLength = entry->LoopRegion.dwTotalSamples;
 
        fmt->LoopCount = 0xff; /* XACTLOOPCOUNT_INFINITE */
 
    }
 
    else
 
    {
 
        fmt->LoopBegin = 0;
 
        fmt->LoopLength = 0;
 
        fmt->LoopCount = 0;
 
    }
 
 
 
    fmt->EncoderVersion = 4; // XMAENCODER_VERSION_XMA2
 
 
 
    fmt->BlockCount = blockCount;
 
}
 
 
 
#ifdef _M_PPCBE
 
#pragma bitfield_order(pop)
 
#endif
 
 
 
#pragma warning(pop)
 
#pragma pack(pop)
 
 
 
#endif // __XACTWB_H__