Cleanup and fixes for new decoders

This commit is contained in:
Sergeanur 2021-01-06 20:22:09 +02:00
parent ff9d6e4fd6
commit 0b73b13b9a

View File

@ -174,35 +174,45 @@ class CWavFile : public IDecoder
tFormatHeader() { memset(this, 0, sizeof(*this)); } tFormatHeader() { memset(this, 0, sizeof(*this)); }
}; };
FILE* pFile; FILE *m_pFile;
bool bIsOpen; bool m_bIsOpen;
tFormatHeader FormatHeader;
uint32 DataStartOffset; tFormatHeader m_FormatHeader;
uint32 SampleCount;
uint32 SamplesPerBlock; uint32 m_DataStartOffset; // TODO: 64 bit?
uint32 m_nSampleCount;
uint32 m_nSamplesPerBlock;
// ADPCM things // ADPCM things
uint8 *AdpcmBlock; uint8 *m_pAdpcmBuffer;
int16 **buffers; int16 **m_ppPcmBuffers;
CImaADPCMDecoder* decoders; CImaADPCMDecoder *m_pAdpcmDecoders;
void Close() void Close()
{ {
if (pFile) { if (m_pFile) {
fclose(pFile); fclose(m_pFile);
pFile = nil; m_pFile = nil;
} }
if (AdpcmBlock) delete[] AdpcmBlock; delete[] m_pAdpcmBuffer;
if (buffers) delete[] buffers; delete[] m_ppPcmBuffers;
if (decoders) delete[] decoders; delete[] m_pAdpcmDecoders;
}
uint32 GetCurrentSample() const
{
// TODO: 64 bit?
uint32 FilePos = ftell(m_pFile);
if (FilePos <= m_DataStartOffset)
return 0;
return (FilePos - m_DataStartOffset) / m_FormatHeader.BlockAlign * m_nSamplesPerBlock;
} }
public: public:
CWavFile(const char* path) : bIsOpen(false), DataStartOffset(0), SampleCount(0), SamplesPerBlock(0), AdpcmBlock(nil), buffers(nil), decoders(nil) CWavFile(const char* path) : m_bIsOpen(false), m_DataStartOffset(0), m_nSampleCount(0), m_nSamplesPerBlock(0), m_pAdpcmBuffer(nil), m_ppPcmBuffers(nil), m_pAdpcmDecoders(nil)
{ {
pFile = fopen(path, "rb"); m_pFile = fopen(path, "rb");
if (!pFile) return; if (!m_pFile) return;
#define CLOSE_ON_ERROR(op)\ #define CLOSE_ON_ERROR(op)\
if (op) { \ if (op) { \
@ -212,52 +222,58 @@ public:
tDataHeader DataHeader; tDataHeader DataHeader;
CLOSE_ON_ERROR(fread(&DataHeader, sizeof(DataHeader), 1, pFile) == 0); CLOSE_ON_ERROR(fread(&DataHeader, sizeof(DataHeader), 1, m_pFile) == 0);
CLOSE_ON_ERROR(DataHeader.ID != 'FFIR'); CLOSE_ON_ERROR(DataHeader.ID != 'FFIR');
// TODO? validate filesizes
int WAVE; int WAVE;
CLOSE_ON_ERROR(fread(&WAVE, 4, 1, pFile) == 0); CLOSE_ON_ERROR(fread(&WAVE, 4, 1, m_pFile) == 0);
CLOSE_ON_ERROR(WAVE != 'EVAW') CLOSE_ON_ERROR(WAVE != 'EVAW')
CLOSE_ON_ERROR(fread(&DataHeader, sizeof(DataHeader), 1, pFile) == 0); CLOSE_ON_ERROR(fread(&DataHeader, sizeof(DataHeader), 1, m_pFile) == 0);
CLOSE_ON_ERROR(DataHeader.ID != ' tmf'); CLOSE_ON_ERROR(DataHeader.ID != ' tmf');
CLOSE_ON_ERROR(fread(&FormatHeader, Min(DataHeader.Size, sizeof(tFormatHeader)), 1, pFile) == 0); CLOSE_ON_ERROR(fread(&m_FormatHeader, Min(DataHeader.Size, sizeof(tFormatHeader)), 1, m_pFile) == 0);
CLOSE_ON_ERROR(DataHeader.Size > sizeof(tFormatHeader)); CLOSE_ON_ERROR(DataHeader.Size > sizeof(tFormatHeader));
switch (FormatHeader.AudioFormat) switch (m_FormatHeader.AudioFormat)
{ {
case WAVEFMT_XBOX_ADPCM: case WAVEFMT_XBOX_ADPCM:
FormatHeader.AudioFormat = WAVEFMT_IMA_ADPCM; m_FormatHeader.AudioFormat = WAVEFMT_IMA_ADPCM;
case WAVEFMT_IMA_ADPCM: case WAVEFMT_IMA_ADPCM:
SamplesPerBlock = (FormatHeader.BlockAlign / FormatHeader.NumChannels - 4) * 2 + 1; m_nSamplesPerBlock = (m_FormatHeader.BlockAlign / m_FormatHeader.NumChannels - 4) * 2 + 1;
AdpcmBlock = new uint8[FormatHeader.BlockAlign]; m_pAdpcmBuffer = new uint8[m_FormatHeader.BlockAlign];
buffers = new int16*[FormatHeader.NumChannels]; m_ppPcmBuffers = new int16*[m_FormatHeader.NumChannels];
decoders = new CImaADPCMDecoder[FormatHeader.NumChannels]; m_pAdpcmDecoders = new CImaADPCMDecoder[m_FormatHeader.NumChannels];
break; break;
case WAVEFMT_PCM: case WAVEFMT_PCM:
SamplesPerBlock = 1; m_nSamplesPerBlock = 1;
if (FormatHeader.BitsPerSample != 16) if (m_FormatHeader.BitsPerSample != 16)
{ {
debug("Unsupported PCM (%d bits), only signed 16-bit is supported (%s)\n", FormatHeader.BitsPerSample, path); debug("Unsupported PCM (%d bits), only signed 16-bit is supported (%s)\n", m_FormatHeader.BitsPerSample, path);
Close();
return; return;
} }
break; break;
default: default:
debug("Unsupported wav format 0x%x (%s)\n", FormatHeader.AudioFormat, path); debug("Unsupported wav format 0x%x (%s)\n", m_FormatHeader.AudioFormat, path);
Close();
return; return;
} }
while (true) { while (true) {
CLOSE_ON_ERROR(fread(&DataHeader, sizeof(DataHeader), 1, pFile) == 0); CLOSE_ON_ERROR(fread(&DataHeader, sizeof(DataHeader), 1, m_pFile) == 0);
if (DataHeader.ID == 'atad') if (DataHeader.ID == 'atad')
break; break;
fseek(pFile, DataHeader.Size, SEEK_CUR); fseek(m_pFile, DataHeader.Size, SEEK_CUR);
// TODO? validate data size
// maybe check if there no extreme custom headers that might break this
} }
DataStartOffset = ftell(pFile); m_DataStartOffset = ftell(m_pFile);
SampleCount = DataHeader.Size / FormatHeader.BlockAlign * SamplesPerBlock; m_nSampleCount = DataHeader.Size / m_FormatHeader.BlockAlign * m_nSamplesPerBlock;
bIsOpen = true; m_bIsOpen = true;
#undef CLOSE_ON_ERROR #undef CLOSE_ON_ERROR
} }
@ -268,7 +284,7 @@ public:
bool IsOpened() bool IsOpened()
{ {
return bIsOpen; return m_bIsOpen;
} }
uint32 GetSampleSize() uint32 GetSampleSize()
@ -278,29 +294,29 @@ public:
uint32 GetSampleCount() uint32 GetSampleCount()
{ {
return SampleCount; return m_nSampleCount;
} }
uint32 GetSampleRate() uint32 GetSampleRate()
{ {
return FormatHeader.SampleRate; return m_FormatHeader.SampleRate;
} }
uint32 GetChannels() uint32 GetChannels()
{ {
return FormatHeader.NumChannels; return m_FormatHeader.NumChannels;
} }
void Seek(uint32 milliseconds) void Seek(uint32 milliseconds)
{ {
if (!IsOpened()) return; if (!IsOpened()) return;
fseek(pFile, DataStartOffset + ms2samples(milliseconds) / SamplesPerBlock * FormatHeader.BlockAlign, SEEK_SET); fseek(m_pFile, m_DataStartOffset + ms2samples(milliseconds) / m_nSamplesPerBlock * m_FormatHeader.BlockAlign, SEEK_SET);
} }
uint32 Tell() uint32 Tell()
{ {
if (!IsOpened()) return 0; if (!IsOpened()) return 0;
return samples2ms((ftell(pFile) - DataStartOffset) / FormatHeader.BlockAlign * SamplesPerBlock); return samples2ms(GetCurrentSample());
} }
#define SAMPLES_IN_LINE (8) #define SAMPLES_IN_LINE (8)
@ -309,52 +325,61 @@ public:
{ {
if (!IsOpened()) return 0; if (!IsOpened()) return 0;
if (FormatHeader.AudioFormat == WAVEFMT_PCM) if (m_FormatHeader.AudioFormat == WAVEFMT_PCM)
{ {
uint32 size = fread(buffer, 1, GetBufferSize(), pFile); // just read the file and sort the samples
if (FormatHeader.NumChannels == 2) uint32 size = fread(buffer, 1, GetBufferSize(), m_pFile);
if (m_FormatHeader.NumChannels == 2)
SortStereoBuffer.SortStereo(buffer, size); SortStereoBuffer.SortStereo(buffer, size);
return size; return size;
} }
else if (FormatHeader.AudioFormat == WAVEFMT_IMA_ADPCM) else if (m_FormatHeader.AudioFormat == WAVEFMT_IMA_ADPCM)
{ {
uint32 MaxSamples = GetBufferSamples() / FormatHeader.NumChannels; // trim the buffer size if we're at the end of our file
uint32 CurSample = (ftell(pFile) - DataStartOffset) / FormatHeader.BlockAlign * SamplesPerBlock; uint32 nMaxSamples = GetBufferSamples() / m_FormatHeader.NumChannels;
uint32 nSamplesLeft = m_nSampleCount - GetCurrentSample();
nMaxSamples = Min(nMaxSamples, nSamplesLeft);
MaxSamples = Min(MaxSamples, SampleCount - CurSample); // align sample count to our block
MaxSamples = MaxSamples / SamplesPerBlock * SamplesPerBlock; nMaxSamples = nMaxSamples / m_nSamplesPerBlock * m_nSamplesPerBlock;
uint32 OutBufSizePerChannel = MaxSamples * GetSampleSize();
uint32 OutBufSize = OutBufSizePerChannel * FormatHeader.NumChannels; // count the size of output buffer
int16** buffers = new int16*[FormatHeader.NumChannels]; uint32 OutBufSizePerChannel = nMaxSamples * GetSampleSize();
CImaADPCMDecoder* decoders = new CImaADPCMDecoder[FormatHeader.NumChannels]; uint32 OutBufSize = OutBufSizePerChannel * m_FormatHeader.NumChannels;
for (uint32 i = 0; i < FormatHeader.NumChannels; i++)
buffers[i] = (int16*)((int8*)buffer + OutBufSizePerChannel * i); // calculate the pointers to individual channel buffers
for (uint32 i = 0; i < m_FormatHeader.NumChannels; i++)
m_ppPcmBuffers[i] = (int16*)((int8*)buffer + OutBufSizePerChannel * i);
uint32 samplesRead = 0; uint32 samplesRead = 0;
while (samplesRead < MaxSamples) while (samplesRead < nMaxSamples)
{ {
uint8* AdpcmBuf = AdpcmBlock; // read the file
if (fread(AdpcmBlock, 1, FormatHeader.BlockAlign, pFile) == 0) uint8 *pAdpcmBuf = m_pAdpcmBuffer;
if (fread(m_pAdpcmBuffer, 1, m_FormatHeader.BlockAlign, m_pFile) == 0)
return 0; return 0;
for (uint32 i = 0; i < FormatHeader.NumChannels; i++) // get the first sample in adpcm block and initialise the decoder(s)
for (uint32 i = 0; i < m_FormatHeader.NumChannels; i++)
{ {
int16 Sample = *(int16*)AdpcmBuf; int16 Sample = *(int16*)pAdpcmBuf;
AdpcmBuf += sizeof(int16); pAdpcmBuf += sizeof(int16);
int16 Step = *(int16*)AdpcmBuf; int16 Step = *(int16*)pAdpcmBuf;
AdpcmBuf += sizeof(int16); pAdpcmBuf += sizeof(int16);
decoders[i].Init(Sample, Step); m_pAdpcmDecoders[i].Init(Sample, Step);
*(buffers[i]) = Sample; *(m_ppPcmBuffers[i]) = Sample;
buffers[i]++; m_ppPcmBuffers[i]++;
} }
samplesRead++; samplesRead++;
for (uint32 s = 1; s < SamplesPerBlock; s += SAMPLES_IN_LINE)
// decode the rest of the block
for (uint32 s = 1; s < m_nSamplesPerBlock; s += SAMPLES_IN_LINE)
{ {
for (uint32 i = 0; i < FormatHeader.NumChannels; i++) for (uint32 i = 0; i < m_FormatHeader.NumChannels; i++)
{ {
decoders[i].Decode(AdpcmBuf, buffers[i], SAMPLES_IN_LINE / 2); m_pAdpcmDecoders[i].Decode(pAdpcmBuf, m_ppPcmBuffers[i], SAMPLES_IN_LINE / 2);
AdpcmBuf += SAMPLES_IN_LINE / 2; pAdpcmBuf += SAMPLES_IN_LINE / 2;
buffers[i] += SAMPLES_IN_LINE; m_ppPcmBuffers[i] += SAMPLES_IN_LINE;
} }
samplesRead += SAMPLES_IN_LINE; samplesRead += SAMPLES_IN_LINE;
} }
@ -620,68 +645,68 @@ public:
class CVbFile : public IDecoder class CVbFile : public IDecoder
{ {
FILE* pFile; FILE *m_pFile;
size_t m_FileSize; CVagDecoder *m_pVagDecoders;
size_t m_nNumberOfBlocks;
CVagDecoder* decoders;
uint32 m_nSampleRate; size_t m_FileSize;
uint8 m_nChannels; size_t m_nNumberOfBlocks;
bool m_bBlockRead;
uint16 m_LineInBlock;
size_t m_CurrentBlock;
uint8** ppTempBuffers; uint32 m_nSampleRate;
int16** buffers; uint8 m_nChannels;
bool m_bBlockRead;
uint16 m_LineInBlock;
size_t m_CurrentBlock;
uint8 **m_ppVagBuffers; // buffers that cache actual ADPCM file data
int16 **m_ppPcmBuffers;
void ReadBlock(int32 block = -1) void ReadBlock(int32 block = -1)
{ {
// just read next block if -1 // just read next block if -1
if (block != -1) if (block != -1)
fseek(pFile, block * m_nChannels * VB_BLOCK_SIZE, SEEK_SET); fseek(m_pFile, block * m_nChannels * VB_BLOCK_SIZE, SEEK_SET);
for (int i = 0; i < m_nChannels; i++) for (int i = 0; i < m_nChannels; i++)
fread(ppTempBuffers[i], VB_BLOCK_SIZE, 1, pFile); fread(m_ppVagBuffers[i], VB_BLOCK_SIZE, 1, m_pFile);
m_bBlockRead = true; m_bBlockRead = true;
} }
public: public:
CVbFile(const char* path, uint32 nSampleRate = 32000, uint8 nChannels = 2) : m_nSampleRate(nSampleRate), m_nChannels(nChannels), decoders(nil), ppTempBuffers(nil), buffers(nil), CVbFile(const char* path, uint32 nSampleRate = 32000, uint8 nChannels = 2) : m_nSampleRate(nSampleRate), m_nChannels(nChannels), m_pVagDecoders(nil), m_ppVagBuffers(nil), m_ppPcmBuffers(nil),
m_FileSize(0), m_nNumberOfBlocks(0), m_bBlockRead(false), m_LineInBlock(0), m_CurrentBlock(0) m_FileSize(0), m_nNumberOfBlocks(0), m_bBlockRead(false), m_LineInBlock(0), m_CurrentBlock(0)
{ {
pFile = fopen(path, "rb"); m_pFile = fopen(path, "rb");
if (!pFile) return; if (!m_pFile) return;
fseek(m_pFile, 0, SEEK_END);
m_FileSize = ftell(m_pFile);
fseek(m_pFile, 0, SEEK_SET);
fseek(pFile, 0, SEEK_END);
m_FileSize = ftell(pFile);
fseek(pFile, 0, SEEK_SET);
m_nNumberOfBlocks = m_FileSize / (nChannels * VB_BLOCK_SIZE); m_nNumberOfBlocks = m_FileSize / (nChannels * VB_BLOCK_SIZE);
decoders = new CVagDecoder[nChannels]; m_pVagDecoders = new CVagDecoder[nChannels];
m_CurrentBlock = 0; m_ppVagBuffers = new uint8*[nChannels];
m_LineInBlock = 0; m_ppPcmBuffers = new int16*[nChannels];
m_bBlockRead = false;
ppTempBuffers = new uint8*[nChannels];
buffers = new int16*[nChannels];
for (uint8 i = 0; i < nChannels; i++) for (uint8 i = 0; i < nChannels; i++)
ppTempBuffers[i] = new uint8[VB_BLOCK_SIZE]; m_ppVagBuffers[i] = new uint8[VB_BLOCK_SIZE];
} }
~CVbFile() ~CVbFile()
{ {
if (pFile) if (m_pFile)
{ {
fclose(pFile); fclose(m_pFile);
delete[] decoders;
delete[] m_pVagDecoders;
for (int i = 0; i < m_nChannels; i++) for (int i = 0; i < m_nChannels; i++)
delete[] ppTempBuffers[i]; delete[] m_ppVagBuffers[i];
delete[] ppTempBuffers; delete[] m_ppVagBuffers;
delete[] buffers; delete[] m_ppPcmBuffers;
} }
} }
bool IsOpened() bool IsOpened()
{ {
return pFile != nil; return m_pFile != nil;
} }
uint32 GetSampleSize() uint32 GetSampleSize()
@ -709,6 +734,8 @@ public:
{ {
if (!IsOpened()) return; if (!IsOpened()) return;
uint32 samples = ms2samples(milliseconds); uint32 samples = ms2samples(milliseconds);
// find the block of our sample
uint32 block = samples / NUM_VAG_SAMPLES_IN_BLOCK; uint32 block = samples / NUM_VAG_SAMPLES_IN_BLOCK;
if (block > m_nNumberOfBlocks) if (block > m_nNumberOfBlocks)
{ {
@ -718,6 +745,7 @@ public:
if (block != m_CurrentBlock) if (block != m_CurrentBlock)
m_bBlockRead = false; m_bBlockRead = false;
// find a line of our sample within our block
uint32 remainingSamples = samples - block * NUM_VAG_SAMPLES_IN_BLOCK; uint32 remainingSamples = samples - block * NUM_VAG_SAMPLES_IN_BLOCK;
uint32 newLine = remainingSamples / VAG_SAMPLES_IN_LINE / VAG_LINE_SIZE; uint32 newLine = remainingSamples / VAG_SAMPLES_IN_LINE / VAG_LINE_SIZE;
@ -726,7 +754,7 @@ public:
m_CurrentBlock = block; m_CurrentBlock = block;
m_LineInBlock = newLine; m_LineInBlock = newLine;
for (uint32 i = 0; i < GetChannels(); i++) for (uint32 i = 0; i < GetChannels(); i++)
decoders[i].ResetState(); m_pVagDecoders[i].ResetState();
} }
} }
@ -742,35 +770,38 @@ public:
{ {
if (!IsOpened()) return 0; if (!IsOpened()) return 0;
if (m_CurrentBlock >= m_nNumberOfBlocks) return 0;
// cache current ADPCM block
if (!m_bBlockRead) if (!m_bBlockRead)
ReadBlock(m_CurrentBlock); ReadBlock(m_CurrentBlock);
if (m_CurrentBlock == m_nNumberOfBlocks) return 0; // trim the buffer size if we're at the end of our file
int size = 0;
int numberOfRequiredLines = GetBufferSamples() / m_nChannels / VAG_SAMPLES_IN_LINE; int numberOfRequiredLines = GetBufferSamples() / m_nChannels / VAG_SAMPLES_IN_LINE;
int numberOfRemainingLines = (m_nNumberOfBlocks - m_CurrentBlock) * NUM_VAG_LINES_IN_BLOCK - m_LineInBlock; int numberOfRemainingLines = (m_nNumberOfBlocks - m_CurrentBlock) * NUM_VAG_LINES_IN_BLOCK - m_LineInBlock;
int bufSizePerChannel = Min(numberOfRequiredLines, numberOfRemainingLines) * VAG_SAMPLES_IN_LINE * GetSampleSize(); int bufSizePerChannel = Min(numberOfRequiredLines, numberOfRemainingLines) * VAG_SAMPLES_IN_LINE * GetSampleSize();
if (numberOfRequiredLines > numberOfRemainingLines) // calculate the pointers to individual channel buffers
numberOfRemainingLines = numberOfRemainingLines;
for (uint32 i = 0; i < m_nChannels; i++) for (uint32 i = 0; i < m_nChannels; i++)
buffers[i] = (int16*)((int8*)buffer + bufSizePerChannel * i); m_ppPcmBuffers[i] = (int16*)((int8*)buffer + bufSizePerChannel * i);
int size = 0;
while (size < bufSizePerChannel) while (size < bufSizePerChannel)
{ {
// decode the VAG lines
for (uint32 i = 0; i < m_nChannels; i++) for (uint32 i = 0; i < m_nChannels; i++)
{ {
decoders[i].Decode(ppTempBuffers[i] + m_LineInBlock * VAG_LINE_SIZE, buffers[i], VAG_LINE_SIZE); m_pVagDecoders[i].Decode(m_ppVagBuffers[i] + m_LineInBlock * VAG_LINE_SIZE, m_ppPcmBuffers[i], VAG_LINE_SIZE);
buffers[i] += VAG_SAMPLES_IN_LINE; m_ppPcmBuffers[i] += VAG_SAMPLES_IN_LINE;
} }
size += VAG_SAMPLES_IN_LINE * GetSampleSize(); size += VAG_SAMPLES_IN_LINE * GetSampleSize();
m_LineInBlock++; m_LineInBlock++;
// block is over, read the next block
if (m_LineInBlock >= NUM_VAG_LINES_IN_BLOCK) if (m_LineInBlock >= NUM_VAG_LINES_IN_BLOCK)
{ {
m_CurrentBlock++; m_CurrentBlock++;
if (m_CurrentBlock >= m_nNumberOfBlocks) if (m_CurrentBlock >= m_nNumberOfBlocks) // end of file
break; break;
m_LineInBlock = 0; m_LineInBlock = 0;
ReadBlock(); ReadBlock();