mirror of
https://gitlab.com/ecodis/exhale.git
synced 2025-06-05 21:59:32 +02:00
finish 1.2.1.2 release
This commit is contained in:
@ -31,7 +31,7 @@ ____________________________________________________________________
|
||||
Copyright
|
||||
---------
|
||||
|
||||
(c) 2024 Christian R. Helmrich, project ecodis. All rights reserved.
|
||||
(c) 2025 Christian R. Helmrich, project ecodis. All rights reserved.
|
||||
|
||||
|
||||
License
|
||||
|
@ -1,4 +1,4 @@
|
||||
<!-- www.ecodis.de/exhale/license.htm - created by Christian R. Helmrich - Copyright (c) 2018-2024 Christian R. Helmrich, project ecodis. All rights reserved. -->
|
||||
<!-- www.ecodis.de/exhale/license.htm - created by Christian R. Helmrich - Copyright (c) 2018-2025 Christian R. Helmrich, project ecodis. All rights reserved. -->
|
||||
|
||||
<!doctype html public "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html>
|
||||
@ -27,7 +27,7 @@
|
||||
<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>referred to as «Software» below; clarifications introduced in October 2020 in <i>italics</i></sup></span><br><br></h1>
|
||||
<h3> This Software is being made available and/or distributed under the following <i>exhale Copyright</i> License. For a list of authors which have contributed to this Software, called «contributors» in the text below, please refer to the respective files provided with this distribution (source files or binary executable) to which modifications were contributed.</h3>
|
||||
<h3><br><b>Licensor's Copyright Notice</b></h3>
|
||||
<h3> Copyright © 2018–2024 Christian R. Helmrich, <a href="http://www.ecodis.de">ecodis</a> (Licensor). All rights reserved.</h3>
|
||||
<h3> Copyright © 2018–2025 Christian R. Helmrich, <a href="http://www.ecodis.de">ecodis</a> (Licensor). All rights reserved.</h3>
|
||||
<h3><br><b>List of License Conditions</b></h3>
|
||||
<h3> Redistribution and use of this Software in source and binary forms, with or without modification, are permitted provided that all of the following four conditions are met:</h3>
|
||||
<ul>
|
||||
|
@ -1,4 +1,4 @@
|
||||
<!-- www.ecodis.de/exhale/release.htm - created by Christian R. Helmrich - Copyright (c) 2018-2024 Christian R. Helmrich, project ecodis. All rights reserved. -->
|
||||
<!-- www.ecodis.de/exhale/release.htm - created by Christian R. Helmrich - Copyright (c) 2018-2025 Christian R. Helmrich, project ecodis. All rights reserved. -->
|
||||
|
||||
<!doctype html public "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html>
|
||||
@ -25,9 +25,17 @@
|
||||
<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.2.1</b> (official pub­lic minor release) from December 2023. 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.2.1.2</b> (official pub­lic bugfix release) from May 2025. 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.2.1 <span class="gray"> Dec. 2023, this release</span></b></h3>
|
||||
<h3> Version <b>1.2.1.2 <span class="gray"> May 2025, this release</span></b></h3>
|
||||
<ul>
|
||||
<li><h3>exhaleApp: avoid out of bounds AU writeouts, fix 5.1 WAV/MPEG channel mapping</h3></li>
|
||||
<li><h3>exhaleLib: relax max. quantized spectral magnitude restriction<sup>1</sup>, fix MSE test code</h3></li>
|
||||
<li><h3>exhaleLib: clean quantizer+entropy coder, increase max. IPF AU size to 12 kbit/ch</h3></li>
|
||||
<li><h3>exhaleLib: fix more high-rate efficiency issues, fix mode-f stereo coding (issue 33)</h3></li>
|
||||
</ul>
|
||||
<h4><span class="gray">1: optional, activated via define SFB_QUANT_PERCEPT_OPT = 1 or 0, triggers FFmpeg decoder bug!</span></h4>
|
||||
<h3> Version <b>1.2.1 <span class="gray"> Dec. 2023</span></b></h3>
|
||||
<ul>
|
||||
<li><h3>exhaleApp: always determine MPEG-4 instantaneous bit-rate across 2048 samples</h3></li>
|
||||
<li><h3>exhaleLib: added code for MSE optimized encoding (for tests, disabled by default)</h3></li>
|
||||
@ -177,11 +185,10 @@
|
||||
<h3><br><b>Roadmap of Upcoming Features</b></h3>
|
||||
<h3> If you are in need of an additional library or application feature <b>not</b> mentioned below, please contact ecodis or a contributor with a request, and we will see what we can do.</h3>
|
||||
<ul>
|
||||
<li><h3>exhaleLib: finalization of support for 5.1–7.1 multichannel coding, no version plan</h3></li>
|
||||
<li><h3>exhaleLib: speed-ups and further quality tuning for difficult signals, as necessary.</h3></li>
|
||||
</ul>
|
||||
<h3><br></h3>
|
||||
<h4><span class="gray">Written by C. R. Helmrich for exhale 1.2.1, Dec. 2023. Available at www.ecodis.de/exhale/release.htm.</span><br><br></h4>
|
||||
<h4><span class="gray">Written by C. R. Helmrich for exhale 1.2.2, Dec. 2025. Available at www.ecodis.de/exhale/release.htm.</span><br><br></h4>
|
||||
|
||||
</td>
|
||||
<td valign="top" colspan="2">
|
||||
|
@ -294,7 +294,7 @@ static inline uint8_t brModeAndFsToMaxSfbShort(const unsigned bitRateMode, const
|
||||
return (samplingRate > 51200 ? 11 : 13) - 2 + (bitRateMode >> 2);
|
||||
}
|
||||
|
||||
#if SFB_QUANT_PERCEPT_OPT && !EE_MORE_MSE
|
||||
#if !EE_MORE_MSE
|
||||
static inline void findActualBandwidthShort (uint8_t* const maxSfbShort, const uint16_t* sfbOffsets,
|
||||
const int32_t* mdctSignals, const int32_t* mdstSignals, const unsigned nSamplesInShort)
|
||||
{
|
||||
@ -322,7 +322,7 @@ static inline void findActualBandwidthShort (uint8_t* const maxSfbShort, const u
|
||||
maxAbs = __max (maxAbs, abs (mdctSignals[e + w * nSamplesInShort]));
|
||||
}
|
||||
}
|
||||
if (maxAbs > maxSfb * (SA_EPS >> 1)) break;
|
||||
if (maxAbs > maxSfb * (SA_EPS >> (3 - SFB_QUANT_PERCEPT_OPT))) break;
|
||||
|
||||
if (e == sfbOffs) sfbOffs = sfbOffsets[(--maxSfb) - 1];
|
||||
}
|
||||
@ -761,9 +761,9 @@ uint32_t ExhaleEncoder::getThr (const unsigned channelIndex, const unsigned sfbI
|
||||
uint32_t sumSfbLoud = 0;
|
||||
|
||||
for (int16_t s = 31; s >= 0; s--) sumSfbLoud += sfbLoudMem[s];
|
||||
sumSfbLoud = (sumSfbLoud + 32) >> 6; // -6 dB
|
||||
sumSfbLoud = (sumSfbLoud + 16) >> 5;
|
||||
|
||||
return sumSfbLoud * (sumSfbLoud >> (toSamplingRate (m_frequencyIdx) >> 13)); // scaled SMR
|
||||
return (sumSfbLoud * sumSfbLoud + 3) >> 2;
|
||||
}
|
||||
|
||||
unsigned ExhaleEncoder::psychBitAllocation () // perceptual bit-allocation via scale factors
|
||||
@ -776,7 +776,7 @@ unsigned ExhaleEncoder::psychBitAllocation () // perceptual bit-allocation via s
|
||||
const uint8_t maxSfbLong = (useMaxBandwidth ? m_numSwbLong : brModeAndFsToMaxSfbLong (m_bitRateMode, samplingRate));
|
||||
const uint16_t scaleSBR = (m_shiftValSBR > 0 || m_nonMpegExt ? sbrRateOffset[m_bitRateMode] : 0); // -25% rate
|
||||
const uint64_t scaleSr = (samplingRate < 27713 ? (samplingRate < 23004 ? 32 : 34) - __min (3 << m_shiftValSBR, m_bitRateMode)
|
||||
: (m_bitRateMode != 3 && samplingRate < 37566 ? 36 : 37)) - (nChannels >> 1) - 4 + 4 * ((SFB_QUANT_PERCEPT_OPT + 1) / 2);
|
||||
: (m_bitRateMode != 3 && samplingRate < 37566 ? 36 : 37) - 4 + 4 * ((SFB_QUANT_PERCEPT_OPT + 1) / 2)) - (nChannels >> 1);
|
||||
const uint64_t scaleBr = (m_bitRateMode == 0 || m_frameCount <= 1 ? __min (32, 17u + (((samplingRate + (1 << 11)) >> 12) << 1) - (nChannels >> 1))
|
||||
: scaleSr - eightTimesSqrt256Minus[256 - m_bitRateMode] - __min (3, (m_bitRateMode - 1) >> 1)) + scaleSBR;
|
||||
uint32_t* sfbStepSizes = (uint32_t*) m_tempIntBuf;
|
||||
@ -975,7 +975,7 @@ unsigned ExhaleEncoder::psychBitAllocation () // perceptual bit-allocation via s
|
||||
}
|
||||
if (grpOff[maxSfbCh] > grpOff[0])
|
||||
{
|
||||
s = BA_EPS + unsigned ((s * (eightShorts ? 1u + 55u/grpData.windowGroupLength[gr] : 7u) + 16383u) >> 14);
|
||||
s = BA_EPS + unsigned ((s * (eightShorts ? 1u + 55u / grpData.windowGroupLength[gr] : 7u) + 16383u) >> 14);
|
||||
# ifndef NO_PREROLL_DATA
|
||||
if (((m_frameCount - 1u) % (m_indepPeriod << 1)) == 1 && m_numElements == 1 && !eightShorts) s = (4u + 9u * s) >> 3;
|
||||
# endif
|
||||
@ -1013,6 +1013,52 @@ unsigned ExhaleEncoder::psychBitAllocation () // perceptual bit-allocation via s
|
||||
#endif
|
||||
grpScaleFacs[b] = m_bitAllocator.getScaleFac (grpStepSizes[b], &m_mdctSignals[ci][grpOff[b]], sfbWidth, grpRms[b]);
|
||||
}
|
||||
#if !SFB_QUANT_PERCEPT_OPT
|
||||
if (m_bitRateMode > 0 && m_numElements == 1)
|
||||
{
|
||||
if (maxSfbCh < (samplingRate < 18783 ? 17 : 24) || !m_frameCount)
|
||||
{
|
||||
for (s = 0; s < 26; s++) m_sfbLoudMem[ch][s][m_frameCount & 31] = uint16_t (m_frameCount ? sqrt ((double) getThr (ch, s)) : 32);
|
||||
}
|
||||
else // limit step-sizes around background noise level
|
||||
{
|
||||
const uint8_t* sd = coreConfig.stereoDataCurr;
|
||||
const unsigned ns = __max (samplingRate < 27713 ? (samplingRate < 18783 ? 17 : 24) : 22, m_specGapFiller.getFirstGapFillSfb ());
|
||||
uint32_t minFrRMS = TA_EPS >> EE_MORE_MSE;
|
||||
|
||||
for (s = ns; s < __min (ns + 26u, maxSfbCh); s++)
|
||||
{
|
||||
uint16_t* const lm = m_sfbLoudMem[ch][s - ns];
|
||||
uint32_t minSfbMem = INT_MAX;
|
||||
|
||||
lm[m_frameCount & 31] = __max (BA_EPS, uint16_t (sqrt (double (sd[s] ? __max (grpRms[s], coreConfig.groupingData[1 - ch].sfbRmsValues[s]) : grpRms[s]))));
|
||||
|
||||
for (int f = 0; f < 32; f++)
|
||||
{
|
||||
const uint32_t slm = (uint32_t) lm[f] * lm[f];
|
||||
|
||||
if (minSfbMem > slm && slm > BA_EPS) minSfbMem = slm;
|
||||
}
|
||||
if (minSfbMem < INT_MAX)
|
||||
{
|
||||
const uint32_t w = grpOff[s + 1] - grpOff[s];
|
||||
|
||||
minSfbMem = (((minSfbMem + 2u) >> 2) + getThr (ch, s - ns) + w - 1u) / w;
|
||||
if (minFrRMS > minSfbMem) minFrRMS = minSfbMem;
|
||||
}
|
||||
}
|
||||
for (s -= ns; s < 26; s++) m_sfbLoudMem[ch][s][m_frameCount & 31] = BA_EPS;
|
||||
|
||||
for (s = (minFrRMS < (TA_EPS >> EE_MORE_MSE) ? ns : 99); s < maxSfbCh; s++)
|
||||
{
|
||||
const uint32_t w = grpOff[s + 1] - grpOff[s];
|
||||
|
||||
if (stepSizes[s] < minFrRMS * (w >> (sd[s] ? 1 : 0)))
|
||||
grpScaleFacs[s] = m_bitAllocator.getScaleFac (minFrRMS * (w >> (sd[s] ? 1 : 0)), &m_mdctSignals[ci][grpOff[s]], w, grpRms[s]);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
} // for gr
|
||||
|
||||
#if !RESTRICT_TO_AAC
|
||||
@ -1026,7 +1072,7 @@ unsigned ExhaleEncoder::psychBitAllocation () // perceptual bit-allocation via s
|
||||
|
||||
if ((m_bitRateMode == 0) && (m_numElements == 1) && (samplingRate < 27713) && eightShorts)
|
||||
{
|
||||
for (s = 0; s < 26; s++) m_sfbLoudMem[ch][s][m_frameCount & 31] = uint16_t (sqrt (double (getThr (ch, s) << (samplingRate >> 13))));
|
||||
for (s = 0; s < 26; s++) m_sfbLoudMem[ch][s][m_frameCount & 31] = uint16_t (sqrt ((double) getThr (ch, s)));
|
||||
}
|
||||
if ((maxSfbCh < numSwbFrame) || (m_bitRateMode <= 2)) // increase coding bandwidth
|
||||
{
|
||||
@ -1046,7 +1092,7 @@ unsigned ExhaleEncoder::psychBitAllocation () // perceptual bit-allocation via s
|
||||
const unsigned sfbIdx = s - sfbStart;
|
||||
|
||||
m_sfbLoudMem[ch][sfbIdx][m_frameCount & 31] = __max (BA_EPS, uint16_t (sqrt (rmsValue)));
|
||||
if (grpRms[s] < getThr (ch, sfbIdx)) grpData.scaleFactors[s + m_numSwbShort * gr] = 0;
|
||||
if (grpRms[s] < (getThr (ch, sfbIdx) >> (samplingRate >> 13))) grpData.scaleFactors[s + m_numSwbShort * gr] = 0;
|
||||
}
|
||||
}
|
||||
else if ((m_bitRateMode <= 4) && (meanSpecFlat[ci] <= (SCHAR_MAX >> 1))) // lo
|
||||
@ -1424,7 +1470,7 @@ unsigned ExhaleEncoder::spectralProcessing () // complete ics_info(), calc TNS
|
||||
m_specAnalyzer.getSpectralBandwidth (m_bandwidPrev, nChannels);
|
||||
|
||||
// spectral analysis for current MCLT signal (windowed time-samples for the current frame)
|
||||
errorValue |= m_specAnalyzer.spectralAnalysis (m_mdctSignals, m_mdstSignals, nChannels, nSamplesInFrame, samplingRate, lfeChannelIndex);
|
||||
errorValue |= m_specAnalyzer.spectralAnalysis (m_mdctSignals, m_mdstSignals, nChannels, nSamplesInFrame, samplingRate, lfeChannelIndex, SFB_QUANT_PERCEPT_OPT);
|
||||
|
||||
// get spectral channel statistics for this frame, used for perceptual model & BW detector
|
||||
m_specAnalyzer.getSpecAnalysisStats (m_specAnaCurr, nChannels);
|
||||
@ -1518,7 +1564,7 @@ unsigned ExhaleEncoder::spectralProcessing () // complete ics_info(), calc TNS
|
||||
}
|
||||
icsCurr.maxSfb = __min (icsCurr.maxSfb, brModeAndFsToMaxSfbLong (m_bitRateMode, samplingRate));
|
||||
}
|
||||
#if SFB_QUANT_PERCEPT_OPT && !EE_MORE_MSE
|
||||
#if !EE_MORE_MSE
|
||||
while (grpSO[icsCurr.maxSfb] > __max (m_bandwidCurr[ci], m_bandwidPrev[ci]) + (icsCurr.maxSfb >> 1)) icsCurr.maxSfb--; // detect BW
|
||||
#endif
|
||||
}
|
||||
@ -1551,7 +1597,7 @@ unsigned ExhaleEncoder::spectralProcessing () // complete ics_info(), calc TNS
|
||||
}
|
||||
}
|
||||
memcpy (grpData.windowGroupLength, windowGroupingTable[icsCurr.windowGrouping], NUM_WINDOW_GROUPS * sizeof (uint8_t));
|
||||
#if SFB_QUANT_PERCEPT_OPT && !EE_MORE_MSE
|
||||
#if !EE_MORE_MSE
|
||||
findActualBandwidthShort (&icsCurr.maxSfb, grpSO, m_mdctSignals[ci], nChannels < 2 ? nullptr : m_mdstSignals[ci], nSamplesInShort);
|
||||
#endif
|
||||
errorValue |= eightShortGrouping (grpData, grpSO, m_mdctSignals[ci], nChannels < 2 ? nullptr : m_mdstSignals[ci]);
|
||||
@ -2190,7 +2236,7 @@ unsigned ExhaleEncoder::initEncoder (unsigned char* const audioConfigBuffer, uin
|
||||
m_elementData[el]->elementType = elementTypeConfig[chConf][el]; // usacElementType[el]
|
||||
}
|
||||
}
|
||||
memset (m_sfbLoudMem, 1, 2 * 26 * 32 * sizeof (uint16_t));
|
||||
memset (m_sfbLoudMem, SFB_QUANT_PERCEPT_OPT, 2 * 26 * 32 * sizeof (uint16_t));
|
||||
|
||||
// allocate all signal buffers
|
||||
if (m_shiftValSBR > 0)
|
||||
|
@ -21,7 +21,7 @@
|
||||
#define SF_THRESH_NEG 0.92044821 // round -1.5 dB
|
||||
#define SF_THRESH_POS 1.09460356 // round +1.5 dB
|
||||
#define SFB_QUANT_OFFSET 0.496094 // 13 - 29^(3/4)
|
||||
#define SFB_QUANT_PERCEPT_OPT 1 // psych. quant.
|
||||
#define SFB_QUANT_PERCEPT_OPT 2 // psych. quant.
|
||||
#define QUANT_MAX 85 + (170 >> SFB_QUANT_PERCEPT_OPT)
|
||||
|
||||
// class for BL USAC quantization
|
||||
|
@ -1,11 +1,11 @@
|
||||
/* specAnalysis.cpp - source file for class providing spectral analysis of MCLT signals
|
||||
* written by C. R. Helmrich, last modified in 2021 - see License.htm for legal notices
|
||||
* written by C. R. Helmrich, last modified in 2025 - 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-2025 Christian R. Helmrich, project ecodis. All rights reserved.
|
||||
*/
|
||||
|
||||
#include "exhaleLibPch.h"
|
||||
@ -228,11 +228,11 @@ unsigned SpecAnalyzer::optimizeGrouping (const unsigned channelIndex, const unsi
|
||||
unsigned SpecAnalyzer::spectralAnalysis (const int32_t* const mdctSignals[USAC_MAX_NUM_CHANNELS],
|
||||
const int32_t* const mdstSignals[USAC_MAX_NUM_CHANNELS],
|
||||
const unsigned nChannels, const unsigned nSamplesInFrame, const unsigned samplingRate,
|
||||
const unsigned lfeChannelIndex /*= USAC_MAX_NUM_CHANNELS*/) // to skip an LFE channel
|
||||
const unsigned lfeChannelIndex /*= USAC_MAX_NUM_CHANNELS*/, const unsigned scale /*= 2*/)
|
||||
{
|
||||
const uint64_t anaBwOffset = SA_BW >> 1;
|
||||
const unsigned lpcStopBand16k = (samplingRate <= 32000 ? nSamplesInFrame : (32000 * nSamplesInFrame) / samplingRate) >> SA_BW_SHIFT;
|
||||
const unsigned thresholdSlope = (48000 + SA_EPS * samplingRate) / 96000;
|
||||
const unsigned thresholdSlope = (96000 + SA_EPS * scale * samplingRate) / 192000;
|
||||
const unsigned thresholdStart = samplingRate >> 15;
|
||||
|
||||
if ((mdctSignals == nullptr) || (mdstSignals == nullptr) || (nChannels > USAC_MAX_NUM_CHANNELS) || (lfeChannelIndex > USAC_MAX_NUM_CHANNELS) ||
|
||||
|
@ -1,11 +1,11 @@
|
||||
/* specAnalysis.h - header file for class providing spectral analysis of MCLT signals
|
||||
* written by C. R. Helmrich, last modified in 2021 - see License.htm for legal notices
|
||||
* written by C. R. Helmrich, last modified in 2025 - 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-2025 Christian R. Helmrich, project ecodis. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef _SPEC_ANALYSIS_H_
|
||||
@ -54,7 +54,7 @@ public:
|
||||
unsigned spectralAnalysis (const int32_t* const mdctSignals[USAC_MAX_NUM_CHANNELS],
|
||||
const int32_t* const mdstSignals[USAC_MAX_NUM_CHANNELS],
|
||||
const unsigned nChannels, const unsigned nSamplesInFrame, const unsigned samplingRate,
|
||||
const unsigned lfeChannelIndex = USAC_MAX_NUM_CHANNELS); // to skip an LFE channel
|
||||
const unsigned lfeChannelIndex = USAC_MAX_NUM_CHANNELS, const unsigned scale = 2);
|
||||
int16_t stereoSigAnalysis (const int32_t* const mdctSignal1, const int32_t* const mdctSignal2,
|
||||
const int32_t* const mdstSignal1, const int32_t* const mdstSignal2,
|
||||
const unsigned nSamplesMax, const unsigned nSamplesInFrame, const bool shortTransforms,
|
||||
|
Reference in New Issue
Block a user