|
|
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: .360.WAV Creation
//
//=====================================================================================//
#include "MakeGameData.h"
#ifndef NO_X360_XDK
#include <XMAEncoder.h>
#endif
#include "datamap.h"
#include "sentence.h"
#include "tier2/riff.h"
#include "resample.h"
#include "xwvfile.h"
// all files are built for streaming compliance
// allows for fastest runtime loading path
// actual streaming or static state is determined by engine
#define XBOX_DVD_SECTORSIZE 2048
#define XMA_BLOCK_SIZE 2048 // must be aligned to 1024
#define MAX_CHUNKS 256
// [0,100]
#define XMA_HIGH_QUALITY 90
#define XMA_DEFAULT_QUALITY 75
#define XMA_MEDIUM_QUALITY 50
#define XMA_LOW_QUALITY 25
typedef struct { unsigned int id; int size; byte *pData; } chunk_t;
struct conversion_t { const char *pSubDir; int quality; bool bForceTo22K; };
// default conversion rules
conversion_t g_defaultConversionRules[] = { // subdir quality 22Khz
{ "", XMA_DEFAULT_QUALITY, false }, // default settings
{ "weapons", XMA_DEFAULT_QUALITY, false }, { "music", XMA_DEFAULT_QUALITY, false }, { "vo", XMA_MEDIUM_QUALITY, false }, { "npc", XMA_MEDIUM_QUALITY, false }, { "ambient", XMA_DEFAULT_QUALITY, false }, { "commentary", XMA_LOW_QUALITY, true }, { NULL }, };
// portal conversion rules
conversion_t g_portalConversionRules[] = { // subdir quality 22Khz
{ "", XMA_DEFAULT_QUALITY, false }, // default settings
{ "commentary", XMA_LOW_QUALITY, true }, { NULL }, };
chunk_t g_chunks[MAX_CHUNKS]; int g_numChunks;
extern IFileReadBinary *g_pSndIO;
//-----------------------------------------------------------------------------
// Purpose: chunk printer
//-----------------------------------------------------------------------------
void PrintChunk( unsigned int chunkName, int size ) { char c[4];
for ( int i=0; i<4; i++ ) { c[i] = ( chunkName >> i*8 ) & 0xFF; if ( !c[i] ) c[i] = ' '; }
Msg( "%c%c%c%c: %d bytes\n", c[0], c[1], c[2], c[3], size ); }
//-----------------------------------------------------------------------------
// Purpose: which chunks are supported, false to ignore
//-----------------------------------------------------------------------------
bool IsValidChunk( unsigned int chunkName ) { switch ( chunkName ) { case WAVE_DATA: case WAVE_CUE: case WAVE_SAMPLER: case WAVE_VALVEDATA: case WAVE_FMT: return true; }
return false; }
//-----------------------------------------------------------------------------
// Purpose: align buffer
//-----------------------------------------------------------------------------
int AlignToBoundary( CUtlBuffer &buf, int alignment ) { int curPosition; int newPosition; byte padByte = 0;
buf.SeekPut( CUtlBuffer::SEEK_TAIL, 0 ); curPosition = buf.TellPut();
if ( alignment <= 1 ) return curPosition;
// advance to aligned position
newPosition = AlignValue( curPosition, alignment ); buf.EnsureCapacity( newPosition );
// write empty
for ( int i=0; i<newPosition-curPosition; i++ ) { buf.Put( &padByte, 1 ); }
return newPosition; }
//--------------------------------------------------------------------------------------
// SampleToXMABlockOffset
//
// Description: converts from a sample index to a block index + the number of samples
// to offset from the beginning of the block.
//
// Parameters:
// dwSampleIndex: sample index to convert
// pdwSeekTable: pointer to the file's XMA2 seek table
// nEntries: number of DWORD entries in the seek table
// out_pBlockIndex: index of block where the desired sample lives
// out_pOffset: number of samples in the block before the desired sample
//--------------------------------------------------------------------------------------
bool SampleToXMABlockOffset( DWORD dwSampleIndex, const DWORD *pdwSeekTable, DWORD nEntries, DWORD *out_pBlockIndex, DWORD *out_pOffset ) { // Run through the seek table to find the block closest to the desired sample.
// Each seek table entry is the index (counting from the beginning of the file)
// of the first sample in the corresponding block, but there's no entry for the
// first block (since the index would always be zero).
bool bFound = false; for ( DWORD i = 0; !bFound && i < nEntries; ++i ) { if ( dwSampleIndex < BigLong( pdwSeekTable[i] ) ) { *out_pBlockIndex = i; bFound = true; } }
// Calculate the sample offset by figuring out what the sample index of the first sample
// in the block is, then subtracting that from dwSampleIndex.
if ( bFound ) { DWORD dwStartOfBlock = (*out_pBlockIndex == 0) ? 0 : BigLong( pdwSeekTable[*out_pBlockIndex - 1] ); *out_pOffset = dwSampleIndex - dwStartOfBlock; }
return bFound; }
//-----------------------------------------------------------------------------
// Compile and compress vdat
//-----------------------------------------------------------------------------
bool CompressVDAT( chunk_t *pChunk ) { CSentence *pSentence;
CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER );
buf.EnsureCapacity( pChunk->size ); memcpy( buf.Base(), pChunk->pData, pChunk->size ); buf.SeekPut( CUtlBuffer::SEEK_HEAD, pChunk->size );
pSentence = new CSentence();
// Make binary version of VDAT
// Throws all phonemes into one word, discards sentence memory, etc.
pSentence->InitFromDataChunk( buf.Base(), buf.TellPut() ); pSentence->MakeRuntimeOnly(); CUtlBuffer binaryBuffer( 0, 0, 0 ); binaryBuffer.SetBigEndian( true ); pSentence->CacheSaveToBuffer( binaryBuffer, CACHED_SENTENCE_VERSION_ALIGNED ); delete pSentence;
unsigned int compressedSize = 0; unsigned char *pCompressedOutput = LZMA_OpportunisticCompress( (unsigned char *)binaryBuffer.Base(), binaryBuffer.TellPut(), &compressedSize ); if ( pCompressedOutput ) { if ( !g_bQuiet ) { Msg( "CompressVDAT: Compressed %d to %d\n", binaryBuffer.TellPut(), compressedSize ); }
free( pChunk->pData ); pChunk->size = compressedSize; pChunk->pData = pCompressedOutput; } else { // save binary VDAT as-is
free( pChunk->pData ); pChunk->size = binaryBuffer.TellPut(); pChunk->pData = (byte *)malloc( pChunk->size ); memcpy( pChunk->pData, binaryBuffer.Base(), pChunk->size ); }
// success
return true; }
//-----------------------------------------------------------------------------
// Purpose: read chunks into provided array
//-----------------------------------------------------------------------------
bool ReadChunks( const char *pFileName, int &numChunks, chunk_t chunks[MAX_CHUNKS] ) { numChunks = 0;
InFileRIFF riff( pFileName, *g_pSndIO ); if ( riff.RIFFName() != RIFF_WAVE ) { return false; }
IterateRIFF walk( riff, riff.RIFFSize() );
while ( walk.ChunkAvailable() ) { chunks[numChunks].id = walk.ChunkName(); chunks[numChunks].size = walk.ChunkSize();
int size = chunks[numChunks].size; if ( walk.ChunkName() == WAVE_FMT && size < sizeof( WAVEFORMATEXTENSIBLE ) ) { // format chunks are variable and cast to different structures
// ensure the data footprint is at least the structure we want to manipulate
size = sizeof( WAVEFORMATEXTENSIBLE ); }
chunks[numChunks].pData = (byte *)malloc( size ); memset( chunks[numChunks].pData, 0, size );
walk.ChunkRead( chunks[numChunks].pData );
numChunks++; if ( numChunks >= MAX_CHUNKS ) return false;
walk.ChunkNext(); }
// success
return true; }
//-----------------------------------------------------------------------------
// Purpose: promote pcm 8 bit to 16 bit pcm
//-----------------------------------------------------------------------------
void ConvertPCMDataChunk8To16( chunk_t *pFormatChunk, chunk_t *pDataChunk ) { WAVEFORMATEX *pFormat = (WAVEFORMATEX*)pFormatChunk->pData;
int sampleSize = ( pFormat->nChannels * pFormat->wBitsPerSample ) >> 3; int sampleCount = pDataChunk->size / sampleSize; int outputSize = sizeof( short ) * ( sampleCount * pFormat->nChannels ); short *pOut = (short *)malloc( outputSize );
// in-place convert data from 8-bits to 16-bits
Convert8To16( pDataChunk->pData, pOut, sampleCount, pFormat->nChannels );
free( pDataChunk->pData ); pDataChunk->pData = (byte *)pOut; pDataChunk->size = outputSize;
pFormat->wFormatTag = WAVE_FORMAT_PCM; pFormat->nBlockAlign = 2 * pFormat->nChannels; pFormat->wBitsPerSample = 16; pFormat->nAvgBytesPerSec = 2 * pFormat->nSamplesPerSec * pFormat->nChannels; }
//-----------------------------------------------------------------------------
// Purpose: convert adpcm to 16 bit pcm
//-----------------------------------------------------------------------------
void ConvertADPCMDataChunkTo16( chunk_t *pFormatChunk, chunk_t *pDataChunk ) { WAVEFORMATEX *pFormat = (WAVEFORMATEX*)pFormatChunk->pData;
int sampleCount = ADPCMSampleCount( (byte *)pFormat, pDataChunk->pData, pDataChunk->size ); int outputSize = sizeof( short ) * sampleCount * pFormat->nChannels; short *pOut = (short *)malloc( outputSize );
// convert to PCM 16bit format
DecompressADPCMSamples( (byte*)pFormat, (byte*)pDataChunk->pData, pDataChunk->size, pOut );
free( pDataChunk->pData ); pDataChunk->pData = (byte *)pOut; pDataChunk->size = outputSize;
pFormat->wFormatTag = WAVE_FORMAT_PCM; pFormat->nBlockAlign = 2 * pFormat->nChannels; pFormat->wBitsPerSample = 16; pFormat->nAvgBytesPerSec = 2 * pFormat->nSamplesPerSec * pFormat->nChannels;
pFormatChunk->size = 16; }
//-----------------------------------------------------------------------------
// Purpose: Decimate to 22K
//-----------------------------------------------------------------------------
void ConvertPCMDataChunk16To22K( chunk_t *pFormatChunk, chunk_t *pDataChunk ) { WAVEFORMATEX *pFormat = (WAVEFORMATEX*)pFormatChunk->pData;
if ( pFormat->nSamplesPerSec != 44100 || pFormat->wBitsPerSample != 16 || pFormat->wFormatTag != WAVE_FORMAT_PCM ) { // not in expected format
return; }
int sampleSize = ( pFormat->nChannels * pFormat->wBitsPerSample ) >> 3; int sampleCount = pDataChunk->size / sampleSize; short *pOut = (short *)malloc( sizeof( short ) * ( sampleCount * pFormat->nChannels ) );
DecimateSampleRateBy2_16( (short *)pDataChunk->pData, pOut, sampleCount, pFormat->nChannels );
free( pDataChunk->pData ); pDataChunk->pData = (byte *)pOut; pDataChunk->size = sizeof( short ) * ( sampleCount/2 * pFormat->nChannels );
pFormat->nSamplesPerSec = 22050; pFormat->nBlockAlign = 2 * pFormat->nChannels; pFormat->nAvgBytesPerSec = 2 * pFormat->nSamplesPerSec * pFormat->nChannels; }
//-----------------------------------------------------------------------------
// Purpose: determine loop start
//-----------------------------------------------------------------------------
int FindLoopStart( int samplerChunk, int cueChunk ) { int loopStartFromCue = -1; int loopStartFromSampler = -1;
if ( cueChunk != -1 ) { struct cuechunk_t { unsigned int dwName; unsigned int dwPosition; unsigned int fccChunk; unsigned int dwChunkStart; unsigned int dwBlockStart; unsigned int dwSampleOffset; }; struct cueRIFF_t { int cueCount; cuechunk_t cues[1]; };
cueRIFF_t *pCue = (cueRIFF_t *)g_chunks[cueChunk].pData; if ( pCue->cueCount > 0 ) { loopStartFromCue = pCue->cues[0].dwSampleOffset; } } if ( samplerChunk != -1 ) { struct SampleLoop { unsigned int dwIdentifier; unsigned int dwType; unsigned int dwStart; unsigned int dwEnd; unsigned int dwFraction; unsigned int dwPlayCount; };
struct samplerchunk_t { unsigned int dwManufacturer; unsigned int dwProduct; unsigned int dwSamplePeriod; unsigned int dwMIDIUnityNote; unsigned int dwMIDIPitchFraction; unsigned int dwSMPTEFormat; unsigned int dwSMPTEOffset; unsigned int cSampleLoops; unsigned int cbSamplerData; struct SampleLoop Loops[1]; };
// assume that the loop end is the sample end
// assume that only the first loop is relevant
samplerchunk_t *pSampler = (samplerchunk_t *)g_chunks[samplerChunk].pData; if ( pSampler->cSampleLoops > 0 ) { // only support normal forward loops
if ( pSampler->Loops[0].dwType == 0 ) { loopStartFromSampler = pSampler->Loops[0].dwStart; } } }
return ( max( loopStartFromCue, loopStartFromSampler ) ); }
//-----------------------------------------------------------------------------
// Purpose: returns chunk, -1 if not found
//-----------------------------------------------------------------------------
int FindChunk( unsigned int id ) { int i; for ( i=0; i<g_numChunks; i++ ) { if ( g_chunks[i].id == id ) { return i; } }
// not found
return - 1; }
bool EncodeAsXMA( const char *pDebugName, CUtlBuffer &targetBuff, int quality, bool bIsVoiceOver ) { #ifdef NO_X360_XDK
return false; #else
int formatChunk = FindChunk( WAVE_FMT ); int dataChunk = FindChunk( WAVE_DATA ); if ( formatChunk == -1 || dataChunk == -1 ) { // huh? these should have been pre-validated
return false; }
int vdatSize = 0; int vdatChunk = FindChunk( WAVE_VALVEDATA ); if ( vdatChunk != -1 ) { vdatSize = g_chunks[vdatChunk].size; }
int loopStart = FindLoopStart( FindChunk( WAVE_SAMPLER ), FindChunk( WAVE_CUE ) );
// format structure must be expected 16 bit PCM, otherwise encoder crashes
WAVEFORMATEX *pFormat = (WAVEFORMATEX *)g_chunks[formatChunk].pData; pFormat->nAvgBytesPerSec = pFormat->nSamplesPerSec * pFormat->nChannels * 2; pFormat->nBlockAlign = 2 * pFormat->nChannels; pFormat->cbSize = 0;
XMAENCODERSTREAM inputStream = { 0 };
WAVEFORMATEXTENSIBLE wfx; Assert( g_chunks[formatChunk].size <= sizeof( WAVEFORMATEXTENSIBLE ) ); memcpy( &wfx, g_chunks[formatChunk].pData, g_chunks[formatChunk].size ); if ( g_chunks[formatChunk].size < sizeof( WAVEFORMATEXTENSIBLE ) ) { memset( (unsigned char*)&wfx + g_chunks[formatChunk].size, 0, sizeof( WAVEFORMATEXTENSIBLE ) - g_chunks[formatChunk].size ); }
memcpy( &inputStream.Format, &wfx, sizeof( WAVEFORMATEX ) ); inputStream.pBuffer = g_chunks[dataChunk].pData; inputStream.BufferSize = g_chunks[dataChunk].size; if ( loopStart != -1 ) { // can only support a single loop point until end of file
inputStream.LoopStart = loopStart; inputStream.LoopLength = inputStream.BufferSize / ( pFormat->nChannels * sizeof( short ) ) - loopStart; }
void *pXMAData = NULL; DWORD XMADataSize = 0; XMA2WAVEFORMAT *pXMA2Format = NULL; DWORD XMA2FormatSize = 0; DWORD *pXMASeekTable = NULL; DWORD XMASeekTableSize = 0; HRESULT hr = S_OK;
DWORD xmaFlags = XMAENCODER_NOFILTER; if ( loopStart != -1 ) { xmaFlags |= XMAENCODER_LOOP; }
int numAttempts = 1; while ( numAttempts < 10 ) { hr = XMA2InMemoryEncoder( 1, &inputStream, quality, xmaFlags, XMA_BLOCK_SIZE/1024, &pXMAData, &XMADataSize, &pXMA2Format, &XMA2FormatSize, &pXMASeekTable, &XMASeekTableSize ); if ( !FAILED( hr ) ) break;
// make small jumps
quality += 5; if ( quality > 100 ) quality = 100; if ( !g_bQuiet ) { Msg( "XMA Encoding Error on '%s', Attempting increasing quality to %d\n", pDebugName, quality ); }
numAttempts++;
pXMAData = NULL; XMADataSize = 0; pXMA2Format = NULL; XMA2FormatSize = 0; pXMASeekTable = NULL; XMASeekTableSize = 0; }
if ( FAILED( hr ) ) { // unrecoverable
return false; } else if ( numAttempts > 1 ) { if ( !g_bQuiet ) { Msg( "XMA Encoding Success on '%s' at quality %d\n", pDebugName, quality ); } }
DWORD loopBlock = 0; DWORD numLeadingSamples = 0; DWORD numTrailingSamples = 0;
if ( loopStart != -1 ) { // calculate start block/offset
DWORD loopBlockStartIndex = 0; DWORD loopBlockStartOffset = 0;
if ( !SampleToXMABlockOffset( BigLong( pXMA2Format->LoopBegin ), pXMASeekTable, XMASeekTableSize/sizeof( DWORD ), &loopBlockStartIndex, &loopBlockStartOffset ) ) { // could not determine loop point, out of range of encoded samples
Msg( "XMA Loop Encoding Error on '%s', loop %d\n", pDebugName, loopStart ); return false; }
loopBlock = loopBlockStartIndex; numLeadingSamples = loopBlockStartOffset;
if ( BigLong( pXMA2Format->LoopEnd ) < BigLong( pXMA2Format->SamplesEncoded ) ) { // calculate end block/offset
DWORD loopBlockEndIndex = 0; DWORD loopBlockEndOffset = 0;
if ( !SampleToXMABlockOffset( BigLong( pXMA2Format->LoopEnd ), pXMASeekTable, XMASeekTableSize/sizeof( DWORD ), &loopBlockEndIndex, &loopBlockEndOffset ) ) { // could not determine loop point, out of range of encoded samples
Msg( "XMA Loop Encoding Error on '%s', loop %d\n", pDebugName, loopStart ); return false; }
if ( loopBlockEndIndex != BigLong( pXMA2Format->BlockCount ) - 1 ) { // end block MUST be last block
Msg( "XMA Loop Encoding Error on '%s', block end is %d/%d\n", pDebugName, loopBlockEndOffset, BigLong( pXMA2Format->BlockCount ) ); return false; }
numTrailingSamples = BigLong( pXMA2Format->SamplesEncoded ) - BigLong( pXMA2Format->LoopEnd ); }
// check for proper encoding range
if ( loopBlock > 32767 ) { Msg( "XMA Loop Encoding Error on '%s', loop block exceeds 16 bits %d\n", pDebugName, loopBlock ); return false; } if ( numLeadingSamples > 32767 ) { Msg( "XMA Loop Encoding Error on '%s', leading samples exceeds 16 bits %d\n", pDebugName, numLeadingSamples ); return false; } if ( numTrailingSamples > 32767 ) { Msg( "XMA Loop Encoding Error on '%s', trailing samples exceeds 16 bits %d\n", pDebugName, numTrailingSamples ); return false; } }
xwvHeader_t header; memset( &header, 0, sizeof( xwvHeader_t ) );
int seekTableSize = 0; if ( vdatSize || bIsVoiceOver ) { // save the optional seek table only for vdat or vo
// the seek table size is expected to be derived by this calculation
seekTableSize = ( XMADataSize / XMA_BYTES_PER_PACKET ) * sizeof( int ); if ( seekTableSize != XMASeekTableSize ) { Msg( "XMA Error: Unexpected seek table calculation in '%s'!", pDebugName ); return false; } }
if ( loopStart != -1 && ( vdatSize || bIsVoiceOver ) ) { Msg( "XMA Warning: Unexpected loop in vo data '%s'!", pDebugName );
// do not write the seek table for looping sounds
seekTableSize = 0; }
header.id = BigLong( XWV_ID ); header.version = BigLong( XWV_VERSION ); header.headerSize = BigLong( sizeof( xwvHeader_t ) ); header.staticDataSize = BigLong( seekTableSize + vdatSize ); header.dataOffset = BigLong( AlignValue( sizeof( xwvHeader_t) + seekTableSize + vdatSize, XBOX_DVD_SECTORSIZE ) ); header.dataSize = BigLong( XMADataSize );
// track the XMA number of samples that will get decoded
// which is NOT the same as what the source actually encoded
header.numDecodedSamples = pXMA2Format->SamplesEncoded;
if ( loopStart != -1 ) { // the loop start is in source space (now meaningless), need the loop in XMA decoding sample space
header.loopStart = pXMA2Format->LoopBegin; } else { header.loopStart = BigLong( -1 ); } header.loopBlock = BigShort( (unsigned short)loopBlock ); header.numLeadingSamples = BigShort( (unsigned short)numLeadingSamples ); header.numTrailingSamples = BigShort( (unsigned short)numTrailingSamples );
header.vdatSize = BigShort( (short)vdatSize ); header.format = XWV_FORMAT_XMA; header.bitsPerSample = 16; header.SetSampleRate( pFormat->nSamplesPerSec ); header.SetChannels( pFormat->nChannels ); header.quality = quality; header.bHasSeekTable = ( seekTableSize != 0 );
// output header
targetBuff.Put( &header, sizeof( xwvHeader_t ) );
// output optional seek table
if ( seekTableSize ) { // seek table is already in big-endian format
targetBuff.Put( pXMASeekTable, seekTableSize ); }
// output vdat
if ( vdatSize ) { targetBuff.Put( g_chunks[vdatChunk].pData, g_chunks[vdatChunk].size ); }
AlignToBoundary( targetBuff, XBOX_DVD_SECTORSIZE );
// write data
targetBuff.Put( pXMAData, XMADataSize );
// pad to EOF
AlignToBoundary( targetBuff, XBOX_DVD_SECTORSIZE );
free( pXMAData ); free( pXMA2Format ); free( pXMASeekTable );
// xma encoder leaves its temporary files, we'll delete
scriptlib->DeleteTemporaryFiles( "LoopStrm*" ); scriptlib->DeleteTemporaryFiles( "EncStrm*" );
return true; #endif
}
bool EncodeAsPCM( const char *pTargetName, CUtlBuffer &targetBuff ) { int formatChunk = FindChunk( WAVE_FMT ); int dataChunk = FindChunk( WAVE_DATA ); if ( formatChunk == -1 || dataChunk == -1 ) { // huh? these should have been pre-validated
return false; }
WAVEFORMATEX *pFormat = (WAVEFORMATEX *)g_chunks[formatChunk].pData; if ( pFormat->wBitsPerSample != 16 ) { // huh? the input is expeted to be 16 bit PCM
return false; }
int vdatSize = 0; int vdatChunk = FindChunk( WAVE_VALVEDATA ); if ( vdatChunk != -1 ) { vdatSize = g_chunks[vdatChunk].size; }
chunk_t *pDataChunk = &g_chunks[dataChunk];
xwvHeader_t header; memset( &header, 0, sizeof( xwvHeader_t ) );
int sampleSize = pFormat->nChannels * sizeof( short ); int sampleCount = pDataChunk->size / sampleSize;
header.id = BigLong( XWV_ID ); header.version = BigLong( XWV_VERSION ); header.headerSize = BigLong( sizeof( xwvHeader_t ) ); header.staticDataSize = BigLong( vdatSize ); header.dataOffset = BigLong( AlignValue( sizeof( xwvHeader_t) + vdatSize, XBOX_DVD_SECTORSIZE ) ); header.dataSize = BigLong( pDataChunk->size ); header.numDecodedSamples = BigLong( sampleCount ); header.loopStart = BigLong( -1 ); header.loopBlock = 0; header.numLeadingSamples = 0; header.numTrailingSamples = 0; header.vdatSize = BigShort( (short)vdatSize ); header.format = XWV_FORMAT_PCM; header.bitsPerSample = 16; header.SetSampleRate( pFormat->nSamplesPerSec ); header.SetChannels( pFormat->nChannels ); header.quality = 100;
// output header
targetBuff.Put( &header, sizeof( xwvHeader_t ) );
// output vdat
if ( vdatSize ) { targetBuff.Put( g_chunks[vdatChunk].pData, g_chunks[vdatChunk].size ); }
AlignToBoundary( targetBuff, XBOX_DVD_SECTORSIZE );
for ( int i = 0; i < sampleCount * pFormat->nChannels; i++ ) { ((short *)pDataChunk->pData)[i] = BigShort( ((short *)pDataChunk->pData)[i] ); }
// write data
targetBuff.Put( pDataChunk->pData, pDataChunk->size );
// pad to EOF
AlignToBoundary( targetBuff, XBOX_DVD_SECTORSIZE );
return true; }
//-----------------------------------------------------------------------------
// Purpose: read source, do work, and write to target
//-----------------------------------------------------------------------------
bool CreateTargetFile_WAV( const char *pSourceName, const char *pTargetName, bool bWriteToZip ) { g_numChunks = 0;
// resolve relative source to absolute path
char fullSourcePath[MAX_PATH]; if ( _fullpath( fullSourcePath, pSourceName, sizeof( fullSourcePath ) ) ) { pSourceName = fullSourcePath; }
if ( !ReadChunks( pSourceName, g_numChunks, g_chunks ) ) { Msg( "No RIFF Chunks on '%s'\n", pSourceName ); return false; }
int formatChunk = FindChunk( WAVE_FMT ); if ( formatChunk == -1 ) { Msg( "RIFF Format Chunk not found on '%s'\n", pSourceName ); return false; }
int dataChunk = FindChunk( WAVE_DATA ); if ( dataChunk == -1 ) { Msg( "RIFF Data Chunk not found on '%s'\n", pSourceName ); return false; }
// get the conversion rules
conversion_t *pConversion = g_defaultConversionRules; if ( V_stristr( g_szModPath, "\\portal" ) ) { pConversion = g_portalConversionRules; }
// conversion rules are based on matching subdir
for ( int i=1; ;i++ ) { char subString[MAX_PATH]; if ( !pConversion[i].pSubDir ) { // end of list
break; }
sprintf( subString, "\\%s\\", pConversion[i].pSubDir ); if ( V_stristr( pSourceName, subString ) ) { // use matched conversion rules
pConversion = &pConversion[i]; break; } }
bool bForceTo22K = pConversion->bForceTo22K; int quality = pConversion->quality;
// cannot trust the localization depots to have matched their sources
// cannot allow 44K
if ( IsLocalizedFile( pSourceName ) ) { bForceTo22K = true; }
// classify strict vo from /sound/vo only
bool bIsVoiceOver = V_stristr( pSourceName, "\\sound\\vo\\" ) != NULL;
// can override default settings
quality = CommandLine()->ParmValue( "-xmaquality", quality ); if ( quality < 0 ) quality = 0; else if ( quality > 100 ) quality = 100; if ( !g_bQuiet ) { Msg( "Encoding quality: %d on '%s'\n", quality, pSourceName ); }
int vdatSize = 0; int vdatChunk = FindChunk( WAVE_VALVEDATA ); if ( vdatChunk != -1 ) { // compile to optimal block
if ( !CompressVDAT( &g_chunks[vdatChunk] ) ) { Msg( "Compress VDAT Error on '%s'\n", pSourceName ); return false; } vdatSize = g_chunks[vdatChunk].size; }
// for safety (not trusting their decoding) and simplicity convert all data to 16 bit PCM before encoding
WAVEFORMATEX *pFormat = (WAVEFORMATEX *)g_chunks[formatChunk].pData; if ( ( pFormat->wFormatTag == WAVE_FORMAT_PCM ) ) { if ( pFormat->wBitsPerSample == 8 ) { ConvertPCMDataChunk8To16( &g_chunks[formatChunk], &g_chunks[dataChunk] ); } } else if ( pFormat->wFormatTag == WAVE_FORMAT_ADPCM ) { ConvertADPCMDataChunkTo16( &g_chunks[formatChunk], &g_chunks[dataChunk] ); } else { Msg( "Unknown RIFF Format on '%s'\n", pSourceName ); return false; }
// optionally decimate to 22K
if ( pFormat->nSamplesPerSec == 44100 && bForceTo22K ) { if ( !g_bQuiet ) { Msg( "Converting to 22K '%s'\n", pSourceName ); } ConvertPCMDataChunk16To22K( &g_chunks[formatChunk], &g_chunks[dataChunk] ); }
CUtlBuffer targetBuff; bool bSuccess;
bSuccess = EncodeAsXMA( pSourceName, targetBuff, quality, bIsVoiceOver ); if ( bSuccess ) { WriteBufferToFile( pTargetName, targetBuff, bWriteToZip, g_WriteModeForConversions ); }
// release data
for ( int i = 0; i < g_numChunks; i++ ) { free( g_chunks[i].pData ); }
return bSuccess; }
//-----------------------------------------------------------------------------
// Purpose: MP3's are already pre-converted into .360.wav
//-----------------------------------------------------------------------------
bool CreateTargetFile_MP3( const char *pSourceName, const char *pTargetName, bool bWriteToZip ) { CUtlBuffer targetBuffer;
// ignore the .mp3 source, the .360.wav target should have been pre-converted, checked in, and exist
// use the expected target as the source
if ( !scriptlib->ReadFileToBuffer( pTargetName, targetBuffer ) ) { // the .360.wav target does not exist
// try again using a .wav version and convert from that
char wavFilename[MAX_PATH]; V_StripExtension( pSourceName, wavFilename, sizeof( wavFilename ) ); V_SetExtension( wavFilename, ".wav", sizeof( wavFilename ) ); if ( scriptlib->DoesFileExist( wavFilename ) ) { if ( CreateTargetFile_WAV( wavFilename, pTargetName, bWriteToZip ) ) { return true; } }
return false; }
// no conversion to write, but possibly zipped
bool bSuccess = WriteBufferToFile( pTargetName, targetBuffer, bWriteToZip, WRITE_TO_DISK_NEVER ); return bSuccess; }
//-----------------------------------------------------------------------------
// Get the preload data for a wav file
//-----------------------------------------------------------------------------
bool GetPreloadData_WAV( const char *pFilename, CUtlBuffer &fileBufferIn, CUtlBuffer &preloadBufferOut ) { xwvHeader_t *pHeader = ( xwvHeader_t * )fileBufferIn.Base(); if ( pHeader->id != ( unsigned int )BigLong( XWV_ID ) || pHeader->version != ( unsigned int )BigLong( XWV_VERSION ) || pHeader->headerSize != BigLong( sizeof( xwvHeader_t ) ) ) { // bad version
Msg( "Can't preload: '%s', has bad version\n", pFilename ); return false; }
// ensure caller's buffer is clean
// caller determines preload size, via TellMaxPut()
preloadBufferOut.Purge(); unsigned int preloadSize = BigLong( pHeader->headerSize ) + BigLong( pHeader->staticDataSize ); preloadBufferOut.Put( fileBufferIn.Base(), preloadSize );
return true; }
|