Files
GTASource/rage/base/tools/audio/audwaveencoder/waveencoder.cpp
expvintl 419f2e4752 init
2025-02-23 17:40:52 +08:00

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