clean WAVE reader

This commit is contained in:
Christian R. Helmrich 2024-05-27 01:00:00 +02:00
parent b17f659c35
commit 14cdd94f9e
5 changed files with 63 additions and 106 deletions

View File

@ -1,11 +1,11 @@
/* basicWavReader.cpp - source file for class with basic WAVE file reading capability
* written by C. R. Helmrich, last modified in 2021 - see License.htm for legal notices
* written by C. R. Helmrich, last modified in 2024 - see License.htm for legal notices
*
* The copyright in this software is being made available under the exhale Copyright License
* and comes with ABSOLUTELY NO WARRANTY. This software may be subject to other third-
* party rights, including patent rights. No such rights are granted under this License.
*
* Copyright (c) 2018-2021 Christian R. Helmrich, project ecodis. All rights reserved.
* Copyright (c) 2018-2024 Christian R. Helmrich, project ecodis. All rights reserved.
*/
#include "exhaleAppPch.h"
@ -25,7 +25,7 @@ static int64_t fourBytesToLength (const uint8_t* b, const int64_t lengthLimit)
return __min (lengthLimit, chunkLength); // for security
}
#if BWR_BUFFERED_READ
static inline unsigned getFrames (const int fileHandle, void* dataBuf, const unsigned frameCount,
const bool roundSize, const unsigned bytesPerFrame)
{
@ -40,7 +40,8 @@ static inline unsigned getFrames (const int fileHandle, void* dataBuf, const uns
}
return bytesRead / bytesPerFrame; // num of frames read
}
#endif
static const uint8_t allowedChMask[7] = {0x00, 0x04, 0xC0, 0x00, 0x00, 0x37, 0xCF};
// private reader functions
bool BasicWavReader::readRiffHeader ()
@ -70,15 +71,18 @@ bool BasicWavReader::readFormatChunk ()
if (m_bytesRead != m_chunkLength) return false; // error
m_bytesRemaining -= m_bytesRead;
m_waveChannels = (unsigned (b[3]) << 8) | b[2]; // <64k
if ((b[0] == 0xFE) && (b[1] == 0xFF) && (m_chunkLength == CHUNK_FORMAT_MAX) && (b[16] == CHUNK_FORMAT_MAX - CHUNK_FORMAT_SIZE - 2) &&
(b[17] == 0) && (b[18] == b[14]) && ((b[19] | b[25] | b[26] | b[27] | b[28] | b[29] | b[31] | b[33] | b[34] | b[36]) == 0))
(b[17] == 0) && (b[18] <= b[14]) && ((b[19] | b[25] | b[26] | b[27] | b[28] | b[29] | b[31] | b[33] | b[34] | b[36]) == 0))
{
m_waveDataType = WAV_TYPE (b[24]-1); // extensible WAV
b[1] = 0;
if ((b[18] + 8u <= b[14]) || (b[20] != 0 && b[20] != (m_waveChannels < 7 ? allowedChMask[m_waveChannels] : 0) &&
b[20] != (1u << __min (8u, m_waveChannels)) - 1u)) return false; // no unusual or nonstandard channel configs
b[14] = b[18];
b[ 1] = 0;
}
else
m_waveDataType = WAV_TYPE (b[0]-1); // 1: PCM, 3: float
m_waveChannels = b[2]; // only 1, 2, ..., 63 supported
m_waveFrameRate = reverseFourBytes (&b[4]); // frames/s
m_waveBitRate = reverseFourBytes (&b[8]) * 8; // bit/s
m_waveFrameSize = b[12]; // bytes/s divided by frames/s
@ -133,7 +137,6 @@ bool BasicWavReader::seekToChunkTag (uint8_t* const buf, const uint32_t tagID)
unsigned BasicWavReader::readDataFloat16 (const int fileHandle, int32_t* frameBuf, const unsigned frameCount,
const unsigned chanCount, void* tempBuf)
{
#if BWR_BUFFERED_READ
const unsigned rest = ((frameCount >> (BWR_READ_FRACT - 1)) << (BWR_READ_FRACT - 1)) < frameCount ? 1 : 0;
unsigned framesRead = 0;
@ -159,30 +162,11 @@ unsigned BasicWavReader::readDataFloat16 (const int fileHandle, int32_t* frameBu
memset (frameBuf, 0, (frameCount - framesRead) * chanCount * sizeof (int32_t));
}
return framesRead;
#else
unsigned bytesRead = 0;
for (unsigned i = frameCount * chanCount; i > 0; i--)
{
int16_t i16 = 0;
bytesRead += _READ (fileHandle, &i16, 2);
const int32_t e = ((i16 & 0x7C00) >> 10) - 18; // exp.
// an exponent e <= -12 will lead to zero-quantization
*frameBuf = int32_t (e < 0 ? (1024 + (i16 & 0x03FF) + (1 << (-1 - e)) /*rounding offset*/) >> -e
: (e > 12 ? MAX_VALUE_AUDIO24 /*inf*/ : (1024 + (i16 & 0x03FF)) << e));
if ((i16 & 0x8000) != 0) *frameBuf *= -1; // neg. sign
frameBuf++;
}
return bytesRead / (chanCount * 2);
#endif
}
unsigned BasicWavReader::readDataFloat32 (const int fileHandle, int32_t* frameBuf, const unsigned frameCount,
const unsigned chanCount, void* tempBuf)
{
#if BWR_BUFFERED_READ
const unsigned rest = ((frameCount >> (BWR_READ_FRACT - 1)) << (BWR_READ_FRACT - 1)) < frameCount ? 1 : 0;
unsigned framesRead = 0;
@ -208,28 +192,11 @@ unsigned BasicWavReader::readDataFloat32 (const int fileHandle, int32_t* frameBu
memset (frameBuf, 0, (frameCount - framesRead) * chanCount * sizeof (int32_t));
}
return framesRead;
#else
unsigned bytesRead = 0;
for (unsigned i = frameCount * chanCount; i > 0; i--)
{
float f32 = 0.0;
bytesRead += _READ (fileHandle, &f32, 4);
*frameBuf = int32_t (f32 * (1 << 23) + (f32 < 0.0 ? -0.5 : 0.5)); // * 2^23 with rounding
if (*frameBuf < MIN_VALUE_AUDIO24) *frameBuf = MIN_VALUE_AUDIO24;
else
if (*frameBuf > MAX_VALUE_AUDIO24) *frameBuf = MAX_VALUE_AUDIO24;
frameBuf++;
}
return bytesRead / (chanCount * 4);
#endif
}
unsigned BasicWavReader::readDataLnPcm08 (const int fileHandle, int32_t* frameBuf, const unsigned frameCount,
const unsigned chanCount, void* tempBuf)
{
#if BWR_BUFFERED_READ
const unsigned rest = ((frameCount >> (BWR_READ_FRACT - 1)) << (BWR_READ_FRACT - 1)) < frameCount ? 1 : 0;
unsigned framesRead = 0;
@ -249,24 +216,11 @@ unsigned BasicWavReader::readDataLnPcm08 (const int fileHandle, int32_t* frameBu
memset (frameBuf, 0, (frameCount - framesRead) * chanCount * sizeof (int32_t));
}
return framesRead;
#else
unsigned bytesRead = 0;
for (unsigned i = frameCount * chanCount; i > 0; i--)
{
uint8_t ui8 = 128;
bytesRead += _READ (fileHandle, &ui8, 1);
*(frameBuf++) = ((int32_t) ui8 - 128) << 16; // * 2^16
}
return bytesRead / chanCount;
#endif
}
unsigned BasicWavReader::readDataLnPcm16 (const int fileHandle, int32_t* frameBuf, const unsigned frameCount,
const unsigned chanCount, void* tempBuf)
{
#if BWR_BUFFERED_READ
const unsigned rest = ((frameCount >> (BWR_READ_FRACT - 1)) << (BWR_READ_FRACT - 1)) < frameCount ? 1 : 0;
unsigned framesRead = 0;
@ -286,24 +240,11 @@ unsigned BasicWavReader::readDataLnPcm16 (const int fileHandle, int32_t* frameBu
memset (frameBuf, 0, (frameCount - framesRead) * chanCount * sizeof (int32_t));
}
return framesRead;
#else
unsigned bytesRead = 0;
for (unsigned i = frameCount * chanCount; i > 0; i--)
{
int16_t i16 = 0;
bytesRead += _READ (fileHandle, &i16, 2);
*(frameBuf++) = (int32_t) i16 << 8; // * 2^8
}
return bytesRead / (chanCount * 2);
#endif
}
unsigned BasicWavReader::readDataLnPcm24 (const int fileHandle, int32_t* frameBuf, const unsigned frameCount,
const unsigned chanCount, void* tempBuf)
{
#if BWR_BUFFERED_READ
const unsigned rest = ((frameCount >> (BWR_READ_FRACT - 1)) << (BWR_READ_FRACT - 1)) < frameCount ? 1 : 0;
unsigned framesRead = 0;
@ -325,24 +266,11 @@ unsigned BasicWavReader::readDataLnPcm24 (const int fileHandle, int32_t* frameBu
memset (frameBuf, 0, (frameCount - framesRead) * chanCount * sizeof (int32_t));
}
return framesRead;
#else
unsigned bytesRead = 0;
for (unsigned i = frameCount * chanCount; i > 0; i--)
{
int32_t i24 = 0;
bytesRead += _READ (fileHandle, &i24, 3);
*(frameBuf++) = (i24 > MAX_VALUE_AUDIO24 ? i24 + 2 * MIN_VALUE_AUDIO24 : i24);
}
return bytesRead / (chanCount * 3);
#endif
}
unsigned BasicWavReader::readDataLnPcm32 (const int fileHandle, int32_t* frameBuf, const unsigned frameCount,
const unsigned chanCount, void* tempBuf)
{
#if BWR_BUFFERED_READ
const unsigned rest = ((frameCount >> (BWR_READ_FRACT - 1)) << (BWR_READ_FRACT - 1)) < frameCount ? 1 : 0;
unsigned framesRead = 0;
@ -364,18 +292,6 @@ unsigned BasicWavReader::readDataLnPcm32 (const int fileHandle, int32_t* frameBu
memset (frameBuf, 0, (frameCount - framesRead) * chanCount * sizeof (int32_t));
}
return framesRead;
#else
unsigned bytesRead = 0;
for (unsigned i = frameCount * chanCount; i > 0; i--)
{
int32_t i24 = 0;
bytesRead += _READ (fileHandle, &i24, 4);
i24 = ((i24 >> 1) + (1 << 6)) >> 7; // * 2^-8 with rounding, overflow-safe
*(frameBuf++) = __min (MAX_VALUE_AUDIO24, i24);
}
return bytesRead / (chanCount * 4);
#endif
}
// public functions
@ -414,6 +330,13 @@ unsigned BasicWavReader::open (const int wavFileHandle, const uint16_t maxFrameR
}
m_frameLimit = maxFrameRead;
if (m_waveChMpegMap > 0)
{
if (m_waveChannels < 3) m_waveChMpegMap = 0; // mono and stereo inputs do not need to be remapped
else
if (m_waveChannels == 6) m_waveChMpegMap++; // when m_waveChMpegMap is even, relocate LFE channel
}
// ready to read audio data: initialize byte counter
if (m_bytesRemaining > m_chunkLength)
{
@ -463,6 +386,32 @@ unsigned BasicWavReader::read (int32_t* const frameBuf, const uint16_t frameCoun
if (framesRead < framesTotal) eaExtrapolate (frameBuf, framesRead, framesTotal, m_waveChannels); // fade-out, for gapless playback on more content
if (m_waveChMpegMap > 0)
{
int32_t* sampBuf = frameBuf; // remap multichannel PCM input to MPEG-4 or D channel configuration
if (m_waveChMpegMap & 1)
{
for (unsigned i = framesTotal; i > 0; i--, sampBuf += m_waveChannels)
{
int32_t c = sampBuf[2];
sampBuf[2] = sampBuf[1];
sampBuf[1] = sampBuf[0];
sampBuf[0] = c; // cntr.
}
}
else // m_waveChMpegMap even
{
for (unsigned i = framesTotal; i > 0; i--, sampBuf += m_waveChannels)
{
int32_t c = sampBuf[2], /*int32*/ l = sampBuf[3];
sampBuf[2] = sampBuf[1]; sampBuf[3] = sampBuf[4];
sampBuf[1] = sampBuf[0]; sampBuf[4] = sampBuf[5];
sampBuf[0] = c; /*cntr.*/ sampBuf[5] = l; // LFE
}
}
}
return framesRead;
}

View File

@ -1,11 +1,11 @@
/* basicWavReader.h - header file for class with basic WAVE file reading capability
* written by C. R. Helmrich, last modified in 2020 - see License.htm for legal notices
* written by C. R. Helmrich, last modified in 2024 - see License.htm for legal notices
*
* The copyright in this software is being made available under the exhale Copyright License
* and comes with ABSOLUTELY NO WARRANTY. This software may be subject to other third-
* party rights, including patent rights. No such rights are granted under this License.
*
* Copyright (c) 2018-2021 Christian R. Helmrich, project ecodis. All rights reserved.
* Copyright (c) 2018-2024 Christian R. Helmrich, project ecodis. All rights reserved.
*/
#ifndef _BASIC_WAV_READER_H_
@ -14,7 +14,6 @@
#include "exhaleAppPch.h"
// constant data sizes & limits
#define BWR_BUFFERED_READ 1 // faster reader
#define BWR_READ_FRACT 5 // 2^-READ_FRACT
#define CHUNK_FORMAT_MAX 40
#define CHUNK_FORMAT_SIZE 16
@ -24,7 +23,7 @@
#define MIN_VALUE_AUDIO24 -8388608 // (1 << 23) *-1
// WAVE data format definitions
typedef enum WAV_TYPE
typedef enum WAV_TYPE : int16_t
{
WAV_PCM = 0, // linear PCM
WAV_ADPCM, // ADPCM
@ -51,6 +50,7 @@ private:
unsigned m_waveBitDepth;
unsigned m_waveBitRate;
unsigned m_waveChannels;
uint16_t m_waveChMpegMap;
WAV_TYPE m_waveDataType;
unsigned m_waveFrameRate;
unsigned m_waveFrameSize;
@ -76,7 +76,7 @@ private:
public:
// constructor
BasicWavReader () { m_fileHandle = -1; reset (); }
BasicWavReader (const int mpegChCfg) { m_fileHandle = -1; m_waveChMpegMap = (!mpegChCfg ? 0 : 1); reset (); }
// destructor
~BasicWavReader() { if (m_byteBuffer != nullptr) free ((void*) m_byteBuffer); }
// public functions

View File

@ -1,5 +1,5 @@
/* exhaleApp.cpp - source file with main() routine for exhale application executable
* written by C. R. Helmrich, last modified in 2023 - see License.htm for legal notices
* written by C. R. Helmrich, last modified in 2024 - see License.htm for legal notices
*
* The copyright in this software is being made available under the exhale Copyright License
* and comes with ABSOLUTELY NO WARRANTY. This software may be subject to other third-
@ -325,7 +325,7 @@ int main (const int argc, char* argv[])
if (argc <= 0) return argc; // for safety
const bool readStdin = (argc == 3 || argc == 5);
BasicWavReader wavReader;
BasicWavReader wavReader(1);
int32_t* inPcmData = nullptr; // 24-bit WAVE audio input buffer
int32_t* inPcmRsmp = nullptr; // temporary buffer for resampler
uint8_t* outAuData = nullptr; // access unit (AU) output buffer

View File

@ -1,5 +1,5 @@
/* exhaleApp.rc - resource file for exhale application binaries compiled under Windows
* written by C. R. Helmrich, last modified in 2023 - see License.htm for legal notices
* written by C. R. Helmrich, last modified in 2024 - see License.htm for legal notices
*
* The copyright in this software is being made available under the exhale Copyright License
* and comes with ABSOLUTELY NO WARRANTY. This software may be subject to other third-
@ -13,7 +13,7 @@
0 ICON "exhaleApp.ico"
VS_VERSION_INFO VERSIONINFO
FILEVERSION 1,2,1,0
FILEVERSION 1,2,1,1
BEGIN
BLOCK "StringFileInfo"
BEGIN

View File

@ -1,5 +1,5 @@
/* exhaleEnc.cpp - source file for class providing Extended HE-AAC encoding capability
* written by C. R. Helmrich, last modified in 2023 - see License.htm for legal notices
* written by C. R. Helmrich, last modified in 2024 - see License.htm for legal notices
* C API corrected and API compilation extended by J. Regan in 2022, see merge request 8
*
* The copyright in this software is being made available under the exhale Copyright License
@ -828,9 +828,14 @@ unsigned ExhaleEncoder::psychBitAllocation () // perceptual bit-allocation via s
const int16_t chanCorrSign = (coreConfig.stereoConfig & 2 ? -1 : 1);
const uint16_t nSamplesMax = (useMaxBandwidth ? nSamplesInFrame : swbOffsetsL[m_swbTableIdx][__min (m_numSwbLong, maxSfbLong + 1)]);
const bool reducedStrength = (coreConfig.tnsActive && (m_bitRateMode > 0)) || (m_bitRateMode >= 5);
const uint8_t steppFadeLen = (eightShorts0 ? 4 : (reducedStrength ? 32 : 64));
const uint8_t steppFadeOff = ((m_bitRateMode + 77000 / samplingRate) & 6) << (eightShorts0 ? 2 : 5);
#if BA_MORE_CBR
const uint8_t steppFadeLen = (eightShorts0 ? 4 : (reducedStrength || (m_bitRateMode == 0) ? 32 : 64));
const int64_t steppWeightI = __min (64, m_perCorrHCurr[el] - 128) >> ((eightShorts0 && (m_bitRateMode > 0)) || reducedStrength ? 1 : 0); // crosstalk * 128
#else
const uint8_t steppFadeLen = (eightShorts0 ? 4 : (reducedStrength ? 32 : 64));
const int64_t steppWeightI = __min (64, m_perCorrHCurr[el] - 128) >> (eightShorts0 || reducedStrength ? 1 : 0); // crosstalk * 128
#endif
const int64_t steppWeightD = 128 - steppWeightI; // decrement, (1 - crosstalk) * 128
for (uint16_t n = 0, gr = 0; gr < coreConfig.groupingData[0].numWindowGroups; gr++)
@ -1465,6 +1470,9 @@ unsigned ExhaleEncoder::spectralProcessing () // complete ics_info(), calc TNS
m_perCorrHCurr[el] = (uint8_t) __max (prevPerCorr - allowedDiff, __min (prevPerCorr + allowedDiff, currPerCorr));
}
#if BA_MORE_CBR
if (m_bitRateMode == 0) m_perCorrHCurr[el] = uint8_t (85 + (2 * s) / 3); // stronger
#endif
m_perCorrLCurr[el] = coreConfig.stereoDataCurr[0];
if ((int) s == steAnaStats * -1) coreConfig.stereoConfig = 2; // 2: S>M, pred_dir=1