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

 *

 *  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__



