mirror of https://gitlab.com/ecodis/exhale.git
improve IPF writing
This commit is contained in:
parent
7d2b818e5a
commit
86f24988e2
|
@ -16,7 +16,7 @@ if("${CMAKE_CURRENT_SOURCE_DIR}" STREQUAL "${CMAKE_CURRENT_BINARY_DIR}")
|
|||
endif()
|
||||
|
||||
|
||||
project(exhale VERSION 1.1.2 LANGUAGES CXX)
|
||||
project(exhale VERSION 1.1.3 LANGUAGES CXX)
|
||||
|
||||
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
|
||||
set(CMAKE_BUILD_TYPE Release
|
||||
|
|
|
@ -33,7 +33,7 @@ exhale is being made available under an open-source license which is
|
|||
based on the 3-clause BSD license but modified to address particular
|
||||
aspects dictated by the nature and the output of this application.
|
||||
|
||||
The license text and release notes for the current version 1.1.2 can
|
||||
The license text and release notes for the current version 1.1.3 can
|
||||
be found in the `include` subdirectory of the exhale distribution.
|
||||
|
||||
|
||||
|
|
|
@ -25,9 +25,14 @@
|
|||
<td valign="top">
|
||||
|
||||
<h1><br><span class="pink">exhale</span> - <span class="pink">e</span>codis e<span class="pink">x</span>tended <span class="pink">h</span>igh-efficiency <span class="pink">a</span>nd <span class="pink">l</span>ow-complexity <span class="pink">e</span>ncoder<br><span class="gray"><sup><br>Software Release Notes, Version History, Known Issues, Upcoming Feature Roadmap</sup></span><br><br></h1>
|
||||
<h3> The version of this distribution of the «exhale» software release is <b>1.1.2</b> (official pub­lic minor release) from January 2021. Please check <a href="http://www.ecodis.de/audio.htm#mpeg">www.ecodis.de</a> regularly for new versions of this software. A summary of each version up to this release, a list of known issues with this release, and a roadmap of additional functionality are provided below.</h3>
|
||||
<h3> The version of this distribution of the «exhale» software release is <b>1.1.3</b> (official pub­lic minor release) from February 2021. Please check <a href="http://www.ecodis.de/audio.htm#mpeg">www.ecodis.de</a> regularly for new versions of this software. A summary of each version up to this release, a list of known issues with this release, and a roadmap of additional functionality are provided below.</h3>
|
||||
<h3><br><b>Chronological Version History</b></h3>
|
||||
<h3> Version <b>1.1.2 <span class="gray"> Jan. 2021, this release</span></b></h3>
|
||||
<h3> Version <b>1.1.3 <span class="gray"> Feb. 2021, this release</span></b></h3>
|
||||
<ul>
|
||||
<li><h3>exhaleApp: allow specifying loudness (LUFS) and peak sample (dBFS) after preset</h3></li>
|
||||
<li><h3>exhaleLib: write UsacConfig in immediate playout frames (increases compatibility)</h3></li>
|
||||
</ul>
|
||||
<h3> Version <b>1.1.2 <span class="gray"> Jan. 2021</span></b></h3>
|
||||
<ul>
|
||||
<li><h3>further improved file interoperability and seekability with some playback software</h3></li>
|
||||
<li><h3>exhaleLib: write all frames in «stss» data as immediate playout frames (issue 15)</h3></li>
|
||||
|
|
|
@ -15,5 +15,5 @@
|
|||
# define EXHALELIB_VERSION_MINOR "1"
|
||||
#endif
|
||||
#ifndef EXHALELIB_VERSION_BUGFIX
|
||||
# define EXHALELIB_VERSION_BUGFIX ".2" // "RC" or ".0", ".1", ...
|
||||
# define EXHALELIB_VERSION_BUGFIX ".3" // "RC" or ".0", ".1", ...
|
||||
#endif
|
||||
|
|
|
@ -230,6 +230,33 @@ static void eaApplyDownsampler (int32_t* const pcmBuffer, int32_t* const resampl
|
|||
}
|
||||
#endif // ENABLE_RESAMPLING
|
||||
|
||||
static uint32_t eaInitLoudnessInfo (const uint32_t defaultLoudStats, const bool loudnessInfoProvided,
|
||||
#ifdef EXHALE_APP_WCHAR
|
||||
const wchar_t* const strings[])
|
||||
#else
|
||||
const char* const strings[])
|
||||
#endif
|
||||
{
|
||||
float loudnessInfo[2];
|
||||
uint32_t qLoud, qPeak;
|
||||
|
||||
if (!loudnessInfoProvided) return defaultLoudStats;
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
char cString[8];
|
||||
|
||||
for (int c = 0; c < 8; c++) cString[c] = char (strings[i][c]); // make sure string is of type char
|
||||
cString[7] = '\0';
|
||||
loudnessInfo[i] = (float) atof (cString);
|
||||
}
|
||||
|
||||
qLoud = uint32_t (4.0f * __max (0.0f, loudnessInfo[0] + 57.75f) + 0.5f); // quantize LUFS to 8 bits
|
||||
qPeak = uint32_t (32.0f * __max (0.0f, 20.0f - loudnessInfo[1]) + 0.5f); // quantize peak to 12 bits
|
||||
|
||||
return EA_LOUD_INIT | (qPeak << 18) | (qLoud << 6) | 11u; // measurementSystem = 2, reliability = 3
|
||||
}
|
||||
|
||||
// main routine
|
||||
#ifdef EXHALE_APP_WCHAR
|
||||
# ifdef __MINGW32__
|
||||
|
@ -242,7 +269,7 @@ int main (const int argc, char* argv[])
|
|||
{
|
||||
if (argc <= 0) return argc; // for safety
|
||||
|
||||
const bool readStdin = (argc == 3);
|
||||
const bool readStdin = (argc == 3 || argc == 5);
|
||||
BasicWavReader wavReader;
|
||||
int32_t* inPcmData = nullptr; // 24-bit WAVE audio input buffer
|
||||
#if ENABLE_RESAMPLING
|
||||
|
@ -360,7 +387,7 @@ int main (const int argc, char* argv[])
|
|||
fprintf_s (stdout, " ---------------------------------------------------------------------\n\n");
|
||||
|
||||
// check arg. list, print usage if needed
|
||||
if ((argc < 3) || (argc > 4) || (argc > 1 && argv[1][1] != 0))
|
||||
if ((argc < 3) || (argc > 6) || (argc > 1 && argv[1][1] != 0))
|
||||
{
|
||||
fprintf_s (stdout, " Copyright 2018-2021 C.R.Helmrich, project ecodis. See License.htm for details.\n\n");
|
||||
|
||||
|
@ -481,12 +508,12 @@ int main (const int argc, char* argv[])
|
|||
inFileHandle = fileno (stdin);
|
||||
#endif
|
||||
}
|
||||
else // argc = 4, open input file
|
||||
else // argc = 4/6, open WAV file
|
||||
{
|
||||
#ifdef EXHALE_APP_WCHAR
|
||||
const wchar_t* inFileName = argv[2];
|
||||
const wchar_t* inFileName = argv[argc - 2];
|
||||
#else
|
||||
const char* inFileName = argv[2];
|
||||
const char* inFileName = argv[argc - 2];
|
||||
#endif
|
||||
uint16_t inPathEnd = 0;
|
||||
|
||||
|
@ -510,11 +537,11 @@ int main (const int argc, char* argv[])
|
|||
#ifdef EXHALE_APP_WCHAR
|
||||
inFileName = (const wchar_t*) malloc ((exePathEnd + i + 1) * sizeof (wchar_t)); // 0-terminated
|
||||
memcpy ((void*) inFileName, argv[0], exePathEnd * sizeof (wchar_t)); // prepend executable path
|
||||
memcpy ((void*)(inFileName + exePathEnd), argv[2], (i + 1) * sizeof (wchar_t)); // to file name
|
||||
memcpy ((void*)(inFileName + exePathEnd), argv[argc - 2], (i + 1) * sizeof (wchar_t));// to name
|
||||
#else
|
||||
inFileName = (const char*) malloc ((exePathEnd + i + 1) * sizeof (char)); // 0-terminated string
|
||||
memcpy ((void*) inFileName, argv[0], exePathEnd * sizeof (char)); // prepend executable path ...
|
||||
memcpy ((void*)(inFileName + exePathEnd), argv[2], (i + 1) * sizeof (char)); // ... to file name
|
||||
memcpy ((void*)(inFileName + exePathEnd), argv[argc - 2], (i + 1) * sizeof (char)); //...to name
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -716,7 +743,7 @@ int main (const int argc, char* argv[])
|
|||
#endif
|
||||
const unsigned indepPeriod = (sampleRate < 48000 ? (sampleRate - 320) / frameLength : 45 /*for 50-Hz video, use 50 for 60-Hz video*/);
|
||||
const unsigned mod3Percent = unsigned ((expectLength * (3 + (coreSbrFrameLengthIndex & 3))) >> 17);
|
||||
uint32_t byteCount = 0, bw = (numChannels < 7 ? loudStats : 0);
|
||||
uint32_t byteCount = 0, bw = (numChannels < 7 ? eaInitLoudnessInfo (loudStats, argc > 4, &argv[2]) : 0);
|
||||
uint32_t br, bwMax = 0; // br will be used to hold bytes read and/or bit-rate
|
||||
uint32_t headerRes = 0;
|
||||
// initialize LoudnessEstimator object
|
||||
|
@ -739,6 +766,7 @@ int main (const int argc, char* argv[])
|
|||
// init encoder, generate UsacConfig()
|
||||
memset (outAuData, 0, 108 * sizeof (uint8_t)); // max. allowed ASC + UC size
|
||||
i = exhaleEnc.initEncoder (outAuData, &bw); // bw stores actual ASC + UC size
|
||||
if ((i == 256) && (argc > 4)) i = 0; // clear LUFS warning
|
||||
|
||||
if ((i |= mp4Writer.open (outFileHandle, sampleRate, numChannels, inSampDepth, frameLength, startLength
|
||||
#if ENABLE_SIMPLE_SBR
|
||||
|
@ -984,7 +1012,7 @@ int main (const int argc, char* argv[])
|
|||
const uint32_t qPeak = uint32_t (32.0f * (20.0f - 20.0f * log10 (__max (EA_PEAK_MIN, float (loudStats & USHRT_MAX))) - EA_PEAK_NORM) + 0.5f);
|
||||
|
||||
// recreate ASC + UC + loudness data
|
||||
bw = EA_LOUD_INIT | (qPeak << 18) | (qLoud << 6) | 11; // measurementSystem
|
||||
bw = EA_LOUD_INIT | (qPeak << 18) | (qLoud << 6) | 11u;
|
||||
memset (outAuData, 0, 108 * sizeof (uint8_t)); // max allowed ASC + UC size
|
||||
i = exhaleEnc.initEncoder (outAuData, &bw); // with finished loudnessInfo()
|
||||
}
|
||||
|
@ -1049,12 +1077,12 @@ mainFinish:
|
|||
{
|
||||
fprintf_s (stderr, " ERROR while trying to close stdin stream! Has it already been closed?\n\n");
|
||||
}
|
||||
else // argc = 4, file
|
||||
else // argc = 4/6, WAV
|
||||
{
|
||||
#ifdef EXHALE_APP_WCHAR
|
||||
fwprintf_s (stderr, L" ERROR while trying to close input file %s! Does it still exist?\n\n", argv[2]);
|
||||
fwprintf_s (stderr, L" ERROR while trying to close input file %s! Does it still exist?\n\n", argv[argc - 2]);
|
||||
#else
|
||||
fprintf_s (stderr, " ERROR while trying to close input file %s! Does it still exist?\n\n", argv[2]);
|
||||
fprintf_s (stderr, " ERROR while trying to close input file %s! Does it still exist?\n\n", argv[argc - 2]);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
|
||||
0 ICON "exhaleApp.ico"
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 1,1,2
|
||||
FILEVERSION 1,1,3
|
||||
BEGIN
|
||||
BLOCK "StringFileInfo"
|
||||
BEGIN
|
||||
|
|
|
@ -715,7 +715,10 @@ unsigned BitStreamWriter::createAudioConfig (const char samplingFrequencyIndex,
|
|||
{
|
||||
const uint8_t fli = (sbrRatioShiftValue == 0 ? 1 /*no SBR*/ : __min (2, sbrRatioShiftValue) + 2);
|
||||
const int8_t usfi = __max (0, samplingFrequencyIndex - 3 * sbrRatioShiftValue); // TODO: non-standard sampling rates
|
||||
unsigned bitCount = 37;
|
||||
unsigned bitCount = 37, auLen;
|
||||
#ifndef NO_PREROLL_DATA
|
||||
unsigned ucOffset = (samplingFrequencyIndex < AAC_NUM_SAMPLE_RATES ? 2 : 5);
|
||||
#endif
|
||||
|
||||
if ((elementType == nullptr) || (audioConfig == nullptr) || (chConfigurationIndex >= USAC_MAX_NUM_ELCONFIGS) ||
|
||||
#if !RESTRICT_TO_AAC
|
||||
|
@ -816,8 +819,12 @@ unsigned BitStreamWriter::createAudioConfig (const char samplingFrequencyIndex,
|
|||
|
||||
bitCount += (8 - m_auBitStream.heldBitCount) & 7;
|
||||
writeByteAlignment (); // flush bytes
|
||||
|
||||
memcpy (audioConfig, &m_auBitStream.stream.front (), __min (17u + fli, bitCount >> 3));
|
||||
auLen = __min (18u + fli, bitCount >> 3);
|
||||
#ifndef NO_PREROLL_DATA
|
||||
m_usacConfigLen = uint16_t (__max (9, auLen - ucOffset)); // excl. ASC payload
|
||||
memcpy (m_usacConfig, &m_auBitStream.stream.at (ucOffset), auLen - ucOffset);
|
||||
#endif
|
||||
memcpy (audioConfig, &m_auBitStream.stream.front (), auLen);
|
||||
|
||||
return (bitCount >> 3); // byte count
|
||||
}
|
||||
|
@ -854,7 +861,7 @@ unsigned BitStreamWriter::createAudioFrame (CoreCoderData** const elementData,
|
|||
if ((ipf == 2) || (ipf == 1 && (numElements > 1 || !noiseFilling[0])))
|
||||
{
|
||||
bitCount = __min (nSamplesInFrame << 2, (uint32_t) m_auBitStream.stream.size ());
|
||||
memcpy (tempBuffer, &m_auBitStream.stream.front (), bitCount);
|
||||
memcpy (tempBuffer, &m_auBitStream.stream.front (), bitCount); // prev fr AU
|
||||
}
|
||||
#endif
|
||||
m_auBitStream.reset ();
|
||||
|
@ -869,19 +876,28 @@ unsigned BitStreamWriter::createAudioFrame (CoreCoderData** const elementData,
|
|||
{
|
||||
const uint16_t idxPreRollExt = elementData[0]->elementType & 1;
|
||||
const bool lowRatePreRollExt = (ipf == 1 && numElements == 1 && noiseFilling[0]);
|
||||
const unsigned payloadLength = (lowRatePreRollExt ? (8 + idxPreRollExt * 6) >> (sbrRatioShiftValue > 0 ? 0 : 1) : bitCount) + 3; // in bytes!
|
||||
const unsigned extraLength = (m_usacConfigLen > 14 ? 4 : 3) + m_usacConfigLen;
|
||||
const unsigned payloadLength = (lowRatePreRollExt ? (8 + idxPreRollExt * 6) >> (sbrRatioShiftValue > 0 ? 0 : 1) : bitCount) + extraLength; // in bytes
|
||||
|
||||
m_auBitStream.write (0, 1); // usacExtElementUseDefaultLength = 0 (variable)
|
||||
m_auBitStream.write (CLIP_UCHAR (payloadLength), 8);
|
||||
if (payloadLength > 254) m_auBitStream.write (payloadLength - 253, 16);
|
||||
|
||||
m_auBitStream.write (0, 6); // start AudioPreRoll - configLen = reserved = 0
|
||||
m_auBitStream.write (__min (15, m_usacConfigLen), 4); // configLen (part #1)
|
||||
if (m_usacConfigLen > 14) m_auBitStream.write (m_usacConfigLen - 15, 4);
|
||||
|
||||
m_auBitStream.write (m_usacConfig[ci++] & 31, 5); // 1st 3 bits are from ASC
|
||||
while (ci < m_usacConfigLen) m_auBitStream.write (m_usacConfig[ci++], 8);
|
||||
ci = 0;
|
||||
m_auBitStream.write (0, 8 - 5); // pad end of UsacConfig() data
|
||||
|
||||
m_auBitStream.write (0, 2); // applyCrossfade = 0 and reserved = 0 (part #2)
|
||||
m_auBitStream.write (1, 2); // numPreRollFrames, only one supported for now!
|
||||
m_auBitStream.write (payloadLength - 3, 16); // auLen
|
||||
m_auBitStream.write (payloadLength - extraLength, 16); // auLen
|
||||
|
||||
if (lowRatePreRollExt)
|
||||
{
|
||||
bitCount = payloadLength - 3;
|
||||
bitCount = payloadLength - extraLength;
|
||||
memcpy (tempBuffer, zeroAu[idxPreRollExt], bitCount);
|
||||
if (elementData[0]->elementType < ID_USAC_LFE) // correct window_sequence
|
||||
{
|
||||
|
@ -893,6 +909,8 @@ unsigned BitStreamWriter::createAudioFrame (CoreCoderData** const elementData,
|
|||
}
|
||||
while (ci < bitCount) m_auBitStream.write (tempBuffer[ci++], 8); // write AU
|
||||
ci = 0;
|
||||
if (m_usacConfigLen > 14) m_auBitStream.write (0, 4); // pad end of ext data
|
||||
|
||||
bitCount = (payloadLength > 254 ? 26 : 10) + (payloadLength << 3); // for PR
|
||||
}
|
||||
bitCount++; // for ElementPresent flag
|
||||
|
|
|
@ -16,8 +16,8 @@
|
|||
|
||||
// constants, experimental macros
|
||||
#define CORE_MODE_FD 0
|
||||
#define ID_EXT_LOUDNESS_INFO 2
|
||||
#define ID_EXT_ELE_FILL 0
|
||||
#define ID_EXT_LOUDNESS_INFO 2
|
||||
#define SFB_PER_PRED_BAND 2
|
||||
|
||||
// output bit-stream writer class
|
||||
|
@ -30,6 +30,10 @@ private:
|
|||
uint32_t m_frameLength; // number of samples in full frame
|
||||
uint8_t m_numSwbShort; // max. SFB count in short windows
|
||||
uint8_t* m_uCharBuffer; // temporary buffer for ungrouping
|
||||
#ifndef NO_PREROLL_DATA
|
||||
uint8_t m_usacConfig[20]; // buffer for UsacConfig in IPF
|
||||
uint16_t m_usacConfigLen; // length of UsacConfig in bytes
|
||||
#endif
|
||||
|
||||
// helper functions
|
||||
void writeByteAlignment (); // write 0s for byte alignment
|
||||
|
@ -52,7 +56,8 @@ private:
|
|||
public:
|
||||
|
||||
// constructor
|
||||
BitStreamWriter () { m_auBitStream.reset (); m_frameLength = 0; m_numSwbShort = 0; m_uCharBuffer = nullptr; }
|
||||
BitStreamWriter () { m_auBitStream.reset (); m_frameLength = 0; m_numSwbShort = 0; m_uCharBuffer = nullptr;
|
||||
memset (m_usacConfig, 0, 20u); m_usacConfigLen = 0; }
|
||||
// destructor
|
||||
~BitStreamWriter() { m_auBitStream.reset (); }
|
||||
// public functions
|
||||
|
|
Loading…
Reference in New Issue