361 lines
12 KiB
C++
361 lines
12 KiB
C++
//
|
|
// tools/audwaveencoder/waveencoder.cpp
|
|
//
|
|
// Copyright (C) 1999-2005 Rockstar Games. All Rights Reserved.
|
|
//
|
|
|
|
#include "stdafx.h"
|
|
|
|
#include "forceinclude/win32_release.h"
|
|
|
|
#ifdef __WIN32PC
|
|
#undef __WIN32PC
|
|
#endif // __WIN32PC
|
|
#define __WIN32PC 1
|
|
|
|
#include "libresample.h"
|
|
#include "math.h"
|
|
#include "memory.h"
|
|
|
|
#include "waveencoder.h"
|
|
|
|
#include <stdio.h>
|
|
|
|
using namespace audBuildCommon;
|
|
|
|
namespace audWaveEncoding {
|
|
|
|
bool audWaveEncoder::Encode(audWaveMetadataBaseWrapper *waveMetadataInWrapped, unsigned int waveSampleDataOffsetBytes,
|
|
void **waveMetadataOut, unsigned int &waveMetadataOutLengthBytes, void **waveSampleDataOut,
|
|
unsigned int &waveSampleDataOutLengthBytes, int /*compression*/, int & /*preloopPadding*/, unsigned int &)
|
|
{
|
|
audWaveMetadataBase *waveMetadataIn = waveMetadataInWrapped->GetMetadata();
|
|
|
|
//
|
|
// NOTE: If this base implementation is called, no Wave compression is required, so simply convert Wave metadata and
|
|
// sample data to native platform endianness.
|
|
//
|
|
*waveSampleDataOut = new char[waveMetadataIn->lengthBytes];
|
|
waveSampleDataOutLengthBytes = waveMetadataIn->lengthBytes;
|
|
if(audPlatformSpecific::IsPlatformBigEndian())
|
|
{
|
|
for(int i=0; i<(int)(waveMetadataIn->lengthSamples); i++)
|
|
{
|
|
(*((unsigned short **)waveSampleDataOut))[i] =
|
|
audPlatformSpecific::FlipEndian(((unsigned short *)(waveMetadataIn->waveData))[i]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
memcpy(*waveSampleDataOut, waveMetadataIn->waveData, waveMetadataIn->lengthBytes);
|
|
}
|
|
|
|
*waveMetadataOut = new audWaveMetadataBase();
|
|
waveMetadataOutLengthBytes = sizeof(audWaveMetadataBase);
|
|
const unsigned __int64 waveDataOffsetBytes = (unsigned __int64)waveSampleDataOffsetBytes;
|
|
const unsigned int waveFlags = WAVEFLAGS_PCM;
|
|
(*((audWaveMetadataBase **)waveMetadataOut))->waveDataOffsetBytes = audPlatformSpecific::FixEndian(waveDataOffsetBytes);
|
|
(*((audWaveMetadataBase **)waveMetadataOut))->lengthBytes = audPlatformSpecific::FixEndian(waveMetadataIn->lengthBytes);
|
|
(*((audWaveMetadataBase **)waveMetadataOut))->lengthSamples = audPlatformSpecific::FixEndian(waveMetadataIn->lengthSamples);
|
|
(*((audWaveMetadataBase **)waveMetadataOut))->loopStartOffsetSamples = (int)audPlatformSpecific::FixEndian((unsigned int)waveMetadataIn->loopStartOffsetSamples);
|
|
(*((audWaveMetadataBase **)waveMetadataOut))->sampleRate = audPlatformSpecific::FixEndian(waveMetadataIn->sampleRate);
|
|
(*((audWaveMetadataBase **)waveMetadataOut))->headroom = (short)audPlatformSpecific::FixEndian((unsigned short)waveMetadataIn->headroom);
|
|
(*((audWaveMetadataBase **)waveMetadataOut))->flags = audPlatformSpecific::FixEndian(waveFlags);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool audWaveEncoder::Resample(audWaveMetadataBaseWrapper *waveMetadataInWrapped, int targetSampleRate)
|
|
{
|
|
audWaveMetadataBase *waveMetadataIn = waveMetadataInWrapped->GetMetadata();
|
|
|
|
if(targetSampleRate == (int)(waveMetadataIn->sampleRate))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
const float resamplingRatio = (float)targetSampleRate / (float)(waveMetadataIn->sampleRate);
|
|
|
|
//Initialize resampler.
|
|
void *resampler = resample_open(1, resamplingRatio, resamplingRatio);
|
|
if(resampler == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
int resamplerTargetSamplesIn = waveMetadataIn->lengthSamples;
|
|
int resamplerTargetSamplesOut = (int)ceil((float)(waveMetadataIn->lengthSamples) * resamplingRatio);
|
|
|
|
//Allocating floating-point buffers for resampler.
|
|
float *rawData = new float[resamplerTargetSamplesIn];
|
|
float *resampledData = new float[resamplerTargetSamplesOut];
|
|
|
|
//Convert raw samples to floating-point.
|
|
for(int i=0; i<resamplerTargetSamplesIn; i++)
|
|
{
|
|
rawData[i] = (float)(((short *)(waveMetadataIn->waveData))[i]) / 32767.0f;
|
|
}
|
|
|
|
fprintf(stderr, "Starting resample (%d -> %d)\n", waveMetadataIn->sampleRate, targetSampleRate);
|
|
//Resample loop.
|
|
int resamplerSamplesUsed;
|
|
int resamplerSamplesOut = resample_process(resampler, resamplingRatio, rawData, resamplerTargetSamplesIn, 1,
|
|
&resamplerSamplesUsed, resampledData, resamplerTargetSamplesOut);
|
|
|
|
fprintf(stderr, "Completed resample\n");
|
|
|
|
//Convert resampled data back to fixed-point samples.
|
|
short *resampleBuffer = new short[resamplerTargetSamplesOut];
|
|
for(int i=0; i<resamplerTargetSamplesOut; i++)
|
|
{
|
|
resampleBuffer[i] = (short)floor(resampledData[i] * 32767.0f);
|
|
}
|
|
|
|
delete[] waveMetadataIn->waveData;
|
|
waveMetadataIn->waveData = (void *)resampleBuffer;
|
|
waveMetadataIn->lengthSamples = resamplerTargetSamplesOut;
|
|
waveMetadataIn->lengthBytes = resamplerTargetSamplesOut * 2; //16-bit PCM.
|
|
waveMetadataIn->sampleRate = (unsigned short)targetSampleRate;
|
|
if(waveMetadataIn->loopStartOffsetSamples > 0)
|
|
{
|
|
waveMetadataIn->loopStartOffsetSamples = (int)floor((float)waveMetadataIn->loopStartOffsetSamples *
|
|
resamplingRatio);
|
|
}
|
|
|
|
//Free memory and shutdown resampler.
|
|
delete[] rawData;
|
|
delete[] resampledData;
|
|
resample_close(resampler);
|
|
|
|
return ((resamplerSamplesOut == resamplerTargetSamplesOut) && (resamplerSamplesUsed == resamplerTargetSamplesIn));
|
|
}
|
|
|
|
bool audWaveEncoder::AlignLoop(audWaveMetadataBase *waveMetadata, unsigned int alignmentSamples,
|
|
int &preloopPadding)
|
|
{
|
|
unsigned int i;
|
|
unsigned int preloopLengthSamples = (unsigned int )(waveMetadata->loopStartOffsetSamples);
|
|
unsigned int resampledPreloopLengthSamples = 0;
|
|
unsigned int loopLengthSamples = waveMetadata->lengthSamples - preloopLengthSamples;
|
|
unsigned int resampledLoopLengthSamples = 0;
|
|
float *resampledPreloopData = NULL;
|
|
float *resampledLoopData = NULL;
|
|
|
|
preloopPadding = 0;
|
|
|
|
//Check loop length is integrally divisible by block size.
|
|
unsigned int loopPadSamples = alignmentSamples - (loopLengthSamples % alignmentSamples);
|
|
if((loopPadSamples > 0) && (loopPadSamples < alignmentSamples)) //We need to resample the Wave.
|
|
{
|
|
//
|
|
//Resample loop.
|
|
//
|
|
if(loopPadSamples > alignmentSamples / 2)
|
|
{
|
|
//Resample down.
|
|
resampledLoopLengthSamples = loopLengthSamples - (alignmentSamples - loopPadSamples);
|
|
}
|
|
else
|
|
{
|
|
//Resample up.
|
|
resampledLoopLengthSamples = loopLengthSamples + loopPadSamples;
|
|
}
|
|
|
|
float resamplingRatio = (float)resampledLoopLengthSamples / (float)loopLengthSamples;
|
|
|
|
//Ensure that resampling does not result in a sample rate above 48kHz.
|
|
if((int)ceilf(resamplingRatio * (float)(waveMetadata->sampleRate)) > 48000)
|
|
{
|
|
//Resample down instead.
|
|
resampledLoopLengthSamples = loopLengthSamples - (alignmentSamples - loopPadSamples);
|
|
resamplingRatio = (float)resampledLoopLengthSamples / (float)loopLengthSamples;
|
|
}
|
|
|
|
//Initialize resampler.
|
|
void *resampler = resample_open(1, resamplingRatio, resamplingRatio);
|
|
if(resampler == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//Allocating floating-point buffers for resampler.
|
|
float *loopData = new float[loopLengthSamples];
|
|
resampledLoopData = new float[resampledLoopLengthSamples];
|
|
|
|
//Convert loop samples to floating-point.
|
|
for(i=0; i<loopLengthSamples; i++)
|
|
{
|
|
loopData[i] = (float)(((short *)(waveMetadata->waveData))[preloopLengthSamples + i]) / 32767.0f;
|
|
}
|
|
|
|
//Resample loop.
|
|
int resamplerSamplesIn = (int)loopLengthSamples;
|
|
int resamplerSamplesOut = resample_process(resampler, resamplingRatio, loopData, resamplerSamplesIn, 1,
|
|
&resamplerSamplesIn, resampledLoopData, resampledLoopLengthSamples);
|
|
delete[] loopData;
|
|
|
|
if((resamplerSamplesOut != (int)resampledLoopLengthSamples) || (resamplerSamplesIn != (int)loopLengthSamples))
|
|
{
|
|
delete[] resampledLoopData;
|
|
resample_close(resampler);
|
|
return false;
|
|
}
|
|
|
|
if(preloopLengthSamples > 0)
|
|
{
|
|
//
|
|
//Resample preloop.
|
|
//
|
|
resampledPreloopLengthSamples = (unsigned int)ceilf((float)preloopLengthSamples * resamplingRatio);
|
|
|
|
//Check resampled preloop length is integrally divisible by XMA block size.
|
|
unsigned int resampledPreloopPadSamples = alignmentSamples - (resampledPreloopLengthSamples %
|
|
alignmentSamples);
|
|
if(resampledPreloopPadSamples == alignmentSamples)
|
|
{
|
|
resampledPreloopPadSamples = 0;
|
|
}
|
|
|
|
preloopPadding = resampledPreloopPadSamples;
|
|
|
|
//Allocating floating-point buffers for resampler.
|
|
float *preloopData = new float[preloopLengthSamples];
|
|
resampledPreloopData = new float[resampledPreloopLengthSamples + resampledPreloopPadSamples];
|
|
if(resampledPreloopPadSamples > 0)
|
|
{
|
|
//Zero pad the start of the preloop.
|
|
memset(resampledPreloopData, 0, resampledPreloopPadSamples * sizeof(float));
|
|
}
|
|
|
|
//Convert preloop samples to floating-point.
|
|
for(i=0; i<preloopLengthSamples; i++)
|
|
{
|
|
preloopData[i] = (float)(((short *)(waveMetadata->waveData))[i]) / 32767.0f;
|
|
}
|
|
|
|
//Resample loop.
|
|
resamplerSamplesIn = (int)preloopLengthSamples;
|
|
resamplerSamplesOut = resample_process(resampler, resamplingRatio, preloopData, resamplerSamplesIn, 1,
|
|
&resamplerSamplesIn, resampledPreloopData + resampledPreloopPadSamples, resampledPreloopLengthSamples);
|
|
delete[] preloopData;
|
|
|
|
if((resamplerSamplesOut != (int)resampledPreloopLengthSamples) ||
|
|
(resamplerSamplesIn != (int)preloopLengthSamples))
|
|
{
|
|
delete[] resampledPreloopData;
|
|
delete[] resampledLoopData;
|
|
resample_close(resampler);
|
|
return false;
|
|
}
|
|
|
|
resampledPreloopLengthSamples += resampledPreloopPadSamples;
|
|
|
|
} //preloopLengthSamples > 0
|
|
|
|
resample_close(resampler);
|
|
|
|
waveMetadata->lengthSamples = resampledPreloopLengthSamples + resampledLoopLengthSamples;
|
|
waveMetadata->lengthBytes = waveMetadata->lengthSamples * 2;
|
|
waveMetadata->loopStartOffsetSamples = resampledPreloopLengthSamples;
|
|
waveMetadata->sampleRate = (unsigned short)ceilf((float)(waveMetadata->sampleRate) * resamplingRatio);
|
|
|
|
//Convert resampled Wave data back to 16-bit PCM.
|
|
short *aligmentBuffer = new short[waveMetadata->lengthSamples];
|
|
for(i=0; i<resampledPreloopLengthSamples; i++)
|
|
{
|
|
aligmentBuffer[i] = (short)floorf(resampledPreloopData[i] * 32767.0f);
|
|
}
|
|
for(i=0; i<resampledLoopLengthSamples; i++)
|
|
{
|
|
aligmentBuffer[resampledPreloopLengthSamples + i] = (short)floorf(resampledLoopData[i] * 32767.0f);
|
|
}
|
|
|
|
delete[] waveMetadata->waveData;
|
|
waveMetadata->waveData = (void *)aligmentBuffer;
|
|
|
|
if(resampledPreloopData)
|
|
{
|
|
delete[] resampledPreloopData;
|
|
}
|
|
if(resampledLoopData)
|
|
{
|
|
delete[] resampledLoopData;
|
|
}
|
|
|
|
} //(loopPadSamples > 0) && (loopPadSamples < alignmentSamples)
|
|
else if(preloopLengthSamples > 0)
|
|
{
|
|
//Check preloop length is integrally divisible by block size.
|
|
unsigned int preloopPadSamples = alignmentSamples - (preloopLengthSamples % alignmentSamples);
|
|
if((preloopPadSamples > 0) && (preloopPadSamples < alignmentSamples)) //We need to zero pad the preloop.
|
|
{
|
|
//Zero pad preloop to align.
|
|
short *aligmentBuffer = new short[waveMetadata->lengthSamples + preloopPadSamples];
|
|
memset(aligmentBuffer, 0, preloopPadSamples * sizeof(short));
|
|
memcpy(aligmentBuffer + preloopPadSamples, waveMetadata->waveData, waveMetadata->lengthSamples *
|
|
sizeof(short));
|
|
|
|
delete[] waveMetadata->waveData;
|
|
waveMetadata->waveData = (void *)aligmentBuffer;
|
|
waveMetadata->lengthSamples += preloopPadSamples;
|
|
waveMetadata->lengthBytes = waveMetadata->lengthSamples * 2;
|
|
waveMetadata->loopStartOffsetSamples += preloopPadSamples;
|
|
|
|
preloopPadding = preloopPadSamples;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
unsigned short audWaveEncoder::FlipEndian(unsigned short value)
|
|
{
|
|
unsigned char b1, b2;
|
|
|
|
b1 = (unsigned char)(value & 255);
|
|
b2 = (unsigned char)((value >> 8) & 255);
|
|
value = (b1 << 8) + b2;
|
|
|
|
return value;
|
|
}
|
|
|
|
unsigned int audWaveEncoder::FlipEndian(unsigned int value)
|
|
{
|
|
unsigned char b1, b2, b3, b4;
|
|
|
|
b1 = (unsigned char)(value & 255);
|
|
b2 = (unsigned char)((value >> 8 ) & 255);
|
|
b3 = (unsigned char)((value >> 16 ) & 255);
|
|
b4 = (unsigned char)((value >> 24 ) & 255);
|
|
|
|
value = ((unsigned int)b1 << 24) + ((unsigned int)b2 << 16) + ((unsigned int)b3 << 8) + b4;
|
|
|
|
return value;
|
|
}
|
|
|
|
unsigned __int64 audWaveEncoder::FlipEndian(unsigned __int64 value)
|
|
{
|
|
union
|
|
{
|
|
unsigned __int64 i;
|
|
unsigned char b[8];
|
|
} dat1, dat2;
|
|
|
|
dat1.i = value;
|
|
dat2.b[0] = dat1.b[7];
|
|
dat2.b[1] = dat1.b[6];
|
|
dat2.b[2] = dat1.b[5];
|
|
dat2.b[3] = dat1.b[4];
|
|
dat2.b[4] = dat1.b[3];
|
|
dat2.b[5] = dat1.b[2];
|
|
dat2.b[6] = dat1.b[1];
|
|
dat2.b[7] = dat1.b[0];
|
|
|
|
return dat2.i;
|
|
}
|
|
|
|
|
|
} // namespace audWaveEncoding
|
|
|
|
|