469 lines
15 KiB
C++
469 lines
15 KiB
C++
#include "Precompiled.h"
|
|
|
|
|
|
|
|
|
|
#include "RakiEngine/Initializer/TestInitializer.h"
|
|
|
|
|
|
#include "RakiEngine/Data/MemoryBlock/MemoryBlock.h"
|
|
#include "RakiEngine/Data/Serialization/Serializer.h"
|
|
#include "RakiEngine/Data/Stream/SeekableStreamPart.h"
|
|
#include "RakiEngine/Data/WaveFile/RiffChunkManager.h"
|
|
#include "RakiEngine/Data/WaveFile/WaveFile.h"
|
|
#include "RakiEngine/Data/WaveFile/WaveDataPitchConverter.h"
|
|
#include "RakiEngine/Sounds/Format.h"
|
|
|
|
#include "RakiTests/Stream/BinFileReader.h"
|
|
#include "RakiTests/Stream/ReadFileStream.h"
|
|
#include "RakiTests/Stream/WriteFileStream.h"
|
|
|
|
|
|
#ifndef RAKI_PLATFORM_WIN32
|
|
#error // this is a bin test app - only for Win32
|
|
#endif // RAKI_PLATFORM_WIN32
|
|
|
|
|
|
|
|
typedef struct
|
|
{
|
|
// for header generation during decode
|
|
u32 num_samples; // total number of RAW samples
|
|
u32 num_adpcm_nibbles; // number of ADPCM nibbles (including frame headers)
|
|
u32 sample_rate; // Sample rate, in Hz
|
|
// DSP addressing and decode context
|
|
u16 loop_flag; // 1=LOOPED, 0=NOT LOOPED
|
|
u16 format; // Always 0x0000, for ADPCM
|
|
u32 sa; // Start offset address for looped samples (zero for non-looped)
|
|
u32 ea; // End offset address for looped samples
|
|
u32 ca; // always zero
|
|
u16 coef[16]; // decode coefficients (eight pairs of 16-bit words)
|
|
// DSP decoder initial state
|
|
u16 gain; // always zero for ADPCM
|
|
u16 ps; // predictor/scale
|
|
u16 yn1; // sample history
|
|
u16 yn2; // sample history
|
|
// DSP decoder loop context
|
|
u16 lps; // predictor/scale for loop context
|
|
u16 lyn1; // sample history (n-1) for loop context
|
|
u16 lyn2; // sample history (n-2) for loop context
|
|
u16 pad[11]; // reserved
|
|
} DSPADPCM;
|
|
|
|
|
|
namespace raki
|
|
{
|
|
|
|
|
|
|
|
|
|
bool DspadcpmCompress( const char * _inputPath, const char * _outputPath, u32 _sampleSize )
|
|
{
|
|
STARTUPINFOA si = { 0 };
|
|
si.cb = sizeof(si);
|
|
si.dwFlags = STARTF_USESHOWWINDOW;
|
|
si.cbReserved2 = 0;
|
|
si.lpReserved2 = NULL;
|
|
si.wShowWindow = SW_HIDE;//SW_SHOWNORMAL;
|
|
|
|
PROCESS_INFORMATION pi = { 0 };
|
|
|
|
char cmdLine[1024];
|
|
|
|
char exePath[] = "C:\\RVL_SDK\\X86\\bin\\dspadpcm.exe";
|
|
|
|
sprintf( cmdLine, "%s -encode %s %s -l0-%d", exePath, _inputPath, _outputPath, _sampleSize -1 );
|
|
|
|
// Start the child process.
|
|
if( !CreateProcessA( NULL, // No module name (use command line).
|
|
cmdLine, // Command line.
|
|
NULL, // Process handle not inheritable.
|
|
NULL, // Thread handle not inheritable.
|
|
FALSE, // Set handle inheritance to FALSE.
|
|
0, // No creation flags.
|
|
NULL, // Use parent's environment block.
|
|
NULL, // Use parent's starting directory.
|
|
&si, // Pointer to STARTUPINFO structure.
|
|
&pi ) // Pointer to PROCESS_INFORMATION structure.
|
|
)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Wait until child process exits.
|
|
WaitForSingleObject( pi.hProcess, INFINITE );
|
|
|
|
// Close process and thread handles.
|
|
CloseHandle( pi.hProcess );
|
|
CloseHandle( pi.hThread );
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
void WriteWaves( const WaveDataPitchConverter * _waveData, const WAVEFORMATEX * _originalFormat, const char * _originalPath, bool _interleaveStereo = true )
|
|
{
|
|
RiffChunkManager::singleton().setIsWritingToBigEndians( false );
|
|
|
|
switch( _originalFormat->nChannels )
|
|
{
|
|
case 1:
|
|
{
|
|
char inputPath[512];
|
|
sprintf( inputPath, "%s_%d.wav", _originalPath, _waveData->getSamplingRate() );
|
|
char outputPath[512];
|
|
sprintf( outputPath, "%s_%d.wii", _originalPath, _waveData->getSamplingRate() );
|
|
|
|
WaveFile output;
|
|
WAVEFORMATEX waveformatPcm = *_originalFormat;
|
|
waveformatPcm.nSamplesPerSec = _waveData->getSamplingRate();
|
|
waveformatPcm.nAvgBytesPerSec = 2 * waveformatPcm.nSamplesPerSec;
|
|
output.createEmptyRiffChunk();
|
|
output.addChunk( "fmt ", sizeof( WAVEFORMATEX ), &waveformatPcm );
|
|
output.addChunk( "data", waveformatPcm.nBlockAlign * _waveData->getSampleSize(), _waveData->getData() );
|
|
output.recalculateRiffAndListSizes();
|
|
|
|
WriteFileStream wave;
|
|
if ( wave.open( inputPath ) )
|
|
{
|
|
Serializer writeSerializer( &wave );
|
|
output.write( writeSerializer );
|
|
}
|
|
wave.close();
|
|
DspadcpmCompress( inputPath, outputPath, _waveData->getSampleSize() );
|
|
|
|
// now make bin
|
|
|
|
BinFileReader binFileReader;
|
|
binFileReader.readFrom( outputPath );
|
|
|
|
WAVEFORMATEX waveformatAdpcm = waveformatPcm;
|
|
waveformatAdpcm.wFormatTag = WAVE_FORMAT_ADPCM;
|
|
|
|
WaveFile outputBin;
|
|
outputBin.createEmptyRiffChunk();
|
|
outputBin.addChunk( "fmt ", sizeof( WAVEFORMATEX ), &waveformatAdpcm );
|
|
outputBin.addChunk( "dspL", sizeof( DSPADPCM ), binFileReader.getData() );
|
|
outputBin.addChunk( "datL", (u32)( binFileReader.getSize() - sizeof( DSPADPCM ) ), (void*)( sizeof( DSPADPCM ) + (u64)binFileReader.getData() ) );
|
|
outputBin.recalculateRiffAndListSizes();
|
|
outputBin.changeToBigEndian();
|
|
|
|
char binPath[512];
|
|
sprintf( binPath, "%s_%d.bin", _originalPath, _waveData->getSamplingRate() );
|
|
|
|
RiffChunkManager::singleton().setIsWritingToBigEndians( true );
|
|
|
|
WriteFileStream bin;
|
|
if ( bin.open( binPath ) )
|
|
{
|
|
Serializer writeSerializer( &bin );
|
|
outputBin.write( writeSerializer );
|
|
}
|
|
bin.close();
|
|
|
|
DeleteFileA( inputPath );
|
|
DeleteFileA( outputPath );
|
|
|
|
}
|
|
break;
|
|
|
|
case 2:
|
|
{
|
|
WAVEFORMATEX waveformatPcm = *_originalFormat;
|
|
waveformatPcm.wFormatTag = WAVE_FORMAT_PCM;
|
|
waveformatPcm.nChannels = 1;
|
|
waveformatPcm.nBlockAlign = 2;
|
|
waveformatPcm.nSamplesPerSec = _waveData->getSamplingRate();
|
|
waveformatPcm.nAvgBytesPerSec = 2 * waveformatPcm.nSamplesPerSec;
|
|
|
|
// left data
|
|
|
|
char inputPathL[512];
|
|
sprintf( inputPathL, "%s_L_%d.wav", _originalPath, _waveData->getSamplingRate() );
|
|
char outputPathL[512];
|
|
sprintf( outputPathL, "%s_L_%d.wii", _originalPath, _waveData->getSamplingRate() );
|
|
|
|
WaveFile outputL;
|
|
outputL.createEmptyRiffChunk();
|
|
outputL.addChunk( "fmt ", sizeof( WAVEFORMATEX ), &waveformatPcm );
|
|
outputL.addChunk( "data", waveformatPcm.nBlockAlign * _waveData->getSampleSize(), _waveData->getData() );
|
|
outputL.recalculateRiffAndListSizes();
|
|
|
|
WriteFileStream waveL;
|
|
if ( waveL.open( inputPathL ) )
|
|
{
|
|
Serializer writeSerializer( &waveL );
|
|
outputL.write( writeSerializer );
|
|
}
|
|
waveL.close();
|
|
DspadcpmCompress( inputPathL, outputPathL, _waveData->getSampleSize() );
|
|
|
|
// right data
|
|
|
|
char inputPathR[512];
|
|
sprintf( inputPathR, "%s_R_%d.wav", _originalPath, _waveData->getSamplingRate() );
|
|
char outputPathR[512];
|
|
sprintf( outputPathR, "%s_R_%d.wii", _originalPath, _waveData->getSamplingRate() );
|
|
|
|
WaveFile outputR;
|
|
outputR.createEmptyRiffChunk();
|
|
outputR.addChunk( "fmt ", sizeof( WAVEFORMATEX ), &waveformatPcm );
|
|
outputR.addChunk( "data", waveformatPcm.nBlockAlign * _waveData->getSampleSize(), _waveData->getRightData() );
|
|
outputR.recalculateRiffAndListSizes();
|
|
|
|
WriteFileStream waveR;
|
|
if ( waveR.open( inputPathR ) )
|
|
{
|
|
Serializer writeSerializer( &waveR );
|
|
outputR.write( writeSerializer );
|
|
}
|
|
waveR.close();
|
|
DspadcpmCompress( inputPathR, outputPathR, _waveData->getSampleSize() );
|
|
|
|
|
|
// now make bin
|
|
|
|
BinFileReader binFileReaderL;
|
|
binFileReaderL.readFrom( outputPathL );
|
|
BinFileReader binFileReaderR;
|
|
binFileReaderR.readFrom( outputPathR );
|
|
|
|
WAVEFORMATEX waveformatAdpcm = waveformatPcm;
|
|
waveformatAdpcm.wFormatTag = WAVE_FORMAT_ADPCM;
|
|
waveformatAdpcm.nChannels = 2;
|
|
|
|
WaveFile outputBin;
|
|
outputBin.createEmptyRiffChunk();
|
|
outputBin.addChunk( "fmt ", sizeof( WAVEFORMATEX ), &waveformatAdpcm );
|
|
outputBin.addChunk( "dspL", sizeof( DSPADPCM ), binFileReaderL.getData() );
|
|
outputBin.addChunk( "dspR", sizeof( DSPADPCM ), binFileReaderR.getData() );
|
|
|
|
if ( _interleaveStereo )
|
|
{
|
|
// interleave on aligned size...
|
|
|
|
u32 channelSize = (u32)binFileReaderL.getSize() - sizeof( DSPADPCM );
|
|
RAKI_ASSERT( channelSize == ( binFileReaderR.getSize() - sizeof( DSPADPCM ) ) );
|
|
|
|
if ( channelSize % 8 )
|
|
channelSize = ( channelSize + 8 ) & 0xfffffff8;
|
|
|
|
MemoryBlock blockL( channelSize );
|
|
Memory::memcpy( blockL.getBuffer(), (void*)( sizeof( DSPADPCM ) + (u64)binFileReaderL.getData() ), (u32)binFileReaderL.getSize() - sizeof( DSPADPCM ) );
|
|
|
|
MemoryBlock blockR( channelSize );
|
|
Memory::memcpy( blockR.getBuffer(), (void*)( sizeof( DSPADPCM ) + (u64)binFileReaderR.getData() ), (u32)binFileReaderR.getSize() - sizeof( DSPADPCM ) );
|
|
|
|
MemoryBlock block( 2 * channelSize );
|
|
|
|
u32 * srcL = (u32*) blockL.getBuffer();
|
|
u32 * srcR = (u32*) blockR.getBuffer();
|
|
u32 * dst = (u32*) block.getBuffer();
|
|
|
|
for ( u32 u = 0 ; u < ( channelSize / 8 ) ; ++u )
|
|
{
|
|
*dst++ = *srcL++;
|
|
*dst++ = *srcL++;
|
|
*dst++ = *srcR++;
|
|
*dst++ = *srcR++;
|
|
}
|
|
|
|
outputBin.addChunk( "datS", (u32)block.getSize(), block.getBuffer() );
|
|
}
|
|
else
|
|
{
|
|
outputBin.addChunk( "datL", (u32)( binFileReaderL.getSize() - sizeof( DSPADPCM ) ), (void*)( sizeof( DSPADPCM ) + (u64)binFileReaderL.getData() ) );
|
|
outputBin.addChunk( "datR", (u32)( binFileReaderR.getSize() - sizeof( DSPADPCM ) ), (void*)( sizeof( DSPADPCM ) + (u64)binFileReaderR.getData() ) );
|
|
}
|
|
|
|
outputBin.recalculateRiffAndListSizes();
|
|
outputBin.changeToBigEndian();
|
|
|
|
char binPath[512];
|
|
sprintf( binPath, "%s_%d.bin", _originalPath, _waveData->getSamplingRate() );
|
|
|
|
RiffChunkManager::singleton().setIsWritingToBigEndians( true );
|
|
|
|
WriteFileStream bin;
|
|
if ( bin.open( binPath ) )
|
|
{
|
|
Serializer writeSerializer( &bin );
|
|
outputBin.write( writeSerializer );
|
|
}
|
|
bin.close();
|
|
|
|
DeleteFileA( inputPathL );
|
|
DeleteFileA( outputPathL );
|
|
DeleteFileA( inputPathR );
|
|
DeleteFileA( outputPathR );
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
bool convertTo32AndByteSwap( const char * _inputPath, const char * _outputPath )
|
|
{
|
|
RiffChunkManager::singleton().setIsWritingToBigEndians( false );
|
|
RiffChunkManager::singleton().setShouldReadDataBlockForWaveData( true );
|
|
|
|
ReadFileStream inputStream;
|
|
|
|
if ( !inputStream.open( _inputPath ) )
|
|
return false;
|
|
|
|
const char fmtType[] = "fmt ";
|
|
WaveFile inputWaveFile;
|
|
Serializer readSerializer( &inputStream );
|
|
|
|
inputWaveFile.serialize( readSerializer );
|
|
|
|
if ( !inputWaveFile.getChunkData( fmtType ) )
|
|
return false;
|
|
|
|
WAVEFORMATEX * format = (WAVEFORMATEX*)inputWaveFile.getChunkData( fmtType );
|
|
|
|
if ( format->nSamplesPerSec != 48000 )
|
|
return false;
|
|
|
|
void * data = inputWaveFile.getChunkData( "data" );
|
|
|
|
u32 sampleSize = inputWaveFile.getChunkSize( "data" ) / (u32) format->nBlockAlign;
|
|
|
|
WAVEFORMATEX * _originalFormat = ( WAVEFORMATEX * ) inputWaveFile.getChunkData( fmtType );
|
|
|
|
WaveDataPitchConverter convert( data, sampleSize, 48000, 32000, _originalFormat->nChannels, false );
|
|
|
|
WAVEFORMATEX waveformat = *_originalFormat;
|
|
waveformat.nSamplesPerSec = 32000;
|
|
waveformat.nAvgBytesPerSec = waveformat.nBlockAlign * waveformat.nSamplesPerSec;
|
|
|
|
RiffChunkManager::singleton().setIsWritingToBigEndians( true );
|
|
|
|
WaveFile outputBin;
|
|
outputBin.createEmptyRiffChunk();
|
|
outputBin.addChunk( "fmt ", sizeof( WAVEFORMATEX ), &waveformat );
|
|
outputBin.addChunk( "data", convert.getSampleSize() * 4 , convert.getData() );
|
|
outputBin.recalculateRiffAndListSizes();
|
|
outputBin.changeToBigEndian();
|
|
|
|
WriteFileStream bin;
|
|
if ( bin.open( _outputPath ) )
|
|
{
|
|
Serializer writeSerializer( &bin );
|
|
outputBin.write( writeSerializer );
|
|
}
|
|
else
|
|
return false;
|
|
|
|
bin.close();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
bool TestBinWii( const char * _path )
|
|
{
|
|
TestInitializer initializer;
|
|
|
|
char swapWavePath[512];
|
|
sprintf( swapWavePath, "%s_swapped.wav", _path );
|
|
|
|
if ( !convertTo32AndByteSwap( _path, swapWavePath ) )
|
|
return false;
|
|
|
|
char path_32[512];
|
|
sprintf( path_32, "%s_32.wav", _path );
|
|
|
|
RiffChunkManager::singleton().setIsWritingToBigEndians( false );
|
|
RiffChunkManager::singleton().setShouldReadDataBlockForWaveData( true );
|
|
|
|
ReadFileStream inputStream;
|
|
if ( !inputStream.open( _path ) )
|
|
return false;
|
|
|
|
const char fmtType[] = "fmt ";
|
|
WaveFile inputWaveFile;
|
|
Serializer readSerializer( &inputStream );
|
|
|
|
inputWaveFile.serialize( readSerializer );
|
|
|
|
if ( !inputWaveFile.getChunkData( fmtType ) )
|
|
return false;
|
|
|
|
WAVEFORMATEX * format = (WAVEFORMATEX*)inputWaveFile.getChunkData( fmtType );
|
|
|
|
if ( format->nSamplesPerSec != 48000 )
|
|
return false;
|
|
|
|
void * data = inputWaveFile.getChunkData( "data" );
|
|
|
|
u32 sampleSize = inputWaveFile.getChunkSize( "data" ) / (u32) format->nBlockAlign;
|
|
|
|
WaveDataPitchConverter w32( data, sampleSize, 48000, 32000, format->nChannels );
|
|
WriteWaves( &w32, format, _path );
|
|
|
|
WaveDataPitchConverter w24( data, sampleSize, 48000, 24000, format->nChannels );
|
|
WriteWaves( &w24, format, _path );
|
|
|
|
WaveDataPitchConverter w16( data, sampleSize, 48000, 16000, format->nChannels );
|
|
WriteWaves( &w16, format, _path );
|
|
|
|
/*
|
|
BinFileReader msEncFile;
|
|
if ( !msEncFile.readFrom( msEncOutputPath ) )
|
|
return false;
|
|
|
|
if ( !msEncFile.getData() )
|
|
return false;
|
|
|
|
|
|
WaveFile outputWaveFile;
|
|
outputWaveFile.createEmptyRiffChunk();
|
|
|
|
// add fmt chunk
|
|
outputWaveFile.addChunk( fmtType, inputWaveFile.getChunkSize( fmtType ), inputWaveFile.getChunkData( fmtType ) );
|
|
|
|
// add MSEnc data
|
|
outputWaveFile.addChunk( "msf ", (u32)msEncFile.getSize(), msEncFile.getData() );
|
|
|
|
outputWaveFile.recalculateRiffAndListSizes();
|
|
|
|
WriteFileStream outputFileStream;
|
|
if ( !outputFileStream.open( finalBinPath ) )
|
|
return false;
|
|
|
|
outputWaveFile.changeToBigEndian();
|
|
|
|
RiffChunkManager::singleton().setIsWritingToBigEndians( true );
|
|
|
|
Serializer writeSerializer( &outputFileStream );
|
|
|
|
outputWaveFile.serialize( writeSerializer );
|
|
|
|
if ( writeSerializer.encounteredError() )
|
|
return false;
|
|
|
|
DeleteFileA( msEncOutputPath );
|
|
*/
|
|
return true;
|
|
}
|
|
|
|
} // namespace raki
|
|
|
|
int _tmain(int argc, _TCHAR* argv[] )
|
|
{
|
|
if ( argc < 2 )
|
|
return -1;
|
|
|
|
char path[512];
|
|
|
|
wcstombs( path, argv[1], 510 );
|
|
|
|
if ( !raki::TestBinWii( path ) )
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|