mirror of
https://gitlab.com/ecodis/exhale.git
synced 2025-06-05 21:59:32 +02:00
add source code
This commit is contained in:
38
exhale_vs2012.sln
Normal file
38
exhale_vs2012.sln
Normal file
@ -0,0 +1,38 @@
|
||||
Microsoft Visual Studio Solution File, Format Version 11.00
|
||||
# Visual Studio 2012
|
||||
Project("{EC0D1500-2018-1700-4865-6C6D72696368}") = "exhaleLib", "src\lib\exhaleLib_vs2012.vcxproj", "{EC0D1501-2018-1700-4865-6C6D72696368}"
|
||||
EndProject
|
||||
Project("{EC0D1500-2018-1700-4865-6C6D72696368}") = "exhaleApp", "src\app\exhaleApp_vs2012.vcxproj", "{EC0D1502-2018-1700-4865-6C6D72696368}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{EC0D1501-2018-1700-4865-6C6D72696368} = {EC0D1501-2018-1700-4865-6C6D72696368}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Win32 = Debug|Win32
|
||||
Debug|x64 = Debug|x64
|
||||
Release|Win32 = Release|Win32
|
||||
Release|x64 = Release|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{EC0D1501-2018-1700-4865-6C6D72696368}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{EC0D1501-2018-1700-4865-6C6D72696368}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{EC0D1501-2018-1700-4865-6C6D72696368}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{EC0D1501-2018-1700-4865-6C6D72696368}.Debug|x64.Build.0 = Debug|x64
|
||||
{EC0D1501-2018-1700-4865-6C6D72696368}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{EC0D1501-2018-1700-4865-6C6D72696368}.Release|Win32.Build.0 = Release|Win32
|
||||
{EC0D1501-2018-1700-4865-6C6D72696368}.Release|x64.ActiveCfg = Release|x64
|
||||
{EC0D1501-2018-1700-4865-6C6D72696368}.Release|x64.Build.0 = Release|x64
|
||||
{EC0D1502-2018-1700-4865-6C6D72696368}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{EC0D1502-2018-1700-4865-6C6D72696368}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{EC0D1502-2018-1700-4865-6C6D72696368}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{EC0D1502-2018-1700-4865-6C6D72696368}.Debug|x64.Build.0 = Debug|x64
|
||||
{EC0D1502-2018-1700-4865-6C6D72696368}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{EC0D1502-2018-1700-4865-6C6D72696368}.Release|Win32.Build.0 = Release|Win32
|
||||
{EC0D1502-2018-1700-4865-6C6D72696368}.Release|x64.ActiveCfg = Release|x64
|
||||
{EC0D1502-2018-1700-4865-6C6D72696368}.Release|x64.Build.0 = Release|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
53
include/License.htm
Normal file
53
include/License.htm
Normal file
@ -0,0 +1,53 @@
|
||||
<!-- www.ecodis.de/exhale/license.htm - created by Christian R. Helmrich - Copyright (c) 2018-2019 Christian Helmrich -->
|
||||
|
||||
<!doctype html public "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" type="text/css" href="styles.css">
|
||||
<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">
|
||||
<meta name="author" content="Dr.-Ing. Christian R. Helmrich">
|
||||
<meta name="description" content="exhale License Specification">
|
||||
<meta name="keywords" content="audio, data compression, perceptual coding, subjective evaluation, video">
|
||||
<title>ecodis :: exhale :: License Text</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<table align="center" cellpadding="0" cellspacing="0">
|
||||
<colgroup>
|
||||
<col width="2">
|
||||
<col width="60">
|
||||
<col width="595">
|
||||
<col width="60">
|
||||
<col width="2">
|
||||
</colgroup>
|
||||
<tr>
|
||||
<td colspan="2"></td>
|
||||
<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>referred to as «Software» below; additions to original BSD license text marked by [ ]</sup></span><br><br></h1>
|
||||
<h3> This Software is being made available and/or distributed under the following Modified <a href="http://www.linfo.org/bsdlicense.html">BSD</a>-style 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–2019 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 <span class="gray">[</span>of this Software<span class="gray">]</span> in source and binary forms, with or without modification, are permitted provided that<sub> </sub><span class="gray">[</span>all of<span class="gray">]</span><sub> </sub>the following<sub> </sub><span class="gray">[</span>four<span class="gray">]</span><sub> </sub>conditions are met:</h3>
|
||||
<ul>
|
||||
<li><h3>Redistributions of source code must retain the above copyright notice <span class="gray">[</span>in each file provided with the distribution<span class="gray">]</span>, this list of conditions, and the following disclaimer.</h3></li>
|
||||
<li><h3>Redistributions in binary form must reproduce the above copyright notice, this list of conditions, and the following disclaimer in the documentation and/or other files provided with the distribution.</h3></li>
|
||||
<li><h3><span class="gray">[</span>Redistributions of modified versions of this Software, whether in source or binary form, must reproduce a notice in each file provided with the distribution and affec­ted by the modification, documenting the name of the contributor having authored the modification and the year of the modification.<span class="gray">]</span></h3></li>
|
||||
<li><h3>Neither the name of the Licensor nor the names of the contributors may be used to endorse or promote products derived from this Software without specific prior written permission <span class="gray">[</span>from the Licensor<span class="gray">]</span>.</h3></li>
|
||||
</ul>
|
||||
<h3><br><b>Liability and Patent Disclaimer</b></h3>
|
||||
<h3> THIS SOFTWARE IS PROVIDED BY THE LICENSOR AND THE CONTRIBUTORS «<b>AS IS</b>» AND ANY EXPRESS OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PUR­POSE) ARE DISCLAIMED. IN NO EVENT SHALL THE LICENSOR OR ANY CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.</h3>
|
||||
<h3> <span class="gray">[</span>This Software may be subject to other third-party and/or contributor rights, including patent rights. In particular, this Software may implement methods and/or technologies required to perform in compliance with certain specifications of the ISO/IEC (MPEG-D) international standard <a href="https://www.iso.org/standard/76385.html">23003-3</a>, which may be subject to said other rights. NO express or implied licenses to any patent claims related to the use of this Software are granted under this license, and the Licensor provides NO WARRANTY of patent non-infringement with respect to this Software. Patent licenses required for the use of this Software to generate digital bit-streams according to any specifications of ISO/IEC 23003-3 may be obtained through <a href="https://www.via-corp.com/licensing/aac/">Via Licensing</a> and/or the corresponding patent holders individually.<span class="gray">]</span><br><br></h3>
|
||||
<h4><span class="gray">Written by C. R. Helmrich for exhale 1.0.0, Dec. 2019. Available at www.ecodis.de/exhale/license.htm.</span><br><br></h4>
|
||||
|
||||
</td>
|
||||
<td valign="top" colspan="2">
|
||||
<p><br> <img src="../src/app/exhaleApp.ico" alt="exhale" title="exhale" height="48" width="48"></p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<p></p>
|
||||
|
||||
</body>
|
||||
</html>
|
66
include/Release.htm
Normal file
66
include/Release.htm
Normal file
@ -0,0 +1,66 @@
|
||||
<!-- www.ecodis.de/exhale/release.htm - created by Christian R. Helmrich - Copyright (c) 2018-2019 Christian Helmrich -->
|
||||
|
||||
<!doctype html public "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" type="text/css" href="styles.css">
|
||||
<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">
|
||||
<meta name="author" content="Dr.-Ing. Christian R. Helmrich">
|
||||
<meta name="description" content="exhale Software Release Notes">
|
||||
<meta name="keywords" content="audio, data compression, perceptual coding, subjective evaluation, video">
|
||||
<title>ecodis :: exhale :: Release Notes</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<table align="center" cellpadding="0" cellspacing="0">
|
||||
<colgroup>
|
||||
<col width="2">
|
||||
<col width="60">
|
||||
<col width="595">
|
||||
<col width="60">
|
||||
<col width="2">
|
||||
</colgroup>
|
||||
<tr>
|
||||
<td colspan="2"></td>
|
||||
<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.0.0</b> (official pub­lic release candidate) from January 2020. Please check <a href="http://www.ecodis.de">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.0.0 <span class="gray"> Jan. 2020, this release</span></b></h3>
|
||||
<ul>
|
||||
<li><h3>compilation fixes and executable printout changes for Linux and MacOS™ platform</h3></li>
|
||||
</ul>
|
||||
<h3> Version <b>1.0RC <span class="gray">Dec. 2019</span></b></h3>
|
||||
<ul>
|
||||
<li><h3>initial release for testing with only basic channel-independent coding functionality</h3></li>
|
||||
<li><h3>only support for Microsoft Windows™ (32-bit and 64-bit) platform provided so far.</h3></li>
|
||||
</ul>
|
||||
<h3><br><b>Known Issues with This Release</b></h3>
|
||||
<h3> If you notice an issue with this release <b>not</b> mentioned below, please contact ecodis or a contributor with the details (configuration, input file) needed to reproduce the issue.</h3>
|
||||
<ul>
|
||||
<li><h3>exhaleLib: Coding of stereo or multichannel input yields suboptimal audio quality because the joint-channel coding functionality provided by the ISO/IEC <a href="https://www.iso.org/standard/76385.html">23003-3</a> standard has not been implemented so far. See the functionality roadmap below.</h3></li>
|
||||
<li><h3>exhaleApp: Only basic WAVE input file reading functionality has been implemen­ted. Specifically, 8-bit WAVE input is assumed to contain an even number of audio samples, and the Broadcast and Extensible WAVE file formats are not supported.</h3></li>
|
||||
</ul>
|
||||
<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>support for MPEG-D DRC-style peak-level and loudness metadata, no version plan</h3></li>
|
||||
<li><h3>support for compilation as dynamically linked library in Windows™, no version plan</h3></li>
|
||||
<li><h3>exhaleLib: quality tuning and bug fixing for low-bitrate mono coding, version 1.0.1</h3></li>
|
||||
<li><h3>exhaleLib: finalized integration of joint-channel coding functionality, version 1.0.2</h3></li>
|
||||
<li><h3>exhaleLib: quality tuning and bug fixing for low-rate stereo coding, version 1.0.3.</h3></li>
|
||||
</ul>
|
||||
<h3><br></h3>
|
||||
<h4><span class="gray">Written by C. R. Helmrich for exhale 1.0.0, Dec. 2019. Available at www.ecodis.de/exhale/release.htm.</span><br><br></h4>
|
||||
|
||||
</td>
|
||||
<td valign="top" colspan="2">
|
||||
<p><br> <img src="../src/app/exhaleApp.ico" alt="exhale" title="exhale" height="48" width="48"></p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<p></p>
|
||||
|
||||
</body>
|
||||
</html>
|
22
include/exhaleDecl.h
Normal file
22
include/exhaleDecl.h
Normal file
@ -0,0 +1,22 @@
|
||||
/* exhaleDecl.h - header file with declarations for exhale DLL export under Windows
|
||||
* written by C. R. Helmrich, last modified in 2019 - see License.htm for legal notices
|
||||
*
|
||||
* The copyright in this software is being made available under a Modified BSD-Style 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-2020 Christian R. Helmrich, project ecodis. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef _EXHALE_DECL_H_
|
||||
#define _EXHALE_DECL_H_
|
||||
|
||||
#if defined (_WIN32) || defined (WIN32) || defined (_WIN64) || defined (WIN64)
|
||||
# ifdef EXHALE_DYN_LINK
|
||||
# define EXHALE_DECL __declspec (dllexport)
|
||||
# else
|
||||
# define EXHALE_DECL
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#endif // _EXHALE_DECL_H_
|
39
include/styles.css
Normal file
39
include/styles.css
Normal file
@ -0,0 +1,39 @@
|
||||
a:link, a:visited
|
||||
{ color:#36f; font-weight:bold; text-decoration:none; }
|
||||
a:active, a:focus, a:hover
|
||||
{ color:#f36; font-weight:bold; text-decoration:none; }
|
||||
a.current:link, a.current:visited
|
||||
{ background:#fff; color:#000; }
|
||||
|
||||
body
|
||||
{ margin:0px; text-align:center; }
|
||||
|
||||
h1
|
||||
{ color:#666; font-family:Segoe UI, Arial, sans-serif; font-size:13pt; font-weight:bold; }
|
||||
h2
|
||||
{ color:#36f; font-family:Segoe UI, Arial, sans-serif; font-size:13pt; font-weight:bold; }
|
||||
h3
|
||||
{ color:#000; font-family:Verdana, Arial, sans-serif; font-size:10pt; font-weight:normal; line-height:16pt; max-width:594px; text-align:justify; }
|
||||
h4
|
||||
{ color:#000; font-family:Verdana, Arial, sans-serif; font-size:8pt; font-weight:normal; line-height:14pt; max-width:594px; }
|
||||
|
||||
hr
|
||||
{ border:1px solid; color:#ccc; height:1px; }
|
||||
|
||||
img
|
||||
{ border-width:0px; }
|
||||
|
||||
td
|
||||
{ background:#fff; }
|
||||
|
||||
ul
|
||||
{ list-style:square; }
|
||||
|
||||
.block
|
||||
{ text-align:justify; }
|
||||
.center
|
||||
{ text-align:center; }
|
||||
.gray
|
||||
{ color:#ccc; }
|
||||
.pink
|
||||
{ color:#f36; }
|
19
include/version.h
Normal file
19
include/version.h
Normal file
@ -0,0 +1,19 @@
|
||||
/* version.h - header file with major and minor library version numbers as characters
|
||||
* written by C. R. Helmrich, last modified in 2019 - see License.htm for legal notices
|
||||
*
|
||||
* The copyright in this software is being made available under a Modified BSD-Style 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-2020 Christian R. Helmrich, project ecodis. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef EXHALELIB_VERSION_MAJOR
|
||||
# define EXHALELIB_VERSION_MAJOR "1"
|
||||
#endif
|
||||
#ifndef EXHALELIB_VERSION_MINOR
|
||||
# define EXHALELIB_VERSION_MINOR "0"
|
||||
#endif
|
||||
#ifndef EXHALELIB_VERSION_BUGFIX
|
||||
# define EXHALELIB_VERSION_BUGFIX "RC" // "RC" or ".0", ".1", ...
|
||||
#endif
|
30
makefile
Normal file
30
makefile
Normal file
@ -0,0 +1,30 @@
|
||||
## makefile - master user make-file for compiling exhale on Linux and MacOS platforms
|
||||
# written by C. R. Helmrich, last modified 2019 - see License.txt for legal notices
|
||||
#
|
||||
# The copyright in this software is being made available under a Modified BSD 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-2019 Christian R. Helmrich, project ecodis. All rights reserved.
|
||||
##
|
||||
|
||||
## BUILD32=1: compile for 32-bit platforms, BUILD32=0: compile for 64-bit platforms
|
||||
BUILD32?=0
|
||||
|
||||
export BUILD32
|
||||
|
||||
all:
|
||||
$(MAKE) -C src/lib MM32=$(BUILD32)
|
||||
$(MAKE) -C src/app MM32=$(BUILD32)
|
||||
|
||||
debug:
|
||||
$(MAKE) -C src/lib debug MM32=$(BUILD32)
|
||||
$(MAKE) -C src/app debug MM32=$(BUILD32)
|
||||
|
||||
release:
|
||||
$(MAKE) -C src/lib release MM32=$(BUILD32)
|
||||
$(MAKE) -C src/app release MM32=$(BUILD32)
|
||||
|
||||
clean:
|
||||
$(MAKE) -C src/lib clean MM32=$(BUILD32)
|
||||
$(MAKE) -C src/app clean MM32=$(BUILD32)
|
361
src/app/basicMP4Writer.cpp
Normal file
361
src/app/basicMP4Writer.cpp
Normal file
@ -0,0 +1,361 @@
|
||||
/* basicMP4Writer.cpp - source file for class with basic MPEG-4 file writing capability
|
||||
* written by C. R. Helmrich, last modified in 2019 - see License.htm for legal notices
|
||||
*
|
||||
* The copyright in this software is being made available under a Modified BSD-Style 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-2020 Christian R. Helmrich, project ecodis. All rights reserved.
|
||||
*/
|
||||
|
||||
#include "exhaleAppPch.h"
|
||||
#include "basicMP4Writer.h"
|
||||
|
||||
#if 0 // DEBUG
|
||||
static const uint8_t muLawHeader[44] = {
|
||||
0x52, 0x49, 0x46, 0x46, 0xF0, 0xFF, 0xFF, 0xFF, 0x57, 0x41, 0x56, 0x45, 0x66, 0x6D, 0x74, 0x20,
|
||||
0x10, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x08, 0x00, 0x64, 0x61, 0x74, 0x61, 0xF0, 0xFF, 0xFF, 0xFF
|
||||
};
|
||||
#endif
|
||||
|
||||
static const uint8_t staticHeaderTemplate[STAT_HEADER_SIZE] = {
|
||||
0x00, 0x00, 0x00, 0x18, 0x66, 0x74, 0x79, 0x70, 0x6D, 0x70, 0x34, 0x32, 0x00, 0x00, 0x00, 0x00, // ftyp
|
||||
0x6D, 0x70, 0x34, 0x32, 0x69, 0x73, 0x6F, 0x6D, 0x00, 0x00, MOOV_BSIZE, 0x6D, 0x6F, 0x6F, 0x76, // moov
|
||||
0x00, 0x00, 0x00, 0x6C, 0x6D, 0x76, 0x68, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mvhd
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
|
||||
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, // end atom 2.1 (mvhd)
|
||||
0x00, 0x00, 0x00, 0x18, 0x69, 0x6F, 0x64, 0x73, 0x00, 0x00, 0x00, 0x00, 0x10, 0x80, 0x80, 0x80, // iods
|
||||
0x07, 0x00, 0x4F, 0xFF, 0xFF, 0x49, 0xFF, 0xFF, 0x00, 0x00, TRAK_BSIZE, // end atom 2.2 (iods)
|
||||
0x74, 0x72, 0x61, 0x6B, 0x00, 0x00, 0x00, 0x5C, 0x74, 0x6B, 0x68, 0x64, 0x00, 0x00, 0x00, 0x07, // tkhd
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x24, 0x65, 0x64, 0x74, 0x73, 0x00, 0x00, 0x00, 0x1C, 0x65, 0x6C, 0x73, 0x74, // elst
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x01, 0x00, 0x00, 0x00, 0x00, MDIA_BSIZE, 0x6D, 0x64, 0x69, 0x61, 0x00, 0x00, 0x00, 0x20, // mdhd
|
||||
0x6D, 0x64, 0x68, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0xC4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24,
|
||||
0x68, 0x64, 0x6C, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x73, 0x6F, 0x75, 0x6E, // hdlr
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x72, 0x68, 0x00,
|
||||
0x00, 0x00, MINF_BSIZE, 0x6D, 0x69, 0x6E, 0x66, 0x00, 0x00, 0x00, 0x10, 0x73, 0x6D, 0x68, 0x64,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x64, 0x69, 0x6E, 0x66, // dinf
|
||||
0x00, 0x00, 0x00, 0x1C, 0x64, 0x72, 0x65, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
|
||||
0x00, 0x00, 0x00, 0x0C, 0x75, 0x72, 0x6C, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, STBL_BSIZE,
|
||||
0x73, 0x74, 0x62, 0x6C, 0x00, 0x00, 0x00, 0x20, 0x73, 0x74, 0x74, 0x73, 0x00, 0x00, 0x00, 0x00, // stts
|
||||
0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, STSD_BSIZE, 0x73, 0x74, 0x73, 0x64, 0x00, 0x00, 0x00, 0x00, // stsd
|
||||
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, MP4A_BSIZE, 0x6D, 0x70, 0x34, 0x61, 0x00, 0x00, 0x00, 0x00, // mp4a
|
||||
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ESDS_BSIZE, 0x65, 0x73, 0x64, 0x73, // esds
|
||||
0x00, 0x00, 0x00, 0x00, 0x03, 0x80, 0x80, 0x80, 0x25, 0x00, 0x00, 0x00, 0x04, 0x80, 0x80, 0x80, // tag4
|
||||
0x17, 0x40, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x80, // tag5
|
||||
0x80, 0x80, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00 // ASC continued in m_dynamicHeader if >5 bytes
|
||||
};
|
||||
|
||||
// static helper functions
|
||||
static uint32_t toBigEndian (const unsigned ui) // Motorola endianness
|
||||
{
|
||||
return ((ui & UCHAR_MAX) << 24) | (((ui >> 8) & UCHAR_MAX) << 16) | (((ui >> 16) & UCHAR_MAX) << 8) | ((ui >> 24) & UCHAR_MAX);
|
||||
}
|
||||
|
||||
static uint16_t toUShortValue (const uint8_t hiByte, const uint8_t loByte)
|
||||
{
|
||||
return ((uint16_t) hiByte << 8) | (uint16_t) loByte;
|
||||
}
|
||||
|
||||
// public functions
|
||||
int BasicMP4Writer::addFrameAU (const uint8_t* byteBuf, const uint32_t byteOffset, const uint32_t byteCount)
|
||||
{
|
||||
if ((m_fileHandle == -1) || (m_m4aMdatSize > 0xFFFFFFF0u - byteCount))
|
||||
{
|
||||
return 1; // invalid file handle or file getting too big
|
||||
}
|
||||
|
||||
// add frame byte-size, in Big Endian format, to frame size list (stsz)
|
||||
m_dynamicHeader.push_back ((byteCount >> 24) & UCHAR_MAX);
|
||||
m_dynamicHeader.push_back ((byteCount >> 16) & UCHAR_MAX);
|
||||
m_dynamicHeader.push_back ((byteCount >> 8) & UCHAR_MAX);
|
||||
m_dynamicHeader.push_back ( byteCount & UCHAR_MAX);
|
||||
|
||||
if (((m_frameCount++) % m_rndAccPeriod) == 0) // add RAP to list (stco)
|
||||
{
|
||||
m_rndAccOffsets.push_back (m_m4aMdatSize);
|
||||
}
|
||||
m_m4aMdatSize += byteCount;
|
||||
|
||||
return _WRITE (m_fileHandle, byteBuf, byteCount); // write access unit
|
||||
}
|
||||
|
||||
int BasicMP4Writer::finishFile (const unsigned avgBitrate, const unsigned maxBitrate, const uint32_t audioLength,
|
||||
const uint32_t modifTime /*= 0*/)
|
||||
{
|
||||
const unsigned numFramesFirstPeriod = __min (m_frameCount, m_rndAccPeriod);
|
||||
const unsigned numFramesFinalPeriod = (m_frameCount <= m_rndAccPeriod ? 0 : m_frameCount % m_rndAccPeriod);
|
||||
const unsigned numSamplesFinalFrame = (audioLength + m_pregapLength) % m_frameLength;
|
||||
const uint32_t stszAtomSize = STSX_BSIZE + 4 /*bytes for sampleSize*/ + m_frameCount * 4;
|
||||
const uint32_t stscAtomSize = STSX_BSIZE + (numFramesFinalPeriod == 0 ? 12 : 24);
|
||||
const uint32_t stcoAtomSize = STSX_BSIZE + (uint32_t) m_rndAccOffsets.size () * 4;
|
||||
const uint32_t stblIncrSize = m_ascSizeM5 + stszAtomSize + stscAtomSize + stcoAtomSize;
|
||||
const uint32_t moovAtomSize = toBigEndian (toUShortValue (MOOV_BSIZE) + stblIncrSize);
|
||||
const uint32_t trakAtomSize = toBigEndian (toUShortValue (TRAK_BSIZE) + stblIncrSize);
|
||||
const uint32_t mdiaAtomSize = toBigEndian (toUShortValue (MDIA_BSIZE) + stblIncrSize);
|
||||
const uint32_t minfAtomSize = toBigEndian (toUShortValue (MINF_BSIZE) + stblIncrSize);
|
||||
const uint32_t stblAtomSize = toBigEndian (toUShortValue (STBL_BSIZE) + stblIncrSize);
|
||||
const uint32_t numSamplesBE = toBigEndian (audioLength);
|
||||
const uint32_t timeStampBE = toBigEndian (modifTime);
|
||||
const uint32_t headerBytes = STAT_HEADER_SIZE + (uint32_t) m_dynamicHeader.size () + stscAtomSize + stcoAtomSize;
|
||||
uint32_t* const header4Byte = (uint32_t* const) m_staticHeader;
|
||||
int bytesWritten = 0;
|
||||
|
||||
if ((m_fileHandle == -1) || (m_m4aMdatSize > 0xFFFFFFF0u - headerBytes))
|
||||
{
|
||||
return 1; // invalid file handle or file getting too big
|
||||
}
|
||||
|
||||
// finish setup of fixed-length part of MPEG-4 file header
|
||||
if (modifTime > 0)
|
||||
{
|
||||
header4Byte[ 48>>2] = timeStampBE; // mvhd
|
||||
header4Byte[188>>2] = timeStampBE; // tkhd
|
||||
header4Byte[324>>2] = timeStampBE; // mdhd
|
||||
}
|
||||
header4Byte[ 24>>2] = moovAtomSize;
|
||||
header4Byte[ 56>>2] = numSamplesBE;
|
||||
header4Byte[164>>2] = trakAtomSize;
|
||||
header4Byte[200>>2] = numSamplesBE;
|
||||
header4Byte[300>>2] = mdiaAtomSize;
|
||||
header4Byte[332>>2] = toBigEndian (audioLength + m_pregapLength);
|
||||
header4Byte[376>>2] = minfAtomSize;
|
||||
header4Byte[288>>2] = numSamplesBE; // elst
|
||||
header4Byte[436>>2] = stblAtomSize;
|
||||
header4Byte[460>>2] = toBigEndian (m_frameCount - 1); // 2 entries used
|
||||
header4Byte[472>>2] = toBigEndian (numSamplesFinalFrame == 0 ? m_frameLength : numSamplesFinalFrame);
|
||||
|
||||
m_staticHeader[558] = ((maxBitrate >> 24) & UCHAR_MAX);
|
||||
m_staticHeader[559] = ((maxBitrate >> 16) & UCHAR_MAX);
|
||||
m_staticHeader[560] = ((maxBitrate >> 8) & UCHAR_MAX);
|
||||
m_staticHeader[561] = ( maxBitrate & UCHAR_MAX);
|
||||
m_staticHeader[562] = ((avgBitrate >> 24) & UCHAR_MAX);
|
||||
m_staticHeader[563] = ((avgBitrate >> 16) & UCHAR_MAX);
|
||||
m_staticHeader[564] = ((avgBitrate >> 8) & UCHAR_MAX);
|
||||
m_staticHeader[565] = ( avgBitrate & UCHAR_MAX);
|
||||
|
||||
// finish dynamically-sized 2nd part of MPEG-4 file header
|
||||
m_dynamicHeader.at (m_ascSizeM5 + 6) = ((stszAtomSize >> 24) & UCHAR_MAX);
|
||||
m_dynamicHeader.at (m_ascSizeM5 + 7) = ((stszAtomSize >> 16) & UCHAR_MAX);
|
||||
m_dynamicHeader.at (m_ascSizeM5 + 8) = ((stszAtomSize >> 8) & UCHAR_MAX);
|
||||
m_dynamicHeader.at (m_ascSizeM5 + 9) = ( stszAtomSize & UCHAR_MAX);
|
||||
m_dynamicHeader.at (m_ascSizeM5 + 22) = ((m_frameCount >> 24) & UCHAR_MAX);
|
||||
m_dynamicHeader.at (m_ascSizeM5 + 23) = ((m_frameCount >> 16) & UCHAR_MAX);
|
||||
m_dynamicHeader.at (m_ascSizeM5 + 24) = ((m_frameCount >> 8) & UCHAR_MAX);
|
||||
m_dynamicHeader.at (m_ascSizeM5 + 25) = ( m_frameCount & UCHAR_MAX);
|
||||
|
||||
m_dynamicHeader.push_back (0x00); m_dynamicHeader.push_back (0x00);
|
||||
m_dynamicHeader.push_back (0x00); m_dynamicHeader.push_back (stscAtomSize);
|
||||
m_dynamicHeader.push_back (0x73); m_dynamicHeader.push_back (0x74);
|
||||
m_dynamicHeader.push_back (0x73); m_dynamicHeader.push_back (0x63); // stsc
|
||||
m_dynamicHeader.push_back (0x00); m_dynamicHeader.push_back (0x00);
|
||||
m_dynamicHeader.push_back (0x00); m_dynamicHeader.push_back (0x00);
|
||||
m_dynamicHeader.push_back (0x00); m_dynamicHeader.push_back (0x00);
|
||||
m_dynamicHeader.push_back (0x00); m_dynamicHeader.push_back (numFramesFinalPeriod == 0 ? 1 : 2);
|
||||
m_dynamicHeader.push_back (0x00); m_dynamicHeader.push_back (0x00);
|
||||
m_dynamicHeader.push_back (0x00); m_dynamicHeader.push_back (0x01); // 1st
|
||||
m_dynamicHeader.push_back ((numFramesFirstPeriod >> 24) & UCHAR_MAX);
|
||||
m_dynamicHeader.push_back ((numFramesFirstPeriod >> 16) & UCHAR_MAX);
|
||||
m_dynamicHeader.push_back ((numFramesFirstPeriod >> 8) & UCHAR_MAX);
|
||||
m_dynamicHeader.push_back ( numFramesFirstPeriod & UCHAR_MAX);
|
||||
m_dynamicHeader.push_back (0x00); m_dynamicHeader.push_back (0x00);
|
||||
m_dynamicHeader.push_back (0x00); m_dynamicHeader.push_back (0x01); // idx
|
||||
|
||||
if (numFramesFinalPeriod > 0)
|
||||
{
|
||||
m_dynamicHeader.push_back ((m_rndAccOffsets.size () >> 24) & UCHAR_MAX);
|
||||
m_dynamicHeader.push_back ((m_rndAccOffsets.size () >> 16) & UCHAR_MAX);
|
||||
m_dynamicHeader.push_back ((m_rndAccOffsets.size () >> 8) & UCHAR_MAX);
|
||||
m_dynamicHeader.push_back ( m_rndAccOffsets.size () & UCHAR_MAX);
|
||||
m_dynamicHeader.push_back ((numFramesFinalPeriod >> 24) & UCHAR_MAX);
|
||||
m_dynamicHeader.push_back ((numFramesFinalPeriod >> 16) & UCHAR_MAX);
|
||||
m_dynamicHeader.push_back ((numFramesFinalPeriod >> 8) & UCHAR_MAX);
|
||||
m_dynamicHeader.push_back ( numFramesFinalPeriod & UCHAR_MAX);
|
||||
m_dynamicHeader.push_back (0x00); m_dynamicHeader.push_back (0x00);
|
||||
m_dynamicHeader.push_back (0x00); m_dynamicHeader.push_back (0x01);// idx
|
||||
}
|
||||
|
||||
m_dynamicHeader.push_back ((stcoAtomSize >> 24) & UCHAR_MAX);
|
||||
m_dynamicHeader.push_back ((stcoAtomSize >> 16) & UCHAR_MAX);
|
||||
m_dynamicHeader.push_back ((stcoAtomSize >> 8) & UCHAR_MAX);
|
||||
m_dynamicHeader.push_back ( stcoAtomSize & UCHAR_MAX);
|
||||
m_dynamicHeader.push_back (0x73); m_dynamicHeader.push_back (0x74);
|
||||
m_dynamicHeader.push_back (0x63); m_dynamicHeader.push_back (0x6F); // stco
|
||||
m_dynamicHeader.push_back (0x00); m_dynamicHeader.push_back (0x00);
|
||||
m_dynamicHeader.push_back (0x00); m_dynamicHeader.push_back (0x00);
|
||||
m_dynamicHeader.push_back ((m_rndAccOffsets.size () >> 24) & UCHAR_MAX);
|
||||
m_dynamicHeader.push_back ((m_rndAccOffsets.size () >> 16) & UCHAR_MAX);
|
||||
m_dynamicHeader.push_back ((m_rndAccOffsets.size () >> 8) & UCHAR_MAX);
|
||||
m_dynamicHeader.push_back ( m_rndAccOffsets.size () & UCHAR_MAX);
|
||||
|
||||
// add header size corrected random-access offsets to file
|
||||
for (unsigned i = 0; i < m_rndAccOffsets.size (); i++)
|
||||
{
|
||||
const uint32_t rndAccOffset = m_rndAccOffsets.at (i) + headerBytes;
|
||||
|
||||
m_dynamicHeader.push_back ((rndAccOffset >> 24) & UCHAR_MAX);
|
||||
m_dynamicHeader.push_back ((rndAccOffset >> 16) & UCHAR_MAX);
|
||||
m_dynamicHeader.push_back ((rndAccOffset >> 8) & UCHAR_MAX);
|
||||
m_dynamicHeader.push_back ( rndAccOffset & UCHAR_MAX);
|
||||
}
|
||||
m_dynamicHeader.push_back ((m_m4aMdatSize >> 24) & UCHAR_MAX);
|
||||
m_dynamicHeader.push_back ((m_m4aMdatSize >> 16) & UCHAR_MAX);
|
||||
m_dynamicHeader.push_back ((m_m4aMdatSize >> 8) & UCHAR_MAX);
|
||||
m_dynamicHeader.push_back ( m_m4aMdatSize & UCHAR_MAX);
|
||||
m_dynamicHeader.push_back (0x6D); m_dynamicHeader.push_back (0x64);
|
||||
m_dynamicHeader.push_back (0x61); m_dynamicHeader.push_back (0x74); // mdat
|
||||
|
||||
_SEEK (m_fileHandle, 0, 0 /*SEEK_SET*/); // back to start
|
||||
|
||||
bytesWritten += _WRITE (m_fileHandle, m_staticHeader, STAT_HEADER_SIZE);
|
||||
bytesWritten += _WRITE (m_fileHandle, &m_dynamicHeader.front (), (unsigned) m_dynamicHeader.size ());
|
||||
|
||||
return bytesWritten;
|
||||
}
|
||||
|
||||
int BasicMP4Writer::initHeader (const uint32_t audioLength) // reserve bytes for header in file
|
||||
{
|
||||
#if 0 // DEBUG
|
||||
const uint8_t numChannels = m_staticHeader[517];
|
||||
|
||||
// write basic <20>-Law WAVE header for testing
|
||||
memcpy (m_staticHeader, muLawHeader, 44 * sizeof (uint8_t));
|
||||
m_staticHeader[22] = numChannels;
|
||||
m_staticHeader[24] = uint8_t (m_sampleRate & 0xFF);
|
||||
m_staticHeader[25] = uint8_t (m_sampleRate >> 8u);
|
||||
m_staticHeader[26] = uint8_t (m_sampleRate >> 16u);
|
||||
m_staticHeader[28] = uint8_t ((m_sampleRate * numChannels) & 0xFF);
|
||||
m_staticHeader[29] = uint8_t ((m_sampleRate * numChannels) >> 8u);
|
||||
m_staticHeader[30] = uint8_t ((m_sampleRate * numChannels) >> 16u);
|
||||
m_staticHeader[32] = numChannels; // byte count per frame
|
||||
|
||||
return _WRITE (m_fileHandle, m_staticHeader, 44);
|
||||
#else
|
||||
const bool flushFrameUsed = ((audioLength + m_pregapLength) % m_frameLength) > 0;
|
||||
const unsigned frameCount = ((audioLength + m_frameLength - 1) / m_frameLength) + (flushFrameUsed ? 2 : 1);
|
||||
const unsigned chunkCount = ((frameCount + m_rndAccPeriod - 1) / m_rndAccPeriod);
|
||||
const unsigned finalChunk = (frameCount <= m_rndAccPeriod ? 0 : frameCount % m_rndAccPeriod);
|
||||
const int estimHeaderSize = STAT_HEADER_SIZE + m_ascSizeM5 + 6+4 + frameCount * 4 /*stsz*/ + STSX_BSIZE * 3 +
|
||||
(finalChunk == 0 ? 12 : 24) /*stsc*/ + chunkCount * 4 /*stco*/ + 8 /*mdat*/;
|
||||
int bytesWritten = 0;
|
||||
|
||||
for (int i = estimHeaderSize; i > 0; i -= STAT_HEADER_SIZE)
|
||||
{
|
||||
bytesWritten += _WRITE (m_fileHandle, m_staticHeader, __min (i, STAT_HEADER_SIZE));
|
||||
}
|
||||
return bytesWritten;
|
||||
#endif
|
||||
}
|
||||
|
||||
unsigned BasicMP4Writer::open (const int mp4FileHandle, const unsigned sampleRate, const unsigned numChannels,
|
||||
const unsigned bitDepth, const unsigned frameLength, const unsigned pregapLength,
|
||||
const unsigned raPeriod, const uint8_t* ascBuf, const unsigned ascSize,
|
||||
const uint32_t creatTime /*= 0*/, const char vbrQuality /*= 0*/)
|
||||
{
|
||||
const uint32_t frameSizeBE = toBigEndian (frameLength);
|
||||
const uint32_t pregapSizeBE = toBigEndian (pregapLength);
|
||||
const uint32_t sampleRateBE = toBigEndian (sampleRate);
|
||||
const uint32_t timeStampBE = toBigEndian (creatTime);
|
||||
uint32_t* const header4Byte = (uint32_t* const) m_staticHeader;
|
||||
|
||||
if ((mp4FileHandle == -1) || (frameLength == 0) || (sampleRate == 0) || (numChannels == 0) || (numChannels * 3 > UCHAR_MAX) ||
|
||||
(raPeriod == 0) || (ascBuf == nullptr) || (ascSize < 5) || (ascSize > 108) || (bitDepth == 0) || (bitDepth > UCHAR_MAX))
|
||||
{
|
||||
return 1; // invalid file handle or other input variable
|
||||
}
|
||||
|
||||
m_fileHandle = mp4FileHandle;
|
||||
reset (frameLength, pregapLength, raPeriod);
|
||||
#if 0 // DEBUG
|
||||
m_sampleRate = sampleRate;
|
||||
#endif
|
||||
// create fixed-length 576-byte part of MPEG-4 file header
|
||||
memcpy (m_staticHeader, staticHeaderTemplate, STAT_HEADER_SIZE * sizeof (uint8_t));
|
||||
|
||||
header4Byte[ 44>>2] = timeStampBE;
|
||||
header4Byte[ 48>>2] = timeStampBE;
|
||||
header4Byte[ 52>>2] = sampleRateBE;
|
||||
header4Byte[184>>2] = timeStampBE;
|
||||
header4Byte[188>>2] = timeStampBE;
|
||||
header4Byte[292>>2] = pregapSizeBE; // pregap size in elst
|
||||
header4Byte[320>>2] = timeStampBE;
|
||||
header4Byte[324>>2] = timeStampBE;
|
||||
header4Byte[328>>2] = sampleRateBE;
|
||||
header4Byte[332>>2] = pregapSizeBE; // +audio length later
|
||||
header4Byte[464>>2] = frameSizeBE;
|
||||
|
||||
m_staticHeader[339] = vbrQuality;
|
||||
m_staticHeader[517] = (uint8_t) numChannels;
|
||||
m_staticHeader[519] = (uint8_t) bitDepth;
|
||||
m_staticHeader[523] = (sampleRate >> 16) & UCHAR_MAX; // ?
|
||||
m_staticHeader[524] = (sampleRate >> 8) & UCHAR_MAX;
|
||||
m_staticHeader[525] = sampleRate & UCHAR_MAX;
|
||||
m_staticHeader[556] = (uint8_t) numChannels * 3; // 6144 bit/chan
|
||||
|
||||
memcpy (&m_staticHeader[571], ascBuf, 5 * sizeof (uint8_t));
|
||||
|
||||
if (ascSize > 5) // increase atom byte-sizes
|
||||
{
|
||||
const uint8_t inc = m_ascSizeM5 = ascSize - 5;
|
||||
|
||||
m_staticHeader[ 27] += inc; // MOOV_BSIZE
|
||||
m_staticHeader[167] += inc; // TRAK_BSIZE
|
||||
m_staticHeader[303] += inc; // MDIA_BSIZE
|
||||
if (m_staticHeader[379] + m_ascSizeM5 > UCHAR_MAX) m_staticHeader[378]++;
|
||||
m_staticHeader[379] += inc; // MINF_BSIZE
|
||||
m_staticHeader[439] += inc; // STBL_BSIZE
|
||||
m_staticHeader[479] += inc; // STSD_BSIZE
|
||||
m_staticHeader[495] += inc; // MP4A_BSIZE
|
||||
m_staticHeader[531] += inc; // ESDS_BSIZE
|
||||
m_staticHeader[544] += inc; // esds tag 3
|
||||
m_staticHeader[552] += inc; // esds tag 4
|
||||
m_staticHeader[570] += inc; // esds tag 5
|
||||
|
||||
for (unsigned i = 0; i < m_ascSizeM5; i++) m_dynamicHeader.push_back (ascBuf[5 + i]);
|
||||
}
|
||||
|
||||
// prepare variable-length remainder of MPEG-4 file header
|
||||
m_dynamicHeader.push_back (0x06); m_dynamicHeader.push_back (0x80); // esds
|
||||
m_dynamicHeader.push_back (0x80); m_dynamicHeader.push_back (0x80);
|
||||
m_dynamicHeader.push_back (0x01); m_dynamicHeader.push_back (0x02);
|
||||
m_dynamicHeader.push_back (0x00); m_dynamicHeader.push_back (0x00); // + 4N
|
||||
m_dynamicHeader.push_back (0x00); m_dynamicHeader.push_back (STSX_BSIZE + 4);
|
||||
m_dynamicHeader.push_back (0x73); m_dynamicHeader.push_back (0x74);
|
||||
m_dynamicHeader.push_back (0x73); m_dynamicHeader.push_back (0x7A); // stsz
|
||||
m_dynamicHeader.push_back (0x00); m_dynamicHeader.push_back (0x00);
|
||||
m_dynamicHeader.push_back (0x00); m_dynamicHeader.push_back (0x00);
|
||||
m_dynamicHeader.push_back (0x00); m_dynamicHeader.push_back (0x00);
|
||||
m_dynamicHeader.push_back (0x00); m_dynamicHeader.push_back (0x00);
|
||||
m_dynamicHeader.push_back (0x00); m_dynamicHeader.push_back (0x00); // := N
|
||||
m_dynamicHeader.push_back (0x00); m_dynamicHeader.push_back (0x00);
|
||||
|
||||
return 0; // correct operation
|
||||
}
|
||||
|
||||
void BasicMP4Writer::reset (const unsigned frameLength /*= 0*/, const unsigned pregapLength /*= 0*/, const unsigned raPeriod /*= 0*/)
|
||||
{
|
||||
m_ascSizeM5 = 0;
|
||||
m_frameCount = 0;
|
||||
m_frameLength = frameLength;
|
||||
m_m4aMdatSize = 8; // bytes for mdat header
|
||||
m_pregapLength = pregapLength;
|
||||
m_rndAccPeriod = raPeriod;
|
||||
m_sampleRate = 0;
|
||||
m_dynamicHeader.clear ();
|
||||
m_rndAccOffsets.clear ();
|
||||
|
||||
if (m_fileHandle != -1) _SEEK (m_fileHandle, 0, 0 /*SEEK_SET*/);
|
||||
}
|
65
src/app/basicMP4Writer.h
Normal file
65
src/app/basicMP4Writer.h
Normal file
@ -0,0 +1,65 @@
|
||||
/* basicMP4Writer.h - header file for class with basic MPEG-4 file writing capability
|
||||
* written by C. R. Helmrich, last modified in 2019 - see License.htm for legal notices
|
||||
*
|
||||
* The copyright in this software is being made available under a Modified BSD-Style 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-2020 Christian R. Helmrich, project ecodis. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef _BASIC_MP4_WRITER_H_
|
||||
#define _BASIC_MP4_WRITER_H_
|
||||
|
||||
#include "exhaleAppPch.h"
|
||||
|
||||
// constant data sizes in bytes
|
||||
#define STAT_HEADER_SIZE 576
|
||||
#define STSX_BSIZE 0x10
|
||||
#define ESDS_BSIZE 0x00, 0x36 // esds: 54 (+ m_ascSizeM5 later)
|
||||
#define MP4A_BSIZE 0x00, 0x5A // mp4a: 36 + ESDS_BSIZE
|
||||
#define STSD_BSIZE 0x00, 0x6A // mp4a: 16 + MP4A_BSIZE
|
||||
#define STBL_BSIZE 0x00, 0x92 // stbl: 8 + 32 + STSD_BSIZE (+ rem later)
|
||||
#define MINF_BSIZE 0x00, 0xCE // minf: 8 + 16 + 36 + STBL_BSIZE
|
||||
#define MDIA_BSIZE 0x01, 0x1A // mdia: 8 + 32 + 36 + MINF_BSIZE
|
||||
#define TRAK_BSIZE 0x01, 0xA2 // trak: 8 + 92 + 36 + MDIA_BSIZE
|
||||
#define MOOV_BSIZE 0x02, 0x2E // moov: 8 +108 + 24 + TRAK_BSIZE
|
||||
|
||||
// basic MPEG-4 write-out class
|
||||
class BasicMP4Writer
|
||||
{
|
||||
private:
|
||||
|
||||
// member variables
|
||||
unsigned m_ascSizeM5; // ASC + UsacConfig byte-size - 5
|
||||
int m_fileHandle;
|
||||
unsigned m_frameCount;
|
||||
unsigned m_frameLength;
|
||||
unsigned m_m4aMdatSize;
|
||||
unsigned m_pregapLength; // encoder look-ahead, pre-roll
|
||||
unsigned m_rndAccPeriod; // random-access (RA) interval
|
||||
unsigned m_sampleRate;
|
||||
uint8_t m_staticHeader[STAT_HEADER_SIZE]; // fixed-size
|
||||
std::vector <uint8_t> m_dynamicHeader; // variable-sized
|
||||
std::vector <uint32_t> m_rndAccOffsets; // random access
|
||||
|
||||
public:
|
||||
|
||||
// constructor
|
||||
BasicMP4Writer () { m_fileHandle = -1; reset (); }
|
||||
// destructor
|
||||
~BasicMP4Writer() { m_dynamicHeader.clear (); m_rndAccOffsets.clear (); }
|
||||
// public functions
|
||||
int addFrameAU (const uint8_t* byteBuf, const uint32_t byteOffset, const uint32_t byteCount);
|
||||
int finishFile (const unsigned avgBitrate, const unsigned maxBitrate, const uint32_t audioLength,
|
||||
const uint32_t modifTime = 0);
|
||||
unsigned getFrameCount () const { return m_frameCount; }
|
||||
int initHeader (const uint32_t audioLength);
|
||||
unsigned open (const int mp4FileHandle, const unsigned sampleRate, const unsigned numChannels,
|
||||
const unsigned bitDepth, const unsigned frameLength, const unsigned pregapLength,
|
||||
const unsigned raPeriod, const uint8_t* ascBuf, const unsigned ascSize,
|
||||
const uint32_t creatTime = 0, const char vbrQuality = 0);
|
||||
void reset (const unsigned frameLength = 0, const unsigned pregapLength = 0, const unsigned raPeriod = 0);
|
||||
}; // BasicMP4Writer
|
||||
|
||||
#endif // _BASIC_MP4_WRITER_H_
|
415
src/app/basicWavReader.cpp
Normal file
415
src/app/basicWavReader.cpp
Normal file
@ -0,0 +1,415 @@
|
||||
/* basicWavReader.cpp - source file for class with basic WAVE file reading capability
|
||||
* written by C. R. Helmrich, last modified in 2019 - see License.htm for legal notices
|
||||
*
|
||||
* The copyright in this software is being made available under a Modified BSD-Style 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-2020 Christian R. Helmrich, project ecodis. All rights reserved.
|
||||
*/
|
||||
|
||||
#include "exhaleAppPch.h"
|
||||
#include "basicWavReader.h"
|
||||
|
||||
// static helper functions
|
||||
static unsigned reverseFourBytes (const uint8_t* b)
|
||||
{
|
||||
return ((unsigned) b[3] << 24) | ((unsigned) b[2] << 16) | ((unsigned) b[1] << 8) | (unsigned) b[0];
|
||||
}
|
||||
|
||||
static int64_t fourBytesToLength (const uint8_t* b, const int64_t lengthLimit)
|
||||
{
|
||||
int64_t chunkLength = (int64_t) reverseFourBytes (b);
|
||||
|
||||
chunkLength += chunkLength & 1; // make sure it is even
|
||||
|
||||
return __min (lengthLimit, chunkLength); // for security
|
||||
}
|
||||
|
||||
// private reader functions
|
||||
bool BasicWavReader::readRiffHeader ()
|
||||
{
|
||||
uint8_t b[FILE_HEADER_SIZE] = {0}; // temp. byte buffer
|
||||
|
||||
if ((m_bytesRead = _READ (m_fileHandle, b, FILE_HEADER_SIZE)) != FILE_HEADER_SIZE) return false; // error
|
||||
m_bytesRemaining -= m_bytesRead;
|
||||
m_chunkLength = fourBytesToLength (&b[4], m_bytesRemaining) - 4; // minus 4 bytes for WAVE tag
|
||||
|
||||
return (b[0] == 'R' && b[1] == 'I' && b[2] == 'F' && b[3] == 'F' &&
|
||||
b[8] == 'W' && b[9] == 'A' && b[10]== 'V' && b[11]== 'E' &&
|
||||
m_bytesRemaining > 32); // true: RIFF supported
|
||||
}
|
||||
|
||||
bool BasicWavReader::readFormatChunk ()
|
||||
{
|
||||
uint8_t b[CHUNK_FORMAT_MAX] = {0}; // temp. byte buffer
|
||||
|
||||
if (!seekToChunkTag (b, 0x20746D66 /*fmt */) || (m_chunkLength < CHUNK_FORMAT_SIZE) || (m_chunkLength > CHUNK_FORMAT_MAX))
|
||||
{
|
||||
return false; // fmt_ chunk invalid or read incomplete
|
||||
}
|
||||
if ((m_bytesRead = _READ (m_fileHandle, b, (unsigned) m_chunkLength)) != m_chunkLength) return false; // error
|
||||
m_bytesRemaining -= m_bytesRead;
|
||||
|
||||
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
|
||||
m_waveBitDepth = b[14]; // only 8, 16, 24, 32 supported
|
||||
|
||||
return ((m_waveDataType == WAV_PCM || (m_waveDataType == WAV_FLOAT && (m_waveBitDepth & 15) == 0)) &&
|
||||
(m_waveChannels > 0 && m_waveChannels <= 63) && isSamplingRateSupported (m_waveFrameRate) &&
|
||||
(m_waveBitRate == 8 * m_waveFrameRate * m_waveFrameSize) && (b[ 1] == 0) && (b[ 3] == 0) &&
|
||||
(m_waveFrameSize * 8 == m_waveBitDepth * m_waveChannels) && (b[13] == 0) && (b[15] == 0) &&
|
||||
(m_waveBitDepth >= 8 && m_waveBitDepth <= 32 && (m_waveBitDepth & 7) == 0) &&
|
||||
m_bytesRemaining > 8); // true: format supported
|
||||
}
|
||||
|
||||
bool BasicWavReader::readDataHeader ()
|
||||
{
|
||||
uint8_t b[CHUNK_HEADER_SIZE] = {0}; // temp. byte buffer
|
||||
|
||||
if (!seekToChunkTag (b, 0x61746164 /*data*/))
|
||||
{
|
||||
return false; // data chunk invalid or read incomplete
|
||||
}
|
||||
return (m_chunkLength > 0); // true: WAVE data available
|
||||
}
|
||||
|
||||
// private helper function
|
||||
bool BasicWavReader::seekToChunkTag (uint8_t* const buf, const uint32_t tagID)
|
||||
{
|
||||
if ((m_bytesRead = _READ (m_fileHandle, buf, CHUNK_HEADER_SIZE)) != CHUNK_HEADER_SIZE) return false; // error
|
||||
m_bytesRemaining -= m_bytesRead;
|
||||
m_chunkLength = fourBytesToLength (&buf[4], m_bytesRemaining);
|
||||
|
||||
while ((*((uint32_t* const) buf) != tagID) &&
|
||||
(m_bytesRemaining > 0)) // seek until tagID found
|
||||
{
|
||||
if ((m_readOffset = _SEEK (m_fileHandle, m_chunkLength, 1 /*SEEK_CUR*/)) == -1)
|
||||
{
|
||||
// for stdin compatibility, don't abort, try reading
|
||||
for (int64_t i = m_chunkLength >> 1; i > 0; i--)
|
||||
{
|
||||
_READ (m_fileHandle, buf, 2); // as length is even
|
||||
}
|
||||
}
|
||||
m_bytesRemaining -= m_chunkLength;
|
||||
if (m_bytesRemaining <= 0)
|
||||
{
|
||||
return false; // an error which should never happen!
|
||||
}
|
||||
if ((m_bytesRead = _READ (m_fileHandle, buf, CHUNK_HEADER_SIZE)) != CHUNK_HEADER_SIZE) return false; // error
|
||||
m_bytesRemaining -= m_bytesRead;
|
||||
m_chunkLength = fourBytesToLength (&buf[4], m_bytesRemaining);
|
||||
}
|
||||
return (m_bytesRemaining > 0);
|
||||
}
|
||||
|
||||
// static reading functions
|
||||
unsigned BasicWavReader::readDataFloat16 (const int fileHandle, int32_t* frameBuf, const unsigned frameCount,
|
||||
const unsigned chanCount, void* tempBuf)
|
||||
{
|
||||
#if BWR_BUFFERED_READ
|
||||
const int16_t* fBuf = (const int16_t*) tempBuf; // words
|
||||
const int bytesRead = _READ (fileHandle, tempBuf, frameCount * chanCount * 2);
|
||||
unsigned framesRead = __max (0, bytesRead / (chanCount * 2));
|
||||
|
||||
for (unsigned i = framesRead * chanCount; i > 0; i--)
|
||||
{
|
||||
const int16_t i16 = *(fBuf++);
|
||||
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++;
|
||||
}
|
||||
if (framesRead < frameCount) // zero out missing samples
|
||||
{
|
||||
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); // two bytes
|
||||
|
||||
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 float* fBuf = (const float*) tempBuf; // 4 bytes
|
||||
const int bytesRead = _READ (fileHandle, tempBuf, frameCount * chanCount * 4);
|
||||
unsigned framesRead = __max (0, bytesRead / (chanCount * 4));
|
||||
|
||||
for (unsigned i = framesRead * chanCount; i > 0; i--)
|
||||
{
|
||||
const float f32 = *fBuf * float (1 << 23); // * 2^23
|
||||
fBuf++;
|
||||
*frameBuf = int32_t (f32 + (f32 < 0.0 ? -0.5 : 0.5)); // rounding
|
||||
if (*frameBuf < MIN_VALUE_AUDIO24) *frameBuf = MIN_VALUE_AUDIO24;
|
||||
else
|
||||
if (*frameBuf > MAX_VALUE_AUDIO24) *frameBuf = MAX_VALUE_AUDIO24;
|
||||
frameBuf++;
|
||||
}
|
||||
if (framesRead < frameCount) // zero out missing samples
|
||||
{
|
||||
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; // IEEE-754 normalized floating point
|
||||
|
||||
bytesRead += _READ (fileHandle, &f32, 4); // 4 bytes
|
||||
*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 uint8_t* iBuf = (uint8_t*) tempBuf;
|
||||
const int bytesRead = _READ (fileHandle, tempBuf, frameCount * chanCount);
|
||||
unsigned framesRead = __max (0, bytesRead / chanCount);
|
||||
|
||||
for (unsigned i = framesRead * chanCount; i > 0; i--)
|
||||
{
|
||||
*(frameBuf++) = ((int32_t) *(iBuf++) - 128) << 16; // * 2^16
|
||||
}
|
||||
if (framesRead < frameCount) // zero out missing samples
|
||||
{
|
||||
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); // one byte
|
||||
*(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 int16_t* iBuf = (const int16_t*) tempBuf; // words
|
||||
const int bytesRead = _READ (fileHandle, tempBuf, frameCount * chanCount * 2);
|
||||
unsigned framesRead = __max (0, bytesRead / (chanCount * 2));
|
||||
|
||||
for (unsigned i = framesRead * chanCount; i > 0; i--)
|
||||
{
|
||||
*(frameBuf++) = (int32_t) *(iBuf++) << 8; // * 2^8
|
||||
}
|
||||
if (framesRead < frameCount) // zero out missing samples
|
||||
{
|
||||
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); // two bytes
|
||||
*(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 uint8_t* iBuf = (uint8_t*) tempBuf;
|
||||
const int bytesRead = _READ (fileHandle, tempBuf, frameCount * chanCount * 3);
|
||||
unsigned framesRead = __max (0, bytesRead / (chanCount * 3));
|
||||
|
||||
for (unsigned i = framesRead * chanCount; i > 0; i--)
|
||||
{
|
||||
const int32_t i24 = (int32_t) iBuf[0] | ((int32_t) iBuf[1] << 8) | ((int32_t) iBuf[2] << 16);
|
||||
iBuf += 3;
|
||||
*(frameBuf++) = (i24 > MAX_VALUE_AUDIO24 ? i24 + 2 * MIN_VALUE_AUDIO24 : i24);
|
||||
}
|
||||
if (framesRead < frameCount) // zero out missing samples
|
||||
{
|
||||
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); // 3 bytes
|
||||
*(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 int32_t* iBuf = (const int32_t*) tempBuf; // dword
|
||||
const int bytesRead = _READ (fileHandle, tempBuf, frameCount * chanCount * 4);
|
||||
unsigned framesRead = __max (0, bytesRead / (chanCount * 4));
|
||||
|
||||
for (unsigned i = framesRead * chanCount; i > 0; i--)
|
||||
{
|
||||
const int32_t i24 = ((*iBuf >> 1) + (1 << 6)) >> 7; // * 2^-8 with rounding, overflow-safe
|
||||
iBuf++;
|
||||
*(frameBuf++) = __min (MAX_VALUE_AUDIO24, i24);
|
||||
}
|
||||
if (framesRead < frameCount) // zero out missing samples
|
||||
{
|
||||
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); // 4 bytes
|
||||
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
|
||||
unsigned BasicWavReader::open (const int wavFileHandle, const uint16_t maxFrameRead, const int64_t fileLength /*= LLONG_MAX*/)
|
||||
{
|
||||
m_bytesRemaining = fileLength;
|
||||
m_fileHandle = wavFileHandle;
|
||||
|
||||
if ((m_fileHandle == -1) || (fileLength <= 44))
|
||||
{
|
||||
return 1; // file handle invalid or file too small
|
||||
}
|
||||
#if defined (_WIN32) || defined (WIN32) || defined (_WIN64) || defined (WIN64)
|
||||
if ((fileLength < LLONG_MAX) && (m_readOffset = _telli64 (m_fileHandle)) != 0)
|
||||
#else // Linux, MacOS, Unix
|
||||
if ((fileLength < LLONG_MAX) && (m_readOffset = lseek (m_fileHandle, 0, 1 /*SEEK_CUR*/)) != 0)
|
||||
#endif
|
||||
{
|
||||
m_readOffset = _SEEK (m_fileHandle, 0, 0 /*SEEK_SET*/);
|
||||
}
|
||||
if ((m_readOffset != 0) || !readRiffHeader ())
|
||||
{
|
||||
return 2; // file type invalid or file seek failed
|
||||
}
|
||||
if (!readFormatChunk ())
|
||||
{
|
||||
return 3; // audio format invalid or not supported
|
||||
}
|
||||
if (!readDataHeader ())
|
||||
{
|
||||
return 4; // WAVE data part invalid or unsupported
|
||||
}
|
||||
if ((m_byteBuffer = (char*) malloc (m_waveFrameSize * maxFrameRead)) == nullptr)
|
||||
{
|
||||
return 5; // read-in byte buffer allocation failed
|
||||
}
|
||||
m_frameLimit = maxFrameRead;
|
||||
|
||||
// ready to read audio data: initialize byte counter
|
||||
if (m_bytesRemaining > m_chunkLength)
|
||||
{
|
||||
m_bytesRemaining = m_chunkLength;
|
||||
}
|
||||
m_chunkLength = 0;
|
||||
|
||||
if (m_waveDataType == WAV_PCM) // & function pointer
|
||||
{
|
||||
switch (m_waveBitDepth)
|
||||
{
|
||||
case 8:
|
||||
m_readDataFunc = readDataLnPcm08; break;
|
||||
case 16:
|
||||
m_readDataFunc = readDataLnPcm16; break;
|
||||
case 24:
|
||||
m_readDataFunc = readDataLnPcm24; break;
|
||||
default:
|
||||
m_readDataFunc = readDataLnPcm32; break;
|
||||
}
|
||||
}
|
||||
else m_readDataFunc = (m_waveBitDepth == 16 ? readDataFloat16 : readDataFloat32);
|
||||
|
||||
return (m_readDataFunc == nullptr ? 6 : 0); // 0: OK
|
||||
}
|
||||
|
||||
unsigned BasicWavReader::read (int32_t* const frameBuf, const uint16_t frameCount)
|
||||
{
|
||||
unsigned framesRead;
|
||||
|
||||
if ((frameBuf == nullptr) || (m_fileHandle == -1) || (__min (m_frameLimit, frameCount) == 0) || (m_byteBuffer == nullptr))
|
||||
{
|
||||
return 0; // invalid args or class not initialized
|
||||
}
|
||||
framesRead = m_readDataFunc (m_fileHandle, frameBuf, __min (m_frameLimit, frameCount), m_waveChannels, m_byteBuffer);
|
||||
m_bytesRead = m_waveFrameSize * framesRead;
|
||||
m_bytesRemaining -= m_bytesRead;
|
||||
m_chunkLength += m_bytesRead;
|
||||
|
||||
return framesRead;
|
||||
}
|
||||
|
||||
void BasicWavReader::reset ()
|
||||
{
|
||||
m_byteBuffer = nullptr;
|
||||
m_bytesRead = 0;
|
||||
m_bytesRemaining = 0;
|
||||
m_chunkLength = 0;
|
||||
m_frameLimit = 0;
|
||||
m_readDataFunc = nullptr;
|
||||
m_readOffset = 0;
|
||||
m_waveBitDepth = 0;
|
||||
m_waveChannels = 0;
|
||||
m_waveFrameRate = 0;
|
||||
|
||||
if (m_fileHandle != -1) _SEEK (m_fileHandle, 0, 0 /*SEEK_SET*/);
|
||||
}
|
92
src/app/basicWavReader.h
Normal file
92
src/app/basicWavReader.h
Normal file
@ -0,0 +1,92 @@
|
||||
/* basicWavReader.h - header file for class with basic WAVE file reading capability
|
||||
* written by C. R. Helmrich, last modified in 2019 - see License.htm for legal notices
|
||||
*
|
||||
* The copyright in this software is being made available under a Modified BSD-Style 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-2020 Christian R. Helmrich, project ecodis. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef _BASIC_WAV_READER_H_
|
||||
#define _BASIC_WAV_READER_H_
|
||||
|
||||
#include "exhaleAppPch.h"
|
||||
|
||||
// constant data sizes & limits
|
||||
#define BWR_BUFFERED_READ 1 // faster reader
|
||||
#define CHUNK_FORMAT_MAX 20
|
||||
#define CHUNK_FORMAT_SIZE 16
|
||||
#define CHUNK_HEADER_SIZE 8
|
||||
#define FILE_HEADER_SIZE 12
|
||||
#define MAX_VALUE_AUDIO24 8388607 // (1 << 23) - 1
|
||||
#define MIN_VALUE_AUDIO24 -8388608 // (1 << 23) *-1
|
||||
|
||||
// WAVE data format definitions
|
||||
typedef enum WAV_TYPE
|
||||
{
|
||||
WAV_PCM = 0, // linear PCM
|
||||
WAV_ADPCM, // ADPCM
|
||||
WAV_FLOAT // IEEE float
|
||||
} WAV_TYPE;
|
||||
|
||||
// data reader function pointer
|
||||
typedef unsigned (*ReadFunc) (const int, int32_t*, const unsigned, const unsigned, void*);
|
||||
|
||||
// basic WAV audio reader class
|
||||
class BasicWavReader
|
||||
{
|
||||
private:
|
||||
|
||||
// member variables
|
||||
char* m_byteBuffer;
|
||||
unsigned m_bytesRead;
|
||||
int64_t m_bytesRemaining;
|
||||
int64_t m_chunkLength;
|
||||
int m_fileHandle;
|
||||
unsigned m_frameLimit;
|
||||
ReadFunc m_readDataFunc;
|
||||
int64_t m_readOffset;
|
||||
unsigned m_waveBitDepth;
|
||||
unsigned m_waveBitRate;
|
||||
unsigned m_waveChannels;
|
||||
WAV_TYPE m_waveDataType;
|
||||
unsigned m_waveFrameRate;
|
||||
unsigned m_waveFrameSize;
|
||||
// private reader functions
|
||||
bool readRiffHeader ();
|
||||
bool readFormatChunk();
|
||||
bool readDataHeader ();
|
||||
// private helper function
|
||||
bool seekToChunkTag (uint8_t* const buf, const uint32_t tagName);
|
||||
// static reading functions
|
||||
static unsigned readDataFloat16 (const int fileHandle, int32_t* frameBuf, const unsigned frameCount,
|
||||
const unsigned chanCount, void* tempBuf);
|
||||
static unsigned readDataFloat32 (const int fileHandle, int32_t* frameBuf, const unsigned frameCount,
|
||||
const unsigned chanCount, void* tempBuf);
|
||||
static unsigned readDataLnPcm08 (const int fileHandle, int32_t* frameBuf, const unsigned frameCount,
|
||||
const unsigned chanCount, void* tempBuf);
|
||||
static unsigned readDataLnPcm16 (const int fileHandle, int32_t* frameBuf, const unsigned frameCount,
|
||||
const unsigned chanCount, void* tempBuf);
|
||||
static unsigned readDataLnPcm24 (const int fileHandle, int32_t* frameBuf, const unsigned frameCount,
|
||||
const unsigned chanCount, void* tempBuf);
|
||||
static unsigned readDataLnPcm32 (const int fileHandle, int32_t* frameBuf, const unsigned frameCount,
|
||||
const unsigned chanCount, void* tempBuf);
|
||||
public:
|
||||
|
||||
// constructor
|
||||
BasicWavReader () { m_fileHandle = -1; reset (); }
|
||||
// destructor
|
||||
~BasicWavReader() { if (m_byteBuffer != nullptr) free ((void*) m_byteBuffer); }
|
||||
// public functions
|
||||
int64_t getDataBytesLeft () const { return m_bytesRemaining; }
|
||||
int64_t getDataBytesRead () const { return m_chunkLength; }
|
||||
unsigned getBitDepth () const { return m_waveBitDepth; }
|
||||
unsigned getNumChannels () const { return m_waveChannels; }
|
||||
unsigned getSampleRate () const { return m_waveFrameRate; }
|
||||
unsigned open (const int wavFileHandle, const uint16_t maxFrameRead, const int64_t fileLength = LLONG_MAX /*for stdin*/);
|
||||
unsigned read (int32_t* const frameBuf, const uint16_t frameCount);
|
||||
void reset ();
|
||||
}; // BasicWavReader
|
||||
|
||||
#endif // _BASIC_WAV_READER_H_
|
5
src/app/basicWavReader.txt
Normal file
5
src/app/basicWavReader.txt
Normal file
@ -0,0 +1,5 @@
|
||||
basicWavReader.cpp - known issues
|
||||
_________________________________
|
||||
|
||||
2019: for 8-bit PCM audio, the WAVE file must contain an even number of samples.
|
||||
This is guaranteed for an even number of channels (e.g., 2-channel stereo)
|
545
src/app/exhaleApp.cpp
Normal file
545
src/app/exhaleApp.cpp
Normal file
@ -0,0 +1,545 @@
|
||||
/* exhaleApp.cpp - source file with main() routine for exhale application executable
|
||||
* written by C. R. Helmrich, last modified in 2019 - see License.htm for legal notices
|
||||
*
|
||||
* The copyright in this software is being made available under a Modified BSD-Style 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-2020 Christian R. Helmrich, project ecodis. All rights reserved.
|
||||
*/
|
||||
|
||||
#include "exhaleAppPch.h"
|
||||
#include "basicMP4Writer.h"
|
||||
#include "basicWavReader.h"
|
||||
#include "../lib/exhaleEnc.h"
|
||||
#include "version.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <time.h>
|
||||
#if defined (_WIN32) || defined (WIN32) || defined (_WIN64) || defined (WIN64)
|
||||
#include <windows.h>
|
||||
|
||||
#define EXHALE_TEXT_BLUE (FOREGROUND_INTENSITY | FOREGROUND_BLUE | FOREGROUND_GREEN)
|
||||
#define EXHALE_TEXT_PINK (FOREGROUND_INTENSITY | FOREGROUND_BLUE | FOREGROUND_RED)
|
||||
#endif
|
||||
#define IGNORE_WAV_LENGTH 0 // 1: ignore input size indicators (nasty)
|
||||
#define XHE_AAC_LOW_DELAY 0 // 1: allow encoding with 768 frame length
|
||||
|
||||
// main routine
|
||||
int main (const int argc, char* argv[])
|
||||
{
|
||||
if (argc <= 0) return argc; // for safety
|
||||
|
||||
const bool readStdin = (argc == 3);
|
||||
BasicWavReader wavReader;
|
||||
int32_t* inPcmData = nullptr; // 24-bit WAVE audio input buffer
|
||||
uint8_t* outAuData = nullptr; // access unit (AU) output buffer
|
||||
int inFileHandle = -1, outFileHandle = -1;
|
||||
uint16_t i, exePathEnd = 0;
|
||||
uint16_t compatibleExtensionFlag = 0; // 0: disabled, 1: enabled
|
||||
uint16_t coreSbrFrameLengthIndex = 1; // 0: 768, 1: 1024 samples
|
||||
uint16_t variableCoreBitRateMode = 3; // 0: lowest... 9: highest
|
||||
#if defined (_WIN32) || defined (WIN32) || defined (_WIN64) || defined (WIN64)
|
||||
const HANDLE hConsole = GetStdHandle (STD_OUTPUT_HANDLE);
|
||||
CONSOLE_SCREEN_BUFFER_INFO csbi;
|
||||
#endif
|
||||
|
||||
for (i = 0; (argv[0][i] != 0) && (i < 65535); i++)
|
||||
{
|
||||
#if defined (_WIN32) || defined (WIN32) || defined (_WIN64) || defined (WIN64)
|
||||
if (argv[0][i] == '\\') exePathEnd = i + 1;
|
||||
#else // Linux, MacOS, Unix
|
||||
if (argv[0][i] == '/' ) exePathEnd = i + 1;
|
||||
#endif
|
||||
}
|
||||
const char* const exeFileName = argv[0] + exePathEnd;
|
||||
|
||||
if ((exeFileName[0] == 0) || (i == 65535))
|
||||
{
|
||||
fprintf_s (stderr, " ERROR reading executable name or path: the string is invalid!\n\n");
|
||||
|
||||
return 32768; // bad executable string
|
||||
}
|
||||
|
||||
// print program header with compile info
|
||||
fprintf_s (stdout, "\n ---------------------------------------------------------------------\n");
|
||||
#if defined (_WIN32) || defined (WIN32) || defined (_WIN64) || defined (WIN64)
|
||||
GetConsoleScreenBufferInfo (hConsole, &csbi); /*save the text color*/ fprintf_s (stdout, " | ");
|
||||
SetConsoleTextAttribute (hConsole, EXHALE_TEXT_PINK); fprintf_s (stdout, "exhale");
|
||||
SetConsoleTextAttribute (hConsole, csbi.wAttributes); fprintf_s (stdout, " - ");
|
||||
SetConsoleTextAttribute (hConsole, EXHALE_TEXT_PINK); fprintf_s (stdout, "e");
|
||||
SetConsoleTextAttribute (hConsole, csbi.wAttributes); fprintf_s (stdout, "codis e");
|
||||
SetConsoleTextAttribute (hConsole, EXHALE_TEXT_PINK); fprintf_s (stdout, "x");
|
||||
SetConsoleTextAttribute (hConsole, csbi.wAttributes); fprintf_s (stdout, "tended ");
|
||||
SetConsoleTextAttribute (hConsole, EXHALE_TEXT_PINK); fprintf_s (stdout, "h");
|
||||
SetConsoleTextAttribute (hConsole, csbi.wAttributes); fprintf_s (stdout, "igh-efficiency ");
|
||||
SetConsoleTextAttribute (hConsole, EXHALE_TEXT_PINK); fprintf_s (stdout, "a");
|
||||
SetConsoleTextAttribute (hConsole, csbi.wAttributes); fprintf_s (stdout, "nd ");
|
||||
SetConsoleTextAttribute (hConsole, EXHALE_TEXT_PINK); fprintf_s (stdout, "l");
|
||||
SetConsoleTextAttribute (hConsole, csbi.wAttributes); fprintf_s (stdout, "ow-complexity ");
|
||||
SetConsoleTextAttribute (hConsole, EXHALE_TEXT_PINK); fprintf_s (stdout, "e");
|
||||
SetConsoleTextAttribute (hConsole, csbi.wAttributes); fprintf_s (stdout, "ncoder |\n");
|
||||
#else
|
||||
fprintf_s (stdout, " | exhale - ecodis extended high-efficiency and low-complexity encoder |\n");
|
||||
#endif
|
||||
fprintf_s (stdout, " | |\n");
|
||||
#if defined (_WIN64) || defined (WIN64) || defined (__x86_64)
|
||||
fprintf_s (stdout, " | version %s.%s%s (x64, built on %s) - written by C.R.Helmrich |\n",
|
||||
#else // 32-bit OS
|
||||
fprintf_s (stdout, " | version %s.%s%s (x86, built on %s) - written by C.R.Helmrich |\n",
|
||||
#endif
|
||||
EXHALELIB_VERSION_MAJOR, EXHALELIB_VERSION_MINOR, EXHALELIB_VERSION_BUGFIX, __DATE__);
|
||||
fprintf_s (stdout, " ---------------------------------------------------------------------\n\n");
|
||||
|
||||
// check arg. list, print usage if needed
|
||||
if ((argc < 3) || (argc > 4) || (argc > 1 && argv[1][1] != 0))
|
||||
{
|
||||
fprintf_s (stdout, " Copyright 2018-2019 C.R.Helmrich, project ecodis. See License.txt for details.\n\n");
|
||||
|
||||
fprintf_s (stdout, " This software is being made available under a Modified BSD License and comes\n");
|
||||
fprintf_s (stdout, " with ABSOLUTELY NO WARRANTY. This software may be subject to other third-party\n");
|
||||
fprintf_s (stdout, " rights, including patent rights. No such rights are granted under this License.\n\n");
|
||||
#if defined (_WIN32) || defined (WIN32) || defined (_WIN64) || defined (WIN64)
|
||||
SetConsoleTextAttribute (hConsole, EXHALE_TEXT_BLUE); fprintf_s (stdout, " Usage:\t");
|
||||
SetConsoleTextAttribute (hConsole, csbi.wAttributes);
|
||||
#else
|
||||
fprintf_s (stdout, " Usage:\t");
|
||||
#endif
|
||||
fprintf_s (stdout, "%s preset [inputWaveFile.wav] outputMP4File.m4a\n\n where\n\n", exeFileName);
|
||||
fprintf_s (stdout, " preset\t= # (1-9) low-complexity standard compliant xHE-AAC at 16<31>#+48 kbit/s\n");
|
||||
#if XHE_AAC_LOW_DELAY
|
||||
// fprintf_s (stdout, " \t (a-i) low-complexity compatible xHE-AAC with BE at 16<31>#+48 kbit/s\n");
|
||||
fprintf_s (stdout, " \t (A-I) 41ms low-delay compatible xHE-AAC with BE at 16<31>#+48 kbit/s\n");
|
||||
#else
|
||||
// fprintf_s (stdout, " \t (a-i) low-complexity compatible xHE-AAC with BE at 16<31>#+48 kbit/s\n");
|
||||
#endif
|
||||
fprintf_s (stdout, "\n inputWaveFile.wav lossless WAVE audio input, read from stdin if not specified\n\n");
|
||||
fprintf_s (stdout, " outputMP4File.m4a encoded MPEG-4 bit-stream, extension should be .m4a or .mp4\n\n\n");
|
||||
#if defined (_WIN32) || defined (WIN32) || defined (_WIN64) || defined (WIN64)
|
||||
SetConsoleTextAttribute (hConsole, EXHALE_TEXT_BLUE); fprintf_s (stdout, " Notes:\t");
|
||||
SetConsoleTextAttribute (hConsole, csbi.wAttributes);
|
||||
#else
|
||||
fprintf_s (stdout, " Notes:\t");
|
||||
#endif
|
||||
fprintf_s (stdout, "The above bit-rates are for stereo and change for mono or multichannel.\n");
|
||||
if (exePathEnd > 0)
|
||||
{
|
||||
#if defined (_WIN32) || defined (WIN32) || defined (_WIN64) || defined (WIN64)
|
||||
fprintf_s (stdout, " \tUse filename prefix .\\ for the current directory if this executable was\n\tcalled with a path (call: %s).\n", argv[0]);
|
||||
#else // Linux, MacOS, Unix
|
||||
fprintf_s (stdout, " \tUse filename prefix ./ for the current directory if this executable was\n\tcalled with a path (call: %s).\n", argv[0]);
|
||||
#endif
|
||||
}
|
||||
return 0; // no arguments, which is OK
|
||||
}
|
||||
|
||||
// check preset mode, derive coder config
|
||||
#if XHE_AAC_LOW_DELAY
|
||||
if ((*argv[1] >= '1' && *argv[1] <= '9') || (*argv[1] >= 'a' && *argv[1] <= 'i') || (*argv[1] >= 'A' && *argv[1] <= 'I'))
|
||||
#else
|
||||
if ((*argv[1] >= '1' && *argv[1] <= '9') || (*argv[1] >= 'a' && *argv[1] <= 'i'))
|
||||
#endif
|
||||
{
|
||||
i = (uint16_t) argv[1][0];
|
||||
compatibleExtensionFlag = (i & 0x40) >> 6;
|
||||
coreSbrFrameLengthIndex = (i & 0x20) >> 5;
|
||||
variableCoreBitRateMode = (i & 0x0F);
|
||||
}
|
||||
else if (*argv[1] == '#') // default mode
|
||||
{
|
||||
fprintf_s (stdout, " Default preset is specified, encoding to low-complexity xHE-AAC, preset mode %d\n\n", variableCoreBitRateMode);
|
||||
}
|
||||
else
|
||||
{
|
||||
#if XHE_AAC_LOW_DELAY
|
||||
fprintf_s (stderr, " ERROR reading preset mode: character %s is not supported! Use 1-9, a-i, or A-I.\n\n", argv[1]);
|
||||
#else
|
||||
fprintf_s (stderr, " ERROR reading preset mode: character %s is not supported! Use 1-9 or a-i.\n\n", argv[1]);
|
||||
#endif
|
||||
return 16384; // preset isn't supported
|
||||
}
|
||||
|
||||
const unsigned frameLength = (3 + coreSbrFrameLengthIndex) << 8;
|
||||
|
||||
if (readStdin) // configure stdin
|
||||
{
|
||||
#if defined (_WIN32) || defined (WIN32) || defined (_WIN64) || defined (WIN64)
|
||||
inFileHandle = _fileno (stdin);
|
||||
if (_setmode (inFileHandle, _O_RDONLY | _O_BINARY) == -1)
|
||||
#else // Linux, MacOS, Unix
|
||||
inFileHandle = 0; // TODO
|
||||
// if (::setmode (inFileHandle, O_RDONLY) == -1)
|
||||
#endif
|
||||
{
|
||||
fprintf_s (stderr, " ERROR while trying to set stdin to binary mode! Has stdin been closed?\n\n");
|
||||
inFileHandle = -1;
|
||||
|
||||
goto mainFinish; // stdin setup error
|
||||
}
|
||||
}
|
||||
else // argc = 4, open input file
|
||||
{
|
||||
const char* inFileName = argv[2];
|
||||
uint16_t inPathEnd = 0;
|
||||
|
||||
for (i = 0; (inFileName[i] != 0) && (i < 65535); i++)
|
||||
{
|
||||
#if defined (_WIN32) || defined (WIN32) || defined (_WIN64) || defined (WIN64)
|
||||
if (inFileName[i] == '\\') inPathEnd = i + 1;
|
||||
#else // Linux, MacOS, Unix
|
||||
if (inFileName[i] == '/' ) inPathEnd = i + 1;
|
||||
#endif
|
||||
}
|
||||
if ((inFileName[0] == 0) || (i == 65535))
|
||||
{
|
||||
fprintf_s (stderr, " ERROR reading input file name or path: the string is invalid!\n\n");
|
||||
|
||||
goto mainFinish; // bad input string
|
||||
}
|
||||
|
||||
if (inPathEnd == 0) // name has no path
|
||||
{
|
||||
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
|
||||
}
|
||||
|
||||
#if defined (_WIN32) || defined (WIN32) || defined (_WIN64) || defined (WIN64)
|
||||
if (_sopen_s (&inFileHandle, inFileName, _O_RDONLY | _O_SEQUENTIAL | _O_BINARY, _SH_DENYWR, _S_IREAD) != 0)
|
||||
#else // Linux, MacOS, Unix
|
||||
if ((inFileHandle = ::open (inFileName, O_RDONLY | O_LARGEFILE, 0666)) == -1)
|
||||
#endif
|
||||
{
|
||||
fprintf_s (stderr, " ERROR while trying to open input file %s! Does it already exist?\n\n", inFileName);
|
||||
inFileHandle = -1;
|
||||
if (inPathEnd == 0) free ((void*) inFileName);
|
||||
|
||||
goto mainFinish; // input file error
|
||||
}
|
||||
if (inPathEnd == 0) free ((void*) inFileName);
|
||||
}
|
||||
|
||||
#if defined (_WIN32) || defined (WIN32) || defined (_WIN64) || defined (WIN64)
|
||||
if ((wavReader.open (inFileHandle, frameLength, readStdin ? LLONG_MAX : _filelengthi64 (inFileHandle)) != 0) ||
|
||||
#else // Linux, MacOS, Unix
|
||||
if ((wavReader.open (inFileHandle, frameLength, readStdin ? LLONG_MAX : lseek (inFileHandle, 0, 2 /*SEEK_END*/)) != 0) ||
|
||||
#endif
|
||||
(wavReader.getNumChannels () >= 7))
|
||||
{
|
||||
fprintf_s (stderr, " ERROR while trying to open WAVE file: invalid or unsupported audio format!\n\n");
|
||||
i = 8192; // return value
|
||||
|
||||
goto mainFinish; // audio format invalid
|
||||
}
|
||||
else // WAVE OK, open output file
|
||||
{
|
||||
const char* outFileName = argv[argc - 1];
|
||||
uint16_t outPathEnd = readStdin ? 1 : 0; // no path prepends when the input is read from stdin
|
||||
|
||||
for (i = 0; (outFileName[i] != 0) && (i < 65535); i++)
|
||||
{
|
||||
#if defined (_WIN32) || defined (WIN32) || defined (_WIN64) || defined (WIN64)
|
||||
if (outFileName[i] == '\\') outPathEnd = i + 1;
|
||||
#else // Linux, MacOS, Unix
|
||||
if (outFileName[i] == '/' ) outPathEnd = i + 1;
|
||||
#endif
|
||||
}
|
||||
if ((outFileName[0] == 0) || (i == 65535))
|
||||
{
|
||||
fprintf_s (stderr, " ERROR reading output file name or path: the string is invalid!\n\n");
|
||||
|
||||
goto mainFinish; // bad output string
|
||||
}
|
||||
|
||||
if ((variableCoreBitRateMode < 2) && (wavReader.getSampleRate () > 24000))
|
||||
{
|
||||
fprintf_s (stderr, " ERROR during encoding! Input sample rate must be <=24 kHz for preset mode %d!\n\n", variableCoreBitRateMode);
|
||||
i = 4096; // return value
|
||||
|
||||
goto mainFinish; // resample to 24 kHz
|
||||
}
|
||||
if ((variableCoreBitRateMode < 3) && (wavReader.getSampleRate () > 32000))
|
||||
{
|
||||
fprintf_s (stderr, " ERROR during encoding! Input sample rate must be <=32 kHz for preset mode %d!\n\n", variableCoreBitRateMode);
|
||||
i = 4096; // return value
|
||||
|
||||
goto mainFinish; // resample to 32 kHz
|
||||
}
|
||||
|
||||
if (outPathEnd == 0) // name has no path
|
||||
{
|
||||
outFileName = (const char*) malloc ((exePathEnd + i + 1) * sizeof (char)); // 0-terminated string
|
||||
memcpy ((void*) outFileName, argv[0], exePathEnd * sizeof (char)); // prepend executable path ...
|
||||
memcpy ((void*)(outFileName + exePathEnd), argv[argc - 1], (i + 1) * sizeof (char)); //...to name
|
||||
}
|
||||
|
||||
i = (readStdin ? O_RDWR : O_WRONLY);
|
||||
#if defined (_WIN32) || defined (WIN32) || defined (_WIN64) || defined (WIN64)
|
||||
if (_sopen_s (&outFileHandle, outFileName, i | _O_SEQUENTIAL | _O_CREAT | _O_EXCL | _O_BINARY, _SH_DENYRD, _S_IWRITE) != 0)
|
||||
#else // Linux, MacOS, Unix
|
||||
if ((outFileHandle = ::open (outFileName, i | O_LARGEFILE | O_CREAT | O_EXCL, 0666)) == -1)
|
||||
#endif
|
||||
{
|
||||
fprintf_s (stderr, " ERROR while trying to open output file %s! Does it already exist?\n\n", outFileName);
|
||||
outFileHandle = -1;
|
||||
if (outPathEnd == 0) free ((void*) outFileName);
|
||||
|
||||
goto mainFinish; // output file error
|
||||
}
|
||||
if (outPathEnd == 0) free ((void*) outFileName);
|
||||
}
|
||||
|
||||
// enforce executable specific constraints
|
||||
i = __min (USHRT_MAX, wavReader.getSampleRate ());
|
||||
if ((wavReader.getNumChannels () > 3) && (i == 57600 || i == 51200 || i == 40000 || i == 38400 || i == 34150 ||
|
||||
i == 28800 || i == 25600 || i == 20000 || i == 19200 || i == 17075 || i == 14400 || i == 12800 || i == 9600))
|
||||
{
|
||||
fprintf_s (stderr, " ERROR: exhale does not support %d-channel coding with %d Hz sampling rate.\n\n", wavReader.getNumChannels (), i);
|
||||
|
||||
goto mainFinish; // encoder config error
|
||||
}
|
||||
else
|
||||
{
|
||||
const unsigned startLength = (frameLength * 25) >> 4; // encoder PCM look-ahead
|
||||
const unsigned numChannels = wavReader.getNumChannels ();
|
||||
const unsigned inFrameSize = frameLength * sizeof (int32_t);
|
||||
const unsigned inSampDepth = wavReader.getBitDepth ();
|
||||
const int64_t expectLength = wavReader.getDataBytesLeft () / int64_t (numChannels * inSampDepth >> 3);
|
||||
|
||||
// allocate dynamic frame memory buffers
|
||||
inPcmData = (int32_t*) malloc (inFrameSize * numChannels); // max frame in size
|
||||
outAuData = (uint8_t*) malloc ((6144 >> 3) * numChannels); // max frame AU size
|
||||
if ((inPcmData == nullptr) || (outAuData == nullptr))
|
||||
{
|
||||
fprintf_s (stderr, " ERROR while trying to allocate dynamic memory! Not enough free RAM available!\n\n");
|
||||
i = 2048; // return value
|
||||
|
||||
goto mainFinish; // memory alloc error
|
||||
}
|
||||
|
||||
if (wavReader.read (inPcmData, frameLength) != frameLength) // full first frame
|
||||
{
|
||||
fprintf_s (stderr, " ERROR while trying to encode input audio data! The audio stream is too short!\n\n");
|
||||
i = 1024; // return value
|
||||
|
||||
goto mainFinish; // audio is too short
|
||||
}
|
||||
else // start coding loop, show progress
|
||||
{
|
||||
const unsigned sampleRate = wavReader.getSampleRate ();
|
||||
const unsigned indepPeriod = (sampleRate < 48000 ? sampleRate / frameLength : 45 /*for 50-Hz video, use 50 for 60-Hz video*/);
|
||||
const unsigned mod3Percent = unsigned ((expectLength * (3 + coreSbrFrameLengthIndex)) >> 17);
|
||||
// open & prepare ExhaleEncoder object
|
||||
uint32_t byteCount = 0, bw = 0, bwMax = 0, br; // for bytes read and bit-rate
|
||||
uint32_t headerRes = 0;
|
||||
ExhaleEncoder exhaleEnc (inPcmData, outAuData, sampleRate, numChannels, frameLength, indepPeriod, variableCoreBitRateMode +
|
||||
(sampleRate > 24000 ? 0 : 1 - (variableCoreBitRateMode >> 2)) // compensate for low sampling rates
|
||||
#if !RESTRICT_TO_AAC
|
||||
, true /*noise filling*/, compatibleExtensionFlag > 0
|
||||
#endif
|
||||
);
|
||||
BasicMP4Writer mp4Writer; // .m4a file
|
||||
|
||||
// init encoder, generate UsacConfig()
|
||||
memset (outAuData, 0, 108 * sizeof (uint8_t)); // max. allowed ASC + UC size
|
||||
i = exhaleEnc.initEncoder (outAuData, &bw); // bw holds actual ASC + UC size
|
||||
|
||||
if ((i |= mp4Writer.open (outFileHandle, sampleRate, numChannels, inSampDepth, frameLength, startLength,
|
||||
indepPeriod, outAuData, bw, time (nullptr) & UINT_MAX, (char) variableCoreBitRateMode)) != 0)
|
||||
{
|
||||
fprintf_s (stderr, " ERROR while trying to initialize xHE-AAC encoder: error value %d was returned!\n\n", i);
|
||||
i <<= 2; // return value
|
||||
|
||||
goto mainFinish; // coder init error
|
||||
}
|
||||
|
||||
if (*argv[1] != '#') // user-def. mode
|
||||
{
|
||||
fprintf_s (stdout, " Encoding %d-kHz %d-channel %d-bit WAVE to low-complexity xHE-AAC at %d kbit/s\n\n",
|
||||
sampleRate / 1000, numChannels, inSampDepth, __min (4, numChannels) * (24 + variableCoreBitRateMode * 8));
|
||||
}
|
||||
if (!readStdin && (mod3Percent > 0))
|
||||
{
|
||||
#if defined (_WIN32) || defined (WIN32) || defined (_WIN64) || defined (WIN64)
|
||||
SetConsoleTextAttribute (hConsole, EXHALE_TEXT_BLUE);
|
||||
fprintf_s (stdout, " Progress: ");
|
||||
SetConsoleTextAttribute (hConsole, csbi.wAttributes); // initial text color
|
||||
fprintf_s (stdout, "-"); fflush (stdout);
|
||||
#else
|
||||
fprintf_s (stdout, " Progress: -"); fflush (stdout);
|
||||
#endif
|
||||
}
|
||||
#if !IGNORE_WAV_LENGTH
|
||||
if (!readStdin) // reserve space for MP4 file header. TODO: nasty, avoid this
|
||||
{
|
||||
if ((headerRes = (uint32_t) mp4Writer.initHeader (uint32_t (__min (UINT_MAX - startLength, expectLength)))) < 666)
|
||||
{
|
||||
fprintf_s (stderr, "\n ERROR while trying to write MPEG-4 bit-stream header: stopped after %d bytes!\n\n", headerRes);
|
||||
i = 3; // return value
|
||||
|
||||
goto mainFinish; // writeout error
|
||||
}
|
||||
}
|
||||
#endif
|
||||
i = 1; // for progress bar
|
||||
|
||||
// initial frame, encode look-ahead AU
|
||||
if ((bw = exhaleEnc.encodeLookahead ()) < 3)
|
||||
{
|
||||
fprintf_s (stderr, "\n ERROR while trying to create first xHE-AAC frame: error value %d was returned!\n\n", bw);
|
||||
i = 2; // return value
|
||||
|
||||
goto mainFinish; // coder-time error
|
||||
}
|
||||
if (bwMax < bw) bwMax = bw;
|
||||
// write first AU, add frame to header
|
||||
if (mp4Writer.addFrameAU (outAuData, byteCount, bw) != bw) goto mainFinish;
|
||||
byteCount += bw;
|
||||
|
||||
while (wavReader.read (inPcmData, frameLength) > 0) // read a new audio frame
|
||||
{
|
||||
// frame coding loop, encode next AU
|
||||
if ((bw = exhaleEnc.encodeFrame ()) < 3)
|
||||
{
|
||||
fprintf_s (stderr, "\n ERROR while trying to create xHE-AAC frame: error value %d was returned!\n\n", bw);
|
||||
i = 2; // return value
|
||||
|
||||
goto mainFinish;
|
||||
}
|
||||
if (bwMax < bw) bwMax = bw;
|
||||
// write new AU, add frame to header
|
||||
if (mp4Writer.addFrameAU (outAuData, byteCount, bw) != bw) goto mainFinish;
|
||||
byteCount += bw;
|
||||
|
||||
if (!readStdin && (mod3Percent > 0) && !(mp4Writer.getFrameCount () % mod3Percent))
|
||||
{
|
||||
if ((i++) < 34) // for short files
|
||||
{
|
||||
fprintf_s (stdout, "-"); fflush (stdout);
|
||||
}
|
||||
}
|
||||
} // frame loop
|
||||
|
||||
// end of coding loop, encode final AU
|
||||
if ((bw = exhaleEnc.encodeFrame ()) < 3)
|
||||
{
|
||||
fprintf_s (stderr, "\n ERROR while trying to create xHE-AAC frame: error value %d was returned!\n\n", bw);
|
||||
i = 2; // return value
|
||||
|
||||
goto mainFinish; // coder-time error
|
||||
}
|
||||
if (bwMax < bw) bwMax = bw;
|
||||
// write final AU, add frame to header
|
||||
if (mp4Writer.addFrameAU (outAuData, byteCount, bw) != bw) goto mainFinish;
|
||||
byteCount += bw;
|
||||
|
||||
const int64_t actualLength = wavReader.getDataBytesRead () / int64_t (numChannels * inSampDepth >> 3);
|
||||
|
||||
if (((actualLength + startLength) % frameLength) > 0) // flush trailing audio
|
||||
{
|
||||
memset (inPcmData, 0, inFrameSize * numChannels);
|
||||
|
||||
// flush remaining audio into new AU
|
||||
if ((bw = exhaleEnc.encodeFrame ()) < 3)
|
||||
{
|
||||
fprintf_s (stderr, "\n ERROR while trying to create last xHE-AAC frame: error value %d was returned!\n\n", bw);
|
||||
i = 2; // return value
|
||||
|
||||
goto mainFinish;
|
||||
}
|
||||
if (bwMax < bw) bwMax = bw;
|
||||
// the flush AU, add frame to header
|
||||
if (mp4Writer.addFrameAU (outAuData, byteCount, bw) != bw) goto mainFinish;
|
||||
byteCount += bw;
|
||||
} // trailing frame
|
||||
|
||||
#if !IGNORE_WAV_LENGTH
|
||||
if (readStdin) // reserve space for MP4 file header (is there an easier way?)
|
||||
#endif
|
||||
{
|
||||
int64_t pos = _SEEK (outFileHandle, 0, 1 /*SEEK_CUR*/);
|
||||
|
||||
if ((headerRes = (uint32_t) mp4Writer.initHeader (uint32_t (__min (UINT_MAX - startLength, actualLength)))) < 666)
|
||||
{
|
||||
fprintf_s (stderr, "\n ERROR while trying to write MPEG-4 bit-stream header: stopped after %d bytes!\n\n", headerRes);
|
||||
i = 3; // return value
|
||||
|
||||
goto mainFinish; // writeout error
|
||||
}
|
||||
// move AU data forward to make room for actual MP4 header at start of file
|
||||
br = inFrameSize * numChannels;
|
||||
while ((pos -= br) > 0) // move loop
|
||||
{
|
||||
_SEEK (outFileHandle, pos, 0 /*SEEK_SET*/);
|
||||
_READ (outFileHandle, inPcmData, br);
|
||||
_SEEK (outFileHandle, pos + headerRes, 0 /*SEEK_SET*/);
|
||||
_WRITE(outFileHandle, inPcmData, br);
|
||||
}
|
||||
if ((br = (uint32_t) __max (0, pos + br)) > 0) // remainder of data to move
|
||||
{
|
||||
_SEEK (outFileHandle, 0, 0 /*SEEK_SET*/);
|
||||
_READ (outFileHandle, inPcmData, br);
|
||||
_SEEK (outFileHandle, headerRes, 0 /*SEEK_SET*/);
|
||||
_WRITE(outFileHandle, inPcmData, br);
|
||||
}
|
||||
}
|
||||
|
||||
// mean & max. bit-rate of encoded AUs
|
||||
br = uint32_t (((actualLength >> 1) + 8 * (byteCount + 4 * (int64_t) mp4Writer.getFrameCount ()) * sampleRate) / actualLength);
|
||||
bw = uint32_t (((frameLength >> 1) + 8 * (bwMax + 4u /* maximum AU size + stsz as a bit-rate */) * sampleRate) / frameLength);
|
||||
bw = mp4Writer.finishFile (br, bw, uint32_t (__min (UINT_MAX - startLength, actualLength)), time (nullptr) & UINT_MAX);
|
||||
|
||||
fprintf_s (stdout, " Done, actual average %.1f kbit/s\n\n", (float) br * 0.001f);
|
||||
i = 0; // no errors
|
||||
if (!readStdin && (actualLength != expectLength || bw != headerRes))
|
||||
{
|
||||
fprintf_s (stderr, " WARNING: %ld sample frames read but %ld sample frames expected!\n", actualLength, expectLength);
|
||||
if (bw != headerRes) fprintf_s (stderr, " The encoded MPEG-4 bit-stream is likely to be unreadable!\n");
|
||||
fprintf_s (stderr, "\n");
|
||||
}
|
||||
} // end coding loop and stats print-out
|
||||
}
|
||||
|
||||
mainFinish:
|
||||
|
||||
// free all dynamic memory
|
||||
if (inPcmData != nullptr)
|
||||
{
|
||||
free ((void*) inPcmData);
|
||||
inPcmData = nullptr;
|
||||
}
|
||||
if (outAuData != nullptr)
|
||||
{
|
||||
free ((void*) outAuData);
|
||||
outAuData = nullptr;
|
||||
}
|
||||
// close input file
|
||||
if (inFileHandle != -1)
|
||||
{
|
||||
if (_CLOSE (inFileHandle) != 0)
|
||||
{
|
||||
if (readStdin) // stdin
|
||||
{
|
||||
fprintf_s (stderr, " ERROR while trying to close stdin stream! Has it already been closed?\n\n");
|
||||
}
|
||||
else // argc = 4, file
|
||||
{
|
||||
fprintf_s (stderr, " ERROR while trying to close input file %s! Does it still exist?\n\n", argv[2]);
|
||||
}
|
||||
}
|
||||
inFileHandle = 0;
|
||||
}
|
||||
// close output file
|
||||
if (outFileHandle != -1)
|
||||
{
|
||||
if (_CLOSE (outFileHandle) != 0)
|
||||
{
|
||||
fprintf_s (stderr, " ERROR while trying to close output file %s! Does it still exist?\n\n", argv[argc - 1]);
|
||||
}
|
||||
outFileHandle = 0;
|
||||
}
|
||||
|
||||
return (inFileHandle | outFileHandle | i);
|
||||
}
|
BIN
src/app/exhaleApp.ico
Normal file
BIN
src/app/exhaleApp.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.7 KiB |
35
src/app/exhaleApp.rc
Normal file
35
src/app/exhaleApp.rc
Normal file
@ -0,0 +1,35 @@
|
||||
/* exhaleApp.rc - resource file for exhale application binaries compiled under Windows
|
||||
* written by C. R. Helmrich, last modified in 2019 - see License.htm for legal notices
|
||||
*
|
||||
* The copyright in this software is being made available under a Modified BSD-Style 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-2020 Christian R. Helmrich, project ecodis. All rights reserved.
|
||||
*/
|
||||
|
||||
#include "..\..\include\version.h" // for EXHALELIB_VERSION_... strings
|
||||
#include "winres.h"
|
||||
|
||||
0 ICON "exhaleApp.ico"
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 1,0,0
|
||||
BEGIN
|
||||
BLOCK "StringFileInfo"
|
||||
BEGIN
|
||||
BLOCK "040904b0"
|
||||
BEGIN
|
||||
VALUE "CompanyName", "ecodis"
|
||||
VALUE "FileDescription", "exhale - ecodis extended high-efficiency and low-complexity encoder"
|
||||
VALUE "InternalName", "exhaleApp.exe"
|
||||
VALUE "LegalCopyright", "<22> 2018-2020 C. R. Helmrich, ecodis"
|
||||
VALUE "OriginalFilename", "exhale.exe"
|
||||
VALUE "ProductName", "exhaleApp"
|
||||
VALUE "ProductVersion", EXHALELIB_VERSION_MAJOR "." EXHALELIB_VERSION_MINOR EXHALELIB_VERSION_BUGFIX
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
BEGIN
|
||||
VALUE "Translation", 0x409, 1200
|
||||
END
|
||||
END
|
27
src/app/exhaleAppPch.cpp
Normal file
27
src/app/exhaleAppPch.cpp
Normal file
@ -0,0 +1,27 @@
|
||||
/* exhaleAppPch.cpp - pre-compiled source file for source code of exhale application
|
||||
* written by C. R. Helmrich, last modified in 2019 - see License.htm for legal notices
|
||||
*
|
||||
* The copyright in this software is being made available under a Modified BSD-Style 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-2020 Christian R. Helmrich, project ecodis. All rights reserved.
|
||||
*/
|
||||
|
||||
#include "exhaleAppPch.h"
|
||||
|
||||
// ISO/IEC 23003-3 USAC Table 67
|
||||
static const unsigned supportedSamplingRates[26] = {
|
||||
96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350, // AAC
|
||||
57600, 51200, 40000, 38400, 34150, 28800, 25600, 20000, 19200, 17075, 14400, 12800, 9600 // USAC
|
||||
};
|
||||
|
||||
// public sampling rate function
|
||||
bool isSamplingRateSupported (const unsigned samplingRate)
|
||||
{
|
||||
for (unsigned i = 0; i < 26; i++)
|
||||
{
|
||||
if (samplingRate == supportedSamplingRates[i]) return true;
|
||||
}
|
||||
return false; // not supported
|
||||
}
|
48
src/app/exhaleAppPch.h
Normal file
48
src/app/exhaleAppPch.h
Normal file
@ -0,0 +1,48 @@
|
||||
/* exhaleAppPch.h - pre-compiled header file for source code of exhale application
|
||||
* written by C. R. Helmrich, last modified in 2019 - see License.htm for legal notices
|
||||
*
|
||||
* The copyright in this software is being made available under a Modified BSD-Style 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-2020 Christian R. Helmrich, project ecodis. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef _EXHALE_APP_PCH_H_
|
||||
#define _EXHALE_APP_PCH_H_
|
||||
|
||||
#include <limits.h> // for .._MAX, .._MIN
|
||||
#include <stdint.h> // for (u)int8_t, (u)int16_t, (u)int32_t, (u)int64_t
|
||||
#include <stdlib.h> // for abs, div, calloc, malloc, free, (__)max, (__)min, (s)rand
|
||||
#include <string.h> // for memcpy, memset
|
||||
#include <vector> // for std::vector <>
|
||||
#if defined (_WIN32) || defined (WIN32) || defined (_WIN64) || defined (WIN64)
|
||||
# include <io.h>
|
||||
|
||||
# define _CLOSE _close
|
||||
# define _READ _read
|
||||
# define _SEEK _lseeki64
|
||||
# define _WRITE _write
|
||||
#else // Linux, MacOS, Unix
|
||||
# include <unistd.h>
|
||||
|
||||
# define _CLOSE ::close
|
||||
# define _READ ::read
|
||||
# define _SEEK ::lseek
|
||||
# define _WRITE ::write
|
||||
#endif
|
||||
|
||||
#ifndef __max
|
||||
# define __max(a, b) ((a) > (b) ? (a) : (b))
|
||||
#endif
|
||||
#ifndef __min
|
||||
# define __min(a, b) ((a) < (b) ? (a) : (b))
|
||||
#endif
|
||||
#ifndef fprintf_s
|
||||
# define fprintf_s fprintf
|
||||
#endif
|
||||
|
||||
// public sampling rate function
|
||||
bool isSamplingRateSupported (const unsigned samplingRate);
|
||||
|
||||
#endif // _EXHALE_APP_PCH_H_
|
181
src/app/exhaleApp_vs2012.vcxproj
Normal file
181
src/app/exhaleApp_vs2012.vcxproj
Normal file
@ -0,0 +1,181 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{EC0D1502-2018-1700-4865-6C6D72696368}</ProjectGuid>
|
||||
<ProjectName>exhaleApp</ProjectName>
|
||||
<RootNamespace>exhaleApp</RootNamespace>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<PlatformToolset>v110</PlatformToolset>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<PlatformToolset>v110</PlatformToolset>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<PlatformToolset>v110</PlatformToolset>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<PlatformToolset>v110</PlatformToolset>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings" />
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<IntDir>$(SolutionDir)build\$(PlatformToolset)\$(Platform)\$(Configuration)\</IntDir>
|
||||
<OutDir>$(SolutionDir)bin\$(PlatformToolset)\$(Platform)\$(Configuration)\</OutDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<IntDir>$(SolutionDir)build\$(PlatformToolset)\$(Platform)\$(Configuration)\</IntDir>
|
||||
<OutDir>$(SolutionDir)bin\$(PlatformToolset)\$(Platform)\$(Configuration)\</OutDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<IntDir>$(SolutionDir)build\$(PlatformToolset)\$(Platform)\$(Configuration)\</IntDir>
|
||||
<OutDir>$(SolutionDir)bin\$(PlatformToolset)\$(Platform)\$(Configuration)\</OutDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<IntDir>$(SolutionDir)build\$(PlatformToolset)\$(Platform)\$(Configuration)\</IntDir>
|
||||
<OutDir>$(SolutionDir)bin\$(PlatformToolset)\$(Platform)\$(Configuration)\</OutDir>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)include</AdditionalIncludeDirectories>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>exhaleAppPch.h</PrecompiledHeaderFile>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<TreatWarningAsError>true</TreatWarningAsError>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalLibraryDirectories>$(SolutionDir)lib\$(PlatformToolset)\$(Platform)\$(Configuration)\</AdditionalLibraryDirectories>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<SubSystem>Console</SubSystem>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)include</AdditionalIncludeDirectories>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>exhaleAppPch.h</PrecompiledHeaderFile>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<TreatWarningAsError>true</TreatWarningAsError>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalLibraryDirectories>$(SolutionDir)lib\$(PlatformToolset)\$(Platform)\$(Configuration)\</AdditionalLibraryDirectories>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<SubSystem>Console</SubSystem>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)include</AdditionalIncludeDirectories>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>exhaleAppPch.h</PrecompiledHeaderFile>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<TreatWarningAsError>true</TreatWarningAsError>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalLibraryDirectories>$(SolutionDir)lib\$(PlatformToolset)\$(Platform)\$(Configuration)\</AdditionalLibraryDirectories>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<GenerateDebugInformation>false</GenerateDebugInformation>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<SubSystem>Console</SubSystem>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)include</AdditionalIncludeDirectories>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>exhaleAppPch.h</PrecompiledHeaderFile>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<TreatWarningAsError>true</TreatWarningAsError>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalLibraryDirectories>$(SolutionDir)lib\$(PlatformToolset)\$(Platform)\$(Configuration)\</AdditionalLibraryDirectories>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<GenerateDebugInformation>false</GenerateDebugInformation>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<SubSystem>Console</SubSystem>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\..\include\exhaleDecl.h" />
|
||||
<ClInclude Include="..\..\include\version.h" />
|
||||
<ClInclude Include="basicMP4Writer.h" />
|
||||
<ClInclude Include="basicWavReader.h" />
|
||||
<ClInclude Include="exhaleAppPch.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="basicMP4Writer.cpp" />
|
||||
<ClCompile Include="basicWavReader.cpp" />
|
||||
<ClCompile Include="exhaleApp.cpp" />
|
||||
<ClCompile Include="exhaleAppPch.cpp">
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="exhaleApp.rc" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\lib\exhaleLib_vs2012.vcxproj">
|
||||
<Project>{EC0D1501-2018-1700-4865-6C6D72696368}</Project>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
</Project>
|
53
src/app/exhaleApp_vs2012.vcxproj.filters
Normal file
53
src/app/exhaleApp_vs2012.vcxproj.filters
Normal file
@ -0,0 +1,53 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{EC0D1506-2018-1700-4865-6C6D72696368}</UniqueIdentifier>
|
||||
<Extensions>h;hpp</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{EC0D1507-2018-1700-4865-6C6D72696368}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{EC0D1508-2018-1700-4865-6C6D72696368}</UniqueIdentifier>
|
||||
<Extensions>rc;ico</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\..\include\exhaleDecl.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\include\version.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="basicMP4Writer.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="basicWavReader.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="exhaleAppPch.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="basicMP4Writer.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="basicWavReader.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="exhaleApp.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="exhaleAppPch.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="exhaleApp.rc">
|
||||
<Filter>Resource Files</Filter>
|
||||
</ResourceCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
7
src/app/exhaleApp_vs2012.vcxproj.user
Normal file
7
src/app/exhaleApp_vs2012.vcxproj.user
Normal file
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<LocalDebuggerCommandArguments># C:\ha2010.wav debug.m4a > ..\..\debug.txt</LocalDebuggerCommandArguments>
|
||||
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
|
||||
</PropertyGroup>
|
||||
</Project>
|
50
src/app/makefile
Normal file
50
src/app/makefile
Normal file
@ -0,0 +1,50 @@
|
||||
## makefile - application make-file for compiling exhale on Linux and MacOS platforms
|
||||
# written by C. R. Helmrich, last modified 2019 - see License.txt for legal notices
|
||||
#
|
||||
# The copyright in this software is being made available under a Modified BSD 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-2019 Christian R. Helmrich, project ecodis. All rights reserved.
|
||||
##
|
||||
|
||||
# define as console application
|
||||
CONFIG = CONSOLE
|
||||
|
||||
# source and output directories
|
||||
DIR_BIN = ../../bin
|
||||
DIR_OBJ = ../../build
|
||||
DIR_INC = ../../include
|
||||
DIR_LIB = ../../lib
|
||||
DIR_SRC = ../../src/app
|
||||
|
||||
# build with large file support
|
||||
DEFS = -DMSYS_LINUX -DMSYS_UNIX_LARGEFILE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64
|
||||
|
||||
# name of product / binary file
|
||||
PRD_NAME = Exhale
|
||||
|
||||
# name of temporary object file
|
||||
OBJS = \
|
||||
$(DIR_OBJ)/basicMP4Writer.o \
|
||||
$(DIR_OBJ)/basicWavReader.o \
|
||||
$(DIR_OBJ)/exhaleApp.o \
|
||||
$(DIR_OBJ)/exhaleAppPch.o \
|
||||
|
||||
# define libraries to link with
|
||||
LIBS = -ldl
|
||||
DYN_LIBS =
|
||||
STAT_LIBS = -lpthread
|
||||
|
||||
DYN_DEBUG_LIBS = -lExhaleDynd
|
||||
DYN_DEBUG_PREREQS = $(DIR_LIB)/libExhaleDynd.a
|
||||
STAT_DEBUG_LIBS = -lExhaled
|
||||
STAT_DEBUG_PREREQS = $(DIR_LIB)/libExhaled.a
|
||||
|
||||
DYN_RELEASE_LIBS = -lExhale
|
||||
DYN_RELEASE_PREREQS = $(DIR_LIB)/libExhale.a
|
||||
STAT_RELEASE_LIBS = -lExhale
|
||||
STAT_RELEASE_PREREQS = $(DIR_LIB)/libExhale.a
|
||||
|
||||
# include common makefile.base
|
||||
include ../makefile.base
|
347
src/lib/bitAllocation.cpp
Normal file
347
src/lib/bitAllocation.cpp
Normal file
@ -0,0 +1,347 @@
|
||||
/* bitAllocation.cpp - source file for class needed for psychoacoustic bit-allocation
|
||||
* written by C. R. Helmrich, last modified in 2019 - see License.htm for legal notices
|
||||
*
|
||||
* The copyright in this software is being made available under a Modified BSD-Style 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-2020 Christian R. Helmrich, project ecodis. All rights reserved.
|
||||
*/
|
||||
|
||||
#include "exhaleLibPch.h"
|
||||
#include "bitAllocation.h"
|
||||
|
||||
// static helper functions
|
||||
static inline uint32_t jndModel (const uint32_t val, const uint32_t mean,
|
||||
const unsigned expTimes512, const unsigned mulTimes512)
|
||||
{
|
||||
const double exp = (double) expTimes512 / 512.0; // exponent
|
||||
const double mul = (double) mulTimes512 / 512.0; // a factor
|
||||
const double res = pow (mul * (double) val, exp) * pow ((double) mean, 1.0 - exp);
|
||||
|
||||
return uint32_t (__min ((double) UINT_MAX, res + 0.5));
|
||||
}
|
||||
|
||||
static void jndPowerLawAndPeakSmoothing (uint32_t* const stepSizes, const unsigned nStepSizes,
|
||||
const uint32_t avgStepSize, const uint8_t sfm, const uint8_t tfm)
|
||||
{
|
||||
const unsigned expTimes512 = 512u - sfm; // 1.0 - sfm / 2.0
|
||||
const unsigned mulTimes512 = __min (expTimes512, 512u - tfm);
|
||||
uint32_t stepSizeM3 = 0, stepSizeM2 = 0, stepSizeM1 = 99 + BA_EPS; // hearing threshold around DC
|
||||
unsigned b;
|
||||
|
||||
for (b = 0; b < __min (2, nStepSizes); b++)
|
||||
{
|
||||
stepSizeM3 = stepSizeM2;
|
||||
stepSizeM2 = stepSizeM1;
|
||||
stepSizeM1 = stepSizes[b] = jndModel (stepSizes[b], avgStepSize, expTimes512, mulTimes512);
|
||||
}
|
||||
stepSizes[0] = __min (stepSizeM1, stepSizes[0]); // `- becomes --
|
||||
for (/*b*/; b < nStepSizes; b++)
|
||||
{
|
||||
const uint32_t stepSizeB = jndModel (stepSizes[b], avgStepSize, expTimes512, mulTimes512);
|
||||
|
||||
if ((stepSizeM3 <= stepSizeM2) && (stepSizeM3 <= stepSizeM1) && (stepSizeB <= stepSizeM2) && (stepSizeB <= stepSizeM1))
|
||||
{
|
||||
const uint32_t maxM3M0 = __max (stepSizeM3, stepSizeB); // smoothen local spectral peak of _<>`- shape
|
||||
|
||||
stepSizes[b - 2] = __min (maxM3M0, stepSizes[b - 2]); // _-`-
|
||||
stepSizes[b - 1] = __min (maxM3M0, stepSizes[b - 1]); // _---
|
||||
}
|
||||
stepSizeM3 = stepSizeM2;
|
||||
stepSizeM2 = stepSizeM1;
|
||||
stepSizeM1 = (stepSizes[b] = stepSizeB); // modified step-size may be smoothened in next loop iteration
|
||||
}
|
||||
}
|
||||
|
||||
// constructor
|
||||
BitAllocator::BitAllocator ()
|
||||
{
|
||||
for (unsigned ch = 0; ch < USAC_MAX_NUM_CHANNELS; ch++)
|
||||
{
|
||||
m_avgStepSize[ch] = 0;
|
||||
m_avgSpecFlat[ch] = 0;
|
||||
m_avgTempFlat[ch] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// public functions
|
||||
void BitAllocator::getChAverageSpecFlat (uint8_t meanSpecFlatInCh[USAC_MAX_NUM_CHANNELS], const unsigned nChannels)
|
||||
{
|
||||
if ((meanSpecFlatInCh == nullptr) || (nChannels > USAC_MAX_NUM_CHANNELS))
|
||||
{
|
||||
return;
|
||||
}
|
||||
memcpy (meanSpecFlatInCh, m_avgSpecFlat, nChannels * sizeof (uint8_t));
|
||||
}
|
||||
|
||||
void BitAllocator::getChAverageTempFlat (uint8_t meanTempFlatInCh[USAC_MAX_NUM_CHANNELS], const unsigned nChannels)
|
||||
{
|
||||
if ((meanTempFlatInCh == nullptr) || (nChannels > USAC_MAX_NUM_CHANNELS))
|
||||
{
|
||||
return;
|
||||
}
|
||||
memcpy (meanTempFlatInCh, m_avgTempFlat, nChannels * sizeof (uint8_t));
|
||||
}
|
||||
|
||||
uint8_t BitAllocator::getScaleFac (const uint32_t sfbStepSize, const int32_t* const sfbSignal, const uint8_t sfbWidth,
|
||||
const uint32_t sfbRmsValue)
|
||||
{
|
||||
uint8_t sf;
|
||||
uint32_t u;
|
||||
#if !RESTRICT_TO_AAC
|
||||
uint64_t meanSpecLoudness = 0;
|
||||
double d;
|
||||
#endif
|
||||
|
||||
if ((sfbSignal == nullptr) || (sfbWidth == 0) || (sfbRmsValue < 46))
|
||||
{
|
||||
return 0; // use lowest scale factor
|
||||
}
|
||||
#if RESTRICT_TO_AAC
|
||||
u = 0;
|
||||
for (sf = 0; sf < sfbWidth; sf++)
|
||||
{
|
||||
u += uint32_t (0.5 + sqrt (abs ((double) sfbSignal[sf])));
|
||||
}
|
||||
u = uint32_t ((u * 16384ui64 + (sfbWidth >> 1)) / sfbWidth);
|
||||
u = uint32_t (0.5 + sqrt ((double) u) * 128.0);
|
||||
|
||||
if (u < 42567) return 0;
|
||||
|
||||
u = uint32_t ((sfbStepSize * 42567ui64 + (u >> 1)) / u);
|
||||
sf = (u > 1 ? uint8_t (0.5 + 17.7169498394 * log10 ((double) u)) : 4);
|
||||
#else
|
||||
for (sf = 0; sf < sfbWidth; sf++) // simple, low-complexity derivation method for USAC's arithmetic coder
|
||||
{
|
||||
const int64_t temp = ((int64_t) sfbSignal[sf] + 8) >> 4; // avoid overflow
|
||||
|
||||
meanSpecLoudness += temp * temp;
|
||||
}
|
||||
meanSpecLoudness = uint64_t (0.5 + pow (256.0 * (double) meanSpecLoudness / sfbWidth, 0.25));
|
||||
|
||||
u = uint32_t (0.5 + pow ((double) sfbRmsValue, 0.75) * 256.0); // u = 2^8 * (sfbRmsValue^0.75)
|
||||
u = uint32_t ((meanSpecLoudness * sfbStepSize * 665 + (u >> 1)) / u); // u = sqrt(6.75) * m*thr/u
|
||||
d = (u > 1 ? log10 ((double) u) : 0.25);
|
||||
|
||||
u = uint32_t (0.5 + pow ((double) sfbRmsValue, 0.25) * 16384.0); // u = 2^14 * (sfbRmsValue^0.25)
|
||||
u = uint32_t (((uint64_t) sfbStepSize * 42567 + (u >> 1)) / u); // u = sqrt(6.75) * thr/u
|
||||
d += (u > 1 ? log10 ((double) u) : 0.25);
|
||||
|
||||
sf = uint8_t (0.5 + 8.8584749197 * d); // sf = (8/3) * log2(u1*u2) = (8/3) * (log2(u1)+log2(u2))
|
||||
#endif
|
||||
|
||||
return __min (SCHAR_MAX, sf);
|
||||
}
|
||||
|
||||
unsigned BitAllocator::initSfbStepSizes (const SfbGroupData* const groupData[USAC_MAX_NUM_CHANNELS], const uint8_t numSwbShort,
|
||||
const uint32_t specAnaStats[USAC_MAX_NUM_CHANNELS],
|
||||
const uint32_t tempAnaStats[USAC_MAX_NUM_CHANNELS],
|
||||
const unsigned nChannels, const unsigned samplingRate, uint32_t* const sfbStepSizes,
|
||||
const unsigned lfeChannelIndex /*= USAC_MAX_NUM_CHANNELS*/, const bool tnsDisabled /*= false*/)
|
||||
{
|
||||
// equal-loudness weighting based on data from: K. Kurakata, T. Mizunami, and K. Matsushita, "Percentiles
|
||||
// of Normal Hearing-Threshold Distribution Under Free-Field Listening Conditions in Numerical Form," Ac.
|
||||
// Sci. Tech, vol. 26, no. 5, pp. 447-449, Jan. 2005, https://www.researchgate.net/publication/239433096.
|
||||
const unsigned HF/*idx*/= ((123456 - samplingRate) >> 11) + (samplingRate <= 34150 ? 2 : 0); // start SFB
|
||||
const unsigned LF/*idx*/= 9;
|
||||
const unsigned MF/*idx*/= (samplingRate < 28800 ? HF : __min (HF, 30u));
|
||||
const unsigned msShift = (samplingRate + 36736) >> 15; // TODO: 768 smp
|
||||
const unsigned msOffset = 1 << (msShift - 1);
|
||||
uint32_t nMeans = 0, sumMeans = 0;
|
||||
|
||||
if ((groupData == nullptr) || (specAnaStats == nullptr) || (tempAnaStats == nullptr) || (sfbStepSizes == nullptr) ||
|
||||
(numSwbShort < MIN_NUM_SWB_SHORT) || (numSwbShort > MAX_NUM_SWB_SHORT) || (nChannels > USAC_MAX_NUM_CHANNELS) ||
|
||||
(samplingRate < 7350) || (samplingRate > 96000) || (lfeChannelIndex > USAC_MAX_NUM_CHANNELS))
|
||||
{
|
||||
return 1; // invalid arguments error
|
||||
}
|
||||
|
||||
for (unsigned ch = 0; ch < nChannels; ch++)
|
||||
{
|
||||
const SfbGroupData& grpData = *groupData[ch];
|
||||
const uint32_t maxSfbInCh = grpData.sfbsPerGroup;
|
||||
const uint32_t nBandsInCh = grpData.numWindowGroups * maxSfbInCh;
|
||||
const uint32_t* rms = grpData.sfbRmsValues;
|
||||
uint32_t* stepSizes = &sfbStepSizes[ch * numSwbShort * NUM_WINDOW_GROUPS];
|
||||
// --- apply INTRA-channel simultaneous masking, equal-loudness weighting, and thresholding to SFB RMS data
|
||||
uint32_t maskingSlope = LF, b, elw = 58254; // 8/9
|
||||
uint32_t rmsEqualLoud = 0;
|
||||
uint32_t sumStepSizes = 0;
|
||||
|
||||
m_avgStepSize[ch] = 0;
|
||||
|
||||
b = ((specAnaStats[ch] >> 16) & UCHAR_MAX); // start with squared spec. flatness from spectral analysis
|
||||
b = __max (b * b, (tempAnaStats[ch] >> 24) * (tempAnaStats[ch] >> 24)); // ..and from temporal analysis
|
||||
m_avgSpecFlat[ch] = uint8_t ((b + (1 << 7)) >> 8); // normalized maximum
|
||||
|
||||
b = ((tempAnaStats[ch] >> 16) & UCHAR_MAX); // now derive squared temp. flatness from temporal analysis
|
||||
b = __max (b * b, (specAnaStats[ch] >> 24) * (specAnaStats[ch] >> 24)); // ..and from spectral analysis
|
||||
m_avgTempFlat[ch] = uint8_t ((b + (1 << 7)) >> 8); // normalized maximum
|
||||
|
||||
if ((nBandsInCh == 0) || (grpData.numWindowGroups > NUM_WINDOW_GROUPS))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if ((ch == lfeChannelIndex) || (grpData.numWindowGroups != 1)) // LFE, SHORT windows: no masking or ELW
|
||||
{
|
||||
for (unsigned gr = 0; gr < grpData.numWindowGroups; gr++)
|
||||
{
|
||||
const uint32_t* gRms = &rms[numSwbShort * gr];
|
||||
uint32_t* gStepSizes = &stepSizes[numSwbShort * gr];
|
||||
|
||||
for (b = numSwbShort - 1; b >= maxSfbInCh; b--)
|
||||
{
|
||||
gStepSizes[b] = 0;
|
||||
}
|
||||
for (/*b*/; b > 0; b--)
|
||||
{
|
||||
gStepSizes[b] = __max (gRms[b], BA_EPS);
|
||||
sumStepSizes += unsigned (0.5 + sqrt ((double) gStepSizes[b]));
|
||||
}
|
||||
gStepSizes[0] = __max (gRms[0], maskingSlope + BA_EPS);
|
||||
sumStepSizes += unsigned (0.5 + sqrt ((double) gStepSizes[0]));
|
||||
} // for gr
|
||||
|
||||
if (ch != lfeChannelIndex)
|
||||
{
|
||||
// --- SHORT windows: apply perceptual just noticeable difference (JND) model and local band-peak smoothing
|
||||
nMeans++;
|
||||
m_avgStepSize[ch] = __min (USHRT_MAX, uint32_t ((sumStepSizes + (nBandsInCh >> 1)) / nBandsInCh));
|
||||
sumMeans += m_avgStepSize[ch];
|
||||
m_avgStepSize[ch] *= m_avgStepSize[ch];
|
||||
|
||||
for (unsigned gr = 0; gr < grpData.numWindowGroups; gr++) // separate peak smoothing for each group
|
||||
{
|
||||
jndPowerLawAndPeakSmoothing (&stepSizes[numSwbShort * gr], maxSfbInCh, m_avgStepSize[ch], m_avgSpecFlat[ch], 0);
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
stepSizes[0] = __max (rms[0], maskingSlope + BA_EPS);
|
||||
for (b = 1; b < __min (LF, maxSfbInCh); b++) // apply steeper low-frequency simultaneous masking slopes
|
||||
{
|
||||
maskingSlope = (stepSizes[b - 1] + (msOffset << (9u - b))) >> (msShift + 9u - b);
|
||||
stepSizes[b] = __max (rms[b], maskingSlope + BA_EPS);
|
||||
}
|
||||
for (/*b*/; b < __min (MF, maxSfbInCh); b++) // apply typical mid-frequency simultaneous masking slopes
|
||||
{
|
||||
maskingSlope = (stepSizes[b - 1] + msOffset) >> msShift;
|
||||
stepSizes[b] = __max (rms[b], maskingSlope + BA_EPS);
|
||||
}
|
||||
if ((samplingRate >= 28800) && (samplingRate <= 64000))
|
||||
{
|
||||
for (/*b*/; b < __min (HF, maxSfbInCh); b++) // compensate high-frequency slopes for linear SFB width
|
||||
{
|
||||
maskingSlope = ((uint64_t) stepSizes[b - 1] * (9u + b - MF) + (msOffset << 3u)) >> (msShift + 3u);
|
||||
stepSizes[b] = __max (rms[b], maskingSlope + BA_EPS);
|
||||
}
|
||||
for (/*b = HF region*/; b < maxSfbInCh; b++) // apply extra high-frequency equal-loudness attenuation
|
||||
{
|
||||
for (unsigned d = b - HF; d > 0; d--)
|
||||
{
|
||||
elw = (elw * 52430 - SHRT_MIN) >> 16; // elw *= 4/5
|
||||
}
|
||||
rmsEqualLoud = uint32_t (((uint64_t) rms[b] * elw - SHRT_MIN) >> 16); // equal loudness weighting
|
||||
maskingSlope = ((uint64_t) stepSizes[b - 1] * (9u + b - MF) + (msOffset << 3u)) >> (msShift + 3u);
|
||||
stepSizes[b] = __max (rmsEqualLoud, maskingSlope + BA_EPS);
|
||||
}
|
||||
}
|
||||
else // no equal-loudness weighting for low or high rates
|
||||
{
|
||||
for (/*b = MF region*/; b < maxSfbInCh; b++) // compensate high-frequency slopes for linear SFB width
|
||||
{
|
||||
maskingSlope = ((uint64_t) stepSizes[b - 1] * (9u + b - MF) + (msOffset << 3u)) >> (msShift + 3u);
|
||||
stepSizes[b] = __max (rms[b], maskingSlope + BA_EPS);
|
||||
}
|
||||
}
|
||||
for (b -= 1; b > __min (MF, maxSfbInCh); b--) // complete simultaneous masking by reversing the pattern
|
||||
{
|
||||
sumStepSizes += unsigned (0.5 + sqrt ((double) stepSizes[b]));
|
||||
maskingSlope = ((uint64_t) stepSizes[b] * (8u + b - MF) + (msOffset << 3u)) >> (msShift + 3u);
|
||||
stepSizes[b - 1] = __max (stepSizes[b - 1], maskingSlope);
|
||||
}
|
||||
for (/*b*/; b > __min (LF, maxSfbInCh); b--) // typical reversed mid-freq. simultaneous masking slopes
|
||||
{
|
||||
sumStepSizes += unsigned (0.5 + sqrt ((double) stepSizes[b]));
|
||||
maskingSlope = (stepSizes[b] + msOffset) >> msShift;
|
||||
stepSizes[b - 1] = __max (stepSizes[b - 1], maskingSlope);
|
||||
}
|
||||
for (/*b = min (9, maxSfbInCh)*/; b > 0; b--) // steeper reversed low-freq. simultaneous masking slopes
|
||||
{
|
||||
sumStepSizes += unsigned (0.5 + sqrt ((double) stepSizes[b]));
|
||||
maskingSlope = (stepSizes[b] + (msOffset << (10u - b))) >> (msShift + 10u - b);
|
||||
stepSizes[b - 1] = __max (stepSizes[b - 1], maskingSlope);
|
||||
}
|
||||
sumStepSizes += unsigned (0.5 + sqrt ((double) stepSizes[0]));
|
||||
|
||||
// --- LONG window: apply perceptual JND model and local band-peak smoothing, undo equal-loudness weighting
|
||||
nMeans++;
|
||||
m_avgStepSize[ch] = __min (USHRT_MAX, uint32_t ((sumStepSizes + (nBandsInCh >> 1)) / nBandsInCh));
|
||||
sumMeans += m_avgStepSize[ch];
|
||||
m_avgStepSize[ch] *= m_avgStepSize[ch];
|
||||
|
||||
jndPowerLawAndPeakSmoothing (stepSizes, maxSfbInCh, m_avgStepSize[ch], m_avgSpecFlat[ch], tnsDisabled ? m_avgTempFlat[ch] : 0);
|
||||
|
||||
if ((samplingRate >= 28800) && (samplingRate <= 64000))
|
||||
{
|
||||
elw = 36; // 36/32 = 9/8
|
||||
for (b = HF; b < grpData.sfbsPerGroup; b++) // undo additional high-freq. equal-loudness attenuation
|
||||
{
|
||||
for (unsigned d = b - HF; d > 0; d--)
|
||||
{
|
||||
elw = (16u + elw * 40) >> 5; // inverse elw *= 5/4. NOTE: this may overflow for 64 kHz, that's OK
|
||||
}
|
||||
if (elw == 138 || elw >= 1024) elw--;
|
||||
rmsEqualLoud = uint32_t (__min (UINT_MAX, (16u + (uint64_t) stepSizes[b] * elw) >> 5));
|
||||
stepSizes[b] = rmsEqualLoud;
|
||||
}
|
||||
}
|
||||
} // for ch
|
||||
|
||||
if ((nMeans < 2) || (sumMeans <= nMeans * BA_EPS)) // in case of one channel or low-RMS input, we're done
|
||||
{
|
||||
return 0; // no error
|
||||
}
|
||||
|
||||
sumMeans = (sumMeans + (nMeans >> 1)) / nMeans;
|
||||
sumMeans *= sumMeans; // since we've averaged square-roots
|
||||
#if BA_INTER_CHAN_SIM_MASK
|
||||
if (nMeans > 3)
|
||||
{
|
||||
// TODO: cross-channel simultaneous masking for 4.0 - 7.1
|
||||
}
|
||||
#endif
|
||||
|
||||
for (unsigned ch = 0; ch < nChannels; ch++)
|
||||
{
|
||||
const SfbGroupData& grpData = *groupData[ch];
|
||||
const uint32_t maxSfbInCh = grpData.sfbsPerGroup;
|
||||
const uint32_t nBandsInCh = grpData.numWindowGroups * maxSfbInCh;
|
||||
const uint32_t chStepSize = m_avgStepSize[ch];
|
||||
uint32_t* stepSizes = &sfbStepSizes[ch * numSwbShort * NUM_WINDOW_GROUPS];
|
||||
// --- apply INTER-channel simultaneous masking and JND modeling to calculated INTRA-channel step-size data
|
||||
uint64_t mAvgStepSize; // modified and averaged step-size
|
||||
|
||||
if ((nBandsInCh == 0) || (grpData.numWindowGroups > NUM_WINDOW_GROUPS) || (ch == lfeChannelIndex))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
mAvgStepSize = jndModel (chStepSize, sumMeans, 7 << 6 /*7/8*/, 512);
|
||||
|
||||
for (unsigned gr = 0; gr < grpData.numWindowGroups; gr++)
|
||||
{
|
||||
uint32_t* gStepSizes = &stepSizes[numSwbShort * gr];
|
||||
|
||||
for (unsigned b = 0; b < maxSfbInCh; b++)
|
||||
{
|
||||
gStepSizes[b] = uint32_t (__min (UINT_MAX, (mAvgStepSize * gStepSizes[b] + (chStepSize >> 1)) / chStepSize));
|
||||
}
|
||||
} // for gr
|
||||
|
||||
m_avgStepSize[ch] = (uint32_t) mAvgStepSize;
|
||||
} // for ch
|
||||
|
||||
return 0; // no error
|
||||
}
|
48
src/lib/bitAllocation.h
Normal file
48
src/lib/bitAllocation.h
Normal file
@ -0,0 +1,48 @@
|
||||
/* bitAllocation.h - header file for class needed for psychoacoustic bit-allocation
|
||||
* written by C. R. Helmrich, last modified in 2019 - see License.htm for legal notices
|
||||
*
|
||||
* The copyright in this software is being made available under a Modified BSD-Style 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-2020 Christian R. Helmrich, project ecodis. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef _BIT_ALLOCATION_H_
|
||||
#define _BIT_ALLOCATION_H_
|
||||
|
||||
#include "exhaleLibPch.h"
|
||||
|
||||
// constants, experimental macros
|
||||
#define BA_EPS 1
|
||||
#define BA_INTER_CHAN_SIM_MASK 0 // cross-channel simultaneous masking for surround
|
||||
|
||||
// class for audio bit-allocation
|
||||
class BitAllocator
|
||||
{
|
||||
private:
|
||||
|
||||
// member variables
|
||||
uint32_t m_avgStepSize[USAC_MAX_NUM_CHANNELS];
|
||||
uint8_t m_avgSpecFlat[USAC_MAX_NUM_CHANNELS];
|
||||
uint8_t m_avgTempFlat[USAC_MAX_NUM_CHANNELS];
|
||||
|
||||
public:
|
||||
|
||||
// constructor
|
||||
BitAllocator ();
|
||||
// destructor
|
||||
~BitAllocator () { }
|
||||
// public functions
|
||||
void getChAverageSpecFlat (uint8_t meanSpecFlatInCh[USAC_MAX_NUM_CHANNELS], const unsigned nChannels);
|
||||
void getChAverageTempFlat (uint8_t meanTempFlatInCh[USAC_MAX_NUM_CHANNELS], const unsigned nChannels);
|
||||
uint8_t getScaleFac (const uint32_t sfbStepSize, const int32_t* const sfbSignal, const uint8_t sfbWidth,
|
||||
const uint32_t sfbRmsValue);
|
||||
unsigned initSfbStepSizes (const SfbGroupData* const groupData[USAC_MAX_NUM_CHANNELS], const uint8_t numSwbShort,
|
||||
const uint32_t specAnaStats[USAC_MAX_NUM_CHANNELS],
|
||||
const uint32_t tempAnaStats[USAC_MAX_NUM_CHANNELS],
|
||||
const unsigned nChannels, const unsigned samplingRate, uint32_t* const sfbStepSizes,
|
||||
const unsigned lfeChannelIndex = USAC_MAX_NUM_CHANNELS, const bool tnsDisabled = false);
|
||||
}; // BitAllocator
|
||||
|
||||
#endif // _BIT_ALLOCATION_H_
|
559
src/lib/bitStreamWriter.cpp
Normal file
559
src/lib/bitStreamWriter.cpp
Normal file
@ -0,0 +1,559 @@
|
||||
/* bitStreamWriter.cpp - source file for class with basic bit-stream writing capability
|
||||
* written by C. R. Helmrich, last modified in 2019 - see License.htm for legal notices
|
||||
*
|
||||
* The copyright in this software is being made available under a Modified BSD-Style 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-2020 Christian R. Helmrich, project ecodis. All rights reserved.
|
||||
*/
|
||||
|
||||
#include "exhaleLibPch.h"
|
||||
#include "bitStreamWriter.h"
|
||||
|
||||
// private helper functions
|
||||
void BitStreamWriter::writeByteAlignment () // write '0' bits until stream is byte-aligned
|
||||
{
|
||||
if (m_auBitStream.heldBitCount > 0)
|
||||
{
|
||||
m_auBitStream.stream.push_back (m_auBitStream.heldBitChunk);
|
||||
m_auBitStream.heldBitChunk = 0;
|
||||
m_auBitStream.heldBitCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned BitStreamWriter::writeChannelWiseIcsInfo (const IcsInfo& icsInfo) // ics_info()
|
||||
{
|
||||
#if RESTRICT_TO_AAC
|
||||
m_auBitStream.write ((unsigned) icsInfo.windowSequence, 2);
|
||||
#else
|
||||
m_auBitStream.write (unsigned (icsInfo.windowSequence == STOP_START ? LONG_START : icsInfo.windowSequence), 2);
|
||||
#endif
|
||||
m_auBitStream.write ((unsigned) icsInfo.windowShape, 1);
|
||||
if (icsInfo.windowSequence == EIGHT_SHORT)
|
||||
{
|
||||
m_auBitStream.write (icsInfo.maxSfb, 4);
|
||||
m_auBitStream.write (icsInfo.windowGrouping, 7); // scale_factor_grouping
|
||||
|
||||
return 14;
|
||||
}
|
||||
m_auBitStream.write (icsInfo.maxSfb, 6);
|
||||
|
||||
return 9;
|
||||
}
|
||||
|
||||
unsigned BitStreamWriter::writeChannelWiseTnsData (const TnsData& tnsData, const bool eightShorts)
|
||||
{
|
||||
const unsigned numWindows = (eightShorts ? 8 : 1);
|
||||
const unsigned offsetBits = (eightShorts ? 1 : 2);
|
||||
unsigned bitCount = 0, i;
|
||||
|
||||
for (unsigned w = 0; w < numWindows; w++)
|
||||
{
|
||||
bitCount += offsetBits;
|
||||
if (w != tnsData.filteredWindow)
|
||||
{
|
||||
m_auBitStream.write (0/*n_filt[w] = 0*/, offsetBits);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_auBitStream.write (tnsData.numFilters, offsetBits);
|
||||
if (tnsData.numFilters > 0)
|
||||
{
|
||||
m_auBitStream.write (tnsData.coeffResLow ? 0 : 1, 1); // coef_res[w]
|
||||
bitCount++;
|
||||
for (unsigned f = 0; f < tnsData.numFilters; f++)
|
||||
{
|
||||
const unsigned order = tnsData.filterOrder[f];
|
||||
|
||||
m_auBitStream.write (tnsData.filterLength[f], 2 + offsetBits * 2);
|
||||
m_auBitStream.write (order, 2 + offsetBits);
|
||||
bitCount += 4 + offsetBits * 3;
|
||||
if (order > 0)
|
||||
{
|
||||
const int8_t* coeff = tnsData.coeff[f];
|
||||
unsigned coefBits = (tnsData.coeffResLow ? 3 : 4);
|
||||
char coefMaxValue = (tnsData.coeffResLow ? 2 : 4);
|
||||
bool dontCompress = false;
|
||||
|
||||
m_auBitStream.write (tnsData.filterDownward[f] ? 1 : 0, 1);
|
||||
for (i = 0; i < order; i++) // get coef_compress, then write coef
|
||||
{
|
||||
dontCompress |= ((coeff[i] < -coefMaxValue) || (coeff[i] >= coefMaxValue));
|
||||
}
|
||||
m_auBitStream.write (dontCompress ? 0 : 1, 1);
|
||||
coefMaxValue <<= 1;
|
||||
if (dontCompress) coefMaxValue <<= 1; else coefBits--;
|
||||
for (i = 0; i < order; i++)
|
||||
{
|
||||
m_auBitStream.write (unsigned (coeff[i] < 0 ? coefMaxValue + coeff[i] : coeff[i]), coefBits);
|
||||
}
|
||||
bitCount += 2 + order * coefBits;
|
||||
}
|
||||
}
|
||||
} // if (n_filt[w])
|
||||
}
|
||||
} // for w
|
||||
|
||||
return bitCount;
|
||||
}
|
||||
|
||||
unsigned BitStreamWriter::writeFDChannelStream (const CoreCoderData& elData, EntropyCoder& entrCoder, const unsigned ch,
|
||||
const int32_t* const mdctSignal, const uint8_t* const mdctQuantMag,
|
||||
#if !RESTRICT_TO_AAC
|
||||
const bool timeWarping, const bool noiseFilling,
|
||||
#endif
|
||||
const bool indepFlag /*= false*/)
|
||||
{
|
||||
const IcsInfo& icsInfo = elData.icsInfoCurr[ch];
|
||||
const TnsData& tnsData = elData.tnsData[ch];
|
||||
const SfbGroupData& grp = elData.groupingData[ch];
|
||||
const unsigned maxSfb = grp.sfbsPerGroup;
|
||||
const bool eightShorts = icsInfo.windowSequence == EIGHT_SHORT;
|
||||
uint8_t* const sf = (uint8_t* const) grp.scaleFactors;
|
||||
uint8_t sfIdxPred = CLIP_UCHAR (sf[0] > SCHAR_MAX ? 0 : sf[0] + (eightShorts ? 68 : 80));
|
||||
unsigned bitCount = 8, g, b, i;
|
||||
|
||||
m_auBitStream.write (sfIdxPred, 8); // adjusted global_gain
|
||||
#if !RESTRICT_TO_AAC
|
||||
if (noiseFilling)
|
||||
{
|
||||
m_auBitStream.write (elData.specFillData[ch], 8); // noise level | offset
|
||||
bitCount += 8;
|
||||
}
|
||||
#endif
|
||||
if (!elData.commonWindow)
|
||||
{
|
||||
bitCount += writeChannelWiseIcsInfo (icsInfo); // ics_info
|
||||
}
|
||||
#if !RESTRICT_TO_AAC
|
||||
if (timeWarping) // && (!common_tw)
|
||||
{
|
||||
m_auBitStream.write (0, 1); // enforce tw_data_present = 0
|
||||
bitCount++;
|
||||
}
|
||||
#endif
|
||||
sfIdxPred = sf[0]; // scale factors
|
||||
for (g = 0; g < grp.numWindowGroups; g++)
|
||||
{
|
||||
uint8_t* const gSf = &sf[m_numSwbShort * g];
|
||||
|
||||
for (b = 0; b < maxSfb; b++)
|
||||
{
|
||||
uint8_t sfIdx = gSf[b];
|
||||
|
||||
if ((g + 1 < grp.numWindowGroups) && (b + 1 == maxSfb) && ((unsigned) sfIdx + INDEX_OFFSET < sf[m_numSwbShort * (g + 1)]))
|
||||
{
|
||||
// ugly, avoidable if each gr. had its own global_gain
|
||||
gSf[b] = sfIdx = sf[m_numSwbShort * (g + 1)] - INDEX_OFFSET;
|
||||
}
|
||||
if ((g > 0) || (b > 0))
|
||||
{
|
||||
int sfIdxDpcm = (int) sfIdx - sfIdxPred;
|
||||
unsigned sfBits;
|
||||
|
||||
if (sfIdxDpcm > INDEX_OFFSET) // just as sanity checks
|
||||
{
|
||||
sfIdxDpcm = INDEX_OFFSET;
|
||||
sfIdxPred += INDEX_OFFSET;
|
||||
}
|
||||
else if (sfIdxDpcm < -INDEX_OFFSET) // highly unlikely
|
||||
{
|
||||
sfIdxDpcm = -INDEX_OFFSET;
|
||||
sfIdxPred -= INDEX_OFFSET;
|
||||
}
|
||||
else // scale factor range OK
|
||||
{
|
||||
sfIdxPred = sfIdx;
|
||||
}
|
||||
sfBits = entrCoder.indexGetBitCount (sfIdxDpcm);
|
||||
m_auBitStream.write (entrCoder.indexGetHuffCode (sfIdxDpcm), sfBits);
|
||||
bitCount += sfBits;
|
||||
}
|
||||
}
|
||||
} // for g
|
||||
|
||||
if (!elData.commonTnsData && (tnsData.numFilters > 0))
|
||||
{
|
||||
bitCount += writeChannelWiseTnsData (tnsData, eightShorts);
|
||||
}
|
||||
|
||||
bitCount += (indepFlag ? 1 : 2); // arith_reset_flag, fac_data_present bits
|
||||
|
||||
if (maxSfb == 0) // zeroed spectrum
|
||||
{
|
||||
entrCoder.initWindowCoding (!eightShorts /*reset*/, eightShorts);
|
||||
|
||||
if (!indepFlag) m_auBitStream.write (1, 1); // force reset
|
||||
}
|
||||
else // not zeroed, nasty since SFB ungrouping may be needed
|
||||
{
|
||||
const uint16_t* grpOff = grp.sfbOffsets;
|
||||
uint8_t grpLen = grp.windowGroupLength[0];
|
||||
uint8_t grpWin = 0;
|
||||
uint8_t swbSize[MAX_NUM_SWB_SHORT];
|
||||
const uint8_t* winMag = (grpLen > 1 ? m_uCharBuffer : mdctQuantMag);
|
||||
const uint16_t lg = (grpLen > 1 ? grpOff[maxSfb] / grpLen : grpOff[maxSfb]);
|
||||
|
||||
if (eightShorts || (grpLen > 1)) // ungroup the SFB widths
|
||||
{
|
||||
for (b = 0, i = oneTwentyEightOver[grpLen]; b < maxSfb; b++)
|
||||
{
|
||||
swbSize[b] = ((grpOff[b+1] - grpOff[b]) * i) >> 7; // sfbWidth/grpLen
|
||||
}
|
||||
}
|
||||
g = 0;
|
||||
for (int w = 0; w < (eightShorts ? 8 : 1); w++, grpWin++) // window loop
|
||||
{
|
||||
if (grpWin >= grpLen) // next g
|
||||
{
|
||||
grpOff += m_numSwbShort;
|
||||
grpLen = grp.windowGroupLength[++g];
|
||||
grpWin = 0;
|
||||
winMag = (grpLen > 1 ? m_uCharBuffer : &mdctQuantMag[grpOff[0]]);
|
||||
}
|
||||
if (eightShorts && (grpLen > 1))
|
||||
{
|
||||
for (b = i = 0; b < maxSfb; b++) // ungroup magnitudes
|
||||
{
|
||||
memcpy (&m_uCharBuffer[i], &mdctQuantMag[grpOff[b] + grpWin * swbSize[b]], swbSize[b] * sizeof (uint8_t));
|
||||
i += swbSize[b];
|
||||
}
|
||||
}
|
||||
entrCoder.initWindowCoding (indepFlag && (w == 0), eightShorts);
|
||||
|
||||
if (!indepFlag && (w == 0)) // optimize arith_reset_flag
|
||||
{
|
||||
if ((b = entrCoder.arithGetResetBit (winMag, 0, lg)) != 0)
|
||||
{
|
||||
entrCoder.arithResetMemory ();
|
||||
entrCoder.arithSetCodState (USHRT_MAX << 16);
|
||||
entrCoder.arithSetCtxState (0);
|
||||
}
|
||||
m_auBitStream.write (b, 1); // write adapted reset bit
|
||||
}
|
||||
bitCount += entrCoder.arithCodeSigMagn (winMag, 0, lg, true, &m_auBitStream);
|
||||
|
||||
if (eightShorts && (grpLen > 1))
|
||||
{
|
||||
for (b = i = 0; b < maxSfb; b++) // ungroup coef signs
|
||||
{
|
||||
const int32_t* const swbSig = &mdctSignal[grpOff[b] + grpWin * swbSize[b]];
|
||||
|
||||
for (unsigned j = 0; j < swbSize[b]; j++, i++)
|
||||
{
|
||||
if (winMag[i] != 0)
|
||||
{
|
||||
m_auBitStream.write (swbSig[j] < 0 ? 0 : 1, 1); // - = 0, + = 1
|
||||
bitCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else // not grouped long window
|
||||
{
|
||||
const int32_t* const winSig = &mdctSignal[grpOff[0]];
|
||||
|
||||
for (i = 0; i < lg; i++)
|
||||
{
|
||||
if (winMag[i] != 0)
|
||||
{
|
||||
m_auBitStream.write (winSig[i] < 0 ? 0 : 1, 1); // -1 = 0, +1 = 1
|
||||
bitCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
} // for w
|
||||
} // if (maxSfb == 0)
|
||||
|
||||
m_auBitStream.write (0, 1); // fac_data_present, no fac_data
|
||||
|
||||
return bitCount;
|
||||
}
|
||||
|
||||
unsigned BitStreamWriter::writeStereoCoreToolInfo (const CoreCoderData& elData,
|
||||
#if !RESTRICT_TO_AAC
|
||||
const bool timeWarping,
|
||||
#endif
|
||||
const bool indepFlag /*= false*/)
|
||||
{
|
||||
const IcsInfo& icsInfo0 = elData.icsInfoCurr[0];
|
||||
const IcsInfo& icsInfo1 = elData.icsInfoCurr[1];
|
||||
const TnsData& tnsData0 = elData.tnsData[0];
|
||||
const TnsData& tnsData1 = elData.tnsData[1];
|
||||
unsigned bitCount = 2, g, b;
|
||||
|
||||
m_auBitStream.write (elData.tnsActive ? 1 : 0, 1); // tns_active
|
||||
m_auBitStream.write (elData.commonWindow ? 1 : 0, 1);
|
||||
if (elData.commonWindow)
|
||||
{
|
||||
const unsigned maxSfbSte = __max (icsInfo0.maxSfb, icsInfo1.maxSfb);
|
||||
const unsigned sfb1Bits = icsInfo1.windowSequence == EIGHT_SHORT ? 4 : 6;
|
||||
|
||||
bitCount += writeChannelWiseIcsInfo (icsInfo0); // ics_info()
|
||||
m_auBitStream.write (elData.commonMaxSfb ? 1 : 0, 1);
|
||||
if (!elData.commonMaxSfb)
|
||||
{
|
||||
m_auBitStream.write (icsInfo1.maxSfb, sfb1Bits); // max_sfb1
|
||||
bitCount += sfb1Bits;
|
||||
}
|
||||
m_auBitStream.write (__min (3, elData.stereoMode), 2); // ms_mask_present
|
||||
bitCount += 3;
|
||||
if (elData.stereoMode == 1) // write SFB-wise ms_used[][] flag
|
||||
{
|
||||
for (g = 0; g < elData.groupingData[0].numWindowGroups; g++)
|
||||
{
|
||||
const char* const gMsUsed = &elData.stereoData[m_numSwbShort * g];
|
||||
|
||||
for (b = 0; b < maxSfbSte; b++)
|
||||
{
|
||||
m_auBitStream.write (gMsUsed[b] > 0 ? 1 : 0, 1);
|
||||
}
|
||||
}
|
||||
bitCount += maxSfbSte * g;
|
||||
}
|
||||
#if !RESTRICT_TO_AAC
|
||||
else if (elData.stereoMode >= 3) // SFB-wise cplx_pred_data()
|
||||
{
|
||||
m_auBitStream.write (elData.stereoMode - 3, 1); // _pred_all
|
||||
if (elData.stereoMode == 3)
|
||||
{
|
||||
for (g = 0; g < elData.groupingData[0].numWindowGroups; g++)
|
||||
{
|
||||
const char* const gCplxPredUsed = &elData.stereoData[m_numSwbShort * g];
|
||||
|
||||
for (b = 0; b < maxSfbSte; b += SFB_PER_PRED_BAND)
|
||||
{
|
||||
m_auBitStream.write (gCplxPredUsed[b] > 0 ? 1 : 0, 1);
|
||||
}
|
||||
}
|
||||
bitCount += ((maxSfbSte + 1) / SFB_PER_PRED_BAND) * g;
|
||||
}
|
||||
// pred_dir and complex_coef. TODO: rest of cplx_pred_data()
|
||||
m_auBitStream.write (elData.stereoConfig & 3, 2);
|
||||
bitCount += 3;
|
||||
}
|
||||
#endif
|
||||
} // common_window
|
||||
#if !RESTRICT_TO_AAC
|
||||
if (timeWarping)
|
||||
{
|
||||
m_auBitStream.write (0, 1); // common_tw not needed in xHE-AAC
|
||||
bitCount++;
|
||||
} // tw_mdct
|
||||
#endif
|
||||
if (elData.tnsActive)
|
||||
{
|
||||
if (elData.commonWindow)
|
||||
{
|
||||
m_auBitStream.write (elData.commonTnsData ? 1 : 0, 1);
|
||||
bitCount++;
|
||||
}
|
||||
m_auBitStream.write (elData.tnsOnLeftRight ? 1 : 0, 1);
|
||||
bitCount++;
|
||||
if (elData.commonTnsData)
|
||||
{
|
||||
bitCount += writeChannelWiseTnsData (tnsData0, icsInfo0.windowSequence == EIGHT_SHORT);
|
||||
}
|
||||
else // tns_present_both and tns_data_present[1]
|
||||
{
|
||||
const bool tnsPresentBoth = (tnsData0.numFilters > 0) && (tnsData1.numFilters > 0);
|
||||
|
||||
m_auBitStream.write (tnsPresentBoth ? 1 : 0, 1);
|
||||
bitCount++;
|
||||
if (!tnsPresentBoth)
|
||||
{
|
||||
m_auBitStream.write (tnsData1.numFilters > 0 ? 1 : 0, 1);
|
||||
bitCount++;
|
||||
}
|
||||
}
|
||||
} // tns_active
|
||||
|
||||
return bitCount;
|
||||
}
|
||||
|
||||
// public functions
|
||||
unsigned BitStreamWriter::createAudioConfig (const char samplingFrequencyIndex, const bool shortFrameLength,
|
||||
const uint8_t chConfigurationIndex, const uint8_t numElements,
|
||||
const ELEM_TYPE* const elementType, const bool configExtensionPresent,
|
||||
#if !RESTRICT_TO_AAC
|
||||
const bool* const tw_mdct /*N/A*/, const bool* const noiseFilling,
|
||||
#endif
|
||||
unsigned char* const audioConfig)
|
||||
{
|
||||
unsigned bitCount = 37;
|
||||
|
||||
if ((elementType == nullptr) || (audioConfig == nullptr) || (chConfigurationIndex >= USAC_MAX_NUM_ELCONFIGS) ||
|
||||
#if !RESTRICT_TO_AAC
|
||||
(noiseFilling == nullptr) || (tw_mdct == nullptr) ||
|
||||
#endif
|
||||
(numElements == 0) || (numElements > USAC_MAX_NUM_ELEMENTS) || (samplingFrequencyIndex < 0) || (samplingFrequencyIndex >= 0x1F))
|
||||
{
|
||||
return 0; // invalid arguments error
|
||||
}
|
||||
|
||||
m_auBitStream.reset ();
|
||||
// --- AudioSpecificConfig(): https://wiki.multimedia.cx/index.php/MPEG-4_Audio/
|
||||
m_auBitStream.write (0x7CA, 11); // audio object type (AOT) 32 (esc) + 10 = 42
|
||||
if (samplingFrequencyIndex < AAC_NUM_SAMPLE_RATES)
|
||||
{
|
||||
m_auBitStream.write (samplingFrequencyIndex, 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_auBitStream.write (0xF, 4); // esc
|
||||
m_auBitStream.write (toSamplingRate (samplingFrequencyIndex), 24);
|
||||
bitCount += 24;
|
||||
}
|
||||
// for multichannel audio, refer to channel mapping of AotSpecificConfig below
|
||||
m_auBitStream.write (chConfigurationIndex > 2 ? 0 : chConfigurationIndex, 4);
|
||||
|
||||
// --- AotSpecificConfig(): UsacConfig()
|
||||
m_auBitStream.write (samplingFrequencyIndex, 5); // usacSamplingFrequencyIndex
|
||||
m_auBitStream.write (shortFrameLength ? 0 : 1, 3); // coreSbrFrameLengthIndex
|
||||
m_auBitStream.write (chConfigurationIndex, 5); // channelConfigurationIndex
|
||||
m_auBitStream.write (numElements - 1, 4); // numElements in UsacDecoderConfig
|
||||
|
||||
for (unsigned el = 0; el < numElements; el++) // el element loop
|
||||
{
|
||||
m_auBitStream.write ((unsigned) elementType[el], 2); // usacElementType[el]
|
||||
bitCount += 2;
|
||||
if (elementType[el] < ID_USAC_LFE) // SCE, CPE: UsacCoreConfig
|
||||
{
|
||||
#if RESTRICT_TO_AAC
|
||||
m_auBitStream.write (0, 2); // time warping and noise filling not allowed
|
||||
#else
|
||||
m_auBitStream.write ((tw_mdct[el] ? 2 : 0) | (noiseFilling[el] ? 1 : 0), 2);
|
||||
#endif
|
||||
bitCount += 2;
|
||||
}
|
||||
} // for el
|
||||
|
||||
m_auBitStream.write (configExtensionPresent ? 1 : 0, 1); // usacConfigExten...
|
||||
if (configExtensionPresent) // 23003-4: loudnessInfo
|
||||
{
|
||||
m_auBitStream.write (0, 2); // numConfigExtensions
|
||||
m_auBitStream.write (ID_EXT_LOUDNESS_INFO, 4);
|
||||
m_auBitStream.write (8, 4); // usacConfigExtLength
|
||||
|
||||
m_auBitStream.write (1, 12);// loudnessInfoCount=1
|
||||
m_auBitStream.write (1, 14); // peakLevelPresent=1
|
||||
m_auBitStream.write (0, 12); // bsSamplePeakLevel
|
||||
m_auBitStream.write (1, 5); // measurementCount=1
|
||||
|
||||
m_auBitStream.write (1, 4); // methodDefinition=1
|
||||
m_auBitStream.write (0, 8); // methodValue storage
|
||||
m_auBitStream.write (0, 4); // measurementSystem=0
|
||||
m_auBitStream.write (3, 2); // reliability=3, good
|
||||
|
||||
m_auBitStream.write (0, 1); // ...SetExtPresent=0
|
||||
bitCount += 72;
|
||||
}
|
||||
|
||||
bitCount += (8 - m_auBitStream.heldBitCount) & 7;
|
||||
writeByteAlignment (); // flush bytes
|
||||
|
||||
memcpy (audioConfig, &m_auBitStream.stream.front (), __min (16, bitCount >> 3));
|
||||
|
||||
return (bitCount >> 3); // byte count
|
||||
}
|
||||
|
||||
unsigned BitStreamWriter::createAudioFrame (CoreCoderData** const elementData, EntropyCoder* const entropyCoder,
|
||||
int32_t** const mdctSignals, uint8_t** const mdctQuantMag,
|
||||
const bool usacIndependencyFlag, const uint8_t numElements,
|
||||
const uint8_t numSwbShort, uint8_t* const tempBuffer,
|
||||
#if !RESTRICT_TO_AAC
|
||||
const bool* const tw_mdct /*N/A*/, const bool* const noiseFilling,
|
||||
#endif
|
||||
unsigned char* const accessUnit, const unsigned nSamplesInFrame /*= 1024*/)
|
||||
{
|
||||
unsigned bitCount = 1, ci = 0;
|
||||
|
||||
if ((elementData == nullptr) || (entropyCoder == nullptr) || (tempBuffer == nullptr) ||
|
||||
(mdctSignals == nullptr) || (mdctQuantMag == nullptr) || (accessUnit == nullptr) || (nSamplesInFrame > 2048) ||
|
||||
#if !RESTRICT_TO_AAC
|
||||
(noiseFilling == nullptr) || (tw_mdct == nullptr) ||
|
||||
#endif
|
||||
(numElements == 0) || (numElements > USAC_MAX_NUM_ELEMENTS) || (numSwbShort < MIN_NUM_SWB_SHORT) || (numSwbShort > MAX_NUM_SWB_SHORT))
|
||||
{
|
||||
return 0; // invalid arguments error
|
||||
}
|
||||
|
||||
m_auBitStream.reset ();
|
||||
m_frameLength = nSamplesInFrame;
|
||||
m_numSwbShort = numSwbShort;
|
||||
m_uCharBuffer = tempBuffer;
|
||||
m_auBitStream.write (usacIndependencyFlag ? 1 : 0, 1);
|
||||
|
||||
for (unsigned el = 0; el < numElements; el++) // el element loop
|
||||
{
|
||||
const CoreCoderData* const elData = elementData[el];
|
||||
|
||||
if (elData == nullptr)
|
||||
{
|
||||
return 0; // internal memory error
|
||||
}
|
||||
switch (elData->elementType) // write out UsacCoreCoderData()
|
||||
{
|
||||
case ID_USAC_SCE: // UsacSingleChannelElement()
|
||||
{
|
||||
m_auBitStream.write (CORE_MODE_FD, 1);
|
||||
m_auBitStream.write (elData->tnsActive ? 1 : 0, 1); // tns_data_present
|
||||
bitCount += 2;
|
||||
bitCount += writeFDChannelStream (*elData, entropyCoder[ci], 0,
|
||||
mdctSignals[ci], mdctQuantMag[ci],
|
||||
#if !RESTRICT_TO_AAC
|
||||
tw_mdct[el], noiseFilling[el],
|
||||
#endif
|
||||
usacIndependencyFlag);
|
||||
ci++;
|
||||
break;
|
||||
}
|
||||
case ID_USAC_CPE: // UsacChannelPairElement()
|
||||
{
|
||||
m_auBitStream.write (CORE_MODE_FD, 1); // L
|
||||
m_auBitStream.write (CORE_MODE_FD, 1); // R
|
||||
bitCount += 2;
|
||||
bitCount += writeStereoCoreToolInfo (*elData,
|
||||
#if !RESTRICT_TO_AAC
|
||||
tw_mdct[el],
|
||||
#endif
|
||||
usacIndependencyFlag);
|
||||
bitCount += writeFDChannelStream (*elData, entropyCoder[ci], 0, // L
|
||||
mdctSignals[ci], mdctQuantMag[ci],
|
||||
#if !RESTRICT_TO_AAC
|
||||
tw_mdct[el], noiseFilling[el],
|
||||
#endif
|
||||
usacIndependencyFlag);
|
||||
ci++;
|
||||
bitCount += writeFDChannelStream (*elData, entropyCoder[ci], 1, // R
|
||||
mdctSignals[ci], mdctQuantMag[ci],
|
||||
#if !RESTRICT_TO_AAC
|
||||
tw_mdct[el], noiseFilling[el],
|
||||
#endif
|
||||
usacIndependencyFlag);
|
||||
ci++;
|
||||
break;
|
||||
}
|
||||
case ID_USAC_LFE: // UsacLfeElement()
|
||||
{
|
||||
bitCount += writeFDChannelStream (*elData, entropyCoder[ci], 0,
|
||||
mdctSignals[ci], mdctQuantMag[ci],
|
||||
#if !RESTRICT_TO_AAC
|
||||
false, false,
|
||||
#endif
|
||||
usacIndependencyFlag);
|
||||
ci++;
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
} // for el
|
||||
|
||||
bitCount += (8 - m_auBitStream.heldBitCount) & 7;
|
||||
writeByteAlignment (); // flush bytes
|
||||
|
||||
memcpy (accessUnit, &m_auBitStream.stream.front (), __min (768 * ci, bitCount >> 3));
|
||||
|
||||
return (bitCount >> 3); // byte count
|
||||
}
|
74
src/lib/bitStreamWriter.h
Normal file
74
src/lib/bitStreamWriter.h
Normal file
@ -0,0 +1,74 @@
|
||||
/* bitStreamWriter.h - header file for class with basic bit-stream writing capability
|
||||
* written by C. R. Helmrich, last modified in 2019 - see License.htm for legal notices
|
||||
*
|
||||
* The copyright in this software is being made available under a Modified BSD-Style 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-2020 Christian R. Helmrich, project ecodis. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef _BIT_STREAM_WRITER_H_
|
||||
#define _BIT_STREAM_WRITER_H_
|
||||
|
||||
#include "exhaleLibPch.h"
|
||||
#include "entropyCoding.h"
|
||||
|
||||
// constants, experimental macros
|
||||
#define CORE_MODE_FD 0
|
||||
#define ID_EXT_LOUDNESS_INFO 2
|
||||
#define ID_EXT_ELE_FILL 0
|
||||
#define SFB_PER_PRED_BAND 2
|
||||
|
||||
// output bit-stream writer class
|
||||
class BitStreamWriter
|
||||
{
|
||||
private:
|
||||
|
||||
// member variables
|
||||
OutputStream m_auBitStream; // access unit bit-stream to write
|
||||
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
|
||||
|
||||
// helper functions
|
||||
void writeByteAlignment (); // write 0s for byte alignment
|
||||
unsigned writeChannelWiseIcsInfo (const IcsInfo& icsInfo); // ics_info()
|
||||
unsigned writeChannelWiseTnsData (const TnsData& tnsData, const bool eightShorts);
|
||||
unsigned writeFDChannelStream (const CoreCoderData& elData, EntropyCoder& entrCoder, const unsigned ch,
|
||||
const int32_t* const mdctSignal, const uint8_t* const mdctQuantMag,
|
||||
#if !RESTRICT_TO_AAC
|
||||
const bool timeWarping, const bool noiseFilling,
|
||||
#endif
|
||||
const bool indepFlag = false);
|
||||
unsigned writeStereoCoreToolInfo (const CoreCoderData& elData,
|
||||
#if !RESTRICT_TO_AAC
|
||||
const bool timeWarping,
|
||||
#endif
|
||||
const bool indepFlag = false);
|
||||
|
||||
public:
|
||||
|
||||
// constructor
|
||||
BitStreamWriter () { m_auBitStream.reset (); m_frameLength = 0; m_numSwbShort = 0; m_uCharBuffer = nullptr; }
|
||||
// destructor
|
||||
~BitStreamWriter() { m_auBitStream.reset (); }
|
||||
// public functions
|
||||
unsigned createAudioConfig (const char samplingFrequencyIndex, const bool shortFrameLength,
|
||||
const uint8_t chConfigurationIndex, const uint8_t numElements,
|
||||
const ELEM_TYPE* const elementType, const bool configExtensionPresent,
|
||||
#if !RESTRICT_TO_AAC
|
||||
const bool* const tw_mdct /*N/A*/, const bool* const noiseFilling,
|
||||
#endif
|
||||
unsigned char* const audioConfig);
|
||||
unsigned createAudioFrame (CoreCoderData** const elementData, EntropyCoder* const entropyCoder,
|
||||
int32_t** const mdctSignals, uint8_t** const mdctQuantMag,
|
||||
const bool usacIndependencyFlag, const uint8_t numElements,
|
||||
const uint8_t numSwbShort, uint8_t* const tempBuffer,
|
||||
#if !RESTRICT_TO_AAC
|
||||
const bool* const tw_mdct /*N/A*/, const bool* const noiseFilling,
|
||||
#endif
|
||||
unsigned char* const audioFrame, const unsigned nSamplesInFrame = 1024);
|
||||
}; // BitStreamWriter
|
||||
|
||||
#endif // _BIT_STREAM_WRITER_H_
|
605
src/lib/entropyCoding.cpp
Normal file
605
src/lib/entropyCoding.cpp
Normal file
@ -0,0 +1,605 @@
|
||||
/* entropyCoding.cpp - source file for class with lossless entropy coding capability
|
||||
* written by C. R. Helmrich, last modified in 2019 - see License.htm for legal notices
|
||||
*
|
||||
* The copyright in this software is being made available under a Modified BSD-Style 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-2020 Christian R. Helmrich, project ecodis. All rights reserved.
|
||||
*/
|
||||
|
||||
#include "exhaleLibPch.h"
|
||||
#include "entropyCoding.h"
|
||||
|
||||
// ISO/IEC 14496-3, Annex 4.A.1
|
||||
static const uint32_t huffScf[INDEX_SIZE] = { // upper 3 bytes: value, lower byte: length
|
||||
0x03FFE812, 0x03FFE612, 0x03FFE712, 0x03FFE512, 0x07FFF513, 0x07FFF113, 0x07FFED13, 0x07FFF613,
|
||||
0x07FFEE13, 0x07FFEF13, 0x07FFF013, 0x07FFFC13, 0x07FFFD13, 0x07FFFF13, 0x07FFFE13, 0x07FFF713,
|
||||
0x07FFF813, 0x07FFFB13, 0x07FFF913, 0x03FFE412, 0x07FFFA13, 0x03FFE312, 0x01FFEF11, 0x01FFF011,
|
||||
0x00FFF510, 0x01FFEE11, 0x00FFF210, 0x00FFF310, 0x00FFF410, 0x00FFF110, 0x007FF60F, 0x007FF70F,
|
||||
0x003FF90E, 0x003FF50E, 0x003FF70E, 0x003FF30E, 0x003FF60E, 0x003FF20E, 0x001FF70D, 0x001FF50D,
|
||||
0x000FF90C, 0x000FF70C, 0x000FF60C, 0x0007F90B, 0x000FF40C, 0x0007F80B, 0x0003F90A, 0x0003F70A,
|
||||
0x0003F50A, 0x0001F809, 0x0001F709, 0x0000FA08, 0x0000F808, 0x0000F608, 0x00007907, 0x00003A06,
|
||||
0x00003806, 0x00001A05, 0x00000B04, 0x00000403, 0x00000001, 0x00000A04, 0x00000C04, 0x00001B05, 0x00003906,
|
||||
0x00003B06, 0x00007807, 0x00007A07, 0x0000F708, 0x0000F908, 0x0001F609, 0x0001F909, 0x0003F40A,
|
||||
0x0003F60A, 0x0003F80A, 0x0007F50B, 0x0007F40B, 0x0007F60B, 0x0007F70B, 0x000FF50C, 0x000FF80C,
|
||||
0x001FF40D, 0x001FF60D, 0x001FF80D, 0x003FF80E, 0x003FF40E, 0x00FFF010, 0x007FF40F, 0x00FFF610,
|
||||
0x007FF50F, 0x03FFE212, 0x07FFD913, 0x07FFDA13, 0x07FFDB13, 0x07FFDC13, 0x07FFDD13, 0x07FFDE13,
|
||||
0x07FFD813, 0x07FFD213, 0x07FFD313, 0x07FFD413, 0x07FFD513, 0x07FFD613, 0x07FFF213, 0x07FFDF13,
|
||||
0x07FFE713, 0x07FFE813, 0x07FFE913, 0x07FFEA13, 0x07FFEB13, 0x07FFE613, 0x07FFE013, 0x07FFE113,
|
||||
0x07FFE213, 0x07FFE313, 0x07FFE413, 0x07FFE513, 0x07FFD713, 0x07FFEC13, 0x07FFF413, 0x07FFF313
|
||||
};
|
||||
|
||||
// ISO/IEC 23003-3, Annex C.1
|
||||
static const uint8_t arithLookupM[ARITH_SIZE] = { // arith_lookup_m
|
||||
0x01, 0x34, 0x0D, 0x13, 0x12, 0x25, 0x00, 0x3A, 0x05, 0x00, 0x21, 0x13, 0x1F, 0x1A, 0x1D, 0x36,
|
||||
0x24, 0x2B, 0x1B, 0x33, 0x37, 0x29, 0x1D, 0x33, 0x37, 0x33, 0x37, 0x33, 0x37, 0x33, 0x2C, 0x00,
|
||||
0x21, 0x13, 0x25, 0x2A, 0x00, 0x21, 0x24, 0x12, 0x2C, 0x1E, 0x37, 0x24, 0x1F, 0x35, 0x37, 0x24,
|
||||
0x35, 0x37, 0x35, 0x37, 0x38, 0x2D, 0x21, 0x29, 0x1E, 0x21, 0x13, 0x2D, 0x36, 0x38, 0x29, 0x36,
|
||||
0x37, 0x24, 0x36, 0x38, 0x37, 0x38, 0x00, 0x20, 0x23, 0x20, 0x23, 0x36, 0x38, 0x24, 0x3B, 0x24,
|
||||
0x26, 0x29, 0x1F, 0x30, 0x2D, 0x0D, 0x12, 0x3F, 0x2D, 0x21, 0x1C, 0x2A, 0x00, 0x21, 0x12, 0x1E,
|
||||
0x36, 0x38, 0x36, 0x37, 0x3F, 0x1E, 0x0D, 0x1F, 0x2A, 0x1E, 0x21, 0x24, 0x12, 0x2A, 0x3C, 0x21,
|
||||
0x24, 0x1F, 0x3C, 0x21, 0x29, 0x36, 0x38, 0x36, 0x37, 0x38, 0x21, 0x1E, 0x00, 0x3B, 0x25, 0x1E,
|
||||
0x20, 0x10, 0x1F, 0x3C, 0x20, 0x23, 0x29, 0x08, 0x23, 0x12, 0x08, 0x23, 0x21, 0x38, 0x00, 0x20,
|
||||
0x13, 0x20, 0x3B, 0x1C, 0x20, 0x3B, 0x29, 0x20, 0x23, 0x24, 0x21, 0x24, 0x21, 0x24, 0x3B, 0x13,
|
||||
0x23, 0x26, 0x23, 0x13, 0x21, 0x24, 0x26, 0x29, 0x12, 0x22, 0x2B, 0x02, 0x1E, 0x0D, 0x1F, 0x2D,
|
||||
0x00, 0x0D, 0x12, 0x00, 0x3C, 0x21, 0x29, 0x3C, 0x21, 0x2A, 0x3C, 0x3B, 0x22, 0x1E, 0x20, 0x10,
|
||||
0x1F, 0x3C, 0x0D, 0x29, 0x3C, 0x21, 0x24, 0x08, 0x23, 0x20, 0x38, 0x39, 0x3C, 0x20, 0x13, 0x3C,
|
||||
0x00, 0x0D, 0x13, 0x1F, 0x3C, 0x09, 0x26, 0x1F, 0x08, 0x09, 0x26, 0x12, 0x08, 0x23, 0x29, 0x20,
|
||||
0x23, 0x21, 0x24, 0x20, 0x13, 0x20, 0x3B, 0x16, 0x20, 0x3B, 0x29, 0x20, 0x3B, 0x29, 0x20, 0x3B,
|
||||
0x13, 0x21, 0x24, 0x29, 0x0B, 0x13, 0x09, 0x3B, 0x13, 0x09, 0x3B, 0x13, 0x21, 0x3B, 0x13, 0x0D,
|
||||
0x26, 0x29, 0x26, 0x29, 0x3D, 0x12, 0x22, 0x28, 0x2E, 0x04, 0x08, 0x13, 0x3C, 0x3B, 0x3C, 0x20,
|
||||
0x10, 0x3C, 0x21, 0x07, 0x08, 0x10, 0x00, 0x08, 0x0D, 0x29, 0x08, 0x0D, 0x29, 0x08, 0x09, 0x13,
|
||||
0x20, 0x23, 0x39, 0x08, 0x09, 0x13, 0x08, 0x09, 0x16, 0x08, 0x09, 0x10, 0x12, 0x20, 0x3B, 0x3D,
|
||||
0x09, 0x26, 0x20, 0x3B, 0x24, 0x39, 0x09, 0x26, 0x20, 0x0D, 0x13, 0x00, 0x09, 0x13, 0x20, 0x0D,
|
||||
0x26, 0x12, 0x20, 0x3B, 0x13, 0x21, 0x26, 0x0B, 0x12, 0x09, 0x3B, 0x16, 0x09, 0x3B, 0x3D, 0x09,
|
||||
0x26, 0x0D, 0x13, 0x26, 0x3D, 0x1C, 0x12, 0x1F, 0x28, 0x2E, 0x07, 0x0B, 0x08, 0x09, 0x00, 0x39,
|
||||
0x0B, 0x08, 0x26, 0x08, 0x09, 0x13, 0x20, 0x0B, 0x39, 0x10, 0x39, 0x0D, 0x13, 0x20, 0x10, 0x12,
|
||||
0x09, 0x13, 0x20, 0x3B, 0x13, 0x09, 0x26, 0x0B, 0x09, 0x3B, 0x1C, 0x09, 0x3B, 0x13, 0x20, 0x3B,
|
||||
0x13, 0x09, 0x26, 0x0B, 0x16, 0x0D, 0x13, 0x09, 0x13, 0x09, 0x13, 0x26, 0x3D, 0x1C, 0x1F, 0x28,
|
||||
0x2E, 0x07, 0x10, 0x39, 0x0B, 0x39, 0x39, 0x13, 0x39, 0x0B, 0x39, 0x0B, 0x39, 0x26, 0x39, 0x10,
|
||||
0x20, 0x3B, 0x16, 0x20, 0x10, 0x09, 0x26, 0x0B, 0x13, 0x09, 0x13, 0x26, 0x1C, 0x0B, 0x3D, 0x1C,
|
||||
0x1F, 0x28, 0x2B, 0x07, 0x0C, 0x39, 0x0B, 0x39, 0x0B, 0x0C, 0x0B, 0x26, 0x0B, 0x26, 0x3D, 0x0D,
|
||||
0x1C, 0x14, 0x28, 0x2B, 0x39, 0x0B, 0x0C, 0x0E, 0x3D, 0x1C, 0x0D, 0x12, 0x22, 0x2B, 0x07, 0x0C,
|
||||
0x0E, 0x3D, 0x1C, 0x10, 0x1F, 0x2B, 0x0C, 0x0E, 0x19, 0x14, 0x10, 0x1F, 0x28, 0x0C, 0x0E, 0x19,
|
||||
0x14, 0x26, 0x22, 0x2B, 0x0C, 0x0E, 0x19, 0x14, 0x26, 0x28, 0x0E, 0x19, 0x14, 0x26, 0x28, 0x0E,
|
||||
0x19, 0x14, 0x28, 0x0E, 0x19, 0x14, 0x22, 0x28, 0x2B, 0x0E, 0x14, 0x2B, 0x31, 0x00, 0x3A, 0x3A,
|
||||
0x05, 0x05, 0x1B, 0x1D, 0x33, 0x06, 0x35, 0x35, 0x20, 0x21, 0x37, 0x21, 0x24, 0x05, 0x1B, 0x2C,
|
||||
0x2C, 0x2C, 0x06, 0x34, 0x1E, 0x34, 0x00, 0x08, 0x36, 0x09, 0x21, 0x26, 0x1C, 0x2C, 0x00, 0x02,
|
||||
0x02, 0x02, 0x3F, 0x04, 0x04, 0x04, 0x34, 0x39, 0x20, 0x0A, 0x0C, 0x39, 0x0B, 0x0F, 0x07, 0x07,
|
||||
0x07, 0x07, 0x34, 0x39, 0x39, 0x0A, 0x0C, 0x39, 0x0C, 0x0F, 0x07, 0x07, 0x07, 0x00, 0x39, 0x39,
|
||||
0x0C, 0x0F, 0x07, 0x07, 0x39, 0x0C, 0x0F, 0x07, 0x39, 0x0C, 0x0F, 0x39, 0x39, 0x0C, 0x0F, 0x39,
|
||||
0x0C, 0x39, 0x0C, 0x0F, 0x00, 0x11, 0x27, 0x17, 0x2F, 0x27, 0x00, 0x27, 0x17, 0x00, 0x11, 0x17,
|
||||
0x00, 0x11, 0x17, 0x11, 0x00, 0x27, 0x15, 0x11, 0x17, 0x01, 0x15, 0x11, 0x15, 0x11, 0x15, 0x15,
|
||||
0x17, 0x00, 0x27, 0x01, 0x27, 0x27, 0x15, 0x00, 0x27, 0x11, 0x27, 0x15, 0x15, 0x15, 0x27, 0x15,
|
||||
0x15, 0x15, 0x15, 0x17, 0x2F, 0x11, 0x17, 0x27, 0x27, 0x27, 0x11, 0x27, 0x15, 0x27, 0x27, 0x15,
|
||||
0x15, 0x27, 0x17, 0x2F, 0x27, 0x17, 0x2F, 0x27, 0x17, 0x2F, 0x27, 0x17, 0x2F, 0x27, 0x17, 0x2F,
|
||||
0x27, 0x17, 0x2F, 0x27, 0x17, 0x2F, 0x27, 0x17, 0x2F, 0x27, 0x17, 0x2F, 0x27, 0x17, 0x2F, 0x27,
|
||||
0x17, 0x2F, 0x27, 0x17, 0x2F, 0x27, 0x17, 0x2F, 0x17, 0x2F, 0x2B, 0x00, 0x27, 0x00, 0x00, 0x11,
|
||||
0x15, 0x00, 0x11, 0x11, 0x27, 0x27, 0x15, 0x17, 0x15, 0x17, 0x15, 0x17, 0x27, 0x17, 0x27, 0x17,
|
||||
0x27, 0x17, 0x27, 0x17, 0x27, 0x17, 0x27, 0x17, 0x27, 0x17, 0x27, 0x17, 0x27, 0x17, 0x27, 0x17,
|
||||
0x27, 0x15, 0x27, 0x27, 0x15, 0x27
|
||||
};
|
||||
|
||||
// ISO/IEC 23003-3, Annex C.2
|
||||
static const uint32_t arithHashM[ARITH_SIZE] = { // arith_hash_m
|
||||
0x00000104, 0x0000030A, 0x00000510, 0x00000716, 0x00000A1F, 0x00000F2E, 0x00011100, 0x00111103,
|
||||
0x00111306, 0x00111436, 0x00111623, 0x00111929, 0x00111F2E, 0x0011221B, 0x00112435, 0x00112621,
|
||||
0x00112D12, 0x00113130, 0x0011331D, 0x00113535, 0x00113938, 0x0011411B, 0x00114433, 0x00114635,
|
||||
0x00114F29, 0x00116635, 0x00116F24, 0x00117433, 0x0011FF0F, 0x00121102, 0x0012132D, 0x00121436,
|
||||
0x00121623, 0x00121912, 0x0012213F, 0x0012232D, 0x00122436, 0x00122638, 0x00122A29, 0x00122F2B,
|
||||
0x0012322D, 0x00123436, 0x00123738, 0x00123B29, 0x0012411D, 0x00124536, 0x00124938, 0x00124F12,
|
||||
0x00125535, 0x00125F29, 0x00126535, 0x0012B837, 0x0013112A, 0x0013131E, 0x0013163B, 0x0013212D,
|
||||
0x0013233C, 0x00132623, 0x00132F2E, 0x0013321E, 0x00133521, 0x00133824, 0x0013411E, 0x00134336,
|
||||
0x00134838, 0x00135135, 0x00135537, 0x00135F12, 0x00137637, 0x0013FF29, 0x00140024, 0x00142321,
|
||||
0x00143136, 0x00143321, 0x00143F25, 0x00144321, 0x00148638, 0x0014FF29, 0x00154323, 0x0015FF12,
|
||||
0x0016F20C, 0x0018A529, 0x00210031, 0x0021122C, 0x00211408, 0x00211713, 0x00211F2E, 0x0021222A,
|
||||
0x00212408, 0x00212710, 0x00212F2E, 0x0021331E, 0x00213436, 0x00213824, 0x0021412D, 0x0021431E,
|
||||
0x00214536, 0x00214F1F, 0x00216637, 0x00220004, 0x0022122A, 0x00221420, 0x00221829, 0x00221F2E,
|
||||
0x0022222D, 0x00222408, 0x00222623, 0x00222929, 0x00222F2B, 0x0022321E, 0x00223408, 0x00223724,
|
||||
0x00223A29, 0x0022411E, 0x00224436, 0x00224823, 0x00225134, 0x00225621, 0x00225F12, 0x00226336,
|
||||
0x00227637, 0x0022FF29, 0x0023112D, 0x0023133C, 0x00231420, 0x00231916, 0x0023212D, 0x0023233C,
|
||||
0x00232509, 0x00232929, 0x0023312D, 0x00233308, 0x00233509, 0x00233724, 0x0023413C, 0x00234421,
|
||||
0x00234A13, 0x0023513C, 0x00235421, 0x00235F1F, 0x00236421, 0x0023FF29, 0x00240024, 0x0024153B,
|
||||
0x00242108, 0x00242409, 0x00242726, 0x00243108, 0x00243409, 0x00243610, 0x00244136, 0x00244321,
|
||||
0x00244523, 0x00244F1F, 0x00245423, 0x0024610A, 0x00246423, 0x0024FF29, 0x00252510, 0x00253121,
|
||||
0x0025343B, 0x00254121, 0x00254510, 0x00254F25, 0x00255221, 0x0025FF12, 0x00266513, 0x0027F529,
|
||||
0x0029F101, 0x002CF224, 0x00310030, 0x0031122A, 0x00311420, 0x00311816, 0x0031212C, 0x0031231E,
|
||||
0x00312408, 0x00312710, 0x0031312A, 0x0031321E, 0x00313408, 0x00313623, 0x0031411E, 0x0031433C,
|
||||
0x00320007, 0x0032122D, 0x00321420, 0x00321816, 0x0032212D, 0x0032233C, 0x00322509, 0x00322916,
|
||||
0x0032312D, 0x00323420, 0x00323710, 0x00323F2B, 0x00324308, 0x00324623, 0x00324F25, 0x00325421,
|
||||
0x00325F1F, 0x00326421, 0x0032FF29, 0x00331107, 0x00331308, 0x0033150D, 0x0033211E, 0x00332308,
|
||||
0x00332420, 0x00332610, 0x00332929, 0x0033311E, 0x00333308, 0x0033363B, 0x00333A29, 0x0033413C,
|
||||
0x00334320, 0x0033463B, 0x00334A29, 0x0033510A, 0x00335320, 0x00335824, 0x0033610A, 0x00336321,
|
||||
0x00336F12, 0x00337623, 0x00341139, 0x0034153B, 0x00342108, 0x00342409, 0x00342610, 0x00343108,
|
||||
0x00343409, 0x00343610, 0x00344108, 0x0034440D, 0x00344610, 0x0034510A, 0x00345309, 0x0034553B,
|
||||
0x0034610A, 0x00346309, 0x0034F824, 0x00350029, 0x00352510, 0x00353120, 0x0035330D, 0x00353510,
|
||||
0x00354120, 0x0035430D, 0x00354510, 0x00354F28, 0x0035530D, 0x00355510, 0x00355F1F, 0x00356410,
|
||||
0x00359626, 0x0035FF12, 0x00366426, 0x0036FF12, 0x0037F426, 0x0039D712, 0x003BF612, 0x003DF81F,
|
||||
0x00410004, 0x00411207, 0x0041150D, 0x0041212A, 0x00412420, 0x0041311E, 0x00413308, 0x00413509,
|
||||
0x00413F2B, 0x00414208, 0x00420007, 0x0042123C, 0x00421409, 0x00422107, 0x0042223C, 0x00422409,
|
||||
0x00422610, 0x0042313C, 0x00423409, 0x0042363B, 0x0042413C, 0x00424320, 0x0042463B, 0x00425108,
|
||||
0x00425409, 0x0042FF29, 0x00431107, 0x00431320, 0x0043153B, 0x0043213C, 0x00432320, 0x00432610,
|
||||
0x0043313C, 0x00433320, 0x0043353B, 0x00433813, 0x00434108, 0x00434409, 0x00434610, 0x00435108,
|
||||
0x0043553B, 0x00435F25, 0x00436309, 0x0043753B, 0x0043FF29, 0x00441239, 0x0044143B, 0x00442139,
|
||||
0x00442309, 0x0044253B, 0x00443108, 0x00443220, 0x0044353B, 0x0044410A, 0x00444309, 0x0044453B,
|
||||
0x00444813, 0x0044510A, 0x00445309, 0x00445510, 0x00445F25, 0x0044630D, 0x00450026, 0x00452713,
|
||||
0x00453120, 0x0045330D, 0x00453510, 0x00454120, 0x0045430D, 0x00454510, 0x00455120, 0x0045530D,
|
||||
0x00456209, 0x00456410, 0x0045FF12, 0x00466513, 0x0047FF22, 0x0048FF25, 0x0049F43D, 0x004BFB25,
|
||||
0x004EF825, 0x004FFF18, 0x00511339, 0x00512107, 0x00513409, 0x00520007, 0x00521107, 0x00521320,
|
||||
0x00522107, 0x00522409, 0x0052313C, 0x00523320, 0x0052353B, 0x00524108, 0x00524320, 0x00531139,
|
||||
0x00531309, 0x00532139, 0x00532309, 0x0053253B, 0x00533108, 0x0053340D, 0x00533713, 0x00534108,
|
||||
0x0053453B, 0x00534F2B, 0x00535309, 0x00535610, 0x00535F25, 0x0053643B, 0x00541139, 0x00542139,
|
||||
0x00542309, 0x00542613, 0x00543139, 0x00543309, 0x00543510, 0x00543F2B, 0x00544309, 0x00544510,
|
||||
0x00544F28, 0x0054530D, 0x0054FF12, 0x00553613, 0x00553F2B, 0x00554410, 0x0055510A, 0x0055543B,
|
||||
0x00555F25, 0x0055633B, 0x0055FF12, 0x00566513, 0x00577413, 0x0059FF28, 0x005CC33D, 0x005EFB28,
|
||||
0x005FFF18, 0x00611339, 0x00612107, 0x00613320, 0x0061A724, 0x00621107, 0x0062140B, 0x00622107,
|
||||
0x00622320, 0x00623139, 0x00623320, 0x00631139, 0x0063130C, 0x00632139, 0x00632309, 0x00633139,
|
||||
0x00633309, 0x00633626, 0x00633F2B, 0x00634309, 0x00634F2B, 0x0063543B, 0x0063FF12, 0x0064343B,
|
||||
0x00643F2B, 0x0064443B, 0x00645209, 0x00665513, 0x0066610A, 0x00666526, 0x0067A616, 0x0069843D,
|
||||
0x006CF612, 0x006EF326, 0x006FFF18, 0x0071130C, 0x00721107, 0x00722239, 0x0072291C, 0x0072340B,
|
||||
0x00731139, 0x00732239, 0x0073630B, 0x0073FF12, 0x0074430B, 0x00755426, 0x00776F28, 0x00777410,
|
||||
0x0078843D, 0x007CF416, 0x007EF326, 0x007FFF18, 0x00822239, 0x00831139, 0x0083430B, 0x0084530B,
|
||||
0x0087561C, 0x00887F25, 0x00888426, 0x008AF61C, 0x008F0018, 0x008FFF18, 0x00911107, 0x0093230B,
|
||||
0x0094530B, 0x0097743D, 0x00998C25, 0x00999616, 0x009EF825, 0x009FFF18, 0x00A3430B, 0x00A4530B,
|
||||
0x00A7743D, 0x00AA9F2B, 0x00AAA616, 0x00ABD61F, 0x00AFFF18, 0x00B3330B, 0x00B44426, 0x00B7643D,
|
||||
0x00BB971F, 0x00BBB53D, 0x00BEF512, 0x00BFFF18, 0x00C22139, 0x00C5330E, 0x00C7633D, 0x00CCAF2E,
|
||||
0x00CCC616, 0x00CFFF18, 0x00D4440E, 0x00D6420E, 0x00DDCF2E, 0x00DDD516, 0x00DFFF18, 0x00E4330E,
|
||||
0x00E6841C, 0x00EEE61C, 0x00EFFF18, 0x00F3320E, 0x00F55319, 0x00F8F41C, 0x00FAFF2E, 0x00FF002E,
|
||||
0x00FFF10C, 0x00FFF33D, 0x00FFF722, 0x00FFFF18, 0x01000232, 0x0111113E, 0x01112103, 0x0111311A,
|
||||
0x0112111A, 0x01122130, 0x01123130, 0x0112411D, 0x01131102, 0x01132102, 0x01133102, 0x01141108,
|
||||
0x01142136, 0x01143136, 0x01144135, 0x0115223B, 0x01211103, 0x0121211A, 0x01213130, 0x01221130,
|
||||
0x01222130, 0x01223102, 0x01231104, 0x01232104, 0x01233104, 0x01241139, 0x01241220, 0x01242220,
|
||||
0x01251109, 0x0125223B, 0x0125810A, 0x01283212, 0x0131111A, 0x01312130, 0x0131222C, 0x0131322A,
|
||||
0x0132122A, 0x0132222D, 0x0132322D, 0x01331207, 0x01332234, 0x01333234, 0x01341139, 0x01343134,
|
||||
0x01344134, 0x01348134, 0x0135220B, 0x0136110B, 0x01365224, 0x01411102, 0x01412104, 0x01431239,
|
||||
0x01432239, 0x0143320A, 0x01435134, 0x01443107, 0x01444134, 0x01446134, 0x0145220E, 0x01455134,
|
||||
0x0147110E, 0x01511102, 0x01521239, 0x01531239, 0x01532239, 0x01533107, 0x0155220E, 0x01555134,
|
||||
0x0157110E, 0x01611107, 0x01621239, 0x01631239, 0x01661139, 0x01666134, 0x01711107, 0x01721239,
|
||||
0x01745107, 0x0177110C, 0x01811107, 0x01821107, 0x0185110C, 0x0188210C, 0x01911107, 0x01933139,
|
||||
0x01A11107, 0x01A31139, 0x01F5220E, 0x02000001, 0x02000127, 0x02000427, 0x02000727, 0x02000E2F,
|
||||
0x02110000, 0x02111200, 0x02111411, 0x02111827, 0x02111F2F, 0x02112411, 0x02112715, 0x02113200,
|
||||
0x02113411, 0x02113715, 0x02114200, 0x02121200, 0x02121301, 0x02121F2F, 0x02122200, 0x02122615,
|
||||
0x02122F2F, 0x02123311, 0x02123F2F, 0x02124411, 0x02131211, 0x02132311, 0x02133211, 0x02184415,
|
||||
0x02211200, 0x02211311, 0x02211F2F, 0x02212311, 0x02212F2F, 0x02213211, 0x02221201, 0x02221311,
|
||||
0x02221F2F, 0x02222311, 0x02222F2F, 0x02223211, 0x02223F2F, 0x02231211, 0x02232211, 0x02232F2F,
|
||||
0x02233211, 0x02233F2F, 0x02287515, 0x022DAB17, 0x02311211, 0x02311527, 0x02312211, 0x02321211,
|
||||
0x02322211, 0x02322F2F, 0x02323311, 0x02323F2F, 0x02331211, 0x02332211, 0x02332F2F, 0x02333F2F,
|
||||
0x0237FF17, 0x02385615, 0x023D9517, 0x02410027, 0x02487827, 0x024E3117, 0x024FFF2F, 0x02598627,
|
||||
0x025DFF2F, 0x025FFF2F, 0x02687827, 0x026DFA17, 0x026FFF2F, 0x02796427, 0x027E4217, 0x027FFF2F,
|
||||
0x02888727, 0x028EFF2F, 0x028FFF2F, 0x02984327, 0x029F112F, 0x029FFF2F, 0x02A76527, 0x02AEF717,
|
||||
0x02AFFF2F, 0x02B7C827, 0x02BEF917, 0x02BFFF2F, 0x02C66527, 0x02CD5517, 0x02CFFF2F, 0x02D63227,
|
||||
0x02DDD527, 0x02DFFF2B, 0x02E84717, 0x02EEE327, 0x02EFFF2F, 0x02F54527, 0x02FCF817, 0x02FFEF2B,
|
||||
0x02FFFA2F, 0x02FFFE2F, 0x03000127, 0x03000201, 0x03111200, 0x03122115, 0x03123200, 0x03133211,
|
||||
0x03211200, 0x03213127, 0x03221200, 0x03345215, 0x04000F17, 0x04122F17, 0x043F6515, 0x043FFF17,
|
||||
0x044F5527, 0x044FFF17, 0x045F0017, 0x045FFF17, 0x046F6517, 0x04710027, 0x047F4427, 0x04810027,
|
||||
0x048EFA15, 0x048FFF2F, 0x049F4427, 0x049FFF2F, 0x04AEA727, 0x04AFFF2F, 0x04BE9C15, 0x04BFFF2F,
|
||||
0x04CE5427, 0x04CFFF2F, 0x04DE3527, 0x04DFFF17, 0x04EE4627, 0x04EFFF17, 0x04FEF327, 0x04FFFF2F,
|
||||
0x06000F27, 0x069FFF17, 0x06FFFF17, 0x08110017, 0x08EFFF15, 0xFFFFFF00
|
||||
};
|
||||
|
||||
// ISO/IEC 23003-3, Annex C.3
|
||||
static const uint16_t arithCumFreqM[64][ARITH_ESCAPE + 1] = { // arith_cf_m
|
||||
{ 708, 706, 579, 569, 568, 567, 479, 469, 297, 138, 97, 91, 72, 52, 38, 34, 0},
|
||||
{ 7619, 6917, 6519, 6412, 5514, 5003, 4683, 4563, 3907, 3297, 3125, 3060, 2904, 2718, 2631, 2590, 0},
|
||||
{ 7263, 4888, 4810, 4803, 1889, 415, 335, 327, 195, 72, 52, 49, 36, 20, 15, 14, 0},
|
||||
{ 3626, 2197, 2188, 2187, 582, 57, 47, 46, 30, 12, 9, 8, 6, 4, 3, 2, 0},
|
||||
{ 7806, 5541, 5451, 5441, 2720, 834, 691, 674, 487, 243, 179, 167, 139, 98, 77, 70, 0},
|
||||
{ 6684, 4101, 4058, 4055, 1748, 426, 368, 364, 322, 257, 235, 232, 228, 222, 217, 215, 0},
|
||||
{ 9162, 5964, 5831, 5819, 3269, 866, 658, 638, 535, 348, 258, 244, 234, 214, 195, 186, 0},
|
||||
{10638, 8491, 8365, 8351, 4418, 2067, 1859, 1834, 1190, 601, 495, 478, 356, 217, 174, 164, 0},
|
||||
{13389,10514,10032, 9961, 7166, 3488, 2655, 2524, 2015, 1140, 760, 672, 585, 426, 325, 283, 0},
|
||||
{14861,12788,12115,11952, 9987, 6657, 5323, 4984, 4324, 3001, 2205, 1943, 1764, 1394, 1115, 978, 0},
|
||||
{12876,10004, 9661, 9610, 7107, 3435, 2711, 2595, 2257, 1508, 1059, 952, 893, 753, 609, 538, 0},
|
||||
{15125,13591,13049,12874,11192, 8543, 7406, 7023, 6291, 4922, 4104, 3769, 3465, 2890, 2486, 2275, 0},
|
||||
{14574,13106,12731,12638,10453, 7947, 7233, 7037, 6031, 4618, 4081, 3906, 3465, 2802, 2476, 2349, 0},
|
||||
{15070,13179,12517,12351,10742, 7657, 6200, 5825, 5264, 3998, 3014, 2662, 2510, 2153, 1799, 1564, 0},
|
||||
{15542,14466,14007,13844,12489,10409, 9481, 9132, 8305, 6940, 6193, 5867, 5458, 4743, 4291, 4047, 0},
|
||||
{15165,14384,14084,13934,12911,11485,10844,10513,10002, 8993, 8380, 8051, 7711, 7036, 6514, 6233, 0},
|
||||
{15642,14279,13625,13393,12348, 9971, 8405, 7858, 7335, 6119, 4918, 4376, 4185, 3719, 3231, 2860, 0},
|
||||
{13408,13407,11471,11218,11217,11216, 9473, 9216, 6480, 3689, 2857, 2690, 2256, 1732, 1405, 1302, 0},
|
||||
{16098,15584,15191,14931,14514,13578,12703,12103,11830,11172,10475, 9867, 9695, 9281, 8825, 8389, 0},
|
||||
{15844,14873,14277,13996,13230,11535,10205, 9543, 9107, 8086, 7085, 6419, 6214, 5713, 5195, 4731, 0},
|
||||
{16131,15720,15443,15276,14848,13971,13314,12910,12591,11874,11225,10788,10573,10077, 9585, 9209, 0},
|
||||
{16331,16330,12283,11435,11434,11433, 8725, 8049, 6065, 4138, 3187, 2842, 2529, 2171, 1907, 1745, 0},
|
||||
{16011,15292,14782,14528,14008,12767,11556,10921,10591, 9759, 8813, 8043, 7855, 7383, 6863, 6282, 0},
|
||||
{16380,16379,15159,14610,14609,14608,12859,12111,11046, 9536, 8348, 7713, 7216, 6533, 5964, 5546, 0},
|
||||
{16367,16333,16294,16253,16222,16143,16048,15947,15915,15832,15731,15619,15589,15512,15416,15310, 0},
|
||||
{15967,15319,14937,14753,14010,12638,11787,11360,10805, 9706, 8934, 8515, 8166, 7456, 6911, 6575, 0},
|
||||
{ 4906, 3005, 2985, 2984, 875, 102, 83, 81, 47, 17, 12, 11, 8, 5, 4, 3, 0},
|
||||
{ 7217, 4346, 4269, 4264, 1924, 428, 340, 332, 280, 203, 179, 175, 171, 164, 159, 157, 0},
|
||||
{16010,15415,15032,14805,14228,13043,12168,11634,11265,10419, 9645, 9110, 8892, 8378, 7850, 7437, 0},
|
||||
{ 8573, 5218, 5046, 5032, 2787, 771, 555, 533, 443, 286, 218, 205, 197, 181, 168, 162, 0},
|
||||
{11474, 8095, 7822, 7796, 4632, 1443, 1046, 1004, 748, 351, 218, 194, 167, 121, 93, 83, 0},
|
||||
{16152,15764,15463,15264,14925,14189,13536,13070,12846,12314,11763,11277,11131,10777,10383,10011, 0},
|
||||
{14187,11654,11043,10919, 8498, 4885, 3778, 3552, 2947, 1835, 1283, 1134, 998, 749, 585, 514, 0},
|
||||
{14162,11527,10759,10557, 8601, 5417, 4105, 3753, 3286, 2353, 1708, 1473, 1370, 1148, 959, 840, 0},
|
||||
{16205,15902,15669,15498,15213,14601,14068,13674,13463,12970,12471,12061,11916,11564,11183,10841, 0},
|
||||
{15043,12972,12092,11792,10265, 7446, 5934, 5379, 4883, 3825, 3036, 2647, 2507, 2185, 1901, 1699, 0},
|
||||
{15320,13694,12782,12352,11191, 8936, 7433, 6671, 6255, 5366, 4622, 4158, 4020, 3712, 3420, 3198, 0},
|
||||
{16255,16020,15768,15600,15416,14963,14440,14006,13875,13534,13137,12697,12602,12364,12084,11781, 0},
|
||||
{15627,14503,13906,13622,12557,10527, 9269, 8661, 8117, 6933, 5994, 5474, 5222, 4664, 4166, 3841, 0},
|
||||
{16366,16365,14547,14160,14159,14158,11969,11473, 8735, 6147, 4911, 4530, 3865, 3180, 2710, 2473, 0},
|
||||
{16257,16038,15871,15754,15536,15071,14673,14390,14230,13842,13452,13136,13021,12745,12434,12154, 0},
|
||||
{15855,14971,14338,13939,13239,11782,10585, 9805, 9444, 8623, 7846, 7254, 7079, 6673, 6262, 5923, 0},
|
||||
{ 9492, 6318, 6197, 6189, 3004, 652, 489, 477, 333, 143, 96, 90, 78, 60, 50, 47, 0},
|
||||
{16313,16191,16063,15968,15851,15590,15303,15082,14968,14704,14427,14177,14095,13899,13674,13457, 0},
|
||||
{ 8485, 5473, 5389, 5383, 2411, 494, 386, 377, 278, 150, 117, 112, 103, 89, 81, 78, 0},
|
||||
{10497, 7154, 6959, 6943, 3788, 1004, 734, 709, 517, 238, 152, 138, 120, 90, 72, 66, 0},
|
||||
{16317,16226,16127,16040,15955,15762,15547,15345,15277,15111,14922,14723,14671,14546,14396,14239, 0},
|
||||
{16382,16381,15858,15540,15539,15538,14704,14168,13768,13092,12452,11925,11683,11268,10841,10460, 0},
|
||||
{ 5974, 3798, 3758, 3755, 1275, 205, 166, 162, 95, 35, 26, 24, 18, 11, 8, 7, 0},
|
||||
{ 3532, 2258, 2246, 2244, 731, 135, 118, 115, 87, 45, 36, 34, 29, 21, 17, 16, 0},
|
||||
{ 7466, 4882, 4821, 4811, 2476, 886, 788, 771, 688, 531, 469, 457, 437, 400, 369, 361, 0},
|
||||
{ 9580, 5772, 5291, 5216, 3444, 1496, 1025, 928, 806, 578, 433, 384, 366, 331, 296, 273, 0},
|
||||
{10692, 7730, 7543, 7521, 4679, 1746, 1391, 1346, 1128, 692, 495, 458, 424, 353, 291, 268, 0},
|
||||
{11040, 7132, 6549, 6452, 4377, 1875, 1253, 1130, 958, 631, 431, 370, 346, 296, 253, 227, 0},
|
||||
{12687, 9332, 8701, 8585, 6266, 3093, 2182, 2004, 1683, 1072, 712, 608, 559, 458, 373, 323, 0},
|
||||
{13429, 9853, 8860, 8584, 6806, 4039, 2862, 2478, 2239, 1764, 1409, 1224, 1178, 1077, 979, 903, 0},
|
||||
{14685,12163,11061,10668, 9101, 6345, 4871, 4263, 3908, 3200, 2668, 2368, 2285, 2106, 1942, 1819, 0},
|
||||
{13295,11302,10999,10945, 7947, 5036, 4490, 4385, 3391, 2185, 1836, 1757, 1424, 998, 833, 785, 0},
|
||||
{ 4992, 2993, 2972, 2970, 1269, 575, 552, 549, 530, 505, 497, 495, 493, 489, 486, 485, 0},
|
||||
{15419,13862,13104,12819,11429, 8753, 7220, 6651, 6020, 4667, 3663, 3220, 2995, 2511, 2107, 1871, 0},
|
||||
{12468, 9263, 8912, 8873, 5758, 2193, 1625, 1556, 1187, 589, 371, 330, 283, 200, 149, 131, 0},
|
||||
{15870,15076,14615,14369,13586,12034,10990,10423, 9953, 8908, 8031, 7488, 7233, 6648, 6101, 5712, 0},
|
||||
{ 1693, 978, 976, 975, 194, 18, 16, 15, 11, 7, 6, 5, 4, 3, 2, 1, 0},
|
||||
{ 7992, 5218, 5147, 5143, 2152, 366, 282, 276, 173, 59, 38, 35, 27, 16, 11, 10, 0}
|
||||
};
|
||||
|
||||
// ISO/IEC 23003-3, Annex C.4
|
||||
static const uint16_t arithCumFreqR[3][4] = { // arith_cf_r
|
||||
{12571, 10569, 3696, 0},
|
||||
{12661, 5700, 3751, 0},
|
||||
{10827, 6884, 2929, 0}
|
||||
};
|
||||
|
||||
// static helper functions
|
||||
static inline unsigned arithGetPkIndex (const unsigned ctx) // cumul. frequency table index pki = arith_get_pk(c)
|
||||
{
|
||||
int iMax = ARITH_SIZE - 1;
|
||||
int iMin = -1;
|
||||
int i = iMin;
|
||||
uint32_t j, k;
|
||||
|
||||
while (iMax > iMin + 1)
|
||||
{
|
||||
i = iMin + ((iMax - iMin) >> 1);
|
||||
j = arithHashM[i];
|
||||
k = j >> 8;
|
||||
|
||||
if (ctx < k) iMax = i;
|
||||
else if (ctx > k) iMin = i;
|
||||
else return j & UCHAR_MAX;
|
||||
}
|
||||
|
||||
return arithLookupM[iMax]; // pki
|
||||
}
|
||||
|
||||
static inline unsigned writeSymbol (OutputStream* const stream, const bool leadingBitIs1, const uint16_t trailingBits)
|
||||
{
|
||||
stream->write (leadingBitIs1 ? 1 : 0, 1);
|
||||
stream->write (leadingBitIs1 ? 0 : (1u << trailingBits) - 1, (uint8_t) trailingBits);
|
||||
|
||||
return trailingBits + 1; // count
|
||||
}
|
||||
|
||||
// private helper functions
|
||||
unsigned EntropyCoder::arithCodeSymbol (const uint16_t symbol, const uint16_t* table, OutputStream* const stream /*= nullptr*/)
|
||||
{
|
||||
const unsigned range = m_acHigh + 1 - m_acLow;
|
||||
uint16_t high = m_acHigh;
|
||||
uint16_t low = m_acLow;
|
||||
unsigned bitCount = 0;
|
||||
|
||||
if (symbol > 0)
|
||||
{
|
||||
high = low + ((range * table[symbol - 1]) >> 14) - 1;
|
||||
}
|
||||
low += (range * table[symbol]) >> 14; // NOTE: the spec incorrectly reads "[symbol-1]" here
|
||||
|
||||
if (stream != nullptr) // write-out
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
if (high <= SHRT_MAX)
|
||||
{
|
||||
bitCount += writeSymbol (stream, false, m_acBits);
|
||||
m_acBits = 0;
|
||||
}
|
||||
else if (low > SHRT_MAX)
|
||||
{
|
||||
bitCount += writeSymbol (stream, true, m_acBits);
|
||||
m_acBits = 0;
|
||||
|
||||
high += SHRT_MIN;
|
||||
low += SHRT_MIN;
|
||||
}
|
||||
else if ((low > (SHRT_MAX >> 1)) && (high < ((-3 * SHRT_MIN) >> 1)))
|
||||
{
|
||||
m_acBits++;
|
||||
|
||||
high += SHRT_MIN >> 1;
|
||||
low += SHRT_MIN >> 1;
|
||||
}
|
||||
else break;
|
||||
|
||||
high <<= 1; high |= 1;
|
||||
low <<= 1;
|
||||
}
|
||||
}
|
||||
else // stream == nullptr, counting
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
if (high <= SHRT_MAX)
|
||||
{
|
||||
bitCount += m_acBits + 1;
|
||||
m_acBits = 0;
|
||||
}
|
||||
else if (low > SHRT_MAX)
|
||||
{
|
||||
bitCount += m_acBits + 1;
|
||||
m_acBits = 0;
|
||||
|
||||
high += SHRT_MIN;
|
||||
low += SHRT_MIN;
|
||||
}
|
||||
else if ((low > (SHRT_MAX >> 1)) && (high < ((-3 * SHRT_MIN) >> 1)))
|
||||
{
|
||||
m_acBits++;
|
||||
|
||||
high += SHRT_MIN >> 1;
|
||||
low += SHRT_MIN >> 1;
|
||||
}
|
||||
else break;
|
||||
|
||||
high <<= 1; high |= 1;
|
||||
low <<= 1;
|
||||
}
|
||||
}
|
||||
m_acHigh = high;
|
||||
m_acLow = low;
|
||||
|
||||
return bitCount;
|
||||
}
|
||||
|
||||
unsigned EntropyCoder::arithGetContext (const unsigned ctx, const unsigned idx) // c = arith_get_context(c, i, N)
|
||||
{
|
||||
unsigned c = (ctx & 0xFFFF) >> 4; // NOTE: the "& 0xFFFF" was part of some USAC corrigendum
|
||||
|
||||
c = (c | ((unsigned) m_qcPrev[idx + 1] << 12)) & 0xFFF0; // add top-left previous neighbor
|
||||
|
||||
if (idx > 0) // lower neighbor(s)
|
||||
{
|
||||
c |= (unsigned) m_qcCurr[idx - 1];
|
||||
|
||||
if ((idx > 3) && (m_qcCurr[idx - 3] + m_qcCurr[idx - 2] + m_qcCurr[idx - 1] < 5))
|
||||
{
|
||||
return c | 0x10000;
|
||||
}
|
||||
}
|
||||
return c; // updated context ctx
|
||||
}
|
||||
|
||||
unsigned EntropyCoder::arithMapContext (const bool arithResetFlag) // c = arith_map_context(N, arith_reset_flag)
|
||||
{
|
||||
if (arithResetFlag)
|
||||
{
|
||||
memset (m_qcPrev, 0, m_maxTupleLength * sizeof (uint8_t));
|
||||
}
|
||||
else if (m_shortTrafoCurr == m_shortTrafoPrev)
|
||||
{
|
||||
memcpy (m_qcPrev, m_qcCurr, m_acSize * sizeof (uint8_t));
|
||||
}
|
||||
else if (m_shortTrafoCurr && !m_shortTrafoPrev)
|
||||
{
|
||||
for (int i = m_acSize - 1; i >= 0; i--)
|
||||
{
|
||||
m_qcPrev[i] = m_qcCurr[i << 3];
|
||||
}
|
||||
}
|
||||
else // (!m_shortTrafoCurr && m_shortTrafoPrev)
|
||||
{
|
||||
for (int i = m_acSize - 1; i >= 0; i--)
|
||||
{
|
||||
m_qcPrev[i] = m_qcCurr[i >> 3];
|
||||
}
|
||||
}
|
||||
m_qcPrev[m_acSize] = 0; // for encoder speed-up
|
||||
|
||||
return (unsigned) m_qcPrev[0] << 12; // initial context ctx with top-left previous neighbor
|
||||
}
|
||||
|
||||
#if EC_TRELLIS_OPT_CODING
|
||||
void EntropyCoder::arithSetContext (const unsigned newCtxState, const uint16_t sigEnd)
|
||||
{
|
||||
m_csCurr = newCtxState;
|
||||
m_acBits = (m_csCurr >> 17) & 31;
|
||||
for (uint16_t s = 1; s < 4; s++)
|
||||
{
|
||||
if (sigEnd >= s) m_qcCurr[sigEnd - s] = (m_csCurr >> (18 + 4 * s)) & 0xF;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// constructor
|
||||
EntropyCoder::EntropyCoder ()
|
||||
{
|
||||
// initialize all helper buffers
|
||||
m_qcCurr = nullptr;
|
||||
m_qcPrev = nullptr;
|
||||
|
||||
// initialize encoding variables
|
||||
m_acBits = 0;
|
||||
m_acHigh = USHRT_MAX;
|
||||
m_acLow = 0;
|
||||
m_acSize = 0;
|
||||
m_csCurr = 0;
|
||||
m_maxTupleLength = 0;
|
||||
m_shortTrafoCurr = false;
|
||||
m_shortTrafoPrev = false;
|
||||
}
|
||||
|
||||
// destructor
|
||||
EntropyCoder::~EntropyCoder ()
|
||||
{
|
||||
// free allocated helper buffers
|
||||
MFREE (m_qcCurr);
|
||||
MFREE (m_qcPrev);
|
||||
}
|
||||
|
||||
// public functions
|
||||
unsigned EntropyCoder::arithCodeSigMagn (const uint8_t* const magn, const uint16_t sigOffset, const uint16_t sigLength,
|
||||
const bool arithFinish /*= false*/, OutputStream* const stream /*= nullptr*/)
|
||||
{
|
||||
const uint8_t* a = &magn[sigOffset ];
|
||||
const uint8_t* b = &magn[sigOffset + 1];
|
||||
unsigned c = m_csCurr & 0x1FFFF;
|
||||
unsigned bitCount = 0;
|
||||
uint16_t r[7];
|
||||
uint16_t sigEnd = (sigOffset >> 1) + (sigLength >> 1);
|
||||
|
||||
if (arithFinish && (sigLength > 0)) // try to save bits via signalling of ARITH_STOP symbol
|
||||
{
|
||||
int i = sigLength - 2;
|
||||
|
||||
while ((i >= 0) && ((a[i] | b[i]) == 0)) i -= 2;
|
||||
i = (sigOffset + i + 2) >> 1;
|
||||
|
||||
if (i + 28 < (int) sigEnd) sigEnd = (uint16_t) i;
|
||||
}
|
||||
|
||||
for (uint16_t s = sigOffset >> 1; s < sigEnd; s++)
|
||||
{
|
||||
uint32_t lev = 0;
|
||||
uint16_t a1 = *a;
|
||||
uint16_t b1 = *b;
|
||||
|
||||
a += 2; b += 2;
|
||||
|
||||
// arith_get_context, cf Scl. 7.4
|
||||
c = arithGetContext (c, s);
|
||||
// arith_update_context, Scl. 7.4
|
||||
m_qcCurr[s] = __min (0xF, a1 + b1 + 1);
|
||||
|
||||
// MSB encoding as in Scl. B.25.3
|
||||
while ((a1 > 3) || (b1 > 3))
|
||||
{
|
||||
// write escaped codeword value
|
||||
bitCount += arithCodeSymbol (ARITH_ESCAPE, arithCumFreqM[arithGetPkIndex (c | (lev << 17))], stream);
|
||||
// store LSBs in r, right-shift
|
||||
r[lev++] = (a1 & 1) | ((b1 & 1) << 1);
|
||||
a1 >>= 1; b1 >>= 1;
|
||||
}
|
||||
// write the m MSB codeword value
|
||||
bitCount += arithCodeSymbol (a1 | (b1 << 2), arithCumFreqM[arithGetPkIndex (c | (lev << 17))], stream);
|
||||
|
||||
// LSB encoding, Table 38, B.25.3
|
||||
while (lev--)
|
||||
{
|
||||
const uint16_t rLev = r[lev];
|
||||
|
||||
bitCount += arithCodeSymbol (rLev, arithCumFreqR[a1 == 0 ? 1 : (b1 == 0 ? 0 : 2)], stream);
|
||||
a1 = (a1 << 1) | (rLev & 1);
|
||||
b1 = (b1 << 1) | ((rLev >> 1) & 1);
|
||||
}
|
||||
} // for s
|
||||
|
||||
if (arithFinish) // flush last bits
|
||||
{
|
||||
// NOTE: the spec incorrectly reads "m_acBits" below (bits_to_follow++ missing in B.25.3)
|
||||
if (sigLength > 0)
|
||||
{
|
||||
if (sigEnd < (sigOffset >> 1) + (sigLength >> 1)) // write ARITH_STOP flag to save bits
|
||||
{
|
||||
c = arithGetContext (c, sigEnd);
|
||||
bitCount += arithCodeSymbol (ARITH_ESCAPE, arithCumFreqM[arithGetPkIndex (c)], stream);
|
||||
bitCount += arithCodeSymbol (0 /*m=STOP*/, arithCumFreqM[arithGetPkIndex (c | (1 << 17))], stream);
|
||||
}
|
||||
bitCount += writeSymbol (stream, m_acLow > (SHRT_MAX >> 1), m_acBits + 1);
|
||||
}
|
||||
m_csCurr = m_acBits = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_csCurr = 0;
|
||||
|
||||
for (uint16_t s = 1; s < 4; s++)
|
||||
{
|
||||
if (sigEnd >= s) m_csCurr |= __min (255u >> (2 * s), m_qcCurr[sigEnd - s]) << (18 + 4 * s);
|
||||
}
|
||||
}
|
||||
m_csCurr |= ((unsigned) m_acBits << 17) | c;
|
||||
|
||||
return bitCount;
|
||||
}
|
||||
|
||||
unsigned EntropyCoder::arithGetResetBit (const uint8_t* const magn, const uint16_t sigOffset, const uint16_t sigLength)
|
||||
{
|
||||
const uint16_t sigEnd = (sigOffset >> 1) + (sigLength >> 1);
|
||||
const uint8_t* a = &magn[sigOffset ];
|
||||
const uint8_t* b = &magn[sigOffset + 1];
|
||||
unsigned qcDiff = 0;
|
||||
|
||||
for (uint16_t s = sigOffset >> 1; s < sigEnd; s++)
|
||||
{
|
||||
const int qcCurrS = __min (0xF, (int)*a + (int)*b);
|
||||
const int qcDiffS = qcCurrS - m_qcPrev[s];
|
||||
|
||||
qcDiff += qcDiffS * qcDiffS;
|
||||
a += 2; b += 2;
|
||||
}
|
||||
|
||||
return (qcDiff * 2u > sigLength * 7u ? 1 : 0); // use reset if difference exceeds threshold
|
||||
}
|
||||
|
||||
unsigned EntropyCoder::indexGetBitCount (const int scaleFactorDelta) const
|
||||
{
|
||||
return huffScf[CLIP_PM (scaleFactorDelta, INDEX_OFFSET) + INDEX_OFFSET] & UCHAR_MAX;
|
||||
}
|
||||
|
||||
unsigned EntropyCoder::indexGetHuffCode (const int scaleFactorDelta) const
|
||||
{
|
||||
return huffScf[CLIP_PM (scaleFactorDelta, INDEX_OFFSET) + INDEX_OFFSET] >> 8;
|
||||
}
|
||||
|
||||
unsigned EntropyCoder::initCodingMemory (const unsigned maxTransfLength)
|
||||
{
|
||||
const unsigned max2TupleLength = maxTransfLength >> 1; // tuple buffer size, maxWinLength/4
|
||||
|
||||
if ((maxTransfLength < 128) || (maxTransfLength > 8192) || (maxTransfLength & 7))
|
||||
{
|
||||
return 1; // invalid arguments error
|
||||
}
|
||||
|
||||
m_maxTupleLength = max2TupleLength;
|
||||
MFREE (m_qcCurr);
|
||||
MFREE (m_qcPrev);
|
||||
|
||||
if ((m_qcCurr = (uint8_t*) malloc (max2TupleLength * sizeof (uint8_t))) == nullptr ||
|
||||
(m_qcPrev = (uint8_t*) malloc ((max2TupleLength + 1) * sizeof (uint8_t))) == nullptr)
|
||||
{
|
||||
return 2; // memory allocation error
|
||||
}
|
||||
|
||||
memset (m_qcCurr, 0, max2TupleLength * sizeof (uint8_t));
|
||||
|
||||
return 0; // no error
|
||||
}
|
||||
|
||||
unsigned EntropyCoder::initWindowCoding (const bool forceArithReset, const bool shortWin /*= false*/)
|
||||
{
|
||||
// arith_first_symbol() in Scl. B.25.3
|
||||
m_acBits = 0;
|
||||
m_acHigh = USHRT_MAX;
|
||||
m_acLow = 0;
|
||||
m_acSize = (shortWin ? m_maxTupleLength >> 3 : m_maxTupleLength);
|
||||
|
||||
m_shortTrafoPrev = m_shortTrafoCurr;
|
||||
m_shortTrafoCurr = shortWin;
|
||||
|
||||
m_csCurr = arithMapContext (forceArithReset); // m_qcPrev
|
||||
memset (m_qcCurr, 1, m_acSize * sizeof (uint8_t)); // reset m_qcCurr, see also arith_finish
|
||||
|
||||
return 0; // no error
|
||||
}
|
79
src/lib/entropyCoding.h
Normal file
79
src/lib/entropyCoding.h
Normal file
@ -0,0 +1,79 @@
|
||||
/* entropyCoding.h - header file for class with lossless entropy coding capability
|
||||
* written by C. R. Helmrich, last modified in 2019 - see License.htm for legal notices
|
||||
*
|
||||
* The copyright in this software is being made available under a Modified BSD-Style 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-2020 Christian R. Helmrich, project ecodis. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef _ENTROPY_CODING_H_
|
||||
#define _ENTROPY_CODING_H_
|
||||
|
||||
#include "exhaleLibPch.h"
|
||||
|
||||
// constants, experimental macro
|
||||
#define ARITH_ESCAPE 16
|
||||
#define ARITH_SIZE 742
|
||||
#define INDEX_OFFSET 60
|
||||
#define INDEX_SIZE 121
|
||||
#define EC_TRELLIS_OPT_CODING 1
|
||||
|
||||
// lossless entropy coding class
|
||||
class EntropyCoder
|
||||
{
|
||||
private:
|
||||
|
||||
// member variables
|
||||
uint8_t* m_qcCurr; // curr. window's quantized context q[1]
|
||||
uint8_t* m_qcPrev; // prev. window's quantized context q[0]
|
||||
|
||||
uint16_t m_acBits; // bits_to_follow in arith_encode, 0..31
|
||||
uint16_t m_acHigh; // high in arith_encode as in Annex B.25
|
||||
uint16_t m_acLow; // low in arith_encode, as in Annex B.25
|
||||
uint16_t m_acSize; // context window size (N/4 in Scl. 7.4)
|
||||
uint32_t m_csCurr; // context state, see initWindowCoding()
|
||||
unsigned m_maxTupleLength; // maximum half-transform length (<4096)
|
||||
bool m_shortTrafoCurr; // used to derive N in Scl. 7.4 and B.25
|
||||
bool m_shortTrafoPrev; // used to derive previous_N in Scl. 7.4
|
||||
|
||||
// helper functions
|
||||
unsigned arithCodeSymbol (const uint16_t symbol, const uint16_t* table, OutputStream* const stream = nullptr);
|
||||
unsigned arithGetContext (const unsigned ctx, const unsigned idx);
|
||||
unsigned arithMapContext (const bool arithResetFlag);
|
||||
#if EC_TRELLIS_OPT_CODING
|
||||
void arithSetContext (const unsigned newCtxState, const uint16_t sigEnd);
|
||||
#endif
|
||||
|
||||
public:
|
||||
|
||||
// constructor
|
||||
EntropyCoder ();
|
||||
// destructor
|
||||
~EntropyCoder ();
|
||||
// public functions
|
||||
unsigned arithCodeSigMagn (const uint8_t* const magn, const uint16_t sigOffset, const uint16_t sigLength,
|
||||
const bool arithFinish = false, OutputStream* const stream = nullptr);
|
||||
unsigned arithGetCodState () const { return ((unsigned) m_acHigh << 16) | (unsigned) m_acLow; }
|
||||
unsigned arithGetCtxState () const { return m_csCurr; }
|
||||
unsigned arithGetResetBit (const uint8_t* const magn, const uint16_t sigOffset, const uint16_t sigLength);
|
||||
char* arithGetTuplePtr () const { return (char*) m_qcCurr; }
|
||||
void arithResetMemory () { memset (m_qcPrev, 0, (m_maxTupleLength + 1) * sizeof (uint8_t)); m_acBits = 0; }
|
||||
void arithSetCodState (const unsigned newCodState) { m_acHigh = newCodState >> 16; m_acLow = newCodState & USHRT_MAX; }
|
||||
#if EC_TRELLIS_OPT_CODING
|
||||
void arithSetCtxState (const unsigned newCtxState, const uint16_t sigOffset = 0) { arithSetContext (newCtxState, sigOffset >> 1); }
|
||||
#else
|
||||
void arithSetCtxState (const unsigned newCtxState) { m_csCurr = newCtxState; }
|
||||
#endif
|
||||
unsigned indexGetBitCount (const int scaleFactorDelta) const;
|
||||
unsigned indexGetHuffCode (const int scaleFactorDelta) const;
|
||||
|
||||
unsigned initCodingMemory (const unsigned maxTransfLength);
|
||||
unsigned initWindowCoding (const bool forceArithReset, const bool shortWin = false);
|
||||
|
||||
bool getIsShortWindow () const { return m_shortTrafoCurr; }
|
||||
void setIsShortWindow (const bool shortWin) { m_shortTrafoCurr = shortWin; }
|
||||
}; // EntropyCoder
|
||||
|
||||
#endif // _ENTROPY_CODING_H_
|
1590
src/lib/exhaleEnc.cpp
Normal file
1590
src/lib/exhaleEnc.cpp
Normal file
File diff suppressed because it is too large
Load Diff
143
src/lib/exhaleEnc.h
Normal file
143
src/lib/exhaleEnc.h
Normal file
@ -0,0 +1,143 @@
|
||||
/* exhaleEnc.h - header file for class providing Extended HE-AAC encoding capability
|
||||
* written by C. R. Helmrich, last modified in 2019 - see License.htm for legal notices
|
||||
*
|
||||
* The copyright in this software is being made available under a Modified BSD-Style 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-2020 Christian R. Helmrich, project ecodis. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef _EXHALE_ENC_H_
|
||||
#define _EXHALE_ENC_H_
|
||||
|
||||
#include "exhaleLibPch.h"
|
||||
#include "bitAllocation.h"
|
||||
#include "bitStreamWriter.h"
|
||||
#include "entropyCoding.h"
|
||||
#include "lappedTransform.h"
|
||||
#include "linearPrediction.h"
|
||||
#include "quantization.h"
|
||||
#include "specAnalysis.h"
|
||||
#include "specGapFilling.h"
|
||||
#include "tempAnalysis.h"
|
||||
|
||||
// constant and experimental macro
|
||||
#define WIN_SCALE double (1 << 23)
|
||||
#define EE_OPT_TNS_SPEC_RANGE 1
|
||||
|
||||
// channelConfigurationIndex setup
|
||||
typedef enum USAC_CCI : char
|
||||
{
|
||||
CCI_UNDEF = -1,
|
||||
CCI_CONF = 0, // channel-to-speaker mapping defined in UsacChannelConfig() (not to be used here!)
|
||||
CCI_1_CH = 1, // 1.0: front-center
|
||||
CCI_2_CH = 2, // 2.0: front-left, front-right
|
||||
CCI_3_CH = 3, // 3.0: front-center, front-left, front-right
|
||||
CCI_4_CH = 4, // 4.0: front-center, front-left, front-right, back-center
|
||||
CCI_5_CH = 5, // 5.0: front-center, front-left, front-right, back-left, back-right
|
||||
CCI_6_CH = 6, // 5.1: front-center, front-left, front-right, back-left, back-right, LFE
|
||||
CCI_8_CH = 7, // 7.1: front-center, front-left, front-right, side-left, side-right, back-left, back-right, LFE
|
||||
CCI_2_CHM = 8, // 2.0, dual-mono: channel1, channel2
|
||||
CCI_3_CHR = 9, // 3.0, R-rotated: front-left, front-right, back-center
|
||||
CCI_4_CHR = 10, // 4.0, R-rotated: front-left, front-right, back-left, back-right
|
||||
CCI_7_CH = 11, // 6.1: front-center, front-left, front-right, back-left, back-right, back-center, LFE
|
||||
CCI_8_CHS = 12 // 7.1, surround: front-center, front-L, front-R, surround-L, surround-R, back-L, back-R, LFE
|
||||
} USAC_CCI;
|
||||
|
||||
// coreCoderFrameLength definition
|
||||
typedef enum USAC_CCFL : short
|
||||
{
|
||||
CCFL_UNDEF = -1,
|
||||
#if !RESTRICT_TO_AAC
|
||||
CCFL_768 = 768, // LD
|
||||
#endif
|
||||
CCFL_1024 = 1024 // LC
|
||||
} USAC_CCFL;
|
||||
|
||||
// overall xHE-AAC encoding class
|
||||
class ExhaleEncoder
|
||||
{
|
||||
private:
|
||||
|
||||
// member variables
|
||||
uint16_t m_bandwidCurr[USAC_MAX_NUM_CHANNELS];
|
||||
uint16_t m_bandwidPrev[USAC_MAX_NUM_CHANNELS];
|
||||
BitAllocator m_bitAllocator; // for scale factor init
|
||||
uint8_t m_bitRateMode;
|
||||
USAC_CCI m_channelConf;
|
||||
CoreCoderData* m_elementData[USAC_MAX_NUM_ELEMENTS];
|
||||
EntropyCoder m_entropyCoder[USAC_MAX_NUM_CHANNELS];
|
||||
uint32_t m_frameCount;
|
||||
USAC_CCFL m_frameLength;
|
||||
char m_frequencyIdx;
|
||||
bool m_indepFlag; // usacIndependencyFlag bit
|
||||
uint32_t m_indepPeriod;
|
||||
LinearPredictor m_linPredictor; // for pre-roll est, TNS
|
||||
uint8_t* m_mdctQuantMag[USAC_MAX_NUM_CHANNELS];
|
||||
int32_t* m_mdctSignals[USAC_MAX_NUM_CHANNELS];
|
||||
int32_t* m_mdstSignals[USAC_MAX_NUM_CHANNELS];
|
||||
#if !RESTRICT_TO_AAC
|
||||
bool m_noiseFilling[USAC_MAX_NUM_ELEMENTS];
|
||||
bool m_nonMpegExt;
|
||||
#endif
|
||||
uint8_t m_numElements;
|
||||
uint8_t m_numSwbShort;
|
||||
uint8_t* m_outAuData;
|
||||
BitStreamWriter m_outStream; // for access unit creation
|
||||
int32_t* m_pcm24Data;
|
||||
SfbGroupData* m_scaleFacData[USAC_MAX_NUM_CHANNELS];
|
||||
SfbQuantizer m_sfbQuantizer; // powerlaw quantization
|
||||
SpecAnalyzer m_specAnalyzer; // for spectral analysis
|
||||
uint32_t m_specAnaCurr[USAC_MAX_NUM_CHANNELS];
|
||||
uint32_t m_specAnaPrev[USAC_MAX_NUM_CHANNELS];
|
||||
#if !RESTRICT_TO_AAC
|
||||
SpecGapFiller m_specGapFiller;// for noise/gap filling
|
||||
#endif
|
||||
uint8_t m_swbTableIdx;
|
||||
TempAnalyzer m_tempAnalyzer; // for temporal analysis
|
||||
uint32_t m_tempAnaCurr[USAC_MAX_NUM_CHANNELS];
|
||||
uint32_t m_tempAnaNext[USAC_MAX_NUM_CHANNELS];
|
||||
int32_t* m_tempIntBuf; // temporary int32 buffer
|
||||
int32_t* m_timeSignals[USAC_MAX_NUM_CHANNELS];
|
||||
#if !RESTRICT_TO_AAC
|
||||
bool m_timeWarping[USAC_MAX_NUM_ELEMENTS];
|
||||
#endif
|
||||
int32_t* m_timeWindowL[2]; // long window halves
|
||||
int32_t* m_timeWindowS[2]; // short window halves
|
||||
int16_t m_tranLocCurr[USAC_MAX_NUM_CHANNELS];
|
||||
int16_t m_tranLocNext[USAC_MAX_NUM_CHANNELS];
|
||||
LappedTransform m_transform; // time-frequency transform
|
||||
|
||||
// helper functions
|
||||
unsigned applyTnsToWinGroup (TnsData& tnsData, SfbGroupData& grpData, const bool eightShorts, const uint8_t maxSfb,
|
||||
const unsigned channelIndex);
|
||||
unsigned eightShortGrouping (SfbGroupData& grpData, uint16_t* const grpOffsets, int32_t* const mdctSignal);
|
||||
unsigned getOptParCorCoeffs (const int32_t* const mdctSignal, const SfbGroupData& grpData, const uint8_t maxSfb,
|
||||
const unsigned channelIndex, TnsData& tnsData, const uint8_t firstGroupIndexToTest = 0);
|
||||
unsigned psychBitAllocation ();
|
||||
unsigned quantizationCoding ();
|
||||
unsigned spectralProcessing ();
|
||||
unsigned temporalProcessing ();
|
||||
|
||||
public:
|
||||
|
||||
// constructor
|
||||
ExhaleEncoder (int32_t* const inputPcmData, unsigned char* const outputAuData,
|
||||
const unsigned sampleRate = 44100, const unsigned numChannels = 2,
|
||||
const unsigned frameLength = 1024, const unsigned indepPeriod = 45,
|
||||
const unsigned varBitRateMode = 3
|
||||
#if !RESTRICT_TO_AAC
|
||||
, const bool useNoiseFilling = true, const bool useEcodisExt = false
|
||||
#endif
|
||||
);
|
||||
// destructor
|
||||
~ExhaleEncoder ();
|
||||
// public functions
|
||||
unsigned encodeLookahead ();
|
||||
unsigned encodeFrame ();
|
||||
unsigned initEncoder (unsigned char* const audioConfigBuffer, uint32_t* const audioConfigBytes = nullptr);
|
||||
|
||||
}; // ExhaleEncoder
|
||||
|
||||
#endif // _EXHALE_ENC_H_
|
85
src/lib/exhaleLibPch.cpp
Normal file
85
src/lib/exhaleLibPch.cpp
Normal file
@ -0,0 +1,85 @@
|
||||
/* exhaleLibPch.cpp - pre-compiled source file for classes of exhaleLib coding library
|
||||
* written by C. R. Helmrich, last modified in 2019 - see License.htm for legal notices
|
||||
*
|
||||
* The copyright in this software is being made available under a Modified BSD-Style 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-2020 Christian R. Helmrich, project ecodis. All rights reserved.
|
||||
*/
|
||||
|
||||
#include "exhaleLibPch.h"
|
||||
|
||||
// public bit-stream functions
|
||||
void OutputStream::reset () // clear writer states and byte buffer
|
||||
{
|
||||
heldBitChunk = 0;
|
||||
heldBitCount = 0;
|
||||
stream.clear ();
|
||||
}
|
||||
|
||||
void OutputStream::write (const uint32_t bitChunk, const uint8_t bitCount)
|
||||
{
|
||||
if (bitCount > 32) return; // only a maximum of 32 bits is writable at once
|
||||
|
||||
const uint8_t totalBitCount = bitCount + heldBitCount;
|
||||
const uint8_t totalByteCount = totalBitCount >> 3; // to be written
|
||||
const uint8_t newHeldBitCount = totalBitCount & 7; // not yet written
|
||||
const uint8_t newHeldBitChunk = (bitChunk << (8 - newHeldBitCount)) & UCHAR_MAX;
|
||||
|
||||
if (totalByteCount == 0) // not enough bits to write, only update held bits
|
||||
{
|
||||
heldBitChunk |= newHeldBitChunk;
|
||||
}
|
||||
else // write bits
|
||||
{
|
||||
const uint32_t writtenChunk = (heldBitChunk << uint32_t ((bitCount - newHeldBitCount) & ~7)) | (bitChunk >> newHeldBitCount);
|
||||
switch (totalByteCount)
|
||||
{
|
||||
case 4: stream.push_back (writtenChunk >> 24);
|
||||
case 3: stream.push_back (writtenChunk >> 16);
|
||||
case 2: stream.push_back (writtenChunk >> 8);
|
||||
case 1: stream.push_back (writtenChunk);
|
||||
}
|
||||
heldBitChunk = newHeldBitChunk;
|
||||
}
|
||||
heldBitCount = newHeldBitCount;
|
||||
}
|
||||
|
||||
// ISO/IEC 23003-3, Table 67
|
||||
static const unsigned allowedSamplingRates[USAC_NUM_SAMPLE_RATES] = {
|
||||
96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350, // AAC
|
||||
57600, 51200, 40000, 38400, 34150, 28800, 25600, 20000, 19200, 17075, 14400, 12800, 9600 // USAC
|
||||
};
|
||||
|
||||
// public sampling rate functions
|
||||
char toSamplingFrequencyIndex (const unsigned samplingRate)
|
||||
{
|
||||
for (char i = 0; i < AAC_NUM_SAMPLE_RATES; i++)
|
||||
{
|
||||
if (samplingRate == allowedSamplingRates[(int) i]) // AAC rate
|
||||
{
|
||||
return i;
|
||||
}
|
||||
#if !RESTRICT_TO_AAC
|
||||
if (samplingRate == allowedSamplingRates[i + AAC_NUM_SAMPLE_RATES])
|
||||
{
|
||||
return i + AAC_NUM_SAMPLE_RATES + 2; // skip reserved entry
|
||||
}
|
||||
#endif
|
||||
}
|
||||
return -1; // no index found
|
||||
}
|
||||
|
||||
unsigned toSamplingRate (const char samplingFrequencyIndex)
|
||||
{
|
||||
#if RESTRICT_TO_AAC
|
||||
if ((samplingFrequencyIndex < 0) || (samplingFrequencyIndex >= AAC_NUM_SAMPLE_RATES))
|
||||
#else
|
||||
if ((samplingFrequencyIndex < 0) || (samplingFrequencyIndex >= USAC_NUM_SAMPLE_RATES + 2))
|
||||
#endif
|
||||
{
|
||||
return 0; // invalid index
|
||||
}
|
||||
return allowedSamplingRates[samplingFrequencyIndex > AAC_NUM_SAMPLE_RATES ? samplingFrequencyIndex - 2 : samplingFrequencyIndex];
|
||||
}
|
176
src/lib/exhaleLibPch.h
Normal file
176
src/lib/exhaleLibPch.h
Normal file
@ -0,0 +1,176 @@
|
||||
/* exhaleLibPch.h - pre-compiled header file for classes of exhaleLib coding library
|
||||
* written by C. R. Helmrich, last modified in 2019 - see License.htm for legal notices
|
||||
*
|
||||
* The copyright in this software is being made available under a Modified BSD-Style 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-2020 Christian R. Helmrich, project ecodis. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef _EXHALE_LIB_PCH_H_
|
||||
#define _EXHALE_LIB_PCH_H_
|
||||
|
||||
#include <limits.h> // for .._MAX, .._MIN
|
||||
#include <math.h> // for pow, sin, sqrt
|
||||
#include <stdint.h> // for (u)int8_t, (u)int16_t, (u)int32_t, (u)int64_t
|
||||
#include <stdlib.h> // for abs, div, calloc, malloc, free, (__)max, (__)min, (s)rand
|
||||
#include <string.h> // for memcpy, memset
|
||||
#include <vector> // for std::vector <>
|
||||
|
||||
// constants, experimental macros
|
||||
#define AAC_NUM_SAMPLE_RATES 13
|
||||
#define MAX_PREDICTION_ORDER 4
|
||||
#define MAX_NUM_SWB_LFE 6
|
||||
#define MAX_NUM_SWB_SHORT 15
|
||||
#define MIN_NUM_SWB_SHORT 12
|
||||
#define NUM_WINDOW_GROUPS 4 // must be between 4 and 8
|
||||
#define USAC_MAX_NUM_CHANNELS 8
|
||||
#define USAC_MAX_NUM_ELCONFIGS 13
|
||||
#define USAC_MAX_NUM_ELEMENTS 5
|
||||
#define USAC_NUM_FREQ_TABLES 6
|
||||
#define USAC_NUM_SAMPLE_RATES (2 * AAC_NUM_SAMPLE_RATES)
|
||||
|
||||
#define RESTRICT_TO_AAC 0 // allow only AAC tool-set
|
||||
|
||||
#if RESTRICT_TO_AAC
|
||||
# define LFE_MAX 12
|
||||
#else
|
||||
# define LFE_MAX 24
|
||||
#endif
|
||||
|
||||
#ifndef __max
|
||||
# define __max(a, b) ((a) > (b) ? (a) : (b))
|
||||
#endif
|
||||
#ifndef __min
|
||||
# define __min(a, b) ((a) < (b) ? (a) : (b))
|
||||
#endif
|
||||
#ifndef CLIP_PM
|
||||
# define CLIP_PM(x, clip) (__max (-clip, __min (+clip, x)))
|
||||
#endif
|
||||
#ifndef CLIP_UCHAR
|
||||
# define CLIP_UCHAR(x) (__max (0, __min (UCHAR_MAX, x)))
|
||||
#endif
|
||||
|
||||
#ifndef MFREE
|
||||
# define MFREE(x) if (x != nullptr) { free ((void*) x); x = nullptr; }
|
||||
#endif
|
||||
|
||||
// usacElementType[el] definition
|
||||
typedef enum ELEM_TYPE : int8_t
|
||||
{
|
||||
ID_EL_UNDEF = -1,
|
||||
ID_USAC_SCE = 0, // single-channel element (CCI_1_CH)
|
||||
ID_USAC_CPE = 1, // channel-pair element (CCI_2_CH)
|
||||
ID_USAC_LFE = 2, // low-frequency effects element (CCI_1_CH) with ONLY_LONG_SEQUENCE, no TNS
|
||||
ID_USAC_EXT = 3 // extension element (not to be used here!)
|
||||
} ELEM_TYPE;
|
||||
|
||||
// window_sequence defines for ICS
|
||||
typedef enum USAC_WSEQ : uint8_t
|
||||
{
|
||||
ONLY_LONG = 0, // one symmetric long window .<2E>`.
|
||||
LONG_START = 1, // asymmet. long-start window .<2E>|_
|
||||
EIGHT_SHORT = 2, // 8 symmetric short windows _MM_
|
||||
LONG_STOP = 3, // asymmet. long-stop window _|`.
|
||||
STOP_START = 4 // symmet. stop-start window _||_
|
||||
} USAC_WSEQ;
|
||||
|
||||
// window_shape definition for ICS
|
||||
typedef enum USAC_WSHP : uint8_t
|
||||
{
|
||||
WINDOW_SINE = 0, // half-sine window = sin(pi*((0:L-1)'+0.5)/L)
|
||||
WINDOW_KBD = 1 // Kaiser-Bessel derived (KBD) window by Dolby
|
||||
} USAC_WSHP;
|
||||
|
||||
// ics_info(): channel data struct
|
||||
struct IcsInfo
|
||||
{
|
||||
uint8_t maxSfb; // max_sfb(1)
|
||||
uint8_t windowGrouping; // scale_factor_grouping (index)
|
||||
USAC_WSEQ windowSequence; // window_sequence
|
||||
USAC_WSHP windowShape; // window_shape
|
||||
};
|
||||
|
||||
// tns_data(): channel data struct
|
||||
struct TnsData
|
||||
{
|
||||
int8_t coeff[3][MAX_PREDICTION_ORDER];
|
||||
int16_t coeffParCor[MAX_PREDICTION_ORDER];
|
||||
bool coeffResLow; // means coef_res[w]=0
|
||||
uint8_t filteredWindow; // filtered window w
|
||||
bool filterDownward[3]; // direction[f]=1
|
||||
uint8_t filterLength[3]; // filter length[f]
|
||||
uint8_t filterOrder[3]; // filter order[f]
|
||||
uint8_t numFilters; // n_filt for window w
|
||||
};
|
||||
|
||||
// scale factor group. data struct
|
||||
struct SfbGroupData
|
||||
{
|
||||
uint16_t numWindowGroups; // 1 | NUM_WINDOW_GROUPS, num_window_groups
|
||||
uint16_t sfbOffsets[1+MAX_NUM_SWB_SHORT * NUM_WINDOW_GROUPS];
|
||||
uint32_t sfbRmsValues[MAX_NUM_SWB_SHORT * NUM_WINDOW_GROUPS];
|
||||
uint8_t scaleFactors[MAX_NUM_SWB_SHORT * NUM_WINDOW_GROUPS]; // sf[]
|
||||
uint8_t sfbsPerGroup; // max_sfb(1) duplicate needed by BitAllocator
|
||||
uint8_t windowGroupLength[NUM_WINDOW_GROUPS]; // window_group_length
|
||||
};
|
||||
|
||||
// UsacCoreCoderData() data struct
|
||||
struct CoreCoderData
|
||||
{
|
||||
bool commonMaxSfb; // common_max_sfb in StereoCoreToolInfo()
|
||||
bool commonTnsData; // common_tns in StereoCoreToolInfo()
|
||||
bool commonWindow; // common_window in StereoCoreToolInfo()
|
||||
ELEM_TYPE elementType; // usacElementType in UsacDecoderConfig()
|
||||
SfbGroupData groupingData[2]; // window grouping and SFB offset data
|
||||
IcsInfo icsInfoCurr[2]; // current ics_info() for each channel
|
||||
IcsInfo icsInfoPrev[2]; // previous ics_info() for each channel
|
||||
#if !RESTRICT_TO_AAC
|
||||
uint8_t specFillData[2]; // noise filling data for each channel
|
||||
#endif
|
||||
uint8_t stereoConfig; // cplx_pred_data() config: pred_dir etc.
|
||||
char stereoData[MAX_NUM_SWB_SHORT * NUM_WINDOW_GROUPS];
|
||||
uint8_t stereoMode; // ms_mask_present in StereoCoreToolInfo()
|
||||
bool tnsActive; // tns_active flag in StereoCoreToolInfo()
|
||||
TnsData tnsData[2]; // current tns_data() for each channel
|
||||
bool tnsOnLeftRight; // tns_on_lr in StereoCoreToolInfo()
|
||||
};
|
||||
|
||||
// bit-stream encoding data struct
|
||||
struct OutputStream
|
||||
{
|
||||
uint8_t heldBitChunk; // bits not yet flushed to buffer
|
||||
uint8_t heldBitCount; // number of bits not yet flushed
|
||||
std::vector <uint8_t> stream; // FIFO bit-stream buffer
|
||||
// constructor
|
||||
OutputStream () { reset (); }
|
||||
// destructor
|
||||
~OutputStream() { stream.clear (); }
|
||||
// public functions
|
||||
void reset (); // clear writer states and byte buffer
|
||||
void write (const uint32_t bitChunk, const uint8_t bitCount);
|
||||
}; // OutputStream
|
||||
|
||||
// fast calculation of sqrt (256 - x): (4 + eightTimesSqrt256Minus[x]) >> 3, for 0 <= x <= 255
|
||||
const uint8_t eightTimesSqrt256Minus[256] = {
|
||||
128, 128, 127, 127, 127, 127, 126, 126, 126, 126, 125, 125, 125, 125, 124, 124, 124, 124, 123, 123, 123, 123, 122, 122, 122, 122,
|
||||
121, 121, 121, 121, 120, 120, 120, 119, 119, 119, 119, 118, 118, 118, 118, 117, 117, 117, 116, 116, 116, 116, 115, 115, 115, 115,
|
||||
114, 114, 114, 113, 113, 113, 113, 112, 112, 112, 111, 111, 111, 111, 110, 110, 110, 109, 109, 109, 109, 108, 108, 108, 107, 107,
|
||||
107, 106, 106, 106, 106, 105, 105, 105, 104, 104, 104, 103, 103, 103, 102, 102, 102, 102, 101, 101, 101, 100, 100, 100, 99, 99,
|
||||
99, 98, 98, 98, 97, 97, 97, 96, 96, 96, 95, 95, 95, 94, 94, 94, 93, 93, 93, 92, 92, 92, 91, 91, 91, 90,
|
||||
90, 89, 89, 89, 88, 88, 88, 87, 87, 87, 86, 86, 85, 85, 85, 84, 84, 84, 83, 83, 82, 82, 82, 81, 81, 80,
|
||||
80, 80, 79, 79, 78, 78, 78, 77, 77, 76, 76, 75, 75, 75, 74, 74, 73, 73, 72, 72, 72, 71, 71, 70, 70, 69,
|
||||
69, 68, 68, 67, 67, 66, 66, 65, 65, 64, 64, 63, 63, 62, 62, 61, 61, 60, 60, 59, 59, 58, 58, 57, 57, 56,
|
||||
55, 55, 54, 54, 53, 52, 52, 51, 51, 50, 49, 49, 48, 47, 47, 46, 45, 45, 44, 43, 42, 42, 41, 40, 39, 38,
|
||||
38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 25, 24, 23, 21, 20, 18, 16, 14, 11, 8
|
||||
};
|
||||
|
||||
// fast calculation of x / den: (x * oneTwentyEightOver[den]) >> 7, accurate for 0 <= x <= 162
|
||||
const uint8_t oneTwentyEightOver[14] = {0, 128, 64, 43, 32, 26, 22, 19, 16, 15, 13, 12, 11, 10};
|
||||
|
||||
// public sampling rate functions
|
||||
char toSamplingFrequencyIndex (const unsigned samplingRate);
|
||||
unsigned toSamplingRate (const char samplingFrequencyIndex);
|
||||
|
||||
#endif // _EXHALE_LIB_PCH_H_
|
188
src/lib/exhaleLib_vs2012.vcxproj
Normal file
188
src/lib/exhaleLib_vs2012.vcxproj
Normal file
@ -0,0 +1,188 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{EC0D1501-2018-1700-4865-6C6D72696368}</ProjectGuid>
|
||||
<ProjectName>exhaleLib</ProjectName>
|
||||
<RootNamespace>exhaleLib</RootNamespace>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<PlatformToolset>v110</PlatformToolset>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<PlatformToolset>v110</PlatformToolset>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<PlatformToolset>v110</PlatformToolset>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<PlatformToolset>v110</PlatformToolset>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings" />
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<IntDir>$(SolutionDir)build\$(PlatformToolset)\$(Platform)\$(Configuration)\</IntDir>
|
||||
<OutDir>$(SolutionDir)lib\$(PlatformToolset)\$(Platform)\$(Configuration)\</OutDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<IntDir>$(SolutionDir)build\$(PlatformToolset)\$(Platform)\$(Configuration)\</IntDir>
|
||||
<OutDir>$(SolutionDir)lib\$(PlatformToolset)\$(Platform)\$(Configuration)\</OutDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<IntDir>$(SolutionDir)build\$(PlatformToolset)\$(Platform)\$(Configuration)\</IntDir>
|
||||
<OutDir>$(SolutionDir)lib\$(PlatformToolset)\$(Platform)\$(Configuration)\</OutDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<IntDir>$(SolutionDir)build\$(PlatformToolset)\$(Platform)\$(Configuration)\</IntDir>
|
||||
<OutDir>$(SolutionDir)lib\$(PlatformToolset)\$(Platform)\$(Configuration)\</OutDir>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)include</AdditionalIncludeDirectories>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>exhaleLibPch.h</PrecompiledHeaderFile>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<TreatWarningAsError>true</TreatWarningAsError>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)include</AdditionalIncludeDirectories>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>exhaleLibPch.h</PrecompiledHeaderFile>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<TreatWarningAsError>true</TreatWarningAsError>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)include</AdditionalIncludeDirectories>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>exhaleLibPch.h</PrecompiledHeaderFile>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<TreatWarningAsError>true</TreatWarningAsError>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<GenerateDebugInformation>false</GenerateDebugInformation>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)include</AdditionalIncludeDirectories>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>exhaleLibPch.h</PrecompiledHeaderFile>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<TreatWarningAsError>true</TreatWarningAsError>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<GenerateDebugInformation>false</GenerateDebugInformation>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\..\include\exhaleDecl.h" />
|
||||
<ClInclude Include="..\..\include\version.h" />
|
||||
<ClInclude Include="bitAllocation.h" />
|
||||
<ClInclude Include="bitStreamWriter.h" />
|
||||
<ClInclude Include="entropyCoding.h" />
|
||||
<ClInclude Include="exhaleEnc.h" />
|
||||
<ClInclude Include="exhaleLibPch.h" />
|
||||
<ClInclude Include="lappedTransform.h" />
|
||||
<ClInclude Include="linearPrediction.h" />
|
||||
<ClInclude Include="quantization.h" />
|
||||
<ClInclude Include="specAnalysis.h" />
|
||||
<ClInclude Include="specGapFilling.h" />
|
||||
<ClInclude Include="tempAnalysis.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="bitAllocation.cpp" />
|
||||
<ClCompile Include="bitStreamWriter.cpp" />
|
||||
<ClCompile Include="entropyCoding.cpp" />
|
||||
<ClCompile Include="exhaleEnc.cpp" />
|
||||
<ClCompile Include="exhaleLibPch.cpp">
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<ClCompile Include="lappedTransform.cpp" />
|
||||
<ClCompile Include="linearPrediction.cpp" />
|
||||
<ClCompile Include="quantization.cpp" />
|
||||
<ClCompile Include="specAnalysis.cpp" />
|
||||
<ClCompile Include="specGapFilling.cpp" />
|
||||
<ClCompile Include="tempAnalysis.cpp" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
</Project>
|
93
src/lib/exhaleLib_vs2012.vcxproj.filters
Normal file
93
src/lib/exhaleLib_vs2012.vcxproj.filters
Normal file
@ -0,0 +1,93 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{EC0D1503-2018-1700-4865-6C6D72696368}</UniqueIdentifier>
|
||||
<Extensions>h;hpp</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{EC0D1504-2018-1700-4865-6C6D72696368}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{EC0D1505-2018-1700-4865-6C6D72696368}</UniqueIdentifier>
|
||||
<Extensions>rc;ico</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\..\include\exhaleDecl.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\include\version.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="bitAllocation.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="bitStreamWriter.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="entropyCoding.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="exhaleEnc.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="exhaleLibPch.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="lappedTransform.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="linearPrediction.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="quantization.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="specAnalysis.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="specGapFilling.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="tempAnalysis.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="bitAllocation.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="bitStreamWriter.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="entropyCoding.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="exhaleEnc.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="exhaleLibPch.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="lappedTransform.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="linearPrediction.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="quantization.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="specAnalysis.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="specGapFilling.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="tempAnalysis.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
431
src/lib/lappedTransform.cpp
Normal file
431
src/lib/lappedTransform.cpp
Normal file
@ -0,0 +1,431 @@
|
||||
/* lappedTransform.cpp - source file for class providing time-frequency transformation
|
||||
* written by C. R. Helmrich, last modified in 2019 - see License.htm for legal notices
|
||||
*
|
||||
* The copyright in this software is being made available under a Modified BSD-Style 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-2020 Christian R. Helmrich, project ecodis. All rights reserved.
|
||||
*/
|
||||
|
||||
#include "exhaleLibPch.h"
|
||||
#include "lappedTransform.h"
|
||||
#ifdef _MSC_VER
|
||||
# include <intrin.h> // _BitScanReverse
|
||||
|
||||
# pragma intrinsic (_BitScanReverse)
|
||||
#endif
|
||||
|
||||
// static helper functions
|
||||
static short* createPermutTable (const short tableSize)
|
||||
{
|
||||
const short lOver2 = tableSize >> 1;
|
||||
short* permutTable = nullptr;
|
||||
short i = 0;
|
||||
|
||||
if ((permutTable = (short*) malloc (tableSize * sizeof (short))) == nullptr)
|
||||
{
|
||||
return nullptr; // allocation error
|
||||
}
|
||||
|
||||
permutTable[0] = 0;
|
||||
for (short s = 1; s < tableSize; s++)
|
||||
{
|
||||
short l = lOver2;
|
||||
|
||||
while (i >= l)
|
||||
{
|
||||
i -= l;
|
||||
l >>= 1;
|
||||
}
|
||||
permutTable[s] = (i += l);
|
||||
}
|
||||
|
||||
return permutTable;
|
||||
}
|
||||
|
||||
static inline int shortIntLog2 (uint16_t s)
|
||||
{
|
||||
#ifdef _MSC_VER
|
||||
unsigned long l;
|
||||
|
||||
_BitScanReverse (&l, s);
|
||||
|
||||
return (int) l;
|
||||
#else
|
||||
// fast base-2 integer logarithm by Todd Lehman (Stack Overflow), July 2014
|
||||
int i = 0;
|
||||
# define S(k) if (s >= (1u << k)) { i += k; s >>= k; }
|
||||
S (8); S (4); S (2); S (1);
|
||||
# undef S
|
||||
return i;
|
||||
#endif
|
||||
}
|
||||
|
||||
// private helper functions
|
||||
void LappedTransform::applyHalfSizeFFT (int32_t* const iR/*eal*/, int32_t* const iI/*mag*/, const bool shortTransform) // works in-place
|
||||
{
|
||||
// int32 FFT version based on http://paulbourke.net/miscellaneous/dft, 1993
|
||||
const int l = (shortTransform ? m_transfLengthS : m_transfLengthL) >> 1;
|
||||
const short* p = (shortTransform ? m_fftPermutS : m_fftPermutL); // look-up
|
||||
int l2 = 1, l3 = m_transfLengthL >> 1;
|
||||
|
||||
if (iR == nullptr)
|
||||
{
|
||||
return; // null-pointer input error
|
||||
}
|
||||
|
||||
// sort input with permutation look-up table
|
||||
if (iI != nullptr)
|
||||
{
|
||||
for (int i = l - 1; i >= 0; i--)
|
||||
{
|
||||
const int j = p[i];
|
||||
|
||||
if (j > i) // swap input data at i and j
|
||||
{
|
||||
const int32_t iRTmp = iR[i]; // use re-
|
||||
const int32_t iITmp = iI[i]; // gisters
|
||||
|
||||
iR[i] = iR[j];
|
||||
iI[i] = iI[j];
|
||||
iR[j] = iRTmp;
|
||||
iI[j] = iITmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
else // real-valued input, no imaginary part
|
||||
{
|
||||
for (int i = l - 1; i >= 0; i--)
|
||||
{
|
||||
const int j = p[i];
|
||||
|
||||
if (j > i) // swap real input at i and j
|
||||
{
|
||||
const int32_t iRTmp = iR[i];
|
||||
|
||||
iR[i] = iR[j];
|
||||
iR[j] = iRTmp;
|
||||
}
|
||||
iI[i] = 0; // zero imaginary input data
|
||||
}
|
||||
}
|
||||
|
||||
// get length-l Fast Fourier Transform (FFT)
|
||||
for (int k = shortIntLog2 ((uint16_t) l) - 1; k >= 0; k--)
|
||||
{
|
||||
const int l1 = l2;
|
||||
l2 <<= 1;
|
||||
l3 >>= 1;
|
||||
|
||||
for (int j = l1 - 1; j >= 0; j--)
|
||||
{
|
||||
const int jTl3 = j * l3;
|
||||
const int64_t cosjl3 = m_fftHalfCos[jTl3]; // cos/sin
|
||||
const int64_t sinjl3 = m_fftHalfSin[jTl3]; // look-up
|
||||
|
||||
for (int i = j; i < l; i += l2)
|
||||
{
|
||||
const int iPl1 = i + l1;
|
||||
const int32_t rotR = int32_t ((cosjl3 * iR[iPl1] + sinjl3 * iI[iPl1] + LUT_OFFSET) >> LUT_SHIFT); // clockwise
|
||||
const int32_t rotI = int32_t ((cosjl3 * iI[iPl1] - sinjl3 * iR[iPl1] + LUT_OFFSET) >> LUT_SHIFT); // rotation
|
||||
|
||||
iR[iPl1] = iR[i] + rotR; iR[i] -= rotR;
|
||||
iI[iPl1] = iI[i] + rotI; iI[i] -= rotI;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LappedTransform::windowAndFoldInL (const int32_t* inputL, const bool shortTransform, const bool kbdWindowL, const bool lowOverlapL,
|
||||
const bool mdstKernel, int32_t* const output)
|
||||
{
|
||||
const unsigned ws = (kbdWindowL ? 1 : 0); // shape
|
||||
const int32_t* wl = (lowOverlapL ? m_timeWindowS[ws] : m_timeWindowL[ws]);
|
||||
const int Mo2 = (shortTransform ? m_transfLengthS : m_transfLengthL) >> 1;
|
||||
const int Mm1 = Mo2 * 2 - 1;
|
||||
const int Mo2m1 = Mo2 - 1;
|
||||
const int Mo2mO = (lowOverlapL ? Mo2 - (m_transfLengthS >> 1) : 0);
|
||||
const int Mm1mO = Mm1 - Mo2mO; // overlap offset
|
||||
int n;
|
||||
|
||||
if (mdstKernel) // time-reversal and TDA sign flip
|
||||
{
|
||||
for (n = Mo2m1; n >= Mo2mO; n--) // windowed pt.
|
||||
{
|
||||
const int64_t i64 = (int64_t) inputL[Mm1 - n] * wl[Mm1mO - n] + (int64_t) inputL[n] * wl[n - Mo2mO];
|
||||
|
||||
output[Mo2m1 - n] = int32_t ((i64 + WIN_OFFSET) >> WIN_SHIFT);
|
||||
}
|
||||
for (/*Mo2mO-1*/; n >= 0; n--) // unwindowed pt.
|
||||
{
|
||||
output[Mo2m1 - n] = (inputL[Mm1 - n] + 2) >> 2;
|
||||
}
|
||||
}
|
||||
else // MDCT kernel, no time-reversal or sign flip
|
||||
{
|
||||
for (n = Mo2m1; n >= Mo2mO; n--) // windowed pt.
|
||||
{
|
||||
const int64_t i64 = (int64_t) inputL[Mm1 - n] * wl[Mm1mO - n] - (int64_t) inputL[n] * wl[n - Mo2mO];
|
||||
|
||||
output[Mo2 + n] = int32_t ((i64 + WIN_OFFSET) >> WIN_SHIFT);
|
||||
}
|
||||
for (/*Mo2mO-1*/; n >= 0; n--) // unwindowed pt.
|
||||
{
|
||||
output[Mo2 + n] = (inputL[Mm1 - n] + 2) >> 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LappedTransform::windowAndFoldInR (const int32_t* inputR, const bool shortTransform, const bool kbdWindowR, const bool lowOverlapR,
|
||||
const bool mdstKernel, int32_t* const output)
|
||||
{
|
||||
const unsigned ws = (kbdWindowR ? 1 : 0); // shape
|
||||
const int32_t* wr = (lowOverlapR ? m_timeWindowS[ws] : m_timeWindowL[ws]);
|
||||
const int Mo2 = (shortTransform ? m_transfLengthS : m_transfLengthL) >> 1;
|
||||
const int Mm1 = Mo2 * 2 - 1;
|
||||
const int Mo2m1 = Mo2 - 1;
|
||||
const int Mo2mO = (lowOverlapR ? Mo2 - (m_transfLengthS >> 1) : 0);
|
||||
const int Mm1mO = Mm1 - Mo2mO; // overlap offset
|
||||
int n;
|
||||
|
||||
if (mdstKernel) // time-reversal and TDA sign flip
|
||||
{
|
||||
for (n = Mo2m1; n >= Mo2mO; n--) // windowed pt.
|
||||
{
|
||||
const int64_t i64 = (int64_t) inputR[n] * wr[Mm1mO - n] - (int64_t) inputR[Mm1 - n] * wr[n - Mo2mO];
|
||||
|
||||
output[Mo2 + n] = int32_t ((i64 + WIN_OFFSET) >> WIN_SHIFT);
|
||||
}
|
||||
for (/*Mo2mO-1*/; n >= 0; n--) // unwindowed pt.
|
||||
{
|
||||
output[Mo2 + n] = (inputR[n] + 2) >> 2;
|
||||
}
|
||||
}
|
||||
else // MDCT kernel, no time-reversal or sign flip
|
||||
{
|
||||
for (n = Mo2m1; n >= Mo2mO; n--) // windowed pt.
|
||||
{
|
||||
const int64_t i64 = (int64_t) inputR[n] * wr[Mm1mO - n] + (int64_t) inputR[Mm1 - n] * wr[n - Mo2mO];
|
||||
|
||||
output[Mo2m1 - n] = int32_t ((i64 + WIN_OFFSET) >> WIN_SHIFT);
|
||||
}
|
||||
for (/*Mo2mO-1*/; n >= 0; n--) // unwindowed pt.
|
||||
{
|
||||
output[Mo2m1 - n] = (inputR[n] + 2) >> 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// constructor
|
||||
LappedTransform::LappedTransform ()
|
||||
{
|
||||
// initialize all helper buffers
|
||||
m_dctRotCosL = nullptr;
|
||||
m_dctRotCosS = nullptr;
|
||||
m_dctRotSinL = nullptr;
|
||||
m_dctRotSinS = nullptr;
|
||||
m_fftHalfCos = nullptr;
|
||||
m_fftHalfSin = nullptr;
|
||||
m_fftPermutL = nullptr;
|
||||
m_fftPermutS = nullptr;
|
||||
m_tempIntBuf = nullptr;
|
||||
|
||||
// initialize all window buffers
|
||||
for (short s = 0; s < 2; s++)
|
||||
{
|
||||
m_timeWindowL[s] = nullptr;
|
||||
m_timeWindowS[s] = nullptr;
|
||||
}
|
||||
m_transfLengthL = 0;
|
||||
m_transfLengthS = 0;
|
||||
}
|
||||
|
||||
// destructor
|
||||
LappedTransform::~LappedTransform ()
|
||||
{
|
||||
// free allocated helper buffers
|
||||
MFREE (m_dctRotCosL);
|
||||
MFREE (m_dctRotCosS);
|
||||
MFREE (m_dctRotSinL);
|
||||
MFREE (m_dctRotSinS);
|
||||
MFREE (m_fftHalfCos);
|
||||
MFREE (m_fftHalfSin);
|
||||
MFREE (m_fftPermutL);
|
||||
MFREE (m_fftPermutS);
|
||||
m_tempIntBuf = nullptr;
|
||||
}
|
||||
|
||||
// public functions
|
||||
unsigned LappedTransform::applyNegDCT4 (int32_t* const signal, const bool shortTransform) // works in-place
|
||||
{
|
||||
// int32 negative-DCT-IV version based on http://www.ee.columbia.edu/~marios/mdct/mdct_giraffe.html, 2003
|
||||
// NOTE: amplifies short-transform results (8 times shorter than long-transform results) by a factor of 8
|
||||
const int lm1 = (shortTransform ? m_transfLengthS : m_transfLengthL) - 1;
|
||||
const int lm1o2 = lm1 >> 1;
|
||||
const int32_t* rotatCos = (shortTransform ? m_dctRotCosS : m_dctRotCosL);
|
||||
const int32_t* rotatSin = (shortTransform ? m_dctRotSinS : m_dctRotSinL);
|
||||
const int64_t rotOffset = (shortTransform ? LUT_OFFSET>>3 : LUT_OFFSET);
|
||||
const int64_t rotShift = (shortTransform ? LUT_SHIFT - 3 : LUT_SHIFT);
|
||||
int32_t* const tempReal = m_tempIntBuf;
|
||||
int32_t* const tempImag = &m_tempIntBuf[lm1o2 + 1];
|
||||
int i, i2;
|
||||
|
||||
if (signal == nullptr)
|
||||
{
|
||||
return 1; // null-pointer input error
|
||||
}
|
||||
|
||||
// resort, separate, pre-twiddle signal
|
||||
for (i = lm1o2, i2 = lm1 - 1; i >= 0; i--, i2 -= 2)
|
||||
{
|
||||
const int64_t c = rotatCos[i];
|
||||
const int64_t s = rotatSin[i];
|
||||
const int64_t e = signal[i2/*even*/];
|
||||
const int64_t o = signal[lm1 - i2];
|
||||
|
||||
tempReal[i] = int32_t ((rotOffset + e * c - o * s) >> rotShift);
|
||||
tempImag[i] = int32_t ((rotOffset + o * c + e * s) >> rotShift);
|
||||
}
|
||||
|
||||
applyHalfSizeFFT (tempReal, tempImag, shortTransform);
|
||||
|
||||
// post-twiddle, combine, resort output
|
||||
for (i = lm1o2, i2 = lm1 - 1; i >= 0; i--, i2 -= 2)
|
||||
{
|
||||
const int64_t c = rotatCos[i];
|
||||
const int64_t s = rotatSin[i];
|
||||
const int64_t e = tempReal[i];
|
||||
const int64_t o = tempImag[i];
|
||||
|
||||
signal[i2/*even*/] = int32_t ((LUT_OFFSET + o * s - e * c) >> LUT_SHIFT);
|
||||
signal[lm1 - i2] = int32_t ((LUT_OFFSET + e * s + o * c) >> LUT_SHIFT);
|
||||
}
|
||||
|
||||
return 0; // no error
|
||||
}
|
||||
|
||||
unsigned LappedTransform::applyMCLT (const int32_t* timeSig, const bool eightTransforms, bool kbdWindowL, const bool kbdWindowR,
|
||||
const bool lowOverlapL, const bool lowOverlapR, int32_t* const outMdct, int32_t* const outMdst)
|
||||
{
|
||||
if ((timeSig == nullptr) || (outMdct == nullptr) || (outMdst == nullptr))
|
||||
{
|
||||
return 1; // invalid arguments error
|
||||
}
|
||||
|
||||
if (eightTransforms) // short windows
|
||||
{
|
||||
const int32_t* tSigS = &timeSig[(m_transfLengthL - m_transfLengthS) >> 1];
|
||||
int32_t* outMdctS = outMdct;
|
||||
int32_t* outMdstS = outMdst;
|
||||
|
||||
for (unsigned w = 0; w < 8; w++)
|
||||
{
|
||||
windowAndFoldInL (tSigS /*window half 1*/, true, kbdWindowL, lowOverlapL, false, outMdctS);
|
||||
windowAndFoldInR (&tSigS[m_transfLengthS], true, kbdWindowR, lowOverlapR, false, outMdctS);
|
||||
windowAndFoldInL (tSigS /*window half 1*/, true, kbdWindowL, lowOverlapL, true, outMdstS);
|
||||
windowAndFoldInR (&tSigS[m_transfLengthS], true, kbdWindowR, lowOverlapR, true, outMdstS);
|
||||
// MDCT via DCT-IV
|
||||
applyNegDCT4 (outMdctS, true);
|
||||
// MDST via DCT-IV
|
||||
applyNegDCT4 (outMdstS, true);
|
||||
#if 1 // not needed here
|
||||
for (int i = m_transfLengthS - 2; i >= 0; i -= 2)
|
||||
{
|
||||
outMdstS[i] *= -1; // DCT to DST
|
||||
}
|
||||
#endif
|
||||
kbdWindowL = kbdWindowR; // only first window uses last frame's shape
|
||||
tSigS += m_transfLengthS;
|
||||
outMdctS += m_transfLengthS;
|
||||
outMdstS += m_transfLengthS;
|
||||
}
|
||||
}
|
||||
else // 1 long window
|
||||
{
|
||||
windowAndFoldInL (timeSig /*window half 1*/, false, kbdWindowL, lowOverlapL, false, outMdct);
|
||||
windowAndFoldInR (&timeSig[m_transfLengthL], false, kbdWindowR, lowOverlapR, false, outMdct);
|
||||
windowAndFoldInL (timeSig /*window half 1*/, false, kbdWindowL, lowOverlapL, true, outMdst);
|
||||
windowAndFoldInR (&timeSig[m_transfLengthL], false, kbdWindowR, lowOverlapR, true, outMdst);
|
||||
// MDCT using DCT-IV
|
||||
applyNegDCT4 (outMdct, false);
|
||||
// MDST using DCT-IV
|
||||
applyNegDCT4 (outMdst, false);
|
||||
#if 1 // not needed here
|
||||
for (int i = m_transfLengthL - 2; i >= 0; i -= 2)
|
||||
{
|
||||
outMdst[i] *= -1; // DCT to DST
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
return 0; // no error
|
||||
}
|
||||
|
||||
unsigned LappedTransform::initConstants (int32_t* const tempIntBuf, int32_t* const timeWindowL[2], int32_t* const timeWindowS[2],
|
||||
const unsigned maxTransfLength)
|
||||
{
|
||||
const short halfLength = short (maxTransfLength >> 1);
|
||||
const short sixtLength = short (maxTransfLength >> 4);
|
||||
const double dNormL = 3.141592653589793 / (2.0 * halfLength);
|
||||
const double dNormS = 3.141592653589793 / (2.0 * sixtLength);
|
||||
const double dNormL4 = dNormL * 4.0;
|
||||
short s;
|
||||
|
||||
if ((tempIntBuf == nullptr) || (timeWindowL == nullptr) || (timeWindowS == nullptr) ||
|
||||
(maxTransfLength < 128) || (maxTransfLength > 8192) || (maxTransfLength & (maxTransfLength - 1)))
|
||||
{
|
||||
return 1; // invalid arguments error
|
||||
}
|
||||
|
||||
m_transfLengthL = 2 * halfLength;
|
||||
m_transfLengthS = 2 * sixtLength;
|
||||
|
||||
if ((m_dctRotCosL = (int32_t*) malloc (halfLength * sizeof (int32_t))) == nullptr ||
|
||||
(m_dctRotCosS = (int32_t*) malloc (sixtLength * sizeof (int32_t))) == nullptr ||
|
||||
(m_dctRotSinL = (int32_t*) malloc (halfLength * sizeof (int32_t))) == nullptr ||
|
||||
(m_dctRotSinS = (int32_t*) malloc (sixtLength * sizeof (int32_t))) == nullptr ||
|
||||
(m_fftHalfCos = (int32_t*) malloc ((halfLength >> 1) * sizeof (int32_t))) == nullptr ||
|
||||
(m_fftHalfSin = (int32_t*) malloc ((halfLength >> 1) * sizeof (int32_t))) == nullptr ||
|
||||
(m_fftPermutL = createPermutTable (halfLength)) == nullptr ||
|
||||
(m_fftPermutS = createPermutTable (sixtLength)) == nullptr)
|
||||
{
|
||||
return 2; // memory allocation error
|
||||
}
|
||||
|
||||
// obtain cosine and sine coefficients
|
||||
for (s = 0; s < halfLength; s++)
|
||||
{
|
||||
m_dctRotCosL[s] = int32_t (cos (dNormL * (s + 0.125)) * (INT_MAX + 1.0) + 0.5);
|
||||
m_dctRotSinL[s] = int32_t (sin (dNormL * (s + 0.125)) * INT_MIN - 0.5);
|
||||
}
|
||||
for (s = 0; s < sixtLength; s++)
|
||||
{
|
||||
m_dctRotCosS[s] = int32_t (cos (dNormS * (s + 0.125)) * (INT_MAX + 1.0) + 0.5);
|
||||
m_dctRotSinS[s] = int32_t (sin (dNormS * (s + 0.125)) * INT_MIN - 0.5);
|
||||
}
|
||||
|
||||
for (s = 0; s < m_transfLengthS; s++)
|
||||
{
|
||||
m_fftHalfSin[s] = int32_t (sin (dNormL4 * s) * INT_MIN - 0.5);
|
||||
m_fftHalfCos[m_transfLengthS + s] = -m_fftHalfSin[s];
|
||||
}
|
||||
// complete missing entries by copying
|
||||
m_fftHalfSin[s] = INT_MIN;
|
||||
m_fftHalfCos[0] = INT_MIN;
|
||||
for (s = 1; s < m_transfLengthS; s++)
|
||||
{
|
||||
m_fftHalfSin[m_transfLengthS + s] = m_fftHalfSin[m_transfLengthS - s];
|
||||
m_fftHalfCos[m_transfLengthS - s] = m_fftHalfSin[s];
|
||||
}
|
||||
|
||||
// adopt helper/window buffer pointers
|
||||
m_tempIntBuf = tempIntBuf;
|
||||
for (s = 0; s < 2; s++)
|
||||
{
|
||||
m_timeWindowL[s] = timeWindowL[s];
|
||||
m_timeWindowS[s] = timeWindowS[s];
|
||||
}
|
||||
|
||||
return 0; // no error
|
||||
}
|
63
src/lib/lappedTransform.h
Normal file
63
src/lib/lappedTransform.h
Normal file
@ -0,0 +1,63 @@
|
||||
/* lappedTransform.h - header file for class providing time-frequency transformation
|
||||
* written by C. R. Helmrich, last modified in 2019 - see License.htm for legal notices
|
||||
*
|
||||
* The copyright in this software is being made available under a Modified BSD-Style 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-2020 Christian R. Helmrich, project ecodis. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef _LAPPED_TRANSFORM_H_
|
||||
#define _LAPPED_TRANSFORM_H_
|
||||
|
||||
#include "exhaleLibPch.h"
|
||||
|
||||
// constants, experimental macros
|
||||
#define LUT_OFFSET (1 << 30)
|
||||
#define LUT_SHIFT 31
|
||||
#define WIN_OFFSET (1 << 24)
|
||||
#define WIN_SHIFT 25
|
||||
|
||||
// time-frequency transform class
|
||||
class LappedTransform
|
||||
{
|
||||
private:
|
||||
|
||||
// member variables
|
||||
int32_t* m_dctRotCosL;
|
||||
int32_t* m_dctRotCosS;
|
||||
int32_t* m_dctRotSinL;
|
||||
int32_t* m_dctRotSinS;
|
||||
int32_t* m_fftHalfCos;
|
||||
int32_t* m_fftHalfSin;
|
||||
short* m_fftPermutL;
|
||||
short* m_fftPermutS;
|
||||
int32_t* m_tempIntBuf; // pointer to temporary helper buffer
|
||||
int32_t* m_timeWindowL[2]; // pointer to two long window halves
|
||||
int32_t* m_timeWindowS[2]; // pointer to two short window halves
|
||||
short m_transfLengthL;
|
||||
short m_transfLengthS;
|
||||
|
||||
// helper functions
|
||||
void applyHalfSizeFFT (int32_t* const iR/*eal*/, int32_t* const iI/*mag*/, const bool shortTransform);
|
||||
void windowAndFoldInL (const int32_t* inputL, const bool shortTransform, const bool kbdWindowL, const bool lowOverlapL,
|
||||
const bool mdstKernel, int32_t* const output);
|
||||
void windowAndFoldInR (const int32_t* inputR, const bool shortTransform, const bool kbdWindowR, const bool lowOverlapR,
|
||||
const bool mdstKernel, int32_t* const output);
|
||||
|
||||
public:
|
||||
|
||||
// constructor
|
||||
LappedTransform ();
|
||||
// destructor
|
||||
~LappedTransform ();
|
||||
// public functions
|
||||
unsigned applyNegDCT4 (int32_t* const signal, const bool shortTransform);
|
||||
unsigned applyMCLT (const int32_t* timeSig, const bool eightTransforms, bool kbdWindowL, const bool kbdWindowR,
|
||||
const bool lowOverlapL, const bool lowOverlapR, int32_t* const outMdct, int32_t* const outMdst);
|
||||
unsigned initConstants (int32_t* const tempIntBuf, int32_t* const timeWindowL[2], int32_t* const timeWindowS[2],
|
||||
const unsigned maxTransfLength);
|
||||
}; // LappedTransform
|
||||
|
||||
#endif // _LAPPED_TRANSFORM_H_
|
421
src/lib/linearPrediction.cpp
Normal file
421
src/lib/linearPrediction.cpp
Normal file
@ -0,0 +1,421 @@
|
||||
/* linearPrediction.cpp - source file for class providing linear prediction capability
|
||||
* written by C. R. Helmrich, last modified in 2019 - see License.htm for legal notices
|
||||
*
|
||||
* The copyright in this software is being made available under a Modified BSD-Style 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-2020 Christian R. Helmrich, project ecodis. All rights reserved.
|
||||
*/
|
||||
|
||||
#include "exhaleLibPch.h"
|
||||
#include "linearPrediction.h"
|
||||
|
||||
// reconstructed 3-bit TNS coefficients
|
||||
static const short tnsQuantCoeff3[9 /*2^3+1*/] = { // = round (2^11 * sin (x * pi / (x < 0 ? 9 : 7)))
|
||||
-2017, -1774, -1316, -700, 0, 889, 1601, 1997, 1997
|
||||
};
|
||||
|
||||
// reconstructed 4-bit TNS coefficients
|
||||
static const short tnsQuantCoeff4[17/*2^4+1*/] = { // = round (2^11 * sin (x * pi / (x < 0 ? 17 : 15)))
|
||||
-2039, -1970, -1833, -1634, -1380, -1078, -740, -376, 0, 426, 833, 1204, 1522, 1774, 1948, 2037, 2037
|
||||
};
|
||||
|
||||
static const short* tnsQuantCoeff[2/*coefRes*/] = {tnsQuantCoeff3, tnsQuantCoeff4};
|
||||
|
||||
// ISO/IEC 14496-3, Sec. 4.6.9.3, 3-bit
|
||||
static const char tnsQuantIndex3[SCHAR_MAX + 1] = { // = round (asin (x / 64) * (x < 0 ? 9 : 7) / pi)
|
||||
-4, -4, -4, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3
|
||||
};
|
||||
|
||||
// ISO/IEC 14496-3, Sec. 4.6.9.3, 4-bit
|
||||
static const char tnsQuantIndex4[SCHAR_MAX + 1] = { // = round (asin (x / 64) * (x < 0 ? 17 : 15) / pi)
|
||||
-8, -7, -7, -7, -6, -6, -6, -6, -6, -5, -5, -5, -5, -5, -5, -5, -4, -4, -4, -4, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -3,
|
||||
-3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3,
|
||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7
|
||||
};
|
||||
|
||||
static const char* tnsQuantIndex[2/*coefRes*/] = {tnsQuantIndex3, tnsQuantIndex4};
|
||||
|
||||
// static helper functions
|
||||
static int quantizeParCorCoeffs (const short* const parCorCoeffs, const uint16_t nCoeffs, const short bitDepth, int8_t* const quantCoeffs,
|
||||
const bool lowRes)
|
||||
{
|
||||
const short bitShift = bitDepth - 7;
|
||||
const unsigned tabIdx = (lowRes ? 0 : 1);
|
||||
const char tabOffset = 4 << tabIdx;
|
||||
const short* coeffTab = tnsQuantCoeff[tabIdx];
|
||||
const char* indexTab = tnsQuantIndex[tabIdx];
|
||||
int dist0, dist1, distTotal = 0;
|
||||
|
||||
for (uint16_t s = 0; s < nCoeffs; s++)
|
||||
{
|
||||
const short coeff = (bitShift < 0 ? parCorCoeffs[s] << -bitShift : parCorCoeffs[s] >> bitShift);
|
||||
const char coeff1 = indexTab[coeff + 1 + (SCHAR_MAX >> 1)];
|
||||
const char coeff0 = (coeff1 <= -tabOffset ? coeff1 : coeff1 - 1);
|
||||
|
||||
dist0 = (int) coeffTab[coeff0 + tabOffset] - parCorCoeffs[s];
|
||||
dist0 *= dist0;
|
||||
dist1 = (int) coeffTab[coeff1 + tabOffset] - parCorCoeffs[s];
|
||||
dist1 *= dist1;
|
||||
if (dist0 < dist1) // use down-rounded coeff
|
||||
{
|
||||
quantCoeffs[s] = coeff0;
|
||||
distTotal += dist0;
|
||||
}
|
||||
else // dist0 >= dist1, use up-rounded coeff
|
||||
{
|
||||
quantCoeffs[s] = ((dist0 == dist1) && (abs (coeff0) < abs (coeff1)) ? coeff0 : coeff1);
|
||||
distTotal += dist1;
|
||||
}
|
||||
} // for s
|
||||
|
||||
return distTotal; // total quantization error
|
||||
}
|
||||
|
||||
// constructor
|
||||
LinearPredictor::LinearPredictor ()
|
||||
{
|
||||
// initialize member variables
|
||||
memset (m_tempBuf, 0, 2 * MAX_PREDICTION_ORDER * sizeof (int64_t));
|
||||
}
|
||||
|
||||
// public functions
|
||||
uint32_t LinearPredictor::calcParCorCoeffs (const int32_t* const anaSignal, const uint16_t nAnaSamples, const uint16_t nCoeffs,
|
||||
short* const parCorCoeffs) // returns 256 - 256 / prediction gain per filter order, or 0
|
||||
{
|
||||
// int64 version of algorithm in Figure 1 of J. LeRoux and C. Gueguen, "A Fixed Point Computation
|
||||
// of Partial Correlation Coefficients," IEEE Trans. ASSP, vol. 27, no. 3, pp. 257-259, June 1977.
|
||||
int64_t* const acf = m_tempBuf; // correlation
|
||||
uint32_t pg[MAX_PREDICTION_ORDER] = {0, 0, 0, 0};
|
||||
int64_t pgDen, pgOff; // for prediction gains
|
||||
short s = nAnaSamples - 1;
|
||||
|
||||
if ((anaSignal == nullptr) || (parCorCoeffs == nullptr) || (nCoeffs == 0) || (nCoeffs > MAX_PREDICTION_ORDER) || (nAnaSamples <= nCoeffs))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (nCoeffs >= 2) // high predictor order, 2-4
|
||||
{
|
||||
int64_t* const EN = &m_tempBuf[0];
|
||||
int64_t* const EP = &m_tempBuf[MAX_PREDICTION_ORDER];
|
||||
int64_t sampleHO = anaSignal[s];
|
||||
|
||||
// calculate autocorrelation function values
|
||||
acf[0] = sampleHO * sampleHO;
|
||||
sampleHO = anaSignal[--s];
|
||||
acf[0] += sampleHO * sampleHO;
|
||||
acf[1] = sampleHO * anaSignal[s + 1];
|
||||
sampleHO = anaSignal[--s];
|
||||
acf[0] += sampleHO * sampleHO;
|
||||
acf[1] += sampleHO * anaSignal[s + 1];
|
||||
acf[2] = sampleHO * anaSignal[s + 2];
|
||||
|
||||
if (nCoeffs == 2)
|
||||
{
|
||||
for (s--; s >= 0; s--)
|
||||
{
|
||||
const int64_t sample = anaSignal[s];
|
||||
|
||||
acf[0] += sample * sample;
|
||||
acf[1] += sample * anaSignal[s + 1];
|
||||
acf[2] += sample * anaSignal[s + 2];
|
||||
}
|
||||
}
|
||||
else // order 3-4
|
||||
{
|
||||
sampleHO = anaSignal[--s];
|
||||
acf[0] += sampleHO * sampleHO;
|
||||
acf[1] += sampleHO * anaSignal[s + 1];
|
||||
acf[2] += sampleHO * anaSignal[s + 2];
|
||||
acf[3] = sampleHO * anaSignal[s + 3];
|
||||
|
||||
if (nCoeffs == 3)
|
||||
{
|
||||
for (s--; s >= 0; s--)
|
||||
{
|
||||
const int64_t sample = anaSignal[s];
|
||||
|
||||
acf[0] += sample * sample;
|
||||
acf[1] += sample * anaSignal[s + 1];
|
||||
acf[2] += sample * anaSignal[s + 2];
|
||||
acf[3] += sample * anaSignal[s + 3];
|
||||
}
|
||||
}
|
||||
else // order 4
|
||||
{
|
||||
acf[4] = 0;
|
||||
for (s--; s >= 0; s--)
|
||||
{
|
||||
const int64_t sample = anaSignal[s];
|
||||
|
||||
acf[0] += sample * sample;
|
||||
acf[1] += sample * anaSignal[s + 1];
|
||||
acf[2] += sample * anaSignal[s + 2];
|
||||
acf[3] += sample * anaSignal[s + 3];
|
||||
acf[4] += sample * anaSignal[s + 4];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// reduce correlation value range to <32 bit
|
||||
acf[0] = (acf[0] - INT_MIN/*eps*/) >> 31;
|
||||
|
||||
EP[3] = (acf[4] - (INT_MIN >> 1)) >> 31;
|
||||
EN[3] = (acf[3] - (INT_MIN >> 1)) >> 31;
|
||||
EP[2] = EN[3];
|
||||
EN[2] = (acf[2] - (INT_MIN >> 1)) >> 31;
|
||||
EP[1] = EN[2];
|
||||
EN[1] = (acf[1] - (INT_MIN >> 1)) >> 31;
|
||||
EP[0] = EN[1]; // finish EP buffer creation
|
||||
EN[0] = acf[0]; // finish EN buffer creation
|
||||
|
||||
pgOff = acf[0];
|
||||
pgDen = pgOff << 1;
|
||||
|
||||
for (s = 0; s < (short) nCoeffs; s++)
|
||||
{
|
||||
uint16_t p;
|
||||
// ParCor coefficient Ks & prediction gain
|
||||
int64_t Ks = (EP[s] * -512) / (EN[0] + LP_EPS);
|
||||
|
||||
Ks = CLIP_PM (Ks, 511); // enforce |Ks|<1
|
||||
parCorCoeffs[s] = (short) Ks;
|
||||
|
||||
for (p = s; p < nCoeffs; p++)
|
||||
{
|
||||
sampleHO = EN[p - s]; // use register?
|
||||
EN[p - s] = (EN[p - s] << 9) + EP[p] * Ks;
|
||||
EP[p] = (EP[p] << 9) + sampleHO * Ks;
|
||||
}
|
||||
if (s > 0 && EN[0] < 0) // EN wrap-around
|
||||
{
|
||||
pg[s] = pg[s - 1]; // "invent" some gain
|
||||
}
|
||||
else
|
||||
{
|
||||
sampleHO = (1 << (s * 9)) >> 1;// offset
|
||||
pg[s] = (pgOff <= LP_EPS ? 0 : (1 << 8) - uint32_t ((((EN[0] + sampleHO) >> (s * 9)) + pgOff) / pgDen));
|
||||
}
|
||||
} // for s
|
||||
}
|
||||
else // nCoeffs == 1, minimum predictor order
|
||||
{
|
||||
int64_t sampleMO = anaSignal[s];
|
||||
|
||||
acf[0] = sampleMO * sampleMO;
|
||||
acf[1] = 0;
|
||||
for (s--; s >= 0; s--)
|
||||
{
|
||||
const int64_t sample = anaSignal[s];
|
||||
|
||||
acf[0] += sample * sample;
|
||||
acf[1] += sample * anaSignal[s + 1];
|
||||
}
|
||||
|
||||
// reduce correlation value range to <32 bit
|
||||
acf[0] = (acf[0] - INT_MIN/*eps*/) >> 31;
|
||||
acf[1] = (acf[1] - (INT_MIN >> 1)) >> 31;
|
||||
|
||||
pgOff = acf[0];
|
||||
pgDen = pgOff << 1;
|
||||
|
||||
// 1st-order coefficient K & prediction gain
|
||||
sampleMO = (acf[1] * -512) / (acf[0] + LP_EPS);
|
||||
|
||||
sampleMO = CLIP_PM (sampleMO, 511); // |K|<1
|
||||
parCorCoeffs[0] = (short) sampleMO;
|
||||
|
||||
sampleMO = (acf[0] << 9) + acf[1] * sampleMO;
|
||||
pg[0] = (pgOff <= LP_EPS ? 0 : (1 << 8) - uint32_t ((sampleMO + pgOff) / pgDen));
|
||||
}
|
||||
|
||||
return (CLIP_UCHAR (pg[3]) << 24) | (CLIP_UCHAR (pg[2]) << 16) | (CLIP_UCHAR (pg[1]) << 8) | CLIP_UCHAR (pg[0]);
|
||||
}
|
||||
|
||||
uint8_t LinearPredictor::calcOptTnsCoeffs (short* const parCorCoeffs, int8_t* const quantCoeffs, bool* const lowCoeffRes,
|
||||
const uint16_t maxOrder, const uint8_t predGain, const uint8_t tonality /*= 0*/,
|
||||
const uint16_t parCorCoeffBitDepth /*= 10*/) // returns optimized filter order for TNS
|
||||
{
|
||||
const short bitShift = LP_DEPTH - (short) parCorCoeffBitDepth;
|
||||
short shortBuf[MAX_PREDICTION_ORDER];
|
||||
uint16_t s, order = __min (maxOrder, MAX_PREDICTION_ORDER);
|
||||
int d, i;
|
||||
|
||||
if ((parCorCoeffs == nullptr) || (quantCoeffs == nullptr) || (maxOrder == 0) || (maxOrder > MAX_PREDICTION_ORDER) || (parCorCoeffBitDepth < 2) || (bitShift < 0))
|
||||
{
|
||||
if (quantCoeffs) memset (quantCoeffs, 0, order * sizeof (char));
|
||||
|
||||
return 0; // invalid input arguments error
|
||||
}
|
||||
|
||||
// determine direct-form filter damping factor
|
||||
parCorCoeffs[0] <<= bitShift;
|
||||
i = abs (parCorCoeffs[0]);
|
||||
for (s = 1; s < order; s++)
|
||||
{
|
||||
parCorCoeffs[s] <<= bitShift; // scale coeff
|
||||
i = __max (i, abs (parCorCoeffs[s]));
|
||||
}
|
||||
for (/*s*/; s < MAX_PREDICTION_ORDER; s++)
|
||||
{
|
||||
parCorCoeffs[s] = 0; // similarParCorCoeffs
|
||||
}
|
||||
|
||||
if (predGain < 41 + (tonality >> 3)) // 1.5 dB
|
||||
{
|
||||
memset (quantCoeffs, 0, order * sizeof (char));
|
||||
|
||||
return 0; // LPC prediction gain is too low
|
||||
}
|
||||
|
||||
d = (7 << (LP_DEPTH - 1)) >> 3;
|
||||
|
||||
if (i > d) // apply direct-form filter damping
|
||||
{
|
||||
i = ((i >> 1) + d * (1 << LP_SHIFT)) / i;
|
||||
d = i;
|
||||
if (parCorToLpCoeffs (parCorCoeffs, order, shortBuf, LP_DEPTH) > 0)
|
||||
{
|
||||
return 0; // coefficient conversion error
|
||||
}
|
||||
for (s = 0; s < order; s++)
|
||||
{
|
||||
shortBuf[s] = short ((LP_OFFSET + d * shortBuf[s]) >> LP_SHIFT);
|
||||
d = (LP_OFFSET + d * i) >> LP_SHIFT;
|
||||
}
|
||||
// verify order and go back to ParCor domain
|
||||
if ((s > 0) && (shortBuf[s - 1] == 0))
|
||||
{
|
||||
order--;
|
||||
}
|
||||
if (lpToParCorCoeffs (shortBuf, order, parCorCoeffs, LP_DEPTH) > 0)
|
||||
{
|
||||
return 0; // coefficient conversion error
|
||||
}
|
||||
}
|
||||
|
||||
// high-res quantizer, obtain coeff distortion
|
||||
d = quantizeParCorCoeffs (parCorCoeffs, order, LP_DEPTH, quantCoeffs, false);
|
||||
|
||||
if ((lowCoeffRes != nullptr) && (quantizeParCorCoeffs (parCorCoeffs, order, LP_DEPTH, (int8_t* const) m_tempBuf, true) < d))
|
||||
{
|
||||
// low-res quantizer yields lower distortion
|
||||
*lowCoeffRes = true;
|
||||
memcpy (quantCoeffs, m_tempBuf, order * sizeof (char));
|
||||
}
|
||||
|
||||
for (; order > 0; order--) // return opt order
|
||||
{
|
||||
if (quantCoeffs[order - 1] != 0) return (uint8_t) order;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned LinearPredictor::lpToParCorCoeffs (/*mod!*/short* const lpCoeffs, const uint16_t nCoeffs, short* const parCorCoeffs,
|
||||
const uint16_t parCorCoeffBitDepth /*= 10*/)
|
||||
{
|
||||
int* const intBuf = (int* const) m_tempBuf;
|
||||
const int shift = parCorCoeffBitDepth - 1;
|
||||
const int offset = 1 << (shift - 1);
|
||||
|
||||
if ((lpCoeffs == nullptr) || (parCorCoeffs == nullptr) || (nCoeffs == 0) || (nCoeffs > MAX_PREDICTION_ORDER) || (parCorCoeffBitDepth < 2))
|
||||
{
|
||||
return 1; // error
|
||||
}
|
||||
|
||||
for (uint16_t p, s = nCoeffs - 1; s > 0; s--)
|
||||
{
|
||||
const int i = (parCorCoeffs[s] = lpCoeffs[s]);
|
||||
const int d = (1 << shift) - ((offset + i * i) >> shift);
|
||||
const int o = d >> 1;
|
||||
|
||||
if (d <= 0) return s; // invalid coefficient
|
||||
|
||||
for (p = 0; p < s; p++)
|
||||
{
|
||||
intBuf[p] = lpCoeffs[s - 1 - p];
|
||||
}
|
||||
for (p = 0; p < s; p++)
|
||||
{
|
||||
lpCoeffs[p] = short ((o + ((int) lpCoeffs[p] << shift) - intBuf[p] * i) / d);
|
||||
}
|
||||
}
|
||||
parCorCoeffs[0] = lpCoeffs[0];
|
||||
|
||||
return 0; // no error
|
||||
}
|
||||
|
||||
unsigned LinearPredictor::parCorToLpCoeffs (const short* const parCorCoeffs, const uint16_t nCoeffs, short* const lpCoeffs,
|
||||
const uint16_t parCorCoeffBitDepth /*= 10*/)
|
||||
{
|
||||
int* const intBuf = (int* const) m_tempBuf;
|
||||
const int shift = parCorCoeffBitDepth - 1;
|
||||
const int offset = 1 << (shift - 1);
|
||||
|
||||
if ((parCorCoeffs == nullptr) || (lpCoeffs == nullptr) || (nCoeffs == 0) || (nCoeffs > MAX_PREDICTION_ORDER) || (parCorCoeffBitDepth < 2))
|
||||
{
|
||||
return 1; // error
|
||||
}
|
||||
|
||||
lpCoeffs[0] = parCorCoeffs[0];
|
||||
for (uint16_t p, s = 1; s < nCoeffs; s++)
|
||||
{
|
||||
const int i = (lpCoeffs[s] = parCorCoeffs[s]);
|
||||
|
||||
if (abs (i) > (1 << shift)) return s; // > 1
|
||||
|
||||
for (p = 0; p < s; p++)
|
||||
{
|
||||
intBuf[p] = lpCoeffs[s - 1 - p];
|
||||
}
|
||||
for (p = 0; p < s; p++)
|
||||
{
|
||||
lpCoeffs[p] += short ((offset + intBuf[p] * i) >> shift);
|
||||
}
|
||||
}
|
||||
return 0; // no error
|
||||
}
|
||||
|
||||
unsigned LinearPredictor::quantTnsToLpCoeffs (const int8_t* const quantTnsCoeffs, const uint16_t nCoeffs, const bool lowCoeffRes,
|
||||
short* const parCorCoeffs, short* const lpCoeffs)
|
||||
{
|
||||
const unsigned tabIdx = (lowCoeffRes ? 0 : 1);
|
||||
const char tabOffset = 4 << tabIdx;
|
||||
const short* coeffTab = tnsQuantCoeff[tabIdx];
|
||||
|
||||
if ((quantTnsCoeffs == nullptr) || (parCorCoeffs == nullptr) || (lpCoeffs == nullptr) || (nCoeffs == 0) || (nCoeffs > MAX_PREDICTION_ORDER))
|
||||
{
|
||||
return 1; // error
|
||||
}
|
||||
|
||||
for (uint16_t s = 0; s < nCoeffs; s++)
|
||||
{
|
||||
parCorCoeffs[s] = coeffTab[CLIP_PM (quantTnsCoeffs[s], tabOffset) + tabOffset];
|
||||
}
|
||||
|
||||
return parCorToLpCoeffs (parCorCoeffs, nCoeffs, lpCoeffs, LP_DEPTH);
|
||||
}
|
||||
|
||||
bool LinearPredictor::similarParCorCoeffs (const short* const parCorCoeffs1, const short* const parCorCoeffs2, const uint16_t nCoeffs,
|
||||
const uint16_t parCorCoeffBitDepth /*= 10*/)
|
||||
{
|
||||
unsigned sumAbsDiff = 0;
|
||||
|
||||
if ((parCorCoeffs1 == nullptr) || (parCorCoeffs2 == nullptr) || (nCoeffs == 0) || (nCoeffs > MAX_PREDICTION_ORDER) || (parCorCoeffBitDepth < 2))
|
||||
{
|
||||
return false; // error
|
||||
}
|
||||
|
||||
for (uint16_t s = 0; s < nCoeffs; s++)
|
||||
{
|
||||
sumAbsDiff += abs (parCorCoeffs1[s] - parCorCoeffs2[s]);
|
||||
}
|
||||
|
||||
return (sumAbsDiff < ((unsigned) nCoeffs << (parCorCoeffBitDepth >> 1)));
|
||||
}
|
52
src/lib/linearPrediction.h
Normal file
52
src/lib/linearPrediction.h
Normal file
@ -0,0 +1,52 @@
|
||||
/* linearPrediction.h - header file for class providing linear prediction capability
|
||||
* written by C. R. Helmrich, last modified in 2019 - see License.htm for legal notices
|
||||
*
|
||||
* The copyright in this software is being made available under a Modified BSD-Style 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-2020 Christian R. Helmrich, project ecodis. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef _LINEAR_PREDICTION_H_
|
||||
#define _LINEAR_PREDICTION_H_
|
||||
|
||||
#include "exhaleLibPch.h"
|
||||
|
||||
// constants, experimental macros
|
||||
#define LP_EPS 1
|
||||
#define LP_SHIFT 15
|
||||
#define LP_DEPTH (1 + LP_SHIFT - MAX_PREDICTION_ORDER)
|
||||
#define LP_OFFSET (1 << (LP_SHIFT - 1))
|
||||
|
||||
// linear predictive filter class
|
||||
class LinearPredictor
|
||||
{
|
||||
private:
|
||||
|
||||
// temporary buffer
|
||||
int64_t m_tempBuf[2 * MAX_PREDICTION_ORDER];
|
||||
|
||||
public:
|
||||
|
||||
// constructor
|
||||
LinearPredictor ();
|
||||
// destructor
|
||||
~LinearPredictor () { }
|
||||
// public functions
|
||||
uint32_t calcParCorCoeffs (const int32_t* const anaSignal, const uint16_t nAnaSamples, const uint16_t nCoeffs,
|
||||
short* const parCorCoeffs); // returns 256 - 256 / prediction gain per filter order, or 0
|
||||
uint8_t calcOptTnsCoeffs (short* const parCorCoeffs, int8_t* const quantCoeffs, bool* const lowCoeffRes,
|
||||
const uint16_t maxOrder, const uint8_t predGain, const uint8_t tonality = 0,
|
||||
const uint16_t parCorCoeffBitDepth = 10); // returns optimized filter order for TNS
|
||||
unsigned lpToParCorCoeffs (short* const lpCoeffs, const uint16_t nCoeffs, short* const parCorCoeffs,
|
||||
const uint16_t parCorCoeffBitDepth = 10);
|
||||
unsigned parCorToLpCoeffs (const short* const parCorCoeffs, const uint16_t nCoeffs, short* const lpCoeffs,
|
||||
const uint16_t parCorCoeffBitDepth = 10);
|
||||
unsigned quantTnsToLpCoeffs(const int8_t* const quantCoeffs, const uint16_t nCoeffs, const bool lowCoeffRes,
|
||||
short* const parCorCoeffs, short* const lpCoeffs);
|
||||
bool similarParCorCoeffs (const short* const parCorCoeffs1, const short* const parCorCoeffs2, const uint16_t nCoeffs,
|
||||
const uint16_t parCorCoeffBitDepth = 10);
|
||||
}; // LinearPredictor
|
||||
|
||||
#endif // _LINEAR_PREDICTION_H_
|
52
src/lib/makefile
Normal file
52
src/lib/makefile
Normal file
@ -0,0 +1,52 @@
|
||||
## makefile - code library make-file for compiling exhale on Linux and MacOS platforms
|
||||
# written by C. R. Helmrich, last modified 2019 - see License.txt for legal notices
|
||||
#
|
||||
# The copyright in this software is being made available under a Modified BSD 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-2019 Christian R. Helmrich, project ecodis. All rights reserved.
|
||||
##
|
||||
|
||||
# define as source code library
|
||||
CONFIG = LIBRARY
|
||||
|
||||
# source and output directories
|
||||
DIR_BIN = ../../bin
|
||||
DIR_OBJ = ../../build
|
||||
DIR_INC = ../../include
|
||||
DIR_LIB = ../../lib
|
||||
DIR_SRC = ../../src/lib
|
||||
|
||||
# build with large file support
|
||||
DEFS = -DMSYS_LINUX -DMSYS_UNIX_LARGEFILE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64
|
||||
|
||||
# name of product / binary file
|
||||
PRD_NAME = Exhale
|
||||
|
||||
# name of temporary object file
|
||||
OBJS = \
|
||||
$(DIR_OBJ)/bitAllocation.o \
|
||||
$(DIR_OBJ)/bitStreamWriter.o \
|
||||
$(DIR_OBJ)/entropyCoding.o \
|
||||
$(DIR_OBJ)/exhaleEnc.o \
|
||||
$(DIR_OBJ)/exhaleLibPch.o \
|
||||
$(DIR_OBJ)/lappedTransform.o \
|
||||
$(DIR_OBJ)/linearPrediction.o \
|
||||
$(DIR_OBJ)/quantization.o \
|
||||
$(DIR_OBJ)/specAnalysis.o \
|
||||
$(DIR_OBJ)/specGapFilling.o \
|
||||
$(DIR_OBJ)/tempAnalysis.o \
|
||||
|
||||
# define libraries to link with
|
||||
LIBS = -lpthread
|
||||
DYN_LIBS = -ldl
|
||||
STAT_LIBS =
|
||||
|
||||
DYN_DEBUG_LIBS =
|
||||
DYN_RELEASE_LIBS =
|
||||
STAT_DEBUG_LIBS =
|
||||
STAT_RELEASE_LIBS =
|
||||
|
||||
# include common makefile.base
|
||||
include ../makefile.base
|
788
src/lib/quantization.cpp
Normal file
788
src/lib/quantization.cpp
Normal file
@ -0,0 +1,788 @@
|
||||
/* quantization.cpp - source file for class with nonuniform quantization functionality
|
||||
* written by C. R. Helmrich, last modified in 2019 - see License.htm for legal notices
|
||||
*
|
||||
* The copyright in this software is being made available under a Modified BSD-Style 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-2020 Christian R. Helmrich, project ecodis. All rights reserved.
|
||||
*/
|
||||
|
||||
#include "exhaleLibPch.h"
|
||||
#include "quantization.h"
|
||||
|
||||
// static helper function
|
||||
static inline short getBitCount (EntropyCoder& entrCoder, const int sfIndex, const int sfIndexPred,
|
||||
const unsigned char groupLength, const unsigned char* coeffQuant,
|
||||
const uint16_t coeffOffset, const uint16_t numCoeffs)
|
||||
{
|
||||
unsigned bitCount = (sfIndex != UCHAR_MAX && sfIndexPred == UCHAR_MAX ? 8 : entrCoder.indexGetBitCount (sfIndex - sfIndexPred));
|
||||
|
||||
if (groupLength == 1) // include arithmetic coding in bit count
|
||||
{
|
||||
bitCount += entrCoder.arithCodeSigMagn (coeffQuant, coeffOffset, numCoeffs);
|
||||
}
|
||||
|
||||
return (short) __min (SHRT_MAX, bitCount); // exclude sign bits
|
||||
}
|
||||
|
||||
// private helper functions
|
||||
double SfbQuantizer::getQuantDist (const unsigned* const coeffMagn, const unsigned char scaleFactor,
|
||||
const unsigned char* coeffQuant, const uint16_t numCoeffs)
|
||||
{
|
||||
const double stepSizeDiv = m_lutSfNorm[scaleFactor];
|
||||
double dDist = 0.0;
|
||||
|
||||
for (int i = numCoeffs - 1; i >= 0; i--)
|
||||
{
|
||||
const double d = m_lutXExp43[coeffQuant[i]] - coeffMagn[i] * stepSizeDiv;
|
||||
|
||||
dDist += d * d; // TODO: do this in fixed-point and with SIMD
|
||||
}
|
||||
|
||||
// consider quantization step-size in calculation of distortion
|
||||
return dDist * m_lut2ExpX4[scaleFactor] * m_lut2ExpX4[scaleFactor];
|
||||
}
|
||||
|
||||
unsigned char SfbQuantizer::quantizeMagn (const unsigned* const coeffMagn, const unsigned char scaleFactor,
|
||||
unsigned char* const coeffQuant, const uint16_t numCoeffs,
|
||||
short* const sigMaxQ /*= NULL*/, short* const sigNumQ /*= NULL*/)
|
||||
{
|
||||
const double stepSizeDiv = m_lutSfNorm[scaleFactor];
|
||||
double dNum = 0.0, dDen = 0.0;
|
||||
short maxQ = 0, numQ = 0;
|
||||
|
||||
for (int i = numCoeffs - 1; i >= 0; i--)
|
||||
{
|
||||
const double normalizedMagn = (double) coeffMagn[i] * stepSizeDiv;
|
||||
#if SFB_QUANT_FAST_POW
|
||||
short q;
|
||||
|
||||
if (normalizedMagn < 28.5) // fast approximate pow (d, 0.75)
|
||||
{
|
||||
// based on code from: N. N. Schraudolph, "A Fast, Compact Approximation of the Expo-
|
||||
// nential Function," Neural Comput., vol. 11, pp. 853-862, 1998 and M. Ankerl, 2007,
|
||||
// https://martin.ankerl.com/2007/10/04/optimized-pow-approximation-for-java-and-c-c/
|
||||
union { double d; int32_t i[2]; } u = { normalizedMagn };
|
||||
|
||||
u.i[1] = int32_t (0.75 * (u.i[1] - 1072632447) + 1072632447.0);
|
||||
u.i[0] = 0;
|
||||
q = short (u.d + (u.d < 1.0 ? 0.3822484 : 0.2734375));
|
||||
}
|
||||
else
|
||||
{
|
||||
q = short (SFB_QUANT_OFFSET + pow (normalizedMagn, 0.75));
|
||||
}
|
||||
#else
|
||||
short q = short (SFB_QUANT_OFFSET + pow (normalizedMagn, 0.75));
|
||||
#endif
|
||||
|
||||
if (q > 0)
|
||||
{
|
||||
if (q >= SCHAR_MAX)
|
||||
{
|
||||
if (maxQ < q)
|
||||
{
|
||||
maxQ = q; // find maximum quantized magnitude in vector
|
||||
}
|
||||
q = SCHAR_MAX;
|
||||
}
|
||||
else
|
||||
{
|
||||
const double diffRoundD = normalizedMagn - m_lutXExp43[q];
|
||||
const double diffRoundU = normalizedMagn - m_lutXExp43[q + 1];
|
||||
|
||||
if (diffRoundU * diffRoundU < diffRoundD * diffRoundD)
|
||||
{
|
||||
q++; // round-up gives lower distortion than round-down
|
||||
}
|
||||
}
|
||||
if (maxQ < q)
|
||||
{
|
||||
maxQ = q;
|
||||
}
|
||||
numQ++;
|
||||
dNum += m_lutXExp43[q] * normalizedMagn;
|
||||
dDen += m_lutXExp43[q] * m_lutXExp43[q];
|
||||
}
|
||||
#if SFB_QUANT_PERCEPT_OPT
|
||||
else // q == 0, assume perceptual transparency for code below
|
||||
{
|
||||
dNum += normalizedMagn * normalizedMagn;
|
||||
dDen += normalizedMagn * normalizedMagn;
|
||||
}
|
||||
#endif
|
||||
coeffQuant[i] = (unsigned char) q;
|
||||
}
|
||||
|
||||
if (sigMaxQ) *sigMaxQ = maxQ; // max. quantized value magnitude
|
||||
if (sigNumQ) *sigNumQ = numQ; // nonzero coeff. count (L0 norm)
|
||||
|
||||
// compute least-squares optimal gain multiplied onto step-size
|
||||
numQ = scaleFactor + short (SF_QUANT_OFFSET + FOUR_LOG102 * log10 (dNum <= 0.0 ? 1.0 : dNum / dDen) - (dNum < dDen ? 1.0 : 0.0));
|
||||
|
||||
return (unsigned char) __max (0, numQ); // optimal scale factor
|
||||
}
|
||||
|
||||
// constructor
|
||||
SfbQuantizer::SfbQuantizer ()
|
||||
{
|
||||
// initialize all helper buffers
|
||||
m_coeffMagn = nullptr;
|
||||
m_lut2ExpX4 = nullptr;
|
||||
m_lutSfNorm = nullptr;
|
||||
m_lutXExp43 = nullptr;
|
||||
|
||||
m_maxSfIndex = 0;
|
||||
#if EC_TRELLIS_OPT_CODING
|
||||
m_numCStates = 0;
|
||||
|
||||
for (unsigned b = 0; b < 52; b++)
|
||||
{
|
||||
m_quantDist[b] = nullptr;
|
||||
m_quantInSf[b] = nullptr;
|
||||
m_quantRate[b] = nullptr;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// destructor
|
||||
SfbQuantizer::~SfbQuantizer ()
|
||||
{
|
||||
// free allocated helper buffers
|
||||
MFREE (m_coeffMagn);
|
||||
MFREE (m_lut2ExpX4);
|
||||
MFREE (m_lutSfNorm);
|
||||
MFREE (m_lutXExp43);
|
||||
#if EC_TRELLIS_OPT_CODING
|
||||
|
||||
for (unsigned b = 0; b < 52; b++)
|
||||
{
|
||||
MFREE (m_quantDist[b]);
|
||||
MFREE (m_quantInSf[b]);
|
||||
MFREE (m_quantRate[b]);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// public functions
|
||||
unsigned SfbQuantizer::initQuantMemory (const unsigned maxTransfLength,
|
||||
#if EC_TRELLIS_OPT_CODING
|
||||
const unsigned char numSwb, const unsigned char bitRateMode,
|
||||
#endif
|
||||
const unsigned char maxScaleFacIndex /*= SCHAR_MAX*/)
|
||||
{
|
||||
const unsigned numScaleFactors = (unsigned) maxScaleFacIndex + 1;
|
||||
#if EC_TRELLIS_OPT_CODING
|
||||
const unsigned char numIStates = 8 - __min (5, (bitRateMode + 2) >> 2); // states per SFB
|
||||
const unsigned char numDStates = numIStates * numIStates; // interdependent states per SFB
|
||||
#endif
|
||||
unsigned x;
|
||||
|
||||
if ((maxTransfLength < 128) || (maxTransfLength > 8192) || (maxTransfLength & 7) || (maxScaleFacIndex == 0) || (maxScaleFacIndex > SCHAR_MAX))
|
||||
{
|
||||
return 1; // invalid arguments error
|
||||
}
|
||||
|
||||
m_maxSfIndex = maxScaleFacIndex;
|
||||
|
||||
if ((m_coeffMagn = (unsigned*) malloc (maxTransfLength * sizeof (unsigned))) == nullptr ||
|
||||
(m_lut2ExpX4 = (double* ) malloc (numScaleFactors * sizeof (double ))) == nullptr ||
|
||||
(m_lutSfNorm = (double* ) malloc (numScaleFactors * sizeof (double ))) == nullptr ||
|
||||
(m_lutXExp43 = (double* ) malloc ((SCHAR_MAX + 1) * sizeof (double ))) == nullptr)
|
||||
{
|
||||
return 2; // memory allocation error
|
||||
}
|
||||
#if EC_TRELLIS_OPT_CODING
|
||||
m_numCStates = numIStates;
|
||||
|
||||
for (x = 0; x < __min (52u, numSwb); x++)
|
||||
{
|
||||
if ((m_quantDist[x] = (double* ) malloc (numIStates * sizeof (double ))) == nullptr ||
|
||||
(m_quantInSf[x] = (uint8_t* ) malloc (numIStates * sizeof (uint8_t ))) == nullptr ||
|
||||
(m_quantRate[x] = (uint16_t*) malloc (numDStates * sizeof (uint16_t))) == nullptr)
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
memset (m_coeffTemp, 0, sizeof (m_coeffTemp));
|
||||
|
||||
// calculate scale factor gain 2^(x/4)
|
||||
for (x = 0; x < numScaleFactors; x++)
|
||||
{
|
||||
m_lut2ExpX4[x] = pow (2.0, (double) x / 4.0);
|
||||
m_lutSfNorm[x] = 1.0 / m_lut2ExpX4[x];
|
||||
}
|
||||
// calculate dequantized coeff x^(4/3)
|
||||
for (x = 0; x < (SCHAR_MAX + 1); x++)
|
||||
{
|
||||
m_lutXExp43[x] = pow ((double) x, 4.0 / 3.0);
|
||||
}
|
||||
|
||||
return 0; // no error
|
||||
}
|
||||
|
||||
uint8_t SfbQuantizer::quantizeSpecSfb (EntropyCoder& entropyCoder, const int32_t* const inputCoeffs, const unsigned char grpLength,
|
||||
const uint16_t* const grpOffsets, uint32_t* const grpStats, // quant./coding statistics
|
||||
const unsigned sfb, const unsigned char sfIndex, const unsigned char sfIndexPred /*= UCHAR_MAX*/,
|
||||
unsigned char* const quantCoeffs /*= nullptr*/) // returns the RD optimized scale factor index
|
||||
{
|
||||
unsigned char sfBest = sfIndex;
|
||||
|
||||
if ((inputCoeffs == nullptr) || (grpOffsets == nullptr) || (sfb >= 52) || (sfIndex > m_maxSfIndex))
|
||||
{
|
||||
return UCHAR_MAX; // invalid input error
|
||||
}
|
||||
|
||||
#if EC_TRELLIS_OPT_CODING
|
||||
if (grpLength == 1) // references for RDOC
|
||||
{
|
||||
m_quantDist[sfb][1] = -1.0;
|
||||
m_quantInSf[sfb][1] = sfIndex;
|
||||
m_quantRate[sfb][1] = 0; // for sgn bits
|
||||
m_quantRate[sfb][0] = entropyCoder.arithGetCtxState () & USHRT_MAX; // ref start context
|
||||
}
|
||||
#endif
|
||||
if ((sfIndex == 0) || (sfIndexPred <= m_maxSfIndex && sfIndex + INDEX_OFFSET < sfIndexPred))
|
||||
{
|
||||
const uint16_t grpStart = grpOffsets[0];
|
||||
const uint16_t sfbStart = grpOffsets[sfb];
|
||||
const uint16_t sfbWidth = grpOffsets[sfb + 1] - sfbStart;
|
||||
uint32_t* const coeffMagn = &m_coeffMagn[sfbStart];
|
||||
|
||||
for (int i = sfbWidth - 1; i >= 0; i--) // back up magnitudes. TODO: use SIMD for speed?
|
||||
{
|
||||
coeffMagn[i] = abs (inputCoeffs[sfbStart + i]);
|
||||
}
|
||||
|
||||
if (quantCoeffs)
|
||||
{
|
||||
memset (&quantCoeffs[sfbStart], 0, sfbWidth * sizeof (unsigned char)); // zero output
|
||||
if (grpStats) // approximate bit count
|
||||
{
|
||||
grpStats[sfb] = getBitCount (entropyCoder, 0, 0, grpLength, &quantCoeffs[grpStart], sfbStart - grpStart, sfbWidth);
|
||||
}
|
||||
}
|
||||
return sfIndexPred - (sfIndex == 0 ? 0 : INDEX_OFFSET); // save delta bits if applicable
|
||||
}
|
||||
else // nonzero sf, optimized quantization
|
||||
{
|
||||
const uint16_t grpStart = grpOffsets[0];
|
||||
const uint16_t sfbStart = grpOffsets[sfb];
|
||||
const uint16_t sfbWidth = grpOffsets[sfb + 1] - sfbStart;
|
||||
const uint16_t cpyWidth = sfbWidth * sizeof (unsigned char);
|
||||
uint32_t* const coeffMagn = &m_coeffMagn[sfbStart];
|
||||
unsigned codStart = 0, ctxStart = 0;
|
||||
unsigned codFinal = 0, ctxFinal = 0;
|
||||
double distBest = 0, distCurr = 0;
|
||||
short maxQBest = 0, maxQCurr = 0;
|
||||
short numQBest = 0, numQCurr = 0;
|
||||
#if EC_TRELLIS_OPT_CODING
|
||||
bool rdOptimQuant = (grpLength != 1);
|
||||
#else
|
||||
bool rdOptimQuant = true;
|
||||
#endif
|
||||
unsigned char* ptrBest = &m_coeffTemp[0];
|
||||
unsigned char* ptrCurr = &m_coeffTemp[100];
|
||||
unsigned char sfCurr = sfIndex;
|
||||
|
||||
for (int i = sfbWidth - 1; i >= 0; i--) // back up magnitudes. TODO: use SIMD for speed?
|
||||
{
|
||||
coeffMagn[i] = abs (inputCoeffs[sfbStart + i]);
|
||||
}
|
||||
|
||||
// --- determine default quantization result using range limited scale factor as a reference
|
||||
sfBest = quantizeMagn (coeffMagn, sfCurr, ptrBest, sfbWidth, &maxQBest, &numQBest);
|
||||
|
||||
if (maxQBest > SCHAR_MAX) // limit SNR via scale factor index
|
||||
{
|
||||
for (unsigned char c = 0; (c < 2) && (maxQBest > SCHAR_MAX); c++) // rarely done twice
|
||||
{
|
||||
sfCurr += getScaleFacOffset (pow ((double) maxQBest, 4.0 / 3.0) / m_lutXExp43[SCHAR_MAX]) + c;
|
||||
sfBest = quantizeMagn (coeffMagn, sfCurr, ptrBest, sfbWidth, &maxQBest, &numQBest);
|
||||
}
|
||||
rdOptimQuant = false;
|
||||
}
|
||||
else if ((sfBest < sfCurr) && (sfBest != sfIndexPred)) // re-optimize above quantization
|
||||
{
|
||||
sfBest = quantizeMagn (coeffMagn, --sfCurr, ptrBest, sfbWidth, &maxQBest, &numQBest);
|
||||
|
||||
rdOptimQuant &= (maxQBest <= SCHAR_MAX);
|
||||
}
|
||||
|
||||
#if EC_TRELLIS_OPT_CODING
|
||||
if (grpLength == 1) // ref masking level
|
||||
{
|
||||
m_quantInSf[sfb][1] = __min (sfCurr, m_maxSfIndex);
|
||||
}
|
||||
#endif
|
||||
if (maxQBest == 0) // SFB was quantized to zero - zero output
|
||||
{
|
||||
if (quantCoeffs)
|
||||
{
|
||||
memset (&quantCoeffs[sfbStart], 0, cpyWidth);
|
||||
if (grpStats) // estimated bit count
|
||||
{
|
||||
grpStats[sfb] = getBitCount (entropyCoder, 0, 0, grpLength, &quantCoeffs[grpStart], sfbStart - grpStart, sfbWidth);
|
||||
}
|
||||
}
|
||||
return sfIndexPred; // repeat scale factor, save delta bits
|
||||
}
|
||||
|
||||
// --- check whether optimized quantization and coding results in lower rate-distortion cost
|
||||
distBest = getQuantDist (coeffMagn, sfBest, ptrBest, sfbWidth);
|
||||
|
||||
#if EC_TRELLIS_OPT_CODING
|
||||
if (grpLength == 1) // ref band-wise NMR
|
||||
{
|
||||
const double refSfbNmrDiv = m_lutSfNorm[m_quantInSf[sfb][1]];
|
||||
|
||||
m_quantDist[sfb][1] = distBest * refSfbNmrDiv * refSfbNmrDiv;
|
||||
m_quantRate[sfb][1] = numQBest; // sgn
|
||||
}
|
||||
#endif
|
||||
if (quantCoeffs)
|
||||
{
|
||||
memcpy (&quantCoeffs[sfbStart], ptrBest, cpyWidth);
|
||||
|
||||
codStart = entropyCoder.arithGetCodState (); // start state
|
||||
ctxStart = entropyCoder.arithGetCtxState ();
|
||||
numQBest += getBitCount (entropyCoder, sfBest, sfIndexPred, grpLength, &quantCoeffs[grpStart], sfbStart - grpStart, sfbWidth);
|
||||
codFinal = entropyCoder.arithGetCodState (); // final state
|
||||
ctxFinal = entropyCoder.arithGetCtxState ();
|
||||
}
|
||||
rdOptimQuant &= (distBest > 0.0);
|
||||
|
||||
if ((sfBest < sfCurr) && (sfBest != sfIndexPred) && rdOptimQuant) // R/D re-optimization
|
||||
{
|
||||
sfCurr = quantizeMagn (coeffMagn, sfCurr - 1, ptrCurr, sfbWidth, &maxQCurr, &numQCurr);
|
||||
|
||||
if (maxQCurr <= maxQBest)
|
||||
{
|
||||
distCurr = getQuantDist (coeffMagn, sfCurr, ptrCurr, sfbWidth);
|
||||
if (quantCoeffs)
|
||||
{
|
||||
memcpy (&quantCoeffs[sfbStart], ptrCurr, cpyWidth);
|
||||
|
||||
entropyCoder.arithSetCodState (codStart);// reset state
|
||||
entropyCoder.arithSetCtxState (ctxStart);
|
||||
numQCurr += getBitCount (entropyCoder, sfCurr, sfIndexPred, grpLength, &quantCoeffs[grpStart], sfbStart - grpStart, sfbWidth);
|
||||
}
|
||||
|
||||
// rate-distortion decision with empirical rate threshold
|
||||
if ((numQCurr <= numQBest + (distCurr >= distBest ? -1 : short (0.5 + distBest / __max (1.0, distCurr)))))
|
||||
{
|
||||
unsigned char* ptrTemp = ptrBest;
|
||||
|
||||
ptrBest = ptrCurr;
|
||||
ptrCurr = ptrTemp;
|
||||
distBest = distCurr;
|
||||
maxQBest = maxQCurr;
|
||||
numQBest = numQCurr;
|
||||
sfBest = sfCurr;
|
||||
}
|
||||
else if (quantCoeffs) // discard result, recover best try
|
||||
{
|
||||
memcpy (&quantCoeffs[sfbStart], ptrBest, cpyWidth);
|
||||
|
||||
entropyCoder.arithSetCodState (codFinal); // set state
|
||||
entropyCoder.arithSetCtxState (ctxFinal);
|
||||
}
|
||||
} // if (maxQCurr <= maxQBest)
|
||||
}
|
||||
else sfCurr = sfBest;
|
||||
|
||||
rdOptimQuant &= (distBest > 0.0);
|
||||
|
||||
if ((sfCurr != sfIndexPred) && (sfBest != sfIndexPred) && rdOptimQuant && (sfIndexPred > 0) && (sfIndexPred < m_maxSfIndex))
|
||||
{
|
||||
unsigned char sf;
|
||||
|
||||
// try quantization with repeated scale factor to save bits
|
||||
for (sf = (sfCurr = sfIndexPred + 1); (sf >= sfIndexPred) && (sfCurr > sfIndexPred); sf--)
|
||||
{
|
||||
sfCurr = quantizeMagn (coeffMagn, sf, ptrCurr, sfbWidth, &maxQCurr, &numQCurr);
|
||||
}
|
||||
if ((sfCurr <= sfIndexPred) && (maxQCurr == maxQBest))
|
||||
{
|
||||
bool calcRate = (quantCoeffs != nullptr);
|
||||
double distTemp;
|
||||
|
||||
codFinal = entropyCoder.arithGetCodState (); // new state
|
||||
ctxFinal = entropyCoder.arithGetCtxState ();
|
||||
|
||||
distCurr = getQuantDist (coeffMagn, sfIndexPred, ptrCurr, sfbWidth);
|
||||
|
||||
if (sfCurr < sfIndexPred) // repeated index isn't optimal
|
||||
{
|
||||
distTemp = getQuantDist (coeffMagn, sfIndexPred, ptrBest, sfbWidth);
|
||||
|
||||
if (distTemp < distCurr) // best gives lower distortion
|
||||
{
|
||||
#if SFB_QUANT_MORE_TESTS
|
||||
ptrCurr = ptrBest;
|
||||
#endif
|
||||
distCurr = distTemp;
|
||||
maxQCurr = maxQBest;
|
||||
numQCurr = numQBest;
|
||||
sfCurr = __min (sfBest, sfIndexPred);
|
||||
calcRate = false; // (num)qBest are already complete
|
||||
}
|
||||
}
|
||||
#if SFB_QUANT_MORE_TESTS
|
||||
if (quantCoeffs && (sf >= sfIndexPred)) // a missing test
|
||||
{
|
||||
const short maxQTemp = maxQCurr;
|
||||
const short numQTemp = numQCurr;
|
||||
|
||||
sf = quantizeMagn (coeffMagn, sf, &quantCoeffs[sfbStart], sfbWidth, &maxQCurr, &numQCurr);
|
||||
distTemp = getQuantDist (coeffMagn, sfIndexPred, &quantCoeffs[sfbStart], sfbWidth);
|
||||
|
||||
if (distTemp < distCurr) // also gives lower distortion
|
||||
{
|
||||
# if 1
|
||||
ptrCurr = &quantCoeffs[sfbStart];
|
||||
# endif
|
||||
distCurr = distTemp;
|
||||
entropyCoder.arithSetCodState (codStart);// set state
|
||||
entropyCoder.arithSetCtxState (ctxStart);
|
||||
numQCurr += getBitCount (entropyCoder, sfIndexPred, sfIndexPred, grpLength, &quantCoeffs[grpStart], sfbStart - grpStart, sfbWidth);
|
||||
sfCurr = __min (sf, sfIndexPred);
|
||||
calcRate = false;
|
||||
}
|
||||
else // distortion is not lower - restore maxQ and numQ
|
||||
{
|
||||
maxQCurr = maxQTemp;
|
||||
numQCurr = numQTemp;
|
||||
if (!calcRate) memcpy (&quantCoeffs[sfbStart], ptrCurr, cpyWidth);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (calcRate)
|
||||
{
|
||||
memcpy (&quantCoeffs[sfbStart], ptrCurr, cpyWidth);
|
||||
|
||||
entropyCoder.arithSetCodState (codStart);// reset state
|
||||
entropyCoder.arithSetCtxState (ctxStart);
|
||||
numQCurr += getBitCount (entropyCoder, sfIndexPred, sfIndexPred, grpLength, &quantCoeffs[grpStart], sfbStart - grpStart, sfbWidth);
|
||||
}
|
||||
|
||||
// rate-distortion decision with empirical rate threshold
|
||||
if ((numQCurr <= numQBest + (distCurr >= distBest ? -1 : short (0.5 + distBest / __max (1.0, distCurr)))))
|
||||
{
|
||||
#if SFB_QUANT_MORE_TESTS
|
||||
if ((sfCurr < sfIndexPred) && (distCurr >= distBest) && (numQCurr + 1 + sfIndexPred - sfCurr < numQBest) &&
|
||||
(getQuantDist (coeffMagn, sfCurr, ptrCurr, sfbWidth) < distCurr))
|
||||
{
|
||||
numQCurr += 1 + sfIndexPred - sfCurr; // 14496, 4.A.1
|
||||
sfBest = sfCurr;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
sfBest = sfIndexPred;
|
||||
maxQBest = maxQCurr;
|
||||
numQBest = numQCurr;
|
||||
}
|
||||
else if (quantCoeffs) // discard result, recover best try
|
||||
{
|
||||
memcpy (&quantCoeffs[sfbStart], ptrBest, cpyWidth);
|
||||
|
||||
entropyCoder.arithSetCodState (codFinal); // set state
|
||||
entropyCoder.arithSetCtxState (ctxFinal);
|
||||
}
|
||||
} // if ((sfCurr <= sfIndexPred) && (maxQCurr >= maxQBest))
|
||||
}
|
||||
|
||||
if (grpStats)
|
||||
{
|
||||
grpStats[sfb] = ((uint32_t) maxQBest << 16) | numQBest; // max magnitude and bit count
|
||||
}
|
||||
} // if (sfIndex == 0)
|
||||
|
||||
return __min (sfBest, m_maxSfIndex);
|
||||
}
|
||||
|
||||
#if EC_TRELLIS_OPT_CODING
|
||||
# define EC_TRAIN 0
|
||||
|
||||
unsigned SfbQuantizer::quantizeSpecRDOC (EntropyCoder& entropyCoder, unsigned char* const optimalSf, const unsigned targetBitCount,
|
||||
const uint16_t* const grpOffsets, uint32_t* const grpStats, // quant./coding statistics
|
||||
const unsigned numSfb, unsigned char* const quantCoeffs) // returns RD optimization bit count
|
||||
{
|
||||
// numSfb: number of trellis stages. Based on: A. Aggarwal, S. L. Regunathan, and K. Rose,
|
||||
// "Trellis-Based Optimization of MPEG-4 Advanced Audio Coding," in Proc. IEEE Workshop on
|
||||
// Speech Coding, pp. 142-144, Sep. 2000. Modified for arithmetic instead of Huffman coder
|
||||
const uint32_t codStart = USHRT_MAX << 16;
|
||||
const uint32_t ctxStart = m_quantRate[0][0]; // start context before quantizeSfb()
|
||||
const uint32_t codFinal = entropyCoder.arithGetCodState ();
|
||||
const uint32_t ctxFinal = entropyCoder.arithGetCtxState (); // after quantizeSfb()
|
||||
const uint16_t grpStart = grpOffsets[0];
|
||||
unsigned char* const inScaleFac = &m_coeffTemp[716];
|
||||
uint32_t prevCodState[8] = {0, 0, 0, 0, 0, 0, 0, 0};
|
||||
uint32_t prevCtxState[8] = {0, 0, 0, 0, 0, 0, 0, 0};
|
||||
unsigned char prevScaleFac[8] = {0, 0, 0, 0, 0, 0, 0, 0};
|
||||
double prevVtrbCost[8] = {0, 0, 0, 0, 0, 0, 0, 0};
|
||||
uint32_t tempCodState[8] = {0, 0, 0, 0, 0, 0, 0, 0};
|
||||
uint32_t tempCtxState[8] = {0, 0, 0, 0, 0, 0, 0, 0};
|
||||
unsigned char tempScaleFac[8] = {0, 0, 0, 0, 0, 0, 0, 0};
|
||||
double tempVtrbCost[8] = {0, 0, 0, 0, 0, 0, 0, 0};
|
||||
unsigned tempBitCount, sfb, is;
|
||||
int ds;
|
||||
#if EC_TRAIN
|
||||
double refGrpDist = 0.0, tempGrpDist;
|
||||
#else
|
||||
const double lambda = (108.0 + targetBitCount * targetBitCount) / 1024.0; // Lagrange par.
|
||||
#endif
|
||||
|
||||
if ((optimalSf == nullptr) || (grpOffsets == nullptr) || (numSfb == 0) || (numSfb > 52) || (targetBitCount == 0) || (targetBitCount > SHRT_MAX))
|
||||
{
|
||||
return 0; // invalid input error
|
||||
}
|
||||
|
||||
for (sfb = 0; sfb < numSfb; sfb++) // SFB-wise scale factor, weighted distortion, and rate
|
||||
{
|
||||
const unsigned char refSf = m_quantInSf[sfb][1];
|
||||
const uint16_t refNumQ = m_quantRate[sfb][1];
|
||||
const double refQuantDist = m_quantDist[sfb][1];
|
||||
const double refQuantNorm = m_lutSfNorm[refSf] * m_lutSfNorm[refSf];
|
||||
const uint16_t sfbStart = grpOffsets[sfb];
|
||||
const uint16_t sfbWidth = grpOffsets[sfb + 1] - sfbStart;
|
||||
uint32_t* const coeffMagn = &m_coeffMagn[sfbStart];
|
||||
unsigned char* const tempMagn = &m_coeffTemp[sfbStart];
|
||||
bool maxSnrReached = false;
|
||||
|
||||
if (refQuantDist < 0.0) memset (tempMagn, 0, sfbWidth * sizeof (unsigned char));
|
||||
#if EC_TRAIN
|
||||
else refGrpDist += refQuantDist;
|
||||
#endif
|
||||
if (grpStats)
|
||||
{
|
||||
grpStats[sfb] = (grpStats[sfb] & (USHRT_MAX << 16)) | refNumQ; // keep magn, sign bits
|
||||
}
|
||||
|
||||
for (is = 0; is < m_numCStates; is++) // populate the trellis
|
||||
{
|
||||
unsigned char* const mag = (is != 1 || quantCoeffs == nullptr ? &m_coeffTemp[grpStart] : &quantCoeffs[grpStart]);
|
||||
double* const currDist = &m_quantDist[sfb][is];
|
||||
uint16_t* currRate = &m_quantRate[sfb][is * m_numCStates];
|
||||
unsigned char sfBest = optimalSf[sfb]; // optimal refSf
|
||||
short maxQCurr = 0, numQCurr = 0; // for sign bits counting
|
||||
|
||||
if (refQuantDist < 0.0) // -1.0 means SFB is zero-quantized
|
||||
{
|
||||
*currDist = -1.0;
|
||||
m_quantInSf[sfb][is] = refSf;
|
||||
}
|
||||
else if (is != 1) // quantization & distortion not computed
|
||||
{
|
||||
const unsigned char sfCurr = __max (0, __min (m_maxSfIndex, refSf + 1 - (int) is));
|
||||
|
||||
*currDist = -1.0;
|
||||
if ((sfCurr == 0) || maxSnrReached)
|
||||
{
|
||||
maxSnrReached = true;
|
||||
}
|
||||
else // sfCurr > 0 && sfCurr <= m_maxSfIndex, re-quantize
|
||||
{
|
||||
sfBest = quantizeMagn (coeffMagn, sfCurr, tempMagn, sfbWidth, &maxQCurr, &numQCurr);
|
||||
|
||||
if (maxQCurr > SCHAR_MAX)
|
||||
{
|
||||
maxSnrReached = true; numQCurr = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
*currDist = getQuantDist (coeffMagn, sfBest, tempMagn, sfbWidth) * refQuantNorm;
|
||||
}
|
||||
}
|
||||
if (*currDist < 0.0) memset (tempMagn, 0, sfbWidth * sizeof (unsigned char));
|
||||
m_quantInSf[sfb][is] = sfCurr; // store initial scale fac
|
||||
}
|
||||
else // is == 1, quant. & dist. computed with quantizeSfb()
|
||||
{
|
||||
numQCurr = refNumQ;
|
||||
}
|
||||
|
||||
if (sfb == 0) // first SFB, having sfbStart - grpStart == 0
|
||||
{
|
||||
entropyCoder.arithSetCodState (codStart); // group start
|
||||
entropyCoder.arithSetCtxState (ctxStart, 0);
|
||||
tempBitCount = (maxSnrReached ? USHRT_MAX : numQCurr + getBitCount (entropyCoder, sfBest, UCHAR_MAX, 1, mag, 0, sfbWidth));
|
||||
|
||||
for (ds = m_numCStates - 1; ds >= 0; ds--)
|
||||
{
|
||||
currRate[ds] = (uint16_t) tempBitCount;
|
||||
}
|
||||
tempCodState[is] = entropyCoder.arithGetCodState ();
|
||||
tempCtxState[is] = entropyCoder.arithGetCtxState ();
|
||||
}
|
||||
else // sfb > 0, rate depends on decisions in preceding SFB
|
||||
{
|
||||
for (ds = m_numCStates - 1; ds >= 0; ds--)
|
||||
{
|
||||
const uint16_t prevRate = m_quantRate[sfb - 1][ds * m_numCStates];
|
||||
|
||||
entropyCoder.arithSetCodState (prevCodState[ds]);
|
||||
entropyCoder.arithSetCtxState (prevCtxState[ds], sfbStart - grpStart);
|
||||
tempBitCount = (maxSnrReached || (prevRate == USHRT_MAX) ? USHRT_MAX : numQCurr + getBitCount (entropyCoder,
|
||||
(numQCurr == 0 ? prevScaleFac[ds] : sfBest), prevScaleFac[ds], 1, mag, sfbStart - grpStart, sfbWidth));
|
||||
currRate[ds] = (uint16_t) tempBitCount;
|
||||
|
||||
if ((sfb + 1 == numSfb) && (tempBitCount != USHRT_MAX))
|
||||
{
|
||||
currRate[ds] += ((entropyCoder.arithGetCtxState () >> 17) & 31) + 2; // m_acBits+2
|
||||
}
|
||||
if (ds == 1) // statistically best place to save states
|
||||
{
|
||||
tempCodState[is] = entropyCoder.arithGetCodState ();
|
||||
tempCtxState[is] = entropyCoder.arithGetCtxState ();
|
||||
}
|
||||
}
|
||||
}
|
||||
tempScaleFac[is] = sfBest; // optimized factor for next SFB
|
||||
} // for is
|
||||
|
||||
memcpy (prevCodState, tempCodState, m_numCStates * sizeof (uint32_t));
|
||||
memcpy (prevCtxState, tempCtxState, m_numCStates * sizeof (uint32_t));
|
||||
memcpy (prevScaleFac, tempScaleFac, m_numCStates * sizeof (unsigned char));
|
||||
} // for sfb
|
||||
|
||||
entropyCoder.arithSetCodState (codFinal); // back to last state
|
||||
entropyCoder.arithSetCtxState (ctxFinal, grpOffsets[numSfb] - grpStart);
|
||||
|
||||
#if EC_TRAIN
|
||||
tempBitCount = targetBitCount + 1; // Viterbi search for minimum distortion at target rate
|
||||
for (double lambda = 0.015625; (lambda <= 0.375) && (tempBitCount > targetBitCount); lambda += 0.0078125)
|
||||
#endif
|
||||
{
|
||||
double* const prevCost = prevVtrbCost;
|
||||
unsigned char* const prevPath = m_coeffTemp; // for backtrack
|
||||
double costMinIs = (double) UINT_MAX;
|
||||
#if EC_TRAIN
|
||||
double tempGrpDist = 0.0;
|
||||
#endif
|
||||
unsigned pathMinIs = 1;
|
||||
|
||||
for (is = 0; is < m_numCStates; is++) // initial minimum path
|
||||
{
|
||||
const uint16_t currRate = m_quantRate[0][is * m_numCStates];
|
||||
|
||||
prevCost[is] = (currRate >= USHRT_MAX ? (double) UINT_MAX : lambda * currRate + __max (0.0, m_quantDist[0][is]));
|
||||
prevPath[is] = 0;
|
||||
}
|
||||
|
||||
for (sfb = 1; sfb < numSfb; sfb++) // search for minimum path
|
||||
{
|
||||
double* const currCost = tempVtrbCost;
|
||||
unsigned char* const currPath = &prevPath[sfb * m_numCStates];
|
||||
|
||||
for (is = 0; is < m_numCStates; is++) // SFB's minimum path
|
||||
{
|
||||
uint16_t* currRate = &m_quantRate[sfb][is * m_numCStates];
|
||||
double costMinDs = (double) UINT_MAX;
|
||||
unsigned char pathMinDs = 1;
|
||||
|
||||
for (ds = m_numCStates - 1; ds >= 0; ds--) // transitions
|
||||
{
|
||||
const double costCurr = (currRate[ds] >= USHRT_MAX ? (double) UINT_MAX : prevCost[ds] + lambda * currRate[ds]);
|
||||
|
||||
if (costMinDs > costCurr)
|
||||
{
|
||||
costMinDs = costCurr;
|
||||
pathMinDs = (unsigned char) ds;
|
||||
}
|
||||
}
|
||||
if (costMinDs < UINT_MAX) costMinDs += __max (0.0, m_quantDist[sfb][is]);
|
||||
|
||||
currCost[is] = costMinDs;
|
||||
currPath[is] = pathMinDs;
|
||||
} // for is
|
||||
|
||||
memcpy (prevCost, currCost, m_numCStates * sizeof (double)); // TODO: avoid memcpy, use pointer swapping instead for speed
|
||||
} // for sfb
|
||||
|
||||
for (sfb--, is = 0; is < m_numCStates; is++) // group minimum
|
||||
{
|
||||
if (costMinIs > prevCost[is])
|
||||
{
|
||||
costMinIs = prevCost[is];
|
||||
pathMinIs = is;
|
||||
}
|
||||
}
|
||||
|
||||
for (tempBitCount = 0; sfb > 0; sfb--) // min-cost group rate
|
||||
{
|
||||
const unsigned char* currPath = &prevPath[sfb * m_numCStates];
|
||||
const unsigned char pathMinDs = currPath[pathMinIs];
|
||||
|
||||
inScaleFac[sfb] = (m_quantDist[sfb][pathMinIs] < 0.0 ? UCHAR_MAX : m_quantInSf[sfb][pathMinIs]);
|
||||
tempBitCount += m_quantRate[sfb][pathMinDs + pathMinIs * m_numCStates];
|
||||
#if EC_TRAIN
|
||||
tempGrpDist += __max (0.0, m_quantDist[sfb][pathMinIs]);
|
||||
#endif
|
||||
pathMinIs = pathMinDs;
|
||||
}
|
||||
inScaleFac[0] = (m_quantDist[0][pathMinIs] < 0.0 ? UCHAR_MAX : m_quantInSf[0][pathMinIs]);
|
||||
tempBitCount += m_quantRate[0][pathMinIs * m_numCStates];
|
||||
#if EC_TRAIN
|
||||
tempGrpDist += __max (0.0, m_quantDist[0][pathMinIs]);
|
||||
#endif
|
||||
} // Viterbi search
|
||||
|
||||
#if EC_TRAIN
|
||||
if ((quantCoeffs != nullptr) && (tempGrpDist <= refGrpDist || tempBitCount <= targetBitCount))
|
||||
#else
|
||||
if (quantCoeffs != nullptr)
|
||||
#endif
|
||||
{
|
||||
unsigned char sfIndexPred = UCHAR_MAX;
|
||||
|
||||
if (grpStats)
|
||||
{
|
||||
entropyCoder.arithSetCodState (codStart);// set group start
|
||||
entropyCoder.arithSetCtxState (ctxStart, 0);
|
||||
|
||||
tempBitCount = 0;
|
||||
}
|
||||
for (sfb = 0; sfb < numSfb; sfb++) // re-quantize spectrum with R/D optimized parameters
|
||||
{
|
||||
const uint16_t sfbStart = grpOffsets[sfb];
|
||||
const uint16_t sfbWidth = grpOffsets[sfb + 1] - sfbStart;
|
||||
|
||||
if (inScaleFac[sfb] == UCHAR_MAX) // forced zero-quantized
|
||||
{
|
||||
optimalSf[sfb] = sfIndexPred;
|
||||
memset (&quantCoeffs[sfbStart], 0, sfbWidth * sizeof (unsigned char));
|
||||
}
|
||||
else if (inScaleFac[sfb] != m_quantInSf[sfb][1]) // speedup
|
||||
{
|
||||
short maxQBest = 0, numQBest = 0;
|
||||
|
||||
optimalSf[sfb] = quantizeMagn (&m_coeffMagn[sfbStart], inScaleFac[sfb], &quantCoeffs[sfbStart], sfbWidth, &maxQBest, &numQBest);
|
||||
if (maxQBest == 0) optimalSf[sfb] = sfIndexPred; // empty
|
||||
if (grpStats)
|
||||
{
|
||||
grpStats[sfb] = ((uint32_t) maxQBest << 16) | numQBest; // max magn. and sign bits
|
||||
}
|
||||
}
|
||||
|
||||
if (grpStats) // complete statistics with per-SFB bit count
|
||||
{
|
||||
grpStats[sfb] += getBitCount (entropyCoder, optimalSf[sfb], sfIndexPred, 1, &quantCoeffs[grpStart], sfbStart - grpStart, sfbWidth);
|
||||
tempBitCount += grpStats[sfb] & USHRT_MAX;
|
||||
}
|
||||
|
||||
if ((sfb > 0) && (optimalSf[sfb] < UCHAR_MAX) && (sfIndexPred == UCHAR_MAX))
|
||||
{
|
||||
memset (optimalSf, optimalSf[sfb], sfb * sizeof (unsigned char)); // back-propagate
|
||||
}
|
||||
sfIndexPred = optimalSf[sfb];
|
||||
} // for sfb
|
||||
|
||||
return tempBitCount + (grpStats ? ((entropyCoder.arithGetCtxState () >> 17) & 31) /*m_acBits*/ + 2 : 0);
|
||||
}
|
||||
|
||||
return targetBitCount;
|
||||
}
|
||||
#endif // EC_TRELLIS_OPT_CODING
|
85
src/lib/quantization.h
Normal file
85
src/lib/quantization.h
Normal file
@ -0,0 +1,85 @@
|
||||
/* quantization.h - header file for class with nonuniform quantization functionality
|
||||
* written by C. R. Helmrich, last modified in 2019 - see License.htm for legal notices
|
||||
*
|
||||
* The copyright in this software is being made available under a Modified BSD-Style 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-2020 Christian R. Helmrich, project ecodis. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef _QUANTIZATION_H_
|
||||
#define _QUANTIZATION_H_
|
||||
|
||||
#include "exhaleLibPch.h"
|
||||
#include "entropyCoding.h"
|
||||
|
||||
// constants, experimental macros
|
||||
#define FOUR_LOG102 13.28771238 // 4 / log10 (2)
|
||||
#define SF_QUANT_OFFSET 0.4783662 // for scale fac
|
||||
#define SFB_QUANT_FAST_POW 1 // faster pow ()
|
||||
#define SFB_QUANT_MORE_TESTS 1 // longer search
|
||||
#define SFB_QUANT_PERCEPT_OPT 1 // psych. quant.
|
||||
#if SFB_QUANT_FAST_POW
|
||||
#define SFB_QUANT_OFFSET 0.496094 // 13 - 29^(3/4)
|
||||
#else
|
||||
#define SFB_QUANT_OFFSET 0.405396 // 1 - 0.5^(3/4)
|
||||
#endif
|
||||
|
||||
// class for xHE-AAC quantization
|
||||
class SfbQuantizer
|
||||
{
|
||||
private:
|
||||
|
||||
// member variables
|
||||
unsigned* m_coeffMagn; // temp memory
|
||||
#if EC_TRELLIS_OPT_CODING
|
||||
uint8_t m_coeffTemp[1024]; // TODO?
|
||||
#else
|
||||
uint8_t m_coeffTemp[200]; // 40 * 5 - NOTE: increase this when maximum grpLength > 5
|
||||
#endif
|
||||
double* m_lut2ExpX4; // for 2^(X/4)
|
||||
double* m_lutSfNorm; // 1 / 2^(X/4)
|
||||
double* m_lutXExp43; // for X^(4/3)
|
||||
uint8_t m_maxSfIndex; // 1,..., 127
|
||||
#if EC_TRELLIS_OPT_CODING
|
||||
uint8_t m_numCStates; // states/SFB
|
||||
// trellis memory, max. 8 KB @ num_swb=51
|
||||
double* m_quantDist[52]; // quantizing distortion
|
||||
uint8_t* m_quantInSf[52]; // initial scale factors
|
||||
uint16_t* m_quantRate[52]; // MDCT and SF bit count
|
||||
#endif
|
||||
|
||||
// helper functions
|
||||
double getQuantDist (const unsigned* const coeffMagn, const uint8_t scaleFactor,
|
||||
const uint8_t* const coeffQuant, const uint16_t numCoeffs);
|
||||
uint8_t quantizeMagn (const unsigned* const coeffMagn, const uint8_t scaleFactor,
|
||||
/*mod*/uint8_t* const coeffQuant, const uint16_t numCoeffs,
|
||||
short* const sigMaxQ = nullptr, short* const sigNumQ = nullptr);
|
||||
public:
|
||||
|
||||
// constructor
|
||||
SfbQuantizer ();
|
||||
// destructor
|
||||
~SfbQuantizer ();
|
||||
// public functions
|
||||
unsigned* getCoeffMagnPtr () const { return m_coeffMagn; }
|
||||
double* getSfNormTabPtr () const { return m_lutSfNorm; }
|
||||
uint8_t getScaleFacOffset (const double absValue) const { return uint8_t (SF_QUANT_OFFSET + FOUR_LOG102 * log10 (__max (1.0, absValue))); }
|
||||
unsigned initQuantMemory (const unsigned maxTransfLength,
|
||||
#if EC_TRELLIS_OPT_CODING
|
||||
const uint8_t numSwb, const uint8_t bitRateMode,
|
||||
#endif
|
||||
const uint8_t maxScaleFacIndex = SCHAR_MAX);
|
||||
uint8_t quantizeSpecSfb (EntropyCoder& entropyCoder, const int32_t* const inputCoeffs, const uint8_t grpLength,
|
||||
const uint16_t* const grpOffsets, uint32_t* const grpStats, // quant./coding statistics
|
||||
const unsigned sfb, const uint8_t sfIndex, const uint8_t sfIndexPred = UCHAR_MAX,
|
||||
uint8_t* const quantCoeffs = nullptr); // returns index of the RD optimized scale factor
|
||||
#if EC_TRELLIS_OPT_CODING
|
||||
unsigned quantizeSpecRDOC (EntropyCoder& entropyCoder, uint8_t* const optimalSf, const unsigned targetBitCount,
|
||||
const uint16_t* const grpOffsets, uint32_t* const grpStats, // quant./coding statistics
|
||||
const unsigned numSfb, uint8_t* const quantCoeffs); // returns RD optimization bit count
|
||||
#endif
|
||||
}; // SfbQuantizer
|
||||
|
||||
#endif // _QUANTIZATION_H_
|
342
src/lib/specAnalysis.cpp
Normal file
342
src/lib/specAnalysis.cpp
Normal file
@ -0,0 +1,342 @@
|
||||
/* specAnalysis.cpp - source file for class providing spectral analysis of MCLT signals
|
||||
* written by C. R. Helmrich, last modified in 2019 - see License.htm for legal notices
|
||||
*
|
||||
* The copyright in this software is being made available under a Modified BSD-Style 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-2020 Christian R. Helmrich, project ecodis. All rights reserved.
|
||||
*/
|
||||
|
||||
#include "exhaleLibPch.h"
|
||||
#include "specAnalysis.h"
|
||||
|
||||
// static helper function
|
||||
static inline uint32_t packAvgSpecAnalysisStats (const uint64_t sumAvgBand, const uint64_t sumMaxBand,
|
||||
const unsigned char predGain,
|
||||
const uint16_t idxMaxSpec, const uint16_t idxLpStart)
|
||||
{
|
||||
// temporal flatness, normalized for a value of 256 for a linear prediction gain of 1 (0 dB)
|
||||
const unsigned flatTemp = predGain;
|
||||
// spectral flatness, normalized for a value of 256 for steady low or mid-frequency sinusoid
|
||||
const int32_t flatSpec = 256 - int (((sumAvgBand + SA_EPS) * 402) / (sumMaxBand + SA_EPS));
|
||||
|
||||
return (flatTemp << 24) | (CLIP_UCHAR (flatSpec) << 16) | (__min (2047, idxMaxSpec) << 5) | __min (31, idxLpStart);
|
||||
}
|
||||
|
||||
// constructor
|
||||
SpecAnalyzer::SpecAnalyzer ()
|
||||
{
|
||||
for (unsigned ch = 0; ch < USAC_MAX_NUM_CHANNELS; ch++)
|
||||
{
|
||||
m_bandwidthOff[ch] = 0;
|
||||
m_numAnaBands [ch] = 0;
|
||||
m_specAnaStats[ch] = 0;
|
||||
memset (m_parCorCoeffs[ch], 0, MAX_PREDICTION_ORDER * sizeof (short));
|
||||
}
|
||||
m_tnsPredictor = nullptr;
|
||||
}
|
||||
|
||||
// public functions
|
||||
unsigned SpecAnalyzer::getLinPredCoeffs (short parCorCoeffs[MAX_PREDICTION_ORDER], const unsigned channelIndex) // returns best filter order
|
||||
{
|
||||
unsigned bestOrder = MAX_PREDICTION_ORDER, predGainCurr, predGainPrev;
|
||||
|
||||
if ((parCorCoeffs == nullptr) || (channelIndex >= USAC_MAX_NUM_CHANNELS))
|
||||
{
|
||||
return 0; // invalid arguments error
|
||||
}
|
||||
memcpy (parCorCoeffs, m_parCorCoeffs[channelIndex], MAX_PREDICTION_ORDER * sizeof (short));
|
||||
|
||||
predGainCurr = (m_tnsPredGains[channelIndex] >> 24) & UCHAR_MAX;
|
||||
predGainPrev = (m_tnsPredGains[channelIndex] >> 16) & UCHAR_MAX;
|
||||
while ((bestOrder > 1) && (predGainPrev >= predGainCurr)) // find lowest-order gain maximum
|
||||
{
|
||||
bestOrder--;
|
||||
predGainCurr = predGainPrev;
|
||||
predGainPrev = (m_tnsPredGains[channelIndex] >> (8 * bestOrder - 16)) & UCHAR_MAX;
|
||||
}
|
||||
return ((bestOrder == 1) && (m_parCorCoeffs[channelIndex][0] == 0) ? 0 : bestOrder);
|
||||
}
|
||||
|
||||
unsigned SpecAnalyzer::getMeanAbsValues (const int32_t* const mdctSignal, const int32_t* const mdstSignal, const unsigned nSamplesInFrame,
|
||||
const unsigned channelIndex, const uint16_t* const bandStartOffsets, const unsigned nBands,
|
||||
uint32_t* const meanBandValues)
|
||||
{
|
||||
if ((mdctSignal == nullptr) || (bandStartOffsets == nullptr) || (meanBandValues == nullptr) || (channelIndex >= USAC_MAX_NUM_CHANNELS) ||
|
||||
(nSamplesInFrame > 2048) || (nSamplesInFrame < 2) || (nBands > nSamplesInFrame))
|
||||
{
|
||||
return 1; // invalid arguments error
|
||||
}
|
||||
|
||||
if (mdstSignal != nullptr) // use complex-valued spectral data
|
||||
{
|
||||
for (unsigned b = 0; b < nBands; b++)
|
||||
{
|
||||
const unsigned bandOffset = __min (nSamplesInFrame, bandStartOffsets[b]);
|
||||
const unsigned bandWidth = __min (nSamplesInFrame, bandStartOffsets[b + 1]) - bandOffset;
|
||||
const unsigned anaBandIdx = bandOffset >> SA_BW_SHIFT;
|
||||
|
||||
if ((anaBandIdx < m_numAnaBands[channelIndex]) && (bandOffset == (anaBandIdx << SA_BW_SHIFT)) && ((bandWidth & (SA_BW - 1)) == 0))
|
||||
{
|
||||
const uint32_t* const anaAbsVal = &m_meanAbsValue[channelIndex][anaBandIdx];
|
||||
|
||||
// data available from previous call to spectralAnalysis
|
||||
meanBandValues[b] = (bandWidth == SA_BW ? *anaAbsVal : uint32_t (((int64_t) anaAbsVal[0] + (int64_t) anaAbsVal[1] + 1) >> 1));
|
||||
}
|
||||
else // no previous data available, compute mean magnitude
|
||||
{
|
||||
const int32_t* const bMdct = &mdctSignal[bandOffset];
|
||||
const int32_t* const bMdst = &mdstSignal[bandOffset];
|
||||
uint64_t sumAbsVal = 0;
|
||||
|
||||
for (int s = bandWidth - 1; s >= 0; s--)
|
||||
{
|
||||
#if SA_EXACT_COMPLEX_ABS
|
||||
const double complexSqr = (double) bMdct[s] * (double) bMdct[s] + (double) bMdst[s] * (double) bMdst[s];
|
||||
const unsigned absSample = unsigned (sqrt (complexSqr) + 0.5);
|
||||
#else
|
||||
const unsigned absReal = abs (bMdct[s]); // Richard Lyons, 1997; en.wikipedia.org/
|
||||
const unsigned absImag = abs (bMdst[s]); // wiki/Alpha_max_plus_beta_min_algorithm
|
||||
const unsigned absSample = (absReal > absImag ? absReal + ((absImag * 3) >> 3) : absImag + ((absReal * 3) >> 3));
|
||||
#endif
|
||||
sumAbsVal += absSample;
|
||||
}
|
||||
// average spectral sample magnitude across current band
|
||||
meanBandValues[b] = uint32_t ((sumAbsVal + (bandWidth >> 1)) / bandWidth);
|
||||
}
|
||||
} // for b
|
||||
}
|
||||
else // no imaginary part available, real-valued spectral data
|
||||
{
|
||||
for (unsigned b = 0; b < nBands; b++)
|
||||
{
|
||||
const unsigned bandOffset = __min (nSamplesInFrame, bandStartOffsets[b]);
|
||||
const unsigned bandWidth = __min (nSamplesInFrame, bandStartOffsets[b + 1]) - bandOffset;
|
||||
const int32_t* const bMdct = &mdctSignal[bandOffset];
|
||||
uint64_t sumAbsVal = 0;
|
||||
|
||||
for (int s = bandWidth - 1; s >= 0; s--)
|
||||
{
|
||||
const unsigned absSample = abs (bMdct[s]);
|
||||
|
||||
sumAbsVal += absSample;
|
||||
}
|
||||
// average spectral sample magnitude across frequency band
|
||||
meanBandValues[b] = uint32_t ((sumAbsVal + (bandWidth >> 1)) / bandWidth);
|
||||
} // for b
|
||||
}
|
||||
m_numAnaBands[channelIndex] = 0; // mark spectral data as used
|
||||
|
||||
return 0; // no error
|
||||
}
|
||||
|
||||
void SpecAnalyzer::getSpecAnalysisStats (uint32_t avgSpecAnaStats[USAC_MAX_NUM_CHANNELS], const unsigned nChannels)
|
||||
{
|
||||
if ((avgSpecAnaStats == nullptr) || (nChannels > USAC_MAX_NUM_CHANNELS))
|
||||
{
|
||||
return;
|
||||
}
|
||||
memcpy (avgSpecAnaStats, m_specAnaStats, nChannels * sizeof (uint32_t));
|
||||
}
|
||||
|
||||
void SpecAnalyzer::getSpectralBandwidth (uint16_t bandwidthOffset[USAC_MAX_NUM_CHANNELS], const unsigned nChannels)
|
||||
{
|
||||
if ((bandwidthOffset == nullptr) || (nChannels > USAC_MAX_NUM_CHANNELS))
|
||||
{
|
||||
return;
|
||||
}
|
||||
memcpy (bandwidthOffset, m_bandwidthOff, nChannels * sizeof (uint16_t));
|
||||
}
|
||||
|
||||
unsigned SpecAnalyzer::initLinPredictor (LinearPredictor* const linPredictor)
|
||||
{
|
||||
if (linPredictor == nullptr)
|
||||
{
|
||||
return 1; // invalid arguments error
|
||||
}
|
||||
m_tnsPredictor = linPredictor;
|
||||
|
||||
return 0; // no error
|
||||
}
|
||||
|
||||
#if SA_OPT_WINDOW_GROUPING
|
||||
unsigned SpecAnalyzer::optimizeGrouping (const unsigned channelIndex, const unsigned prefBandwidth, const unsigned prefGroupingIndex)
|
||||
{
|
||||
const uint32_t* meanAbsValCurr = m_meanAbsValue[channelIndex];
|
||||
const uint32_t numAnaBandsInCh = m_numAnaBands [channelIndex];
|
||||
unsigned grpIdxCurr = prefGroupingIndex, maxBands, numBands;
|
||||
uint64_t energyCurrHF, energyPrefHF;
|
||||
uint32_t energyCurrLF, energyPrefLF;
|
||||
unsigned b;
|
||||
|
||||
if ((prefBandwidth > 2048) || (grpIdxCurr == 0) || (grpIdxCurr >= 8) || (channelIndex >= USAC_MAX_NUM_CHANNELS) || (numAnaBandsInCh == 0))
|
||||
{
|
||||
return 8; // invalid arguments error, or pypassing
|
||||
}
|
||||
|
||||
numBands = numAnaBandsInCh >> 3;
|
||||
maxBands = numAnaBandsInCh << SA_BW_SHIFT; // available bandwidth, equal to nSamplesInFrame
|
||||
maxBands = (numBands * __min (maxBands, prefBandwidth) + (maxBands >> 1)) / maxBands;
|
||||
|
||||
if (maxBands * numBands == 0) return 8; // low/no BW
|
||||
if (grpIdxCurr < 7) grpIdxCurr++; // after transient
|
||||
|
||||
meanAbsValCurr += grpIdxCurr * numBands;
|
||||
grpIdxCurr++;
|
||||
energyPrefLF = meanAbsValCurr[0] >> 1; // - 6 dB
|
||||
energyPrefHF = 0;
|
||||
for (b = maxBands - 1; b > 0; b--) // avoid LF band
|
||||
{
|
||||
energyPrefHF += meanAbsValCurr[b];
|
||||
}
|
||||
energyPrefHF >>= 1; // - 6 dB
|
||||
|
||||
do // check whether HF or LF transient starts earlier than preferred grouping index suggests
|
||||
{
|
||||
meanAbsValCurr -= numBands;
|
||||
grpIdxCurr--;
|
||||
energyCurrLF = meanAbsValCurr[0];
|
||||
energyCurrHF = 0;
|
||||
for (b = maxBands - 1; b > 0; b--) // prev. window
|
||||
{
|
||||
energyCurrHF += meanAbsValCurr[b];
|
||||
}
|
||||
}
|
||||
while ((grpIdxCurr > 1) && (energyCurrHF >= energyPrefHF) && (energyCurrLF >= energyPrefLF));
|
||||
|
||||
return __min (grpIdxCurr, prefGroupingIndex); // final optimized grouping index
|
||||
}
|
||||
#endif // SA_OPT_WINDOW_GROUPING
|
||||
|
||||
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 lpcStopBand16k = (samplingRate <= 32000 ? nSamplesInFrame : (32000 * nSamplesInFrame) / samplingRate) >> SA_BW_SHIFT;
|
||||
const unsigned thresholdSlope = (48000 + SA_EPS * samplingRate) / 96000;
|
||||
const unsigned thresholdStart = samplingRate >> 15;
|
||||
|
||||
if ((mdctSignals == nullptr) || (nChannels > USAC_MAX_NUM_CHANNELS) || (lfeChannelIndex > USAC_MAX_NUM_CHANNELS) ||
|
||||
(nSamplesInFrame > 2048) || (nSamplesInFrame < 2) || (samplingRate < 7350) || (samplingRate > 96000))
|
||||
{
|
||||
return 1; // invalid arguments error
|
||||
}
|
||||
|
||||
for (unsigned ch = 0; ch < nChannels; ch++)
|
||||
{
|
||||
const int32_t* const chMdct = mdctSignals[ch];
|
||||
const int32_t* const chMdst = (mdstSignals == nullptr ? nullptr : mdstSignals[ch]);
|
||||
// --- get L1 norm and max value in each band
|
||||
uint16_t idxMaxSpec = 0;
|
||||
uint64_t sumAvgBand = 0;
|
||||
uint64_t sumMaxBand = 0;
|
||||
uint32_t valMaxSpec = 0;
|
||||
int b;
|
||||
|
||||
if (ch == lfeChannelIndex) // no analysis
|
||||
{
|
||||
m_bandwidthOff[ch] = LFE_MAX;
|
||||
m_numAnaBands [ch] = 0;
|
||||
m_specAnaStats[ch] = 0; // flat/stationary frame
|
||||
continue;
|
||||
}
|
||||
|
||||
m_bandwidthOff[ch] = 0;
|
||||
m_numAnaBands [ch] = nSamplesInFrame >> SA_BW_SHIFT;
|
||||
|
||||
for (b = m_numAnaBands[ch] - 1; b >= 0; b--)
|
||||
{
|
||||
const uint16_t offs = b << SA_BW_SHIFT; // start offset of current analysis band
|
||||
const int32_t* const bMdct = &chMdct[offs];
|
||||
const int32_t* const bMdst = (chMdst == nullptr ? nullptr : &chMdst[offs]);
|
||||
uint16_t maxAbsIdx = 0;
|
||||
uint32_t maxAbsVal = 0, tmp = UINT_MAX;
|
||||
uint64_t sumAbsVal = 0;
|
||||
|
||||
if (bMdst != nullptr) // complex-valued spectrum
|
||||
{
|
||||
for (int s = SA_BW - 1; s >= 0; s--)
|
||||
{
|
||||
// sum absolute values of complex signal, derive L1 norm, peak value, and peak index
|
||||
#if SA_EXACT_COMPLEX_ABS
|
||||
const double complexSqr = (double) bMdct[s] * (double) bMdct[s] + (double) bMdst[s] * (double) bMdst[s];
|
||||
const unsigned absSample = unsigned (sqrt (complexSqr) + 0.5);
|
||||
#else
|
||||
const unsigned absReal = abs (bMdct[s]); // Richard Lyons, 1997; en.wikipedia.org/
|
||||
const unsigned absImag = abs (bMdst[s]); // wiki/Alpha_max_plus_beta_min_algorithm
|
||||
const unsigned absSample = (absReal > absImag ? absReal + ((absImag * 3) >> 3) : absImag + ((absReal * 3) >> 3));
|
||||
#endif
|
||||
sumAbsVal += absSample;
|
||||
if (offs + s > 0) // exclude DC from max/min
|
||||
{
|
||||
if (maxAbsVal < absSample) // maximum data
|
||||
{
|
||||
maxAbsVal = absSample;
|
||||
maxAbsIdx = (uint16_t) s;
|
||||
}
|
||||
if (tmp/*min*/> absSample) // minimum data
|
||||
{
|
||||
tmp/*min*/= absSample;
|
||||
}
|
||||
} // b > 0
|
||||
}
|
||||
}
|
||||
else // real-valued spectrum, no imaginary part
|
||||
{
|
||||
for (int s = SA_BW - 1; s >= 0; s--)
|
||||
{
|
||||
// obtain absolute values of real signal, derive L1 norm, peak value, and peak index
|
||||
const unsigned absSample = abs (bMdct[s]);
|
||||
|
||||
sumAbsVal += absSample;
|
||||
if (offs + s > 0) // exclude DC from max/min
|
||||
{
|
||||
if (maxAbsVal < absSample) // maximum data
|
||||
{
|
||||
maxAbsVal = absSample;
|
||||
maxAbsIdx = (uint16_t) s;
|
||||
}
|
||||
if (tmp/*min*/> absSample) // minimum data
|
||||
{
|
||||
tmp/*min*/= absSample;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// bandwidth detection
|
||||
if ((m_bandwidthOff[ch] == 0) && (maxAbsVal > __max (thresholdSlope * (thresholdStart + b), SA_EPS)))
|
||||
{
|
||||
m_bandwidthOff[ch] = __max (maxAbsIdx + 5/*guard*/, SA_BW) + offs;
|
||||
m_bandwidthOff[ch] = __min (m_bandwidthOff[ch], nSamplesInFrame);
|
||||
}
|
||||
// save mean magnitude
|
||||
tmp/*mean*/ = uint32_t ((sumAbsVal + (1 << (SA_BW_SHIFT - 1))) >> SA_BW_SHIFT);
|
||||
m_meanAbsValue[ch][b] = tmp;
|
||||
// spectral statistics
|
||||
if (b > 0)
|
||||
{
|
||||
sumAvgBand += tmp;
|
||||
sumMaxBand += maxAbsVal;
|
||||
}
|
||||
if (valMaxSpec < maxAbsVal)
|
||||
{
|
||||
valMaxSpec = maxAbsVal;
|
||||
idxMaxSpec = maxAbsIdx + offs;
|
||||
}
|
||||
} // for b
|
||||
|
||||
// --- spectral analysis statistics for frame
|
||||
b = 1;
|
||||
while (((unsigned) b + 1 < lpcStopBand16k) && ((uint64_t) m_meanAbsValue[ch][b] * (m_numAnaBands[ch] - 1) > sumAvgBand)) b++;
|
||||
b = __min (m_bandwidthOff[ch], b << SA_BW_SHIFT);
|
||||
|
||||
// obtain prediction gain across spectrum
|
||||
m_tnsPredGains[ch] = m_tnsPredictor->calcParCorCoeffs (&chMdct[b], __min (m_bandwidthOff[ch], lpcStopBand16k << SA_BW_SHIFT) - b,
|
||||
MAX_PREDICTION_ORDER, m_parCorCoeffs[ch]);
|
||||
m_specAnaStats[ch] = packAvgSpecAnalysisStats (sumAvgBand, sumMaxBand, m_tnsPredGains[ch] >> 24, idxMaxSpec, (unsigned) b >> SA_BW_SHIFT);
|
||||
} // for ch
|
||||
|
||||
return 0; // no error
|
||||
}
|
61
src/lib/specAnalysis.h
Normal file
61
src/lib/specAnalysis.h
Normal file
@ -0,0 +1,61 @@
|
||||
/* specAnalysis.h - header file for class providing spectral analysis of MCLT signals
|
||||
* written by C. R. Helmrich, last modified in 2019 - see License.htm for legal notices
|
||||
*
|
||||
* The copyright in this software is being made available under a Modified BSD-Style 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-2020 Christian R. Helmrich, project ecodis. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef _SPEC_ANALYSIS_H_
|
||||
#define _SPEC_ANALYSIS_H_
|
||||
|
||||
#include "exhaleLibPch.h"
|
||||
#include "linearPrediction.h"
|
||||
|
||||
// constants, experimental macros
|
||||
#define SA_BW_SHIFT 5
|
||||
#define SA_BW (1 << SA_BW_SHIFT)
|
||||
#define SA_EPS 1024
|
||||
#define SA_EXACT_COMPLEX_ABS 0
|
||||
#define SA_OPT_WINDOW_GROUPING 1
|
||||
|
||||
// spectral signal analysis class
|
||||
class SpecAnalyzer
|
||||
{
|
||||
private:
|
||||
|
||||
// member variables
|
||||
uint16_t m_bandwidthOff[USAC_MAX_NUM_CHANNELS];
|
||||
uint32_t m_meanAbsValue[USAC_MAX_NUM_CHANNELS][1024 >> SA_BW_SHIFT];
|
||||
uint16_t m_numAnaBands [USAC_MAX_NUM_CHANNELS];
|
||||
short m_parCorCoeffs[USAC_MAX_NUM_CHANNELS][MAX_PREDICTION_ORDER];
|
||||
uint32_t m_specAnaStats[USAC_MAX_NUM_CHANNELS];
|
||||
uint32_t m_tnsPredGains[USAC_MAX_NUM_CHANNELS];
|
||||
LinearPredictor* m_tnsPredictor;
|
||||
|
||||
public:
|
||||
|
||||
// constructor
|
||||
SpecAnalyzer ();
|
||||
// destructor
|
||||
~SpecAnalyzer () { }
|
||||
// public functions
|
||||
unsigned getLinPredCoeffs (short parCorCoeffs[MAX_PREDICTION_ORDER], const unsigned channelIndex); // returns best filter order
|
||||
unsigned getMeanAbsValues (const int32_t* const mdctSignal, const int32_t* const mdstSignal, const unsigned nSamplesInFrame,
|
||||
const unsigned channelIndex, const uint16_t* const bandStartOffsets, const unsigned nBands,
|
||||
uint32_t* const meanBandValues);
|
||||
void getSpecAnalysisStats (uint32_t avgSpecAnaStats[USAC_MAX_NUM_CHANNELS], const unsigned nChannels);
|
||||
void getSpectralBandwidth (uint16_t bandwidthOffset[USAC_MAX_NUM_CHANNELS], const unsigned nChannels);
|
||||
unsigned initLinPredictor (LinearPredictor* const linPredictor);
|
||||
#if SA_OPT_WINDOW_GROUPING
|
||||
unsigned optimizeGrouping (const unsigned channelIndex, const unsigned preferredBandwidth, const unsigned preferredGrouping);
|
||||
#endif
|
||||
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
|
||||
}; // SpecAnalyzer
|
||||
|
||||
#endif // _SPEC_ANALYSIS_H_
|
223
src/lib/specGapFilling.cpp
Normal file
223
src/lib/specGapFilling.cpp
Normal file
@ -0,0 +1,223 @@
|
||||
/* specGapFilling.cpp - source file for class with spectral gap filling coding methods
|
||||
* written by C. R. Helmrich, last modified in 2019 - see License.htm for legal notices
|
||||
*
|
||||
* The copyright in this software is being made available under a Modified BSD-Style 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-2020 Christian R. Helmrich, project ecodis. All rights reserved.
|
||||
*/
|
||||
|
||||
#include "exhaleLibPch.h"
|
||||
#include "specGapFilling.h"
|
||||
|
||||
// ISO/IEC 23003-3, Table 109
|
||||
static const uint16_t noiseFillingStartOffset[2 /*long/short*/][2 /*768/1024*/] = {{120, 160}, {15, 20}};
|
||||
|
||||
// constructor
|
||||
SpecGapFiller::SpecGapFiller ()
|
||||
{
|
||||
m_1stGapFillSfb = 0;
|
||||
memset (m_1stNonZeroSfb, 0, sizeof (m_1stNonZeroSfb));
|
||||
}
|
||||
|
||||
// public functions
|
||||
unsigned char SpecGapFiller::getSpecGapFillParams (const SfbQuantizer& sfbQuantizer, const unsigned char* const quantMagn,
|
||||
const unsigned char numSwbShort, SfbGroupData& grpData /*modified*/,
|
||||
const unsigned nSamplesInFrame /*= 1024*/)
|
||||
{
|
||||
const unsigned* const coeffMagn = sfbQuantizer.getCoeffMagnPtr ();
|
||||
const double* const sfNormFacs = sfbQuantizer.getSfNormTabPtr ();
|
||||
const uint16_t sfbsPerGrp = grpData.sfbsPerGroup;
|
||||
const uint16_t windowNfso = noiseFillingStartOffset[grpData.numWindowGroups == 1 ? 0 : 1][nSamplesInFrame >> 10];
|
||||
unsigned char scaleFactorLimit = 0;
|
||||
uint16_t u = 0;
|
||||
short diff = 0, s = 0;
|
||||
double magnSum = 0.0;
|
||||
|
||||
if ((coeffMagn == nullptr) || (sfNormFacs == nullptr) || (quantMagn == nullptr) ||
|
||||
(numSwbShort < MIN_NUM_SWB_SHORT) || (numSwbShort > MAX_NUM_SWB_SHORT) || (nSamplesInFrame > 1024))
|
||||
{
|
||||
return 1; // invalid arguments error
|
||||
}
|
||||
|
||||
// --- determine noise_level as mean of all coeff magnitudes at zero-quantized coeff indices
|
||||
m_1stGapFillSfb = 0;
|
||||
memset (m_1stNonZeroSfb, -1, sizeof (m_1stNonZeroSfb));
|
||||
|
||||
for (uint16_t gr = 0; gr < grpData.numWindowGroups; gr++)
|
||||
{
|
||||
const uint16_t* grpOff = &grpData.sfbOffsets[numSwbShort * gr];
|
||||
const uint32_t* grpRms = &grpData.sfbRmsValues[numSwbShort * gr]; // quant./coding stats
|
||||
const unsigned char* grpScFacs = &grpData.scaleFactors[numSwbShort * gr];
|
||||
const uint16_t grpLength = grpData.windowGroupLength[gr];
|
||||
const uint16_t grpNfso = grpOff[0] + grpLength * windowNfso;
|
||||
const uint16_t sfbLimit = (grpData.numWindowGroups == 1 ? sfbsPerGrp - (grpOff[sfbsPerGrp] >= nSamplesInFrame ? 1 : 0)
|
||||
: __min (sfbsPerGrp, numSwbShort - 1)); // no high frequencies
|
||||
for (uint16_t b = 0; b < sfbLimit; b++) // find first gap-fill SFB and noise_level
|
||||
{
|
||||
const uint16_t sfbStart = grpOff[b];
|
||||
const uint16_t sfbWidth = grpOff[b + 1] - sfbStart;
|
||||
const unsigned* const sfbMagn = &coeffMagn[sfbStart];
|
||||
const unsigned char* sfbQuant = &quantMagn[sfbStart];
|
||||
const unsigned char sFac = grpScFacs[b];
|
||||
|
||||
if (sfbStart < grpNfso) // SFBs below noiseFillingStartOffset
|
||||
{
|
||||
if ((grpRms[b] >> 16) > 0) // the SFB is non-zero quantized
|
||||
{
|
||||
if (m_1stNonZeroSfb[gr] < 0) m_1stNonZeroSfb[gr] = b;
|
||||
if (scaleFactorLimit < sFac) scaleFactorLimit = sFac;
|
||||
}
|
||||
}
|
||||
else // sfbStart >= grpNfso, so above noiseFillingStartOffset
|
||||
{
|
||||
if (m_1stNonZeroSfb[gr] < 0) m_1stNonZeroSfb[gr] = b;
|
||||
if (m_1stGapFillSfb == 0) m_1stGapFillSfb = b;
|
||||
|
||||
if ((grpRms[b] >> 16) > 0) // the SFB is non-zero quantized
|
||||
{
|
||||
unsigned sfbMagnSum = 0; // NOTE: may overflow, but unlikely, and 32 bit is faster
|
||||
|
||||
if (scaleFactorLimit < sFac) scaleFactorLimit = sFac;
|
||||
#if SGF_OPT_SHORT_WIN_CALC
|
||||
if (grpLength > 1) // eight-short windows: SFB ungrouping
|
||||
{
|
||||
const uint32_t* sfbMagnPtr = sfbMagn;
|
||||
const unsigned char* sfbQuantPtr = sfbQuant;
|
||||
const int swbLength = (sfbWidth * oneTwentyEightOver[grpLength]) >> 7; // sfbWidth / grpLength
|
||||
unsigned sfbMagnMin = USHRT_MAX;
|
||||
uint16_t uMin = 0;
|
||||
|
||||
for (uint16_t w = 0; w < grpLength; w++)
|
||||
{
|
||||
unsigned sfbMagnWin = 0;
|
||||
uint16_t uWin = 0;
|
||||
|
||||
for (int i = swbLength - 1; i >= 0; i--, sfbMagnPtr++, sfbQuantPtr++)
|
||||
{
|
||||
if ((*sfbQuantPtr == 0) && (i == 0 || i == swbLength - 1 || *(sfbQuantPtr-1) + *(sfbQuantPtr+1) < 2))
|
||||
{
|
||||
sfbMagnWin += *sfbMagnPtr;
|
||||
uWin++;
|
||||
}
|
||||
}
|
||||
if (sfbMagnWin * (uint64_t) uMin < sfbMagnMin * (uint64_t) uWin) // new minimum
|
||||
{
|
||||
sfbMagnMin = sfbMagnWin;
|
||||
uMin = uWin;
|
||||
}
|
||||
} // for w
|
||||
|
||||
sfbMagnSum += sfbMagnMin * grpLength; // scaled minimum
|
||||
u += uMin * grpLength;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
for (int i = sfbWidth - 1; i >= 0; i--)
|
||||
{
|
||||
if ((sfbQuant[i] == 0) && (sfbQuant[i - 1] + sfbQuant[i + 1] < 2))
|
||||
{
|
||||
sfbMagnSum += sfbMagn[i];
|
||||
u++;
|
||||
}
|
||||
}
|
||||
magnSum += sfbMagnSum * sfNormFacs[grpScFacs[b]];
|
||||
}
|
||||
}
|
||||
} // for b
|
||||
|
||||
// clip to non-negative value for get function and memset below
|
||||
if (m_1stNonZeroSfb[gr] < 0) m_1stNonZeroSfb[gr] = 0;
|
||||
} // for gr
|
||||
|
||||
// determine quantized noise_level from normalized mean magnitude
|
||||
if ((u < 4) || (magnSum * 359.0 < u * 16.0))
|
||||
{
|
||||
if (sfbsPerGrp <= m_1stGapFillSfb) return 0; // silent, level 0
|
||||
|
||||
magnSum = 1.0; u = 4; // max. level
|
||||
}
|
||||
u = __min (7, uint16_t (14.47118288 + 9.965784285 * log10 (magnSum / (double) u)));
|
||||
|
||||
magnSum = pow (2.0, (14 - u) / 3.0); // noiseVal^-1, 23003-3, 7.2
|
||||
|
||||
// --- calculate gap-fill scale factors for zero quantized SFBs, then determine noise_offset
|
||||
u <<= 5; // left-shift for bit-stream
|
||||
if (scaleFactorLimit < SGF_LIMIT) scaleFactorLimit = SGF_LIMIT;
|
||||
|
||||
for (uint16_t gr = 0; gr < grpData.numWindowGroups; gr++)
|
||||
{
|
||||
const uint16_t* grpOff = &grpData.sfbOffsets[numSwbShort * gr];
|
||||
const uint32_t* grpRms = &grpData.sfbRmsValues[numSwbShort * gr]; // quant./coding stats
|
||||
unsigned char* const grpScFacs = &grpData.scaleFactors[numSwbShort * gr];
|
||||
|
||||
for (uint16_t b = m_1stGapFillSfb; b < sfbsPerGrp; b++) // calculate scale factors
|
||||
{
|
||||
if ((grpRms[b] >> 16) == 0) // the SFB is all-zero quantized
|
||||
{
|
||||
if (grpScFacs[b] > 0)
|
||||
{
|
||||
const uint16_t sfbStart = grpOff[b];
|
||||
const int16_t sfbWidthM1 = grpOff[b + 1] - sfbStart - 1;
|
||||
const unsigned* sfbMagn = &coeffMagn[sfbStart];
|
||||
unsigned sfbMagnMax = 0;
|
||||
unsigned sfbMagnSum = 0; // NOTE: may overflow, but unlikely, and 32 bit is faster
|
||||
|
||||
for (int i = sfbWidthM1; i >= 0; i--)
|
||||
{
|
||||
sfbMagnSum += sfbMagn[i];
|
||||
if (sfbMagnMax < sfbMagn[i]) sfbMagnMax = sfbMagn[i]; // sum up without maximum
|
||||
}
|
||||
grpScFacs[b] = sfbQuantizer.getScaleFacOffset (((sfbMagnSum - sfbMagnMax) * magnSum) / (double) sfbWidthM1);
|
||||
|
||||
if (grpScFacs[b] > scaleFactorLimit) grpScFacs[b] = scaleFactorLimit;
|
||||
}
|
||||
#if SGF_SF_PEAK_SMOOTHING
|
||||
// save delta-code bits by smoothing scale factor peaks in zero quantized SFB ranges
|
||||
if ((b > m_1stGapFillSfb) && ((grpRms[b - 1] >> 16) == 0) && ((grpRms[b - 2] >> 16) == 0) &&
|
||||
(grpScFacs[b - 1] > grpScFacs[b]) && (grpScFacs[b - 1] > grpScFacs[b - 2]))
|
||||
{
|
||||
grpScFacs[b - 1] = (grpScFacs[b - 1] + __max (grpScFacs[b], grpScFacs[b - 2])) >> 1;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if ((b > m_1stGapFillSfb) && (((grpRms[b - 1] >> 16) > 0) ^ ((grpRms[b - 2] >> 16) > 0)))
|
||||
{
|
||||
diff += (int) grpScFacs[b - 1] - (int) grpScFacs[b - 2]; // sum up transition deltas
|
||||
s++;
|
||||
}
|
||||
} // for b
|
||||
} // for gr
|
||||
|
||||
if (s > 0)
|
||||
{
|
||||
diff = (diff + (s >> 1)*(diff < 0 ? -1 : 1)) / s; // mean delta
|
||||
if (diff < -16) diff = -16;
|
||||
else
|
||||
if (diff >= 16) diff = 15;
|
||||
}
|
||||
s = __max (-diff, (short) scaleFactorLimit - SGF_LIMIT); // limit
|
||||
|
||||
for (uint16_t gr = 0; gr < grpData.numWindowGroups; gr++)
|
||||
{
|
||||
const uint32_t* grpRms = &grpData.sfbRmsValues[numSwbShort * gr]; // quant./coding stats
|
||||
unsigned char* const grpScFacs = &grpData.scaleFactors[numSwbShort * gr];
|
||||
|
||||
for (uint16_t b = m_1stGapFillSfb; b < sfbsPerGrp; b++) // account f. noise_offset
|
||||
{
|
||||
if ((grpRms[b] >> 16) == 0) // the SFB is all-zero quantized
|
||||
{
|
||||
grpScFacs[b] = (unsigned char) __max (s, grpScFacs[b] - diff);
|
||||
|
||||
if (grpScFacs[b] > scaleFactorLimit) grpScFacs[b] = scaleFactorLimit;
|
||||
}
|
||||
} // for b
|
||||
|
||||
// repeat first significant scale factor downwards to save bits
|
||||
memset (grpScFacs, grpScFacs[m_1stNonZeroSfb[gr]], m_1stNonZeroSfb[gr] * sizeof (unsigned char));
|
||||
} // for gr
|
||||
|
||||
return CLIP_UCHAR (u | (diff + 16)); // combined level and offset
|
||||
}
|
44
src/lib/specGapFilling.h
Normal file
44
src/lib/specGapFilling.h
Normal file
@ -0,0 +1,44 @@
|
||||
/* specGapFilling.h - header file for class with spectral gap filling coding methods
|
||||
* written by C. R. Helmrich, last modified in 2019 - see License.htm for legal notices
|
||||
*
|
||||
* The copyright in this software is being made available under a Modified BSD-Style 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-2020 Christian R. Helmrich, project ecodis. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef _SPEC_GAP_FILLING_H_
|
||||
#define _SPEC_GAP_FILLING_H_
|
||||
|
||||
#include "exhaleLibPch.h"
|
||||
#include "quantization.h"
|
||||
|
||||
// constants, experimental macro
|
||||
#define SGF_LIMIT 2*INDEX_OFFSET
|
||||
#define SGF_OPT_SHORT_WIN_CALC 1
|
||||
#define SGF_SF_PEAK_SMOOTHING 1
|
||||
|
||||
// MDCT-domain gap filling class
|
||||
class SpecGapFiller
|
||||
{
|
||||
private:
|
||||
|
||||
// member variables
|
||||
uint16_t m_1stGapFillSfb;
|
||||
int16_t m_1stNonZeroSfb[NUM_WINDOW_GROUPS];
|
||||
|
||||
public:
|
||||
|
||||
// constructor
|
||||
SpecGapFiller ();
|
||||
// destructor
|
||||
~SpecGapFiller () { }
|
||||
// public functions
|
||||
uint16_t getFirstGapFillSfb () const { return m_1stGapFillSfb; }
|
||||
uint8_t getSpecGapFillParams (const SfbQuantizer& sfbQuantizer, const uint8_t* const quantMagn,
|
||||
const uint8_t numSwbShort, SfbGroupData& grpData /*modified*/,
|
||||
const unsigned nSamplesInFrame = 1024);
|
||||
}; // SpecGapFiller
|
||||
|
||||
#endif // _SPEC_GAP_FILLING_H_
|
270
src/lib/tempAnalysis.cpp
Normal file
270
src/lib/tempAnalysis.cpp
Normal file
@ -0,0 +1,270 @@
|
||||
/* tempAnalysis.cpp - source file for class providing temporal analysis of PCM signals
|
||||
* written by C. R. Helmrich, last modified in 2019 - see License.htm for legal notices
|
||||
*
|
||||
* The copyright in this software is being made available under a Modified BSD-Style 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-2020 Christian R. Helmrich, project ecodis. All rights reserved.
|
||||
*/
|
||||
|
||||
#include "exhaleLibPch.h"
|
||||
#include "tempAnalysis.h"
|
||||
|
||||
// static helper functions
|
||||
static unsigned updateAbsStats (const int32_t* const chSig, const int nSamples, unsigned* const maxAbsVal, int16_t* const maxAbsIdx)
|
||||
{
|
||||
const int32_t* const chSigM1 = chSig - 1; // for first-order high-pass
|
||||
unsigned sumAbs = 0;
|
||||
|
||||
for (int s = nSamples - 1; s >= 0; s--)
|
||||
{
|
||||
// compute absolute values of high-pass signal, obtain L1 norm, peak value, and peak index
|
||||
const unsigned absSample = abs (chSig[s] - chSigM1[s]);
|
||||
|
||||
sumAbs += absSample;
|
||||
if (*maxAbsVal < absSample)
|
||||
{
|
||||
*maxAbsVal = absSample;
|
||||
*maxAbsIdx = (int16_t) s;
|
||||
}
|
||||
}
|
||||
return sumAbs;
|
||||
}
|
||||
|
||||
static unsigned applyPitchPred (const int32_t* const chSig, const int nSamples, const int pitchLag, const int pitchSign = 1)
|
||||
{
|
||||
const int32_t* const chSigM1 = chSig - 1; // for first-order high-pass
|
||||
const int32_t* const plSig = chSig - pitchLag; // & pitch prediction
|
||||
const int32_t* const plSigM1 = plSig - 1;
|
||||
unsigned sumAbs = 0;
|
||||
|
||||
for (int s = nSamples - 1; s >= 0; s--)
|
||||
{
|
||||
// compute absolute values of pitch-predicted high-pass signal, obtain L1 norm, peak value
|
||||
sumAbs += abs (chSig[s] - chSigM1[s] - pitchSign * (plSig[s] - plSigM1[s]));
|
||||
}
|
||||
return sumAbs;
|
||||
}
|
||||
|
||||
static inline uint32_t packAvgTempAnalysisStats (const unsigned avgAbsHpL, const unsigned avgAbsHpR, const unsigned avgAbsHpP,
|
||||
const unsigned avgAbsPpLR, const unsigned maxAbsHpLR)
|
||||
{
|
||||
// spectral flatness, normalized for a value of 256 for noise-like, spectrally flat waveform
|
||||
const unsigned flatSpec = 256 - int ((int64_t (avgAbsPpLR/*L+R sum*/ + TA_EPS) * 256) / (int64_t (avgAbsHpL + avgAbsHpR + TA_EPS)));
|
||||
// temporal flatness, normalized for a value of 256 for steady low or mid-frequency sinusoid
|
||||
const int32_t flatTemp = 256 - int ((int64_t (avgAbsHpL + avgAbsHpR + TA_EPS) * 402) / (int64_t (maxAbsHpLR/*L+R sum*/ + TA_EPS)));
|
||||
// temporal stationarity, two sides, normalized for values of 256 for L1-stationary waveform
|
||||
const int32_t statTmpL = 256 - int (((__min (avgAbsHpP, avgAbsHpL) + TA_EPS) * 256) / ((__max (avgAbsHpP, avgAbsHpL) + TA_EPS)));
|
||||
const int32_t statTmpR = 256 - int (((__min (avgAbsHpL, avgAbsHpR) + TA_EPS) * 256) / ((__max (avgAbsHpL, avgAbsHpR) + TA_EPS)));
|
||||
|
||||
return (CLIP_UCHAR (flatSpec) << 24) | (CLIP_UCHAR (flatTemp) << 16) | (CLIP_UCHAR (statTmpL) << 8) | CLIP_UCHAR (statTmpR);
|
||||
}
|
||||
|
||||
static inline int16_t getMaxAbsHpValueLocation (const unsigned maxAbsValL, const unsigned maxAbsValR, const unsigned maxAbsValP,
|
||||
const int16_t maxAbsIdxL, const int16_t maxAbsIdxR)
|
||||
{
|
||||
if ((maxAbsValP * 8 < maxAbsValL * 3) || (maxAbsValL * 8 < maxAbsValR * 3)) // has transient
|
||||
{
|
||||
return maxAbsValR > maxAbsValL ? maxAbsIdxR : maxAbsIdxL;
|
||||
}
|
||||
return -1; // no transient
|
||||
}
|
||||
|
||||
// constructor
|
||||
TempAnalyzer::TempAnalyzer ()
|
||||
{
|
||||
for (unsigned ch = 0; ch < USAC_MAX_NUM_CHANNELS; ch++)
|
||||
{
|
||||
m_avgAbsHpPrev[ch] = 0;
|
||||
m_maxAbsHpPrev[ch] = 0;
|
||||
m_maxIdxHpPrev[ch] = 1;
|
||||
m_pitchLagPrev[ch] = 0;
|
||||
m_tempAnaStats[ch] = 0;
|
||||
m_transientLoc[ch] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
// public functions
|
||||
void TempAnalyzer::getTempAnalysisStats (uint32_t avgTempAnaStats[USAC_MAX_NUM_CHANNELS], const unsigned nChannels)
|
||||
{
|
||||
if ((avgTempAnaStats == nullptr) || (nChannels > USAC_MAX_NUM_CHANNELS))
|
||||
{
|
||||
return;
|
||||
}
|
||||
memcpy (avgTempAnaStats, m_tempAnaStats, nChannels * sizeof (uint32_t));
|
||||
}
|
||||
|
||||
void TempAnalyzer::getTransientLocation (int16_t maxHighPassValueLocation[USAC_MAX_NUM_CHANNELS], const unsigned nChannels)
|
||||
{
|
||||
if ((maxHighPassValueLocation == nullptr) || (nChannels > USAC_MAX_NUM_CHANNELS))
|
||||
{
|
||||
return;
|
||||
}
|
||||
memcpy (maxHighPassValueLocation, m_transientLoc, nChannels * sizeof (int16_t));
|
||||
}
|
||||
|
||||
unsigned TempAnalyzer::temporalAnalysis (const int32_t* const timeSignals[USAC_MAX_NUM_CHANNELS], const unsigned nChannels,
|
||||
const int nSamplesInFrame, const unsigned lookaheadOffset,
|
||||
const unsigned lfeChannelIndex /*= USAC_MAX_NUM_CHANNELS*/) // to skip an LFE channel
|
||||
{
|
||||
const int halfFrameOffset = nSamplesInFrame >> 1;
|
||||
|
||||
if ((timeSignals == nullptr) || (nChannels > USAC_MAX_NUM_CHANNELS) || (lfeChannelIndex > USAC_MAX_NUM_CHANNELS) ||
|
||||
(nSamplesInFrame > 2048) || (nSamplesInFrame < 2) || (lookaheadOffset > 2048) || (lookaheadOffset == 0))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (unsigned ch = 0; ch < nChannels; ch++)
|
||||
{
|
||||
const int32_t* const chSig = &timeSignals[ch][lookaheadOffset];
|
||||
const int32_t* const chSigM1 = chSig - 1; // for first-order high-pass
|
||||
// --- get L1 norm and pitch lag of both sides
|
||||
unsigned sumAbsValL = 0, sumAbsValR = 0;
|
||||
unsigned maxAbsValL = 0, maxAbsValR = 0;
|
||||
int16_t maxAbsIdxL = 0, maxAbsIdxR = 0;
|
||||
int splitPtL = 0;
|
||||
int splitPtC = halfFrameOffset;
|
||||
int splitPtR = nSamplesInFrame;
|
||||
unsigned uL0 = abs (chSig[splitPtL ] - chSigM1[splitPtL ]);
|
||||
unsigned uL1 = abs (chSig[splitPtC - 1] - chSigM1[splitPtC - 1]);
|
||||
unsigned uR0 = abs (chSig[splitPtC ] - chSigM1[splitPtC ]);
|
||||
unsigned uR1 = abs (chSig[splitPtR - 1] - chSigM1[splitPtR - 1]);
|
||||
unsigned u; // temporary value - register?
|
||||
|
||||
if (ch == lfeChannelIndex) // no analysis
|
||||
{
|
||||
m_tempAnaStats[ch] = 0; // flat/stationary frame
|
||||
m_transientLoc[ch] = -1;
|
||||
continue;
|
||||
}
|
||||
|
||||
do // find last sample of left-side region
|
||||
{
|
||||
sumAbsValL += (u = uL1);
|
||||
splitPtC--;
|
||||
}
|
||||
while ((splitPtC > /*start +*/1) && (uL1 = abs (chSig[splitPtC - 1] - chSigM1[splitPtC - 1])) < u);
|
||||
|
||||
do // find first sample of left-side range
|
||||
{
|
||||
sumAbsValL += (u = uL0);
|
||||
splitPtL++;
|
||||
}
|
||||
while ((splitPtL < splitPtC - 1) && (uL0 = abs (chSig[splitPtL] - chSigM1[splitPtL])) < u);
|
||||
|
||||
sumAbsValL += updateAbsStats (&chSig[splitPtL], splitPtC - splitPtL, &maxAbsValL, &maxAbsIdxL);
|
||||
maxAbsIdxL += splitPtL; // left-side stats
|
||||
if ((maxAbsIdxL == 1) && (maxAbsValL <= u))
|
||||
{
|
||||
maxAbsValL = u;
|
||||
maxAbsIdxL--;
|
||||
}
|
||||
|
||||
splitPtC = halfFrameOffset;
|
||||
|
||||
do // find last sample of right-side region
|
||||
{
|
||||
sumAbsValR += (u = uR1);
|
||||
splitPtR--;
|
||||
}
|
||||
while ((splitPtR > splitPtC + 1) && (uR1 = abs (chSig[splitPtR - 1] - chSigM1[splitPtR - 1])) < u);
|
||||
|
||||
do // find first sample of right-side range
|
||||
{
|
||||
sumAbsValR += (u = uR0);
|
||||
splitPtC++;
|
||||
}
|
||||
while ((splitPtC < splitPtR - 1) && (uR0 = abs (chSig[splitPtC] - chSigM1[splitPtC])) < u);
|
||||
|
||||
sumAbsValR += updateAbsStats (&chSig[splitPtC], splitPtR - splitPtC, &maxAbsValR, &maxAbsIdxR);
|
||||
maxAbsIdxR += splitPtC; // right-side stats
|
||||
if ((maxAbsIdxR == halfFrameOffset + 1) && (maxAbsValR <= u))
|
||||
{
|
||||
maxAbsValR = u;
|
||||
maxAbsIdxR--;
|
||||
}
|
||||
|
||||
// --- find best pitch lags minimizing L1 norms
|
||||
if (sumAbsValL == 0 && sumAbsValR == 0)
|
||||
{
|
||||
m_tempAnaStats[ch] = 0; // flat/stationary frame
|
||||
m_transientLoc[ch] = -1;
|
||||
// re-init stats history for this channel
|
||||
m_avgAbsHpPrev[ch] = 0; // sumAbsValR >> 9
|
||||
m_maxAbsHpPrev[ch] = 0; // maxAbsValR
|
||||
m_maxIdxHpPrev[ch] = 1; // (unsigned) maxAbsIdxR
|
||||
m_pitchLagPrev[ch] = 0; // (unsigned) pLagBestR
|
||||
}
|
||||
else // nonzero signal in the current frame
|
||||
{
|
||||
const int maxAbsIdxP = __max ((int) m_maxIdxHpPrev[ch] - nSamplesInFrame, 1 - (int) lookaheadOffset);
|
||||
unsigned sumAbsHpL = sumAbsValL, sumAbsHpR = sumAbsValR; // after high-pass filter
|
||||
unsigned sumAbsPpL = sumAbsValL, sumAbsPpR = sumAbsValR; // after pitch prediction
|
||||
int pLag, pLagBestR = 0, pSgn;
|
||||
|
||||
// test left-side pitch lag on this frame
|
||||
pLag = __min (maxAbsIdxL - maxAbsIdxP, (int) lookaheadOffset - 1);
|
||||
pSgn = (((chSig[maxAbsIdxL] - chSigM1[maxAbsIdxL] > 0) && (chSig[maxAbsIdxP] - chSigM1[maxAbsIdxP] < 0)) ||
|
||||
((chSig[maxAbsIdxL] - chSigM1[maxAbsIdxL] < 0) && (chSig[maxAbsIdxP] - chSigM1[maxAbsIdxP] > 0)) ? -1 : 1);
|
||||
if ((sumAbsValL = applyPitchPred (chSig, halfFrameOffset, pLag, pSgn)) < sumAbsPpL)
|
||||
{
|
||||
sumAbsPpL = sumAbsValL; // left side
|
||||
}
|
||||
#if TA_MORE_PITCH_TESTS
|
||||
if ((sumAbsValR = applyPitchPred (chSig + halfFrameOffset, halfFrameOffset, pLag, pSgn)) < sumAbsPpR)
|
||||
{
|
||||
sumAbsPpR = sumAbsValR; // right side
|
||||
pLagBestR = pLag;
|
||||
}
|
||||
#endif
|
||||
// test right-side pitch lag on the frame
|
||||
pLag = __min (maxAbsIdxR - maxAbsIdxL, (int) lookaheadOffset - 1);
|
||||
pSgn = (((chSig[maxAbsIdxR] - chSigM1[maxAbsIdxR] > 0) && (chSig[maxAbsIdxL] - chSigM1[maxAbsIdxL] < 0)) ||
|
||||
((chSig[maxAbsIdxR] - chSigM1[maxAbsIdxR] < 0) && (chSig[maxAbsIdxL] - chSigM1[maxAbsIdxL] > 0)) ? -1 : 1);
|
||||
#if TA_MORE_PITCH_TESTS
|
||||
if ((sumAbsValL = applyPitchPred (chSig, halfFrameOffset, pLag, pSgn)) < sumAbsPpL)
|
||||
{
|
||||
sumAbsPpL = sumAbsValL; // left side
|
||||
}
|
||||
#endif
|
||||
if ((sumAbsValR = applyPitchPred (chSig + halfFrameOffset, halfFrameOffset, pLag, pSgn)) < sumAbsPpR)
|
||||
{
|
||||
sumAbsPpR = sumAbsValR; // right side
|
||||
pLagBestR = pLag;
|
||||
}
|
||||
// try previous frame's lag on this frame
|
||||
pLag = (m_pitchLagPrev[ch] > 0 ? (int) m_pitchLagPrev[ch] : __min (halfFrameOffset, (int) lookaheadOffset - 1));
|
||||
pSgn = (((chSig[maxAbsIdxL] - chSigM1[maxAbsIdxL] > 0) && (chSig[maxAbsIdxL-pLag] - chSigM1[maxAbsIdxL-pLag] < 0)) ||
|
||||
((chSig[maxAbsIdxL] - chSigM1[maxAbsIdxL] < 0) && (chSig[maxAbsIdxL-pLag] - chSigM1[maxAbsIdxL-pLag] > 0)) ? -1 : 1);
|
||||
if ((sumAbsValL = applyPitchPred (chSig, halfFrameOffset, pLag, pSgn)) < sumAbsPpL)
|
||||
{
|
||||
sumAbsPpL = sumAbsValL; // left side
|
||||
}
|
||||
if ((sumAbsValR = applyPitchPred (chSig + halfFrameOffset, halfFrameOffset, pLag, pSgn)) < sumAbsPpR)
|
||||
{
|
||||
sumAbsPpR = sumAbsValR; // right side
|
||||
pLagBestR = pLag;
|
||||
}
|
||||
|
||||
// convert L1 norms into average values
|
||||
sumAbsHpL = (sumAbsHpL + unsigned (halfFrameOffset >> 1)) / unsigned (halfFrameOffset);
|
||||
sumAbsHpR = (sumAbsHpR + unsigned (halfFrameOffset >> 1)) / unsigned (halfFrameOffset);
|
||||
sumAbsPpL = (sumAbsPpL + unsigned (halfFrameOffset >> 1)) / unsigned (halfFrameOffset);
|
||||
sumAbsPpR = (sumAbsPpR + unsigned (halfFrameOffset >> 1)) / unsigned (halfFrameOffset);
|
||||
// --- temporal analysis statistics for frame
|
||||
m_tempAnaStats[ch] = packAvgTempAnalysisStats (sumAbsHpL, sumAbsHpR, m_avgAbsHpPrev[ch],
|
||||
sumAbsPpL + sumAbsPpR, maxAbsValL + maxAbsValR);
|
||||
m_transientLoc[ch] = getMaxAbsHpValueLocation (maxAbsValL, maxAbsValR, m_maxAbsHpPrev[ch],
|
||||
maxAbsIdxL, maxAbsIdxR);
|
||||
// update stats history for this channel
|
||||
m_avgAbsHpPrev[ch] = sumAbsHpR;
|
||||
m_maxAbsHpPrev[ch] = maxAbsValR;
|
||||
m_maxIdxHpPrev[ch] = (unsigned) maxAbsIdxR;
|
||||
m_pitchLagPrev[ch] = (unsigned) pLagBestR;
|
||||
} // if sumAbsValL == 0 && sumAbsValR == 0
|
||||
} // for ch
|
||||
|
||||
return 0; // no error
|
||||
}
|
47
src/lib/tempAnalysis.h
Normal file
47
src/lib/tempAnalysis.h
Normal file
@ -0,0 +1,47 @@
|
||||
/* tempAnalysis.h - header file for class providing temporal analysis of PCM signals
|
||||
* written by C. R. Helmrich, last modified in 2019 - see License.htm for legal notices
|
||||
*
|
||||
* The copyright in this software is being made available under a Modified BSD-Style 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-2020 Christian R. Helmrich, project ecodis. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef _TEMP_ANALYSIS_H_
|
||||
#define _TEMP_ANALYSIS_H_
|
||||
|
||||
#include "exhaleLibPch.h"
|
||||
|
||||
// constants, experimental macros
|
||||
#define TA_EPS 4096
|
||||
#define TA_MORE_PITCH_TESTS 0
|
||||
|
||||
// temporal signal analysis class
|
||||
class TempAnalyzer
|
||||
{
|
||||
private:
|
||||
|
||||
// member variables
|
||||
unsigned m_avgAbsHpPrev[USAC_MAX_NUM_CHANNELS];
|
||||
unsigned m_maxAbsHpPrev[USAC_MAX_NUM_CHANNELS];
|
||||
unsigned m_maxIdxHpPrev[USAC_MAX_NUM_CHANNELS];
|
||||
unsigned m_pitchLagPrev[USAC_MAX_NUM_CHANNELS];
|
||||
uint32_t m_tempAnaStats[USAC_MAX_NUM_CHANNELS];
|
||||
int16_t m_transientLoc[USAC_MAX_NUM_CHANNELS];
|
||||
|
||||
public:
|
||||
|
||||
// constructor
|
||||
TempAnalyzer ();
|
||||
// destructor
|
||||
~TempAnalyzer () { }
|
||||
// public functions
|
||||
void getTempAnalysisStats (uint32_t avgTempAnaStats[USAC_MAX_NUM_CHANNELS], const unsigned nChannels);
|
||||
void getTransientLocation (int16_t maxHighPassValueLocation[USAC_MAX_NUM_CHANNELS], const unsigned nChannels);
|
||||
unsigned temporalAnalysis (const int32_t* const timeSignals[USAC_MAX_NUM_CHANNELS], const unsigned nChannels,
|
||||
const int nSamplesInFrame, const unsigned lookaheadOffset,
|
||||
const unsigned lfeChannelIndex = USAC_MAX_NUM_CHANNELS); // to skip an LFE channel
|
||||
}; // TempAnalyzer
|
||||
|
||||
#endif // _TEMP_ANALYSIS_H_
|
305
src/makefile.base
Normal file
305
src/makefile.base
Normal file
@ -0,0 +1,305 @@
|
||||
## makefile.base - common make-file for compiling exhale on Linux and MacOS platforms
|
||||
# written by C. R. Helmrich, last modified 2019 - see License.txt for legal notices
|
||||
#
|
||||
# The copyright in this software is being made available under a Modified BSD 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-2019 Christian R. Helmrich, project ecodis. All rights reserved.
|
||||
##
|
||||
|
||||
#########################################################
|
||||
# check CONFIG parameter
|
||||
#########################################################
|
||||
|
||||
ifneq ($(CONFIG), CONSOLE)
|
||||
ifneq ($(CONFIG), LIBRARY)
|
||||
CONFIG_ERR = TRUE
|
||||
endif
|
||||
endif
|
||||
|
||||
#########################################################
|
||||
# executables used
|
||||
#########################################################
|
||||
|
||||
AR = ar
|
||||
ASM = nasm
|
||||
CPP = g++
|
||||
LD = $(CPP)
|
||||
|
||||
#########################################################
|
||||
# output file names and version information
|
||||
#########################################################
|
||||
|
||||
ifeq ($(CONFIG), CONSOLE)
|
||||
STAT_DEBUG_OUT = $(DIR_BIN)/$(PRD_NAME)d
|
||||
STAT_RELEASE_OUT = $(DIR_BIN)/$(PRD_NAME)
|
||||
DYN_DEBUG_OUT = $(DIR_BIN)/$(PRD_NAME)Dynd
|
||||
DYN_RELEASE_OUT = $(DIR_BIN)/$(PRD_NAME)Dyn
|
||||
else
|
||||
ifeq ($(CONFIG), LIBRARY)
|
||||
STAT_DEBUG_OUT = $(DIR_LIB)/lib$(PRD_NAME)d.a
|
||||
STAT_RELEASE_OUT = $(DIR_LIB)/lib$(PRD_NAME).a
|
||||
DYN_DEBUG_OUT = $(DIR_LIB)/lib$(PRD_NAME)Dynd.so
|
||||
DYN_RELEASE_OUT = $(DIR_LIB)/lib$(PRD_NAME)Dyn.so
|
||||
endif
|
||||
endif
|
||||
|
||||
#########################################################
|
||||
# c compiler flags
|
||||
#########################################################
|
||||
|
||||
# default cpp flags for all configurations
|
||||
#CPPFLAGS = -Wall -fPIC $(DEFS) -I$(CURDIR)/$(DIR_INC)
|
||||
CPPFLAGS = -fPIC $(DEFS) -I$(CURDIR)/$(DIR_INC) -Wall -Wshadow -Wno-sign-compare -Werror -D_FILE_OFFSET_BITS=64
|
||||
|
||||
##########
|
||||
# enforce 32-bit build : 1=yes, 0=no
|
||||
##########
|
||||
M32?= 0
|
||||
ifeq ($(M32),1)
|
||||
CPPFLAGS+=-m32
|
||||
endif
|
||||
##########
|
||||
|
||||
#
|
||||
# debug cpp flags
|
||||
DEBUG_CPPFLAGS = -g -D_DEBUG
|
||||
#
|
||||
# release cpp
|
||||
RELEASE_CPPFLAGS = -O3 -Wuninitialized
|
||||
|
||||
|
||||
#########################################################
|
||||
# linker flags
|
||||
#########################################################
|
||||
|
||||
# linker flags for all
|
||||
ALL_LDFLAGS = -Wall
|
||||
|
||||
##########
|
||||
# enforce 32-bit build : 1=yes, 0=no
|
||||
##########
|
||||
ifeq ($(M32),1)
|
||||
ALL_LDFLAGS+=-m32
|
||||
endif
|
||||
##########
|
||||
|
||||
|
||||
ifeq ($(CONFIG), LIBRARY)
|
||||
# linker flags for library
|
||||
# LDFLAGS = $(ALL_LDFLAGS) -shared -Wl,-Bsymbolic
|
||||
LDFLAGS = $(ALL_LDFLAGS) -shared
|
||||
#
|
||||
# debug linker flags for library
|
||||
DEBUG_LDFLAGS = -Wl,-soname,lib$(PRD_NAME)d.so
|
||||
#
|
||||
# release linker flags for library
|
||||
RELEASE_LDFLAGS = -Wl,-soname,lib$(PRD_NAME).so
|
||||
#
|
||||
else
|
||||
ifeq ($(CONFIG), CONSOLE)
|
||||
# linker flags for console
|
||||
LDFLAGS = $(ALL_LDFLAGS)
|
||||
#
|
||||
# debug linker flags for console
|
||||
DEBUG_LDFLAGS =
|
||||
#
|
||||
# release linker flags for console
|
||||
RELEASE_LDFLAGS =
|
||||
#
|
||||
endif
|
||||
endif
|
||||
|
||||
|
||||
|
||||
#########################################################
|
||||
# objects that have to be created
|
||||
#########################################################
|
||||
|
||||
# the object types that have to be created
|
||||
RELEASE_OBJS = $(OBJS:.o=.r.o)
|
||||
DEBUG_OBJS = $(OBJS:.o=.d.o)
|
||||
|
||||
|
||||
#########################################################
|
||||
# rules
|
||||
#########################################################
|
||||
|
||||
# suffixes
|
||||
.SUFFIXES: .asm .cpp .d.o .r.o
|
||||
|
||||
|
||||
## specification of assembler rules
|
||||
ASMFLAGS = -f elf $(DEFS)
|
||||
DEBUG_ASMFLAGS = -g
|
||||
RELEASE_ASMFLAGS =
|
||||
|
||||
# creation of ASM debug objects
|
||||
$(DIR_OBJ)/%.d.o: $(DIR_SRC)/%.asm
|
||||
$(ASM) $(ASMFLAGS) $(DEBUG_ASMFLAGS) -o $@ $<
|
||||
|
||||
# creation of ASM release objects
|
||||
$(DIR_OBJ)/%.r.o: $(DIR_SRC)/%.asm
|
||||
$(ASM) $(ASMFLAGS) $(RELEASE_ASMFLAGS) -o $@ $<
|
||||
|
||||
|
||||
## specification of C and C++ rules
|
||||
define COMPILE_AND_DEPEND_DEBUG
|
||||
$(CPP) -c -MMD -MF $(DIR_OBJ)/$*.d.d -MT $(DIR_OBJ)/$*.d.o $(CPPFLAGS) $(DEBUG_CPPFLAGS) -o $@ $(CURDIR)/$<
|
||||
@cp $(DIR_OBJ)/$*.d.d $(DIR_OBJ)/$*.d.p; \
|
||||
sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \
|
||||
-e '/^$$/ d' -e 's/$$/ :/' < $(DIR_OBJ)/$*.d.d >> $(DIR_OBJ)/$*.d.p; \
|
||||
rm -f $(DIR_OBJ)/$*.d.d
|
||||
endef
|
||||
define COMPILE_AND_DEPEND_RELEASE
|
||||
$(CPP) -c -MMD -MF $(DIR_OBJ)/$*.r.d -MT $(DIR_OBJ)/$*.r.o $(CPPFLAGS) $(RELEASE_CPPFLAGS) -o $@ $(CURDIR)/$<
|
||||
@cp $(DIR_OBJ)/$*.r.d $(DIR_OBJ)/$*.r.p; \
|
||||
sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \
|
||||
-e '/^$$/ d' -e 's/$$/ :/' < $(DIR_OBJ)/$*.r.d >> $(DIR_OBJ)/$*.r.p; \
|
||||
rm -f $(DIR_OBJ)/$*.r.d
|
||||
endef
|
||||
|
||||
# creation of C++ debug objects
|
||||
$(DIR_OBJ)/%.d.o: $(DIR_SRC)/%.cpp
|
||||
$(COMPILE_AND_DEPEND_DEBUG)
|
||||
|
||||
# creation of C++ release objects
|
||||
$(DIR_OBJ)/%.r.o: $(DIR_SRC)/%.cpp
|
||||
$(COMPILE_AND_DEPEND_RELEASE)
|
||||
|
||||
# creation of C debug objects
|
||||
$(DIR_OBJ)/%.d.o: $(DIR_SRC)/%.c
|
||||
$(COMPILE_AND_DEPEND_DEBUG)
|
||||
|
||||
# creation of C release objects
|
||||
$(DIR_OBJ)/%.r.o: $(DIR_SRC)/%.c
|
||||
$(COMPILE_AND_DEPEND_RELEASE)
|
||||
|
||||
|
||||
## config dependent directory setup
|
||||
ifeq ($(CONFIG), CONSOLE)
|
||||
CHECK_DIRS = $(DIR_OBJ) $(DIR_BIN)
|
||||
else
|
||||
ifeq ($(CONFIG), LIBRARY)
|
||||
CHECK_DIRS = $(DIR_OBJ) $(DIR_LIB)
|
||||
endif
|
||||
endif
|
||||
|
||||
|
||||
## specification of build targets
|
||||
all: check_errors debug release
|
||||
|
||||
debug: check_errors \
|
||||
$(CHECK_DIRS) \
|
||||
$(STAT_DEBUG_OUT)
|
||||
|
||||
# $(DYN_DEBUG_OUT) \
|
||||
|
||||
release: check_errors \
|
||||
$(CHECK_DIRS) \
|
||||
$(STAT_RELEASE_OUT)
|
||||
|
||||
# $(DYN_RELEASE_OUT) \
|
||||
|
||||
|
||||
## check for configuration errors
|
||||
check_errors:
|
||||
@if [ "$(CONFIG_ERR)" = "TRUE" ]; then\
|
||||
echo "ERROR: Wrong CONFIG parameter specified: $(CONFIG)";\
|
||||
false;\
|
||||
fi
|
||||
|
||||
|
||||
## creation of output directories
|
||||
$(DIR_BIN):
|
||||
@if [ ! -d $(DIR_BIN) ]; then\
|
||||
mkdir $(DIR_BIN);\
|
||||
fi
|
||||
|
||||
$(DIR_OBJ):
|
||||
@if [ ! -d $(DIR_OBJ) ]; then\
|
||||
mkdir $(DIR_OBJ);\
|
||||
fi
|
||||
|
||||
$(DIR_LIB):
|
||||
@if [ ! -d $(DIR_LIB) ]; then\
|
||||
mkdir $(DIR_LIB);\
|
||||
fi
|
||||
|
||||
|
||||
## creation of binary output files
|
||||
ifeq ($(CONFIG), LIBRARY)
|
||||
#
|
||||
# create static debug out
|
||||
$(STAT_DEBUG_OUT): $(DEBUG_OBJS)
|
||||
$(AR) -crs $@ $(DEBUG_OBJS)
|
||||
#
|
||||
#
|
||||
# create release debug out
|
||||
$(STAT_RELEASE_OUT): $(RELEASE_OBJS)
|
||||
$(AR) -crs $@ $(RELEASE_OBJS)
|
||||
#
|
||||
#
|
||||
# create dynamic debug out
|
||||
$(DYN_DEBUG_OUT): $(DYN_DEBUG_OUT)
|
||||
ln -fs lib$(PRD_NAME)d.so $@
|
||||
#
|
||||
# create dynamic debug out
|
||||
$(DYN_DEBUG_OUT): $(DEBUG_OBJS)
|
||||
$(LD) $(LDFLAGS) $(DEBUG_LDFLAGS) -o $@ $(DEBUG_OBJS) -L$(DIR_LIB) $(LIBS) $(DYN_LIBS) $(DYN_DEBUG_LIBS)
|
||||
#
|
||||
#
|
||||
# create dynamic release out
|
||||
$(DYN_RELEASE_OUT): $(DYN_RELEASE_OUT)
|
||||
ln -fs lib$(PRD_NAME).so $@
|
||||
#
|
||||
# create dynamic release out
|
||||
$(DYN_RELEASE_OUT): $(RELEASE_OBJS)
|
||||
$(LD) $(LDFLAGS) $(RELEASE_LDFLAGS) -o $@ $(RELEASE_OBJS) -L$(DIR_LIB) $(LIBS) $(DYN_LIBS) $(DYN_RELEASE_LIBS)
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
else
|
||||
ifeq ($(CONFIG), CONSOLE)
|
||||
#
|
||||
# added linked libraries to target prerequisites - $(*_PREREQS) variables - to force relinking when libraries have been rebuilt
|
||||
#
|
||||
# create static debug out
|
||||
$(STAT_DEBUG_OUT): $(DEBUG_OBJS) $(STAT_DEBUG_PREREQS)
|
||||
$(LD) -o $@ $(LDFLAGS) $(DEBUG_LDFLAGS) $(DEBUG_OBJS) -L$(DIR_LIB) $(LIBS) $(STAT_LIBS) $(STAT_DEBUG_LIBS)
|
||||
#
|
||||
#
|
||||
# create static release out
|
||||
$(STAT_RELEASE_OUT): $(RELEASE_OBJS) $(STAT_RELEASE_PREREQS)
|
||||
$(LD) -o $@ $(LDFLAGS) $(RELEASE_LDFLAGS) $(RELEASE_OBJS) -L$(DIR_LIB) $(LIBS) $(STAT_LIBS) $(STAT_RELEASE_LIBS)
|
||||
#
|
||||
#
|
||||
# create dynamic debug out
|
||||
$(DYN_DEBUG_OUT): $(DEBUG_OBJS) $(DYN_DEBUG_PREREQS)
|
||||
$(LD) -o $@ $(LDFLAGS) $(DEBUG_LDFLAGS) $(DEBUG_OBJS) -L$(DIR_LIB) $(LIBS) $(DYN_LIBS) $(DYN_DEBUG_LIBS)
|
||||
#
|
||||
#
|
||||
# create dynamic release out
|
||||
$(DYN_RELEASE_OUT): $(RELEASE_OBJS) $(DYN_RELEASE_PREREQS)
|
||||
$(LD) -o $@ $(LDFLAGS) $(RELEASE_LDFLAGS) $(RELEASE_OBJS) -L$(DIR_LIB) $(LIBS) $(DYN_LIBS) $(DYN_RELEASE_LIBS)
|
||||
#
|
||||
#
|
||||
endif
|
||||
endif
|
||||
|
||||
|
||||
## clean: delete all created files
|
||||
clean:
|
||||
/bin/rm -rf $(DYN_DEBUG_OUT)
|
||||
/bin/rm -rf $(DYN_RELEASE_OUT)
|
||||
/bin/rm -rf $(STAT_DEBUG_OUT)
|
||||
/bin/rm -rf $(STAT_RELEASE_OUT)
|
||||
/bin/rm -rf $(DIR_OBJ)
|
||||
|
||||
|
||||
## include needed dependency files
|
||||
-include $(OBJS:.o=.d.p)
|
||||
-include $(OBJS:.o=.r.p)
|
Reference in New Issue
Block a user