Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ add_subdirectory(src/eid)
add_subdirectory(src/esdru)
add_subdirectory(src/fir)
add_subdirectory(src/freqresp)
add_subdirectory(src/gain_chk)
add_subdirectory(src/g711)
add_subdirectory(src/g711iplc)
add_subdirectory(src/g722)
Expand Down
2 changes: 1 addition & 1 deletion doc/manual/Makefile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
BIBTEX = bibtex
PDFLATEX = pdflatex

PARTS = STLmanual.tex intro.tex g711.tex g711iplc.tex g726.tex g727.tex g728.tex g722.tex rpe.tex rate.tex eid.tex mnru.tex sv56.tex reverb.tex truncate.tex freqresp.tex stereoop.tex esdru.tex bs1770demo.tex basop.tex utl.tex wmc_tool.tex
PARTS = STLmanual.tex intro.tex g711.tex g711iplc.tex g726.tex g727.tex g728.tex g722.tex rpe.tex rate.tex eid.tex mnru.tex sv56.tex reverb.tex truncate.tex freqresp.tex stereoop.tex esdru.tex bs1770demo.tex gain_chk.tex basop.tex utl.tex wmc_tool.tex

STLmanual.pdf : $(PARTS) bibliography.bib
$(PDFLATEX) $(PARTS)
Expand Down
5 changes: 5 additions & 0 deletions doc/manual/STLmanual.tex
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,11 @@
%=============================================================================
\include{freqresp}

%=============================================================================
% chapter gain_chk: Gain amplification verification tool
%=============================================================================
\include{gain_chk}

%=============================================================================
% chapter stereoop: Stereo processing tool
%=============================================================================
Expand Down
228 changes: 228 additions & 0 deletions doc/manual/gain_chk.tex
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
%=============================================================================
% THIS IS chapter{Gain amplification verification tool}
%
% May 2026 - created for STL integration
%=============================================================================
\chapter{Gain amplification verification tool}
\label{ch:gain_chk}
%=============================================================================

\section{Introduction}
\label{sec:gain_chk-intro}

The \texttt{gain\_chk} tool compares a reference signal and a processed
signal and reports short-time gain differences per frequency band. The
tool is intended for objective verification of spectral gain behavior in
speech and audio processing chains.

Both inputs are raw 16-bit PCM files. The processing is frame-based with
20 ms frames and supports sampling frequencies 8, 16, 32 and 48 kHz.
Optionally, a VAD file can be provided to separate statistics for active
and inactive frames.

\section{Description of the algorithm}
\label{sec:gain_chk-algorithm}

\subsection{Frame and spectrum analysis}

Let $x_1[n]$ be the reference frame and $x_2[n]$ be the processed frame,
with frame length $L=\frac{F_s}{50}$ samples (20 ms). Each frame is
windowed with a Hamming window
\begin{equation}
w[n] = 0.54 - 0.46\cos\left(\frac{2\pi n}{L-1}\right), \quad n=0,\ldots,L-1.
\end{equation}

The tool computes the real FFT of each windowed frame after zero-padding
to the next power-of-two length. For each frequency bin $k$, the spectral
energy is
\begin{equation}
P_i[k] = \Re\{X_i[k]\}^2 + \Im\{X_i[k]\}^2, \quad i \in \{1,2\}.
\end{equation}

\subsection{Band energy accumulation}

The analysis bands are defined by upper frequency limits in the
\texttt{bands[]} table. Values in $(0,1]$ are interpreted as normalized
fractions of Nyquist ($F_s/2$). For each band $b$, the tool accumulates
average bin energy:
\begin{equation}
E_i^{(b)} = \frac{1}{N_b}\sum_{k \in \mathcal{B}_b} P_i[k],
\end{equation}
where $\mathcal{B}_b$ is the set of bins inside the band and $N_b$ its
number of bins.

For each frame class (all/active/inactive), band energies are accumulated
across frames:
\begin{equation}
\bar{E}_i^{(b)} = \sum_{\mbox{\scriptsize frames}} E_i^{(b)}.
\end{equation}

\subsection{Reported gain measures}

For each band, the gain ratio is
\begin{equation}
R^{(b)} = \frac{\bar{E}_2^{(b)} + E_{\min}}{\bar{E}_1^{(b)} + E_{\min}},
\end{equation}
where $E_{\min}$ is a small offset used for numerical robustness.

The reported outputs are:
\begin{equation}
G_{\mathrm{dB}}^{(b)} = 10\log_{10}\left(R^{(b)}\right),
\end{equation}
\begin{equation}
G_{\%}^{(b)} = \left(R^{(b)} - 1\right)\cdot 100.
\end{equation}

The same ratio is also computed on the total energy over all bands.

\subsection{VAD-conditioned processing}

When a VAD file is present (\texttt{-v}), one 16-bit flag is read per
frame. Frames with non-zero VAD are treated as active. Frames with zero
VAD are treated as inactive.

For inactive frames, a low-energy gate is applied on the reference frame:
if mean time-domain energy is below a fixed threshold, that frame is not
included in spectral statistics. This avoids unstable ratios in near-silent
segments.

\subsection{Threshold check mode}

When threshold options are enabled, two checks are available:
\begin{itemize}
\item \texttt{-a}: maximum allowed positive gain (amplification) in dB for active frames;
\item \texttt{-s}: maximum allowed attenuation in dB for inactive frames (requires VAD).
\end{itemize}

The tool returns a numeric code intended for automation scripts:
\begin{itemize}
\item 1: active fail and inactive fail;
\item 2: active pass and inactive fail;
\item 3: active fail and inactive pass;
\item 4: active pass and inactive pass.
\end{itemize}

\section{Implementation}
\label{sec:gain_chk-impl}

\subsection{Data and file format}

The tool expects 16-bit PCM input files in native host byte order,
consistent with other STL command-line tools.

Mandatory input/output arguments:
\begin{itemize}
\item \texttt{-i} reference PCM file;
\item \texttt{-o} processed PCM file;
\item \texttt{-t} output text file;
\item \texttt{-r} sampling frequency in Hz.
\end{itemize}

Optional arguments:
\begin{itemize}
\item \texttt{-v} VAD file (one 16-bit flag per frame);
\item \texttt{-a} active-frame amplification threshold (dB);
\item \texttt{-s} inactive-frame attenuation threshold (dB).
\end{itemize}

\subsection{Output file format}

The results text file (\texttt{-t}) is opened in append mode. For each
invocation, the tool writes:

\begin{enumerate}
\item The command line used.
\item A header line indicating the frame class (\texttt{For all Frames},
\texttt{For Active Frames}, or \texttt{For Inactive Frames}).
\item One line per frequency band:
\begin{verbatim}
Ener Ratio (Out/In) for critical band <b> (<f_lo> Hz - <f_hi> Hz) = <G_dB> dB ( <G_%>%)
\end{verbatim}
\item A full-spectrum summary line covering band $0$--$F_s/2$.
\item A separator line of dashes.
\end{enumerate}

\noindent When a VAD file is provided, the above block is repeated
separately for active and inactive frame classes.

\subsection{Command line syntax}

{\tt {\small
\begin{tabular}{llll}
gain\_chk & -i ref.pcm & -o proc.pcm & -t results.txt \\
& -r Fs & [-v vad.bin] & [-a A\_dB] [-s S\_dB] \\
\end{tabular}
}}

\subsection{Processing flow}

Pseudo code:
{\tt\small
\begin{verbatim}
Read and validate command line
Initialize frame length, bands, and Hamming window
for each complete 20 ms frame in both input files:
read reference and processed PCM frame
read VAD flag if provided (else active by default)
optionally skip low-energy inactive frame
apply Hamming window and zero padding
compute FFT of both frames
for each frequency band:
accumulate average spectral energy in reference/processed
After loop:
print per-band and total gain in dB and percent
optionally evaluate thresholds and return status code
\end{verbatim}
}

\section{Tests and portability}
\label{sec:gain_chk-tests}

The STL CMake integration defines regression tests for \texttt{gain\_chk}
using bundled PCM material. Tests run at 48 kHz and compare expected tool
output against reference files.

The implementation is ANSI C style and uses standard C library calls plus
math functions (\texttt{log}, \texttt{log10}, \texttt{cos}). It is portable
across common Unix-like and Windows toolchains supported by STL.

\section{Example code}
\label{sec:gain_chk-example}

The demonstration program is \texttt{gain\_chk.c}. It implements command
line parsing, frame-based spectral analysis, optional VAD-conditioned
statistics, report generation, and threshold-based return codes.

A typical invocation comparing a reference and processed file at 48~kHz:

{\tt {\small
\begin{verbatim}
$ gain_chk -i ref.pcm -o processed.pcm -t results.txt -r 48000
\end{verbatim}
}}

\noindent Sample output written to \texttt{results.txt}:

{\tt {\small
\begin{verbatim}
For all Frames
Ener Ratio (Out/In) for critical band 0 ( 0 Hz - 4000 Hz) = -1.49 dB ( -29.02%)
Ener Ratio (Out/In) for critical band 1 ( 4000 Hz - 8000 Hz) = -6.58 dB ( -78.00%)
Ener Ratio (Out/In) for critical band 2 ( 8000 Hz - 16000 Hz) = -35.58 dB ( -99.97%)
Ener Ratio (Out/In) for critical band 3 (16000 Hz - 20000 Hz) = -16.10 dB ( -97.55%)
Ener Ratio (Out/In) for the Full Spectrum( 0 Hz - 20000 Hz) = -1.55 dB ( -30.02%)
\end{verbatim}
}}

\noindent With threshold checking enabled for scripted pass/fail:

{\tt {\small
\begin{verbatim}
$ gain_chk -i ref.pcm -o processed.pcm -t results.txt -r 48000 \
-v vad.bin -a 3.0 -s 6.0
$ echo $?
4
\end{verbatim}
}}

\noindent Exit code~4 indicates both active and inactive thresholds passed.
18 changes: 18 additions & 0 deletions src/gain_chk/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
add_executable(gain_chk gain_chk.c)
target_link_libraries(gain_chk ${M_LIBRARY})

set(_gain_chk_testdir "${CMAKE_CURRENT_BINARY_DIR}/gain_chk_ctest_scratch")
file(MAKE_DIRECTORY "${_gain_chk_testdir}")

# Reference PCM: input48c.pcm; processed: syn.{a,b,c,d}.pcm; stderr vs gain_chk_stderr_*.ref
foreach(_case self a b c d)
add_test(
NAME gain_chk_${_case}
COMMAND ${CMAKE_COMMAND}
-DGAIN_CHK=$<TARGET_FILE:gain_chk>
-DPCM_DIR=${CMAKE_CURRENT_SOURCE_DIR}/test_data
-DWORKDIR=${_gain_chk_testdir}
-DCASE=${_case}
-P ${CMAKE_CURRENT_SOURCE_DIR}/test_data/gain_chk_ctest.cmake
)
endforeach()
70 changes: 70 additions & 0 deletions src/gain_chk/Gain_Chk_Release_Notes.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*---------------------------------------------------------------------------*
* Gain Amplification Verification tool *
* ------------------------------------ *
* *
* Version: 3.0 *
* Revision Date: October 17, 2012 *
*---------------------------------------------------------------------------*/


Changes from V2.0 to V3.0
=========================

1) #define OUTPUT_VALUES
This activates the output of values to the screen, printed without headers.
They are printed as is (either 4 or 8 values if VAD file is used).

2) #define THRESHOLD_CHECK
This introduces the two new switches "-a XX" "-s YY".
The switch -a specifies active signal max amplification XX in dB.
The switch -i specifies incatve (silence) signal max attenuation YY
in dB. The results from the gain gain calculation are compared against
the specified thresholds XX and YY.

When defined, the tool returns one of the follwoing codes:
0: Error During Processing
1: Active Signal Amp above Threshold, Inactive Signal Att below Threshold (Both Fail)
2: Inactive Signal Att below Threshold (Inactive Sig Fail, Active Sig Pass)
3: Active Signal Amp above Threshold (Inactive Sig Pass, Active Sig Fail)
4: Active Signal Amp below Threshold, Inactive Signal Att above Threshold (Both Pass)


Both changes simplifies a handling of results inside the gain check script.


Changes from V1.0 to V2.0
=========================

1) Better Protection of 'Division by' 0 and 'Log of 0'.
Now all this checking is done in the 'print_bands()' function.

2) Bands can now be Specified as a Fraction of Fs/2.

3) Energy Calculations and Printout are Seperated when using the VAD Flag File.
In the previous version, frames with VAD=0 were skipped.
In this version, the energy ratio (in % and dB) of all Bands Specified will be
calculated for Active and Inactive Frames. The results will be printed for each.

4) Printout of the Bands Energy is now in the 'print_bands()' function.

5) Better Checking of the Frequency Bands Validity.
The previous version had a bug that caused it to crash
with some combinations of Freq Bands.

6) Removed the 'E_MIN' addition in the energy calculation
(for Both the Original and Processed Files).
This addition was not necessary; the check for Energy < E_MIN is now done
only once now in the 'print_bands()' function.

7) An energy threshold has been added.
When the VAD file is used and a frame is Inactive (VAD=0)
The energy of the original is calculted to see if it is meaningful
to include the frame in the frequency energy calculation.
The threshold is set that if the energy is lower than the
energy of a sine wave of 2 in amplitude, the frame is skipped.
The amplitude of 2 means that the sine wave varies between -2 to +2
if quantized by a 16 bits short.

8) Total Energy (Full Spectrum) will be printed for both Active and Inactive frames.
The corresponds to the Band 0-Fs/2.

Loading