first commit

This commit is contained in:
Uladzimir Karpenka
2026-06-01 15:58:45 +03:00
commit 89c8a0e2b5
313 changed files with 945364 additions and 0 deletions
+504
View File
@@ -0,0 +1,504 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
[This is the first released version of the Lesser GPL. It also counts
as the successor of the GNU Library Public License, version 2, hence
the version number 2.1.]
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it. You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.
When we speak of free software, we are referring to freedom of use,
not price. Our General Public Licenses are designed to make sure that
you have the freedom to distribute copies of free software (and charge
for this service if you wish); that you receive source code or can get
it if you want it; that you can change the software and use pieces of
it in new free programs; and that you are informed that you can do
these things.
To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights. These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.
To protect each distributor, we want to make it very clear that
there is no warranty for the free library. Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.
Finally, software patents pose a constant threat to the existence of
any free program. We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder. Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.
Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License. This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License. We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.
When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library. The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom. The Lesser General
Public License permits more lax criteria for linking other code with
the library.
We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License. It also provides other free software developers Less
of an advantage over competing non-free programs. These disadvantages
are the reason we use the ordinary General Public License for many
libraries. However, the Lesser license provides advantages in certain
special circumstances.
For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard. To achieve this, non-free programs must be
allowed to use the library. A more frequent case is that a free
library does the same job as widely used non-free libraries. In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.
In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software. For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.
Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.
GNU LESSER GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.
If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (1) uses at run time a
copy of the library already present on the user's computer system,
rather than copying library functions into the executable, and (2)
will operate properly with a modified version of the library, if
the user installs one, as long as the modified version is
interface-compatible with the version that the work was made with.
c) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
d) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
e) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the materials to be distributed need not include anything that is
normally distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License. However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.
9. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded. In such case, this License incorporates the limitation as if
written in the body of this License.
13. The Free Software Foundation may publish revised and/or new
versions of the Lesser General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
NO WARRANTY
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Libraries
If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change. You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).
To apply these terms, attach the following notices to the library. It is
safest to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.
<one line to give the library's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
USA
Also add information on how to contact you by electronic and paper mail.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the
library `Frob' (a library for tweaking knobs) written by James Random
Hacker.
<signature of Ty Coon>, 1 April 1990
Ty Coon, President of Vice
That's all there is to it!
+81
View File
@@ -0,0 +1,81 @@
#
# Makefile for SPECBLEACH
# This one creates libspecbleach.a intended for static linking
#
FFTWINCLUDE=`pkg-config --cflags fftw3f`
CFLAGS?= -pthread -O3 -D_GNU_SOURCE -Wno-parentheses $(FFTWINCLUDE)
COMPILE=$(CC) $(CFLAGS) -Iinclude -Isrc -Isrc/shared
SOURCES= \
src/processors/specbleach_adenoiser.c \
src/processors/specbleach_denoiser.c \
src/processors/adaptivedenoiser/adaptive_denoiser.c \
src/shared/gain_estimation/gain_estimators.c \
src/shared/noise_estimation/adaptive_noise_estimator.c \
src/shared/pre_estimation/absolute_hearing_thresholds.c \
src/shared/pre_estimation/critical_bands.c \
src/shared/pre_estimation/masking_estimator.c \
src/shared/pre_estimation/noise_scaling_criterias.c \
src/shared/pre_estimation/spectral_smoother.c \
src/shared/pre_estimation/transient_detector.c \
src/shared/post_estimation/noise_floor_manager.c \
src/shared/post_estimation/postfilter.c \
src/shared/post_estimation/spectral_whitening.c \
src/shared/utils/denoise_mixer.c \
src/shared/utils/general_utils.c \
src/shared/utils/spectral_features.c \
src/shared/utils/spectral_utils.c \
src/shared/stft/stft_processor.c \
src/shared/stft/fft_transform.c \
src/shared/stft/stft_buffer.c \
src/shared/stft/stft_windows.c
HEADERS= \
OBJS= \
src/processors/specbleach_adenoiser.o \
src/processors/specbleach_denoiser.o \
src/processors/adaptivedenoiser/adaptive_denoiser.o \
src/shared/gain_estimation/gain_estimators.o \
src/shared/pre_estimation/absolute_hearing_thresholds.o \
src/shared/pre_estimation/critical_bands.o \
src/shared/pre_estimation/masking_estimator.o \
src/shared/noise_estimation/adaptive_noise_estimator.o \
src/shared/pre_estimation/noise_scaling_criterias.o \
src/shared/pre_estimation/spectral_smoother.o \
src/shared/pre_estimation/transient_detector.o \
src/shared/post_estimation/noise_floor_manager.o \
src/shared/post_estimation/postfilter.o \
src/shared/post_estimation/spectral_whitening.o \
src/shared/utils/denoise_mixer.o \
src/shared/utils/general_utils.o \
src/shared/utils/spectral_features.o \
src/shared/utils/spectral_utils.o \
src/shared/stft/stft_processor.o \
src/shared/stft/fft_transform.o \
src/shared/stft/stft_buffer.o \
src/shared/stft/stft_windows.o
libspecbleach.a: $(OBJS)
ar rv libspecbleach.a $(OBJS)
ranlib libspecbleach.a
.c.o:
$(COMPILE) -c -o $@ $<
clean:
-rm -f libspecbleach.a $(OBJS)
#############################################################################
#
# What follows is automatically generated by the "makedepend" program
#
#############################################################################
# DO NOT DELETE
+19
View File
@@ -0,0 +1,19 @@
libspecbleach
=============
The material put here into piHPSDR comes from
https://github.com/lucianodato/libspecbleach
and reflects commit 52660fa1dbe41991cf420b9d60648f7563b9717e.
A Makefile has been provided that just compiles the files needed
in piHPSDR and produce a library that can be linked statically.
This means, compared to the original repository, only the files
libspecbleach/Makefile
libspecbleach/README.DL1YCF (this file)
have been added and lots of files (e.g. tests, examples,
meson build files, etc.) have been omitted.
+135
View File
@@ -0,0 +1,135 @@
# libspecbleach
[![build](https://github.com/lucianodato/libspecbleach/actions/workflows/build.yml/badge.svg)](https://github.com/lucianodato/libspecbleach/actions/workflows/build.yml)
[![codecov](https://codecov.io/gh/lucianodato/libspecbleach/branch/main/graph/badge.svg)](https://codecov.io/gh/lucianodato/libspecbleach)
[![License: LGPL v2.1](https://img.shields.io/badge/License-LGPL%20v2.1-blue.svg)](https://www.gnu.org/licenses/lgpl-2.1)
C library for audio noise reduction and other spectral effects
## Table of Contents
- [Background](#background)
- [De-noise Algorithms](#de-noise-algorithms)
- [Build](#build)
- [Installation](#installation)
- [Usage Examples](#usage-examples)
- [Development](#development)
- [Contributing](#contributing)
- [License](#license)
## Background
This library is based on the algorithms that were used in [noise-repellent](https://github.com/lucianodato/noise-repellent). These were extracted into a standalone library to remove the lv2 dependency. It was designed to be extensible and modular. It uses the concept of a spectral processor which itself uses a short time Fourier transform (STFT) to process the audio. There are two initial processors in place, one which uses the adaptive part of noise repellent and one that uses the manual capturing profile based denoising. The library could be extended with more spectral processors using any STFT-based algorithm such as de-crackle, de-click and other audio restoration algorithms.
## De-noise Algorithms
There are several techniques implemented in the library that are being used in the denoisers, such as masking thresholds estimation, onset detectors, etc. All these are being used in conjunction to improve the very basic spectral subtraction algorithm. Most of the papers used are listed in the wiki of the project. Also a block diagram is provided to explain the reduction architecture.
## Build
If you wish to compile yourself and install the library you will need:
- A C compiling toolchain (GCC or Clang)
- [Meson](https://mesonbuild.com/) build system (0.60.0 or newer)
- [Ninja](https://ninja-build.org/) build tool
- [FFTW3](http://www.fftw.org/) library (float version)
- [libsndfile](https://github.com/libsndfile/libsndfile) (optional, for examples)
## Installation
```bash
git clone https://github.com/lucianodato/libspecbleach.git
cd libspecbleach
meson setup build --buildtype=release
meson compile -C build
sudo meson install -C build
```
## Build Options
You can configure the build using `-Doption=value`:
- `enable_examples`: Build example applications (default: `false`). Requires `libsndfile`.
- `enable_tests`: Build unit and integration tests (default: `false`). Requires `libsndfile`.
- `static_deps`: Link internal dependencies (like FFTW3) statically (default: `false`). Useful for creating self-contained libraries.
- `custom_warning_level`: 0-3 (default: `2`). Controls compiler warning verbosity.
- `treat_warnings_as_errors`: Treat compiler warnings as errors (default: `false`).
- `enable_sanitizers`: Enable sanitizers in debug builds (default: `false`).
- `sanitize_address`: Enable AddressSanitizer (default: `false`).
- `sanitize_undefined`: Enable UndefinedBehaviorSanitizer (default: `false`).
Example for a static build with examples:
```bash
meson setup build -Dstatic_deps=true -Denable_examples=true
meson compile -C build
```
## Usage Examples
Simple console apps examples are provided to demonstrate how to use the library. It needs libsndfile to compile successfully. You can build them with:
```bash
meson setup build --buildtype=release -Denable_examples=true
meson compile -C build
```
### Adaptive noise reduction
```bash
./build/example/adenoiser_demo <input file name> <output file name>
```
### Manual noise reduction
```bash
./build/example/denoiser_demo <input file name> <output file name>
```
It will recognize any libsndfile supported format.
## Development
### Building for Development
For development builds with debugging symbols:
```bash
meson setup build --buildtype=debug
meson compile -C build
```
### Code Formatting
The project uses `clang-format` for code formatting. To format the code:
```bash
meson compile format -C build
```
### Running Tests
If tests are enabled:
```bash
meson setup build -Denable_tests=true
meson test -C build
```
### Coverage
To generate coverage reports locally, you will need `gcovr` or `lcov` installed.
```bash
meson setup build --buildtype=debug -Db_coverage=true
meson compile -C build
meson test -C build
ninja -C build coverage-html
```
The report will be available in `build/meson-logs/coveragereport/index.html`.
## License
This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version.
See [LICENSE](LICENSE) for more details.
+114
View File
@@ -0,0 +1,114 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef SPECBLEACH_ADENOISER_H_INCLUDED
#define SPECBLEACH_ADENOISER_H_INCLUDED
#ifdef __cplusplus
extern "C" {
#endif
#include <stdbool.h>
#include <stdint.h>
typedef void* SpectralBleachHandle;
typedef struct SpectralBleachAdaptiveParameters {
/* Enables outputting the residue of the reduction processing. It's either
* true or false */
bool residual_listen;
/* Sets the amount of dBs that the noise will be attenuated. It goes from 0 dB
* to 40 dB. This controls both the gain attenuation strength and the residual
* noise mixing level for optimal noise reduction. */
float reduction_amount;
/* Percentage of smoothing to apply. Averages the reduction calculation frame
* per frame so the rate of change is less resulting in less musical noise but
* if too strong it can blur transient and reduce high frequencies. It goes
* from 0 to 100 percent */
float smoothing_factor;
/* Percentage of whitening that is going to be applied to the residue of the
* reduction. It modifies the noise floor to be more like white noise. This
* can help hide musical noise when the noise is colored. It goes from 0 to
* 100 percent */
float whitening_factor;
/* Type of algorithm used to scale noise in order to apply over or under
* subtraction in different parts of the spectrum while calculating the
* reduction. 0 is a-posteriori snr scaling using the complete spectrum, 1 is
* a-posteriori using critical bands, 2 is using masking thresholds and 3 is
* disabled.
*/
int noise_scaling_type;
/* Strength in which the reduction will be applied. It uses the masking
* thresholds of the signal to determine where in the spectrum the reduction
* needs to be stronger. This parameter scales how much in each of the
* frequencies the reduction is going to be applied. It can be a positive dB
* value in between 0 dB and 12 dB */
float noise_rescale;
/* Sets the SNR threshold in dB in which the post-filter will start to blur
* musical noise. It can be a positive or negative dB value in between -10 dB
* and 10 dB */
float post_filter_threshold;
/* Method used for adaptive noise estimation in the adaptive denoiser.
* 0: LOUIZOU_METHOD uses minimum statistics (default)
* 1: SPP_MMSE_METHOD uses Speech Presence Probability with MMSE estimation
* for lower complexity and unbiased noise tracking. */
int noise_estimation_method;
} SpectralBleachAdaptiveParameters;
/**
* Returns a handle to an instance of the library for the adaptive based
* noise reduction. Sample rate could be anything from 4000hz to 192khz.
* Recommended frame size range is between 20ms and 100ms
*/
SpectralBleachHandle specbleach_adaptive_initialize(uint32_t sample_rate,
float frame_size);
/**
* Free instance associated to the handle passed
*/
void specbleach_adaptive_free(SpectralBleachHandle instance);
/**
* Loads the parameters for the reduction.
* This has to be called before processing
*/
bool specbleach_adaptive_load_parameters(
SpectralBleachHandle instance, SpectralBleachAdaptiveParameters parameters);
/**
* Returns the latency in samples associated with the library instance
*/
uint32_t specbleach_adaptive_get_latency(SpectralBleachHandle instance);
/**
* Process buffer of a number of samples
*/
bool specbleach_adaptive_process(SpectralBleachHandle instance,
uint32_t number_of_samples, const float* input,
float* output);
#ifdef __cplusplus
}
#endif
#endif
+170
View File
@@ -0,0 +1,170 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef SPECBLEACH_DENOISER_H_INCLUDED
#define SPECBLEACH_DENOISER_H_INCLUDED
#ifdef __cplusplus
extern "C" {
#endif
#include <stdbool.h>
#include <stdint.h>
typedef void* SpectralBleachHandle;
typedef struct SpectralBleachDenoiserParameters {
/* Sets the processor in listening mode to capture the noise profile.
* 0 is disabled, 1 will learn all profile types simultaneously. */
int learn_noise;
/* Sets the noise reduction mode to use when learning is disabled.
* 1 will use the average profile, 2 will use the median profile
* and 3 will use the max profile. */
int noise_reduction_mode;
/* Enables outputting the residue of the reduction processing. It's either
* true or false */
bool residual_listen;
/* Sets the amount of dBs that the noise will be attenuated. It goes from 0 dB
* to 40 dB. This controls both the gain attenuation strength and the residual
* noise mixing level for optimal noise reduction. */
float reduction_amount;
/* Percentage of smoothing to apply. Averages the reduction calculation frame
* per frame so the rate of change is less resulting in less musical noise but
* if too strong it can blur transient and reduce high frequencies. It goes
* from 0 to 100 percent */
float smoothing_factor;
/* Percentage of whitening that is going to be applied to the residue of the
* reduction. It modifies the noise floor to be more like white noise. This
* can help hide musical noise when the noise is colored. It goes from 0 to
* 100 percent */
float whitening_factor;
/* Type of algorithm used to scale noise in order to apply over or under
* subtraction in different parts of the spectrum while calculating the
* reduction. 0 is a-posteriori snr scaling using the complete spectrum, 1 is
* a-posteriori using critical bands, 2 is using masking thresholds and 3 is
* disabled.
*/
int noise_scaling_type;
/* Strength in which the reduction will be applied. It uses the masking
* thresholds of the signal to determine where in the spectrum the reduction
* needs to be stronger. This parameter scales how much in each of the
* frequencies the reduction is going to be applied. It can be a positive dB
* value in between 0 dB and 12 dB */
float noise_rescale;
/* Sets the SNR threshold in dB in which the post-filter will start to blur
* musical noise. It can be a positive or negative dB value in between -10 dB
* and 10 dB */
float post_filter_threshold;
} SpectralBleachDenoiserParameters;
/**
* Returns a handle to an instance of the library for the spectral based
* noise reduction. Sample rate could be anything from 4000hz to 192khz.
* Recommended frame size range is between 20ms and 100ms
*/
SpectralBleachHandle specbleach_initialize(uint32_t sample_rate,
float frame_size);
/**
* Free instance associated to the handle passed
*/
void specbleach_free(SpectralBleachHandle instance);
/**
* Loads the parameters for the reduction.
* This has to be called before processing
*/
bool specbleach_load_parameters(SpectralBleachHandle instance,
SpectralBleachDenoiserParameters parameters);
/**
* Process buffer of a number of samples
*/
bool specbleach_process(SpectralBleachHandle instance,
uint32_t number_of_samples, const float* input,
float* output);
/**
* Returns the latency in samples associated with the library instance
*/
uint32_t specbleach_get_latency(SpectralBleachHandle instance);
/**
* Returns the size of the noise profile spectrum
*/
uint32_t specbleach_get_noise_profile_size(SpectralBleachHandle instance);
/**
* Returns a pointer to the noise profile calculated inside the instance
*/
float* specbleach_get_noise_profile(SpectralBleachHandle instance);
/**
* Allows to load a custom noise profile
*/
bool specbleach_load_noise_profile(SpectralBleachHandle instance,
const float* restored_profile,
uint32_t profile_size,
uint32_t profile_blocks);
/**
* Allows to load a custom noise profile for a specific mode
*/
bool specbleach_load_noise_profile_for_mode(SpectralBleachHandle instance,
const float* restored_profile,
uint32_t profile_size,
uint32_t profile_blocks, int mode);
/**
* Resets the internal noise profile of the library instance
*/
bool specbleach_reset_noise_profile(SpectralBleachHandle instance);
/**
* Returns if the instance has a noise profile calculated internally
*/
bool specbleach_noise_profile_available(SpectralBleachHandle instance);
/**
* Returns the number of blocks used for the noise profile calculation
*/
uint32_t specbleach_get_noise_profile_blocks_averaged(
SpectralBleachHandle instance);
/**
* Returns the number of blocks used for the noise profile calculation for a
* specific mode
*/
uint32_t specbleach_get_noise_profile_blocks_averaged_for_mode(
SpectralBleachHandle instance, int mode);
/**
* Returns a pointer to the noise profile for a specific mode
*/
float* specbleach_get_noise_profile_for_mode(SpectralBleachHandle instance,
int mode);
/**
* Returns if the instance has a noise profile calculated for a specific mode
*/
bool specbleach_noise_profile_available_for_mode(SpectralBleachHandle instance,
int mode);
#ifdef __cplusplus
}
#endif
#endif
Binary file not shown.
@@ -0,0 +1,354 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "adaptive_denoiser.h"
#include "shared/configurations.h"
#include "shared/gain_estimation/gain_estimators.h"
#include "shared/noise_estimation/adaptive_noise_estimator.h"
#include "shared/post_estimation/noise_floor_manager.h"
#include "shared/post_estimation/postfilter.h"
#include "shared/pre_estimation/critical_bands.h"
#include "shared/pre_estimation/noise_scaling_criterias.h"
#include "shared/pre_estimation/spectral_smoother.h"
#include "shared/utils/denoise_mixer.h"
#include "shared/utils/spectral_features.h"
#include "shared/utils/spectral_utils.h"
#include <float.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
typedef struct SpectralAdaptiveDenoiser {
uint32_t fft_size;
uint32_t real_spectrum_size;
uint32_t sample_rate;
uint32_t hop;
float default_oversubtraction;
float default_undersubtraction;
AdaptiveDenoiserParameters parameters;
float* alpha;
float* beta;
float* gain_spectrum;
float* residual_spectrum;
float* denoised_spectrum;
float* noise_profile;
SpectrumType spectrum_type;
CriticalBandType band_type;
GainEstimationType gain_estimation_type;
TimeSmoothingType time_smoothing_type;
DenoiseMixer* mixer;
NoiseScalingCriterias* noise_scaling_criteria;
SpectralSmoother* spectrum_smoothing;
PostFilter* postfiltering;
AdaptiveNoiseEstimator* adaptive_estimator;
SpectralFeatures* spectral_features;
NoiseFloorManager* noise_floor_manager;
bool postfiltering_enabled;
bool whitening_enabled;
} SpectralAdaptiveDenoiser;
SpectralProcessorHandle spectral_adaptive_denoiser_initialize(
const uint32_t sample_rate, const uint32_t fft_size,
const uint32_t overlap_factor) {
if (sample_rate == 0 || fft_size == 0 || overlap_factor == 0) {
return NULL;
}
SpectralAdaptiveDenoiser* self =
(SpectralAdaptiveDenoiser*)calloc(1U, sizeof(SpectralAdaptiveDenoiser));
if (!self) {
return NULL;
}
self->fft_size = fft_size;
self->real_spectrum_size = (self->fft_size / 2U) + 1U;
self->sample_rate = sample_rate;
self->hop = self->fft_size / overlap_factor;
self->default_oversubtraction = DEFAULT_OVERSUBTRACTION;
self->default_undersubtraction = DEFAULT_UNDERSUBTRACTION;
self->spectrum_type = SPECTRAL_TYPE_SPEECH;
self->band_type = CRITICAL_BANDS_TYPE_SPEECH;
self->gain_estimation_type = GAIN_ESTIMATION_TYPE_SPEECH;
self->time_smoothing_type = TIME_SMOOTHING_TYPE_SPEECH;
self->postfiltering_enabled = POSTFILTER_ENABLED_SPEECH;
self->whitening_enabled = WHITENING_ENABLED_SPEECH;
self->gain_spectrum = (float*)calloc(self->fft_size, sizeof(float));
if (!self->gain_spectrum) {
spectral_adaptive_denoiser_free(self);
return NULL;
}
(void)initialize_spectrum_with_value(self->gain_spectrum, self->fft_size,
1.F);
self->alpha = (float*)calloc(self->real_spectrum_size, sizeof(float));
if (!self->alpha) {
spectral_adaptive_denoiser_free(self);
return NULL;
}
(void)initialize_spectrum_with_value(self->alpha, self->real_spectrum_size,
1.F);
self->beta = (float*)calloc(self->real_spectrum_size, sizeof(float));
if (!self->beta) {
spectral_adaptive_denoiser_free(self);
return NULL;
}
self->noise_profile = (float*)calloc(self->real_spectrum_size, sizeof(float));
if (!self->noise_profile) {
spectral_adaptive_denoiser_free(self);
return NULL;
}
self->adaptive_estimator = louizou_estimator_initialize(
self->real_spectrum_size, sample_rate, fft_size);
if (!self->adaptive_estimator) {
spectral_adaptive_denoiser_free(self);
return NULL;
}
self->residual_spectrum = (float*)calloc((self->fft_size), sizeof(float));
if (!self->residual_spectrum) {
spectral_adaptive_denoiser_free(self);
return NULL;
}
self->denoised_spectrum = (float*)calloc((self->fft_size), sizeof(float));
if (!self->denoised_spectrum) {
spectral_adaptive_denoiser_free(self);
return NULL;
}
if (self->postfiltering_enabled) {
self->postfiltering = postfilter_initialize(self->fft_size);
if (!self->postfiltering) {
spectral_adaptive_denoiser_free(self);
return NULL;
}
}
self->spectrum_smoothing =
spectral_smoothing_initialize(self->fft_size, self->time_smoothing_type);
if (!self->spectrum_smoothing) {
spectral_adaptive_denoiser_free(self);
return NULL;
}
self->noise_scaling_criteria = noise_scaling_criterias_initialize(
self->fft_size, self->band_type, self->sample_rate, self->spectrum_type);
if (!self->noise_scaling_criteria) {
spectral_adaptive_denoiser_free(self);
return NULL;
}
self->spectral_features =
spectral_features_initialize(self->real_spectrum_size);
if (!self->spectral_features) {
spectral_adaptive_denoiser_free(self);
return NULL;
}
self->mixer =
denoise_mixer_initialize(self->fft_size, self->sample_rate, self->hop);
if (!self->mixer) {
spectral_adaptive_denoiser_free(self);
return NULL;
}
self->noise_floor_manager = noise_floor_manager_initialize(
self->fft_size, self->sample_rate, self->hop);
if (!self->noise_floor_manager) {
spectral_adaptive_denoiser_free(self);
return NULL;
}
return self;
}
void spectral_adaptive_denoiser_free(SpectralProcessorHandle instance) {
SpectralAdaptiveDenoiser* self = (SpectralAdaptiveDenoiser*)instance;
if (!self) {
return;
}
if (self->adaptive_estimator) {
if (self->parameters.noise_estimation_method == SPP_MMSE_METHOD) {
spp_mmse_estimator_free(self->adaptive_estimator);
} else {
louizou_estimator_free(self->adaptive_estimator);
}
}
if (self->spectral_features) {
spectral_features_free(self->spectral_features);
}
if (self->noise_scaling_criteria) {
noise_scaling_criterias_free(self->noise_scaling_criteria);
}
if (self->spectrum_smoothing) {
spectral_smoothing_free(self->spectrum_smoothing);
}
if (self->postfiltering) {
postfilter_free(self->postfiltering);
}
if (self->mixer) {
denoise_mixer_free(self->mixer);
}
if (self->residual_spectrum) {
free(self->residual_spectrum);
}
if (self->noise_floor_manager) {
noise_floor_manager_free(self->noise_floor_manager);
}
if (self->denoised_spectrum) {
free(self->denoised_spectrum);
}
if (self->noise_profile) {
free(self->noise_profile);
}
if (self->gain_spectrum) {
free(self->gain_spectrum);
}
if (self->alpha) {
free(self->alpha);
}
if (self->beta) {
free(self->beta);
}
free(self);
}
bool load_adaptive_reduction_parameters(SpectralProcessorHandle instance,
AdaptiveDenoiserParameters parameters) {
if (!instance) {
return false;
}
SpectralAdaptiveDenoiser* self = (SpectralAdaptiveDenoiser*)instance;
// Check if noise estimation method has changed
bool method_changed = (self->parameters.noise_estimation_method !=
parameters.noise_estimation_method);
self->parameters = parameters;
// If method changed, reinitialize the adaptive estimator
if (method_changed && self->adaptive_estimator) {
louizou_estimator_free(self->adaptive_estimator);
self->adaptive_estimator = NULL;
// Initialize the appropriate estimator based on the method
if (self->parameters.noise_estimation_method == SPP_MMSE_METHOD) {
self->adaptive_estimator = spp_mmse_estimator_initialize(
self->real_spectrum_size, self->sample_rate, self->fft_size);
} else {
// Default to Louizou method
self->adaptive_estimator = louizou_estimator_initialize(
self->real_spectrum_size, self->sample_rate, self->fft_size);
}
if (!self->adaptive_estimator) {
return false;
}
}
return true;
}
bool spectral_adaptive_denoiser_run(SpectralProcessorHandle instance,
float* fft_spectrum) {
if (!fft_spectrum || !instance) {
return false;
}
SpectralAdaptiveDenoiser* self = (SpectralAdaptiveDenoiser*)instance;
float* reference_spectrum =
get_spectral_feature(self->spectral_features, fft_spectrum,
self->fft_size, self->spectrum_type);
// Estimate noise using the selected method
if (self->parameters.noise_estimation_method == SPP_MMSE_METHOD) {
spp_mmse_estimator_run(self->adaptive_estimator, reference_spectrum,
self->noise_profile);
} else {
// Default to Louizou method
louizou_estimator_run(self->adaptive_estimator, reference_spectrum,
self->noise_profile);
}
float whitening_factor =
self->whitening_enabled ? self->parameters.whitening_factor : 0.0f;
// Scale estimated noise profile for oversubtraction
NoiseScalingParameters oversubtraction_parameters = (NoiseScalingParameters){
.oversubtraction =
self->default_oversubtraction + self->parameters.noise_rescale,
.undersubtraction = self->parameters.reduction_amount,
.scaling_type = self->parameters.noise_scaling_type,
};
apply_noise_scaling_criteria(self->noise_scaling_criteria, reference_spectrum,
self->noise_profile, self->alpha, self->beta,
oversubtraction_parameters);
TimeSmoothingParameters spectral_smoothing_parameters =
(TimeSmoothingParameters){
.smoothing = self->parameters.smoothing_factor,
};
spectral_smoothing_run(self->spectrum_smoothing,
spectral_smoothing_parameters, reference_spectrum);
estimate_gains(self->real_spectrum_size, self->fft_size, reference_spectrum,
self->noise_profile, self->gain_spectrum, self->alpha,
self->beta, self->gain_estimation_type);
noise_floor_manager_apply(
self->noise_floor_manager, self->real_spectrum_size, self->fft_size,
self->gain_spectrum, self->noise_profile,
self->parameters.reduction_amount, whitening_factor);
if (self->postfiltering_enabled) {
PostFiltersParameters post_filter_parameters = (PostFiltersParameters){
.snr_threshold = self->parameters.post_filter_threshold,
.gain_floor = self->parameters.reduction_amount,
};
postfilter_apply(self->postfiltering, fft_spectrum, self->gain_spectrum,
post_filter_parameters);
}
DenoiseMixerParameters mixer_parameters = (DenoiseMixerParameters){
.noise_level = self->parameters.reduction_amount,
.residual_listen = self->parameters.residual_listen,
.whitening_amount = whitening_factor,
};
denoise_mixer_run(self->mixer, fft_spectrum, self->gain_spectrum,
mixer_parameters);
return true;
}
@@ -0,0 +1,54 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef SPECTRAL_ADAPTIVE_DENOISER_H
#define SPECTRAL_ADAPTIVE_DENOISER_H
#include "shared/spectral_processor.h"
#include <stdbool.h>
#include <stdint.h>
#include "shared/noise_estimation/adaptive_noise_estimator.h"
typedef struct AdaptiveDenoiserParameters {
float reduction_amount;
int noise_scaling_type;
float noise_rescale;
float smoothing_factor;
float whitening_factor;
float post_filter_threshold;
bool residual_listen;
/* Method used for adaptive noise estimation.
* LOUIZOU_METHOD uses minimum statistics (default), SPP_MMSE_METHOD uses
* Speech Presence Probability with MMSE estimation for lower complexity
* and unbiased noise tracking. */
AdaptiveNoiseEstimationMethod noise_estimation_method;
} AdaptiveDenoiserParameters;
SpectralProcessorHandle spectral_adaptive_denoiser_initialize(
uint32_t sample_rate, uint32_t fft_size, uint32_t overlap_factor);
void spectral_adaptive_denoiser_free(SpectralProcessorHandle instance);
bool load_adaptive_reduction_parameters(SpectralProcessorHandle instance,
AdaptiveDenoiserParameters parameters);
bool spectral_adaptive_denoiser_run(SpectralProcessorHandle instance,
float* fft_spectrum);
#endif
@@ -0,0 +1,317 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "spectral_denoiser.h"
#include "shared/configurations.h"
#include "shared/gain_estimation/gain_estimators.h"
#include "shared/noise_estimation/noise_estimator.h"
#include "shared/post_estimation/noise_floor_manager.h"
#include "shared/post_estimation/postfilter.h"
#include "shared/pre_estimation/critical_bands.h"
#include "shared/pre_estimation/noise_scaling_criterias.h"
#include "shared/pre_estimation/spectral_smoother.h"
#include "shared/utils/denoise_mixer.h"
#include "shared/utils/spectral_features.h"
#include "shared/utils/spectral_utils.h"
#include <float.h>
#include <stdlib.h>
#include <string.h>
typedef struct SbSpectralDenoiser {
uint32_t fft_size;
uint32_t real_spectrum_size;
uint32_t sample_rate;
uint32_t hop;
float default_oversubtraction;
float default_undersubtraction;
float* gain_spectrum;
float* alpha;
float* beta;
float* noise_spectrum;
SpectrumType spectrum_type;
CriticalBandType band_type;
DenoiserParameters denoise_parameters;
GainEstimationType gain_estimation_type;
TimeSmoothingType time_smoothing_type;
NoiseEstimatorType noise_estimator_type;
NoiseEstimator* noise_estimator;
PostFilter* postfiltering;
NoiseProfile* noise_profile;
SpectralFeatures* spectral_features;
DenoiseMixer* mixer;
NoiseScalingCriterias* noise_scaling_criteria;
SpectralSmoother* spectrum_smoothing;
NoiseFloorManager* noise_floor_manager;
bool postfiltering_enabled;
bool whitening_enabled;
} SbSpectralDenoiser;
SpectralProcessorHandle spectral_denoiser_initialize(
const uint32_t sample_rate, const uint32_t fft_size,
const uint32_t overlap_factor, NoiseProfile* noise_profile) {
if (!noise_profile || sample_rate == 0 || fft_size == 0 ||
overlap_factor == 0) {
return NULL;
}
SbSpectralDenoiser* self =
(SbSpectralDenoiser*)calloc(1U, sizeof(SbSpectralDenoiser));
if (!self) {
return NULL;
}
self->fft_size = fft_size;
self->real_spectrum_size = (self->fft_size / 2U) + 1U;
self->hop = self->fft_size / overlap_factor;
self->sample_rate = sample_rate;
self->spectrum_type = SPECTRAL_TYPE_GENERAL;
self->band_type = CRITICAL_BANDS_TYPE;
self->default_oversubtraction = DEFAULT_OVERSUBTRACTION;
self->default_undersubtraction = DEFAULT_UNDERSUBTRACTION;
self->gain_estimation_type = GAIN_ESTIMATION_TYPE;
self->time_smoothing_type = TIME_SMOOTHING_TYPE;
self->postfiltering_enabled = POSTFILTER_ENABLED_GENERAL;
self->whitening_enabled = WHITENING_ENABLED_GENERAL;
self->gain_spectrum = (float*)calloc(self->fft_size, sizeof(float));
if (!self->gain_spectrum) {
spectral_denoiser_free(self);
return NULL;
}
(void)initialize_spectrum_with_value(self->gain_spectrum, self->fft_size,
1.F);
self->alpha = (float*)calloc(self->real_spectrum_size, sizeof(float));
if (!self->alpha) {
spectral_denoiser_free(self);
return NULL;
}
(void)initialize_spectrum_with_value(self->alpha, self->real_spectrum_size,
1.F);
self->beta = (float*)calloc(self->real_spectrum_size, sizeof(float));
if (!self->beta) {
spectral_denoiser_free(self);
return NULL;
}
self->noise_profile = noise_profile;
self->noise_spectrum =
(float*)calloc(self->real_spectrum_size, sizeof(float));
if (!self->noise_spectrum) {
spectral_denoiser_free(self);
return NULL;
}
self->noise_estimator =
noise_estimation_initialize(self->fft_size, noise_profile);
if (!self->noise_estimator) {
spectral_denoiser_free(self);
return NULL;
}
self->spectral_features =
spectral_features_initialize(self->real_spectrum_size);
if (!self->spectral_features) {
spectral_denoiser_free(self);
return NULL;
}
if (self->postfiltering_enabled) {
self->postfiltering = postfilter_initialize(self->fft_size);
if (!self->postfiltering) {
spectral_denoiser_free(self);
return NULL;
}
}
self->spectrum_smoothing =
spectral_smoothing_initialize(self->fft_size, self->time_smoothing_type);
if (!self->spectrum_smoothing) {
spectral_denoiser_free(self);
return NULL;
}
self->noise_scaling_criteria = noise_scaling_criterias_initialize(
self->fft_size, self->band_type, self->sample_rate, self->spectrum_type);
if (!self->noise_scaling_criteria) {
spectral_denoiser_free(self);
return NULL;
}
self->mixer =
denoise_mixer_initialize(self->fft_size, self->sample_rate, self->hop);
if (!self->mixer) {
spectral_denoiser_free(self);
return NULL;
}
self->noise_floor_manager = noise_floor_manager_initialize(
self->fft_size, self->sample_rate, self->hop);
if (!self->noise_floor_manager) {
spectral_denoiser_free(self);
return NULL;
}
return self;
}
void spectral_denoiser_free(SpectralProcessorHandle instance) {
SbSpectralDenoiser* self = (SbSpectralDenoiser*)instance;
if (!self) {
return;
}
// Don't free noise profile used as reference here
if (self->noise_estimator) {
noise_estimation_free(self->noise_estimator);
}
if (self->spectral_features) {
spectral_features_free(self->spectral_features);
}
if (self->spectrum_smoothing) {
spectral_smoothing_free(self->spectrum_smoothing);
}
if (self->noise_scaling_criteria) {
noise_scaling_criterias_free(self->noise_scaling_criteria);
}
if (self->postfiltering) {
postfilter_free(self->postfiltering);
}
if (self->mixer) {
denoise_mixer_free(self->mixer);
}
if (self->gain_spectrum) {
free(self->gain_spectrum);
}
if (self->noise_floor_manager) {
noise_floor_manager_free(self->noise_floor_manager);
}
if (self->alpha) {
free(self->alpha);
}
if (self->beta) {
free(self->beta);
}
if (self->noise_spectrum) {
free(self->noise_spectrum);
}
free(self);
}
bool load_reduction_parameters(SpectralProcessorHandle instance,
DenoiserParameters parameters) {
if (!instance) {
return false;
}
SbSpectralDenoiser* self = (SbSpectralDenoiser*)instance;
self->denoise_parameters = parameters;
return true;
}
bool spectral_denoiser_run(SpectralProcessorHandle instance,
float* fft_spectrum) {
if (!fft_spectrum || !instance) {
return false;
}
SbSpectralDenoiser* self = (SbSpectralDenoiser*)instance;
float* reference_spectrum =
get_spectral_feature(self->spectral_features, fft_spectrum,
self->fft_size, self->spectrum_type);
if (self->denoise_parameters.learn_noise > 0) {
// Learn all modes simultaneously
for (int mode = ROLLING_MEAN; mode <= MAX; mode++) {
noise_estimation_run(self->noise_estimator, (NoiseEstimatorType)mode,
reference_spectrum);
}
} else if (is_noise_estimation_available(
self->noise_profile,
self->denoise_parameters.noise_reduction_mode)) {
memcpy(self->noise_spectrum,
get_noise_profile(self->noise_profile,
self->denoise_parameters.noise_reduction_mode),
self->real_spectrum_size * sizeof(float));
NoiseScalingParameters oversubtraction_parameters =
(NoiseScalingParameters){
.oversubtraction = (self->default_oversubtraction +
self->denoise_parameters.noise_rescale),
.undersubtraction = self->denoise_parameters.reduction_amount,
.scaling_type = self->denoise_parameters.noise_scaling_type,
};
float whitening_factor = self->whitening_enabled
? self->denoise_parameters.whitening_factor
: 0.0f;
apply_noise_scaling_criteria(
self->noise_scaling_criteria, reference_spectrum, self->noise_spectrum,
self->alpha, self->beta, oversubtraction_parameters);
TimeSmoothingParameters spectral_smoothing_parameters =
(TimeSmoothingParameters){
.smoothing = self->denoise_parameters.smoothing_factor,
};
spectral_smoothing_run(self->spectrum_smoothing,
spectral_smoothing_parameters, reference_spectrum);
estimate_gains(self->real_spectrum_size, self->fft_size, reference_spectrum,
self->noise_spectrum, self->gain_spectrum, self->alpha,
self->beta, self->gain_estimation_type);
noise_floor_manager_apply(
self->noise_floor_manager, self->real_spectrum_size, self->fft_size,
self->gain_spectrum, self->noise_spectrum,
self->denoise_parameters.reduction_amount, whitening_factor);
if (self->postfiltering_enabled) {
PostFiltersParameters post_filter_parameters = (PostFiltersParameters){
.snr_threshold = self->denoise_parameters.post_filter_threshold,
.gain_floor = self->denoise_parameters.reduction_amount,
};
postfilter_apply(self->postfiltering, fft_spectrum, self->gain_spectrum,
post_filter_parameters);
}
DenoiseMixerParameters mixer_parameters = (DenoiseMixerParameters){
.noise_level = self->denoise_parameters.reduction_amount,
.residual_listen = self->denoise_parameters.residual_listen,
.whitening_amount = whitening_factor,
};
denoise_mixer_run(self->mixer, fft_spectrum, self->gain_spectrum,
mixer_parameters);
}
return true;
}
@@ -0,0 +1,50 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef SPECTRAL_DENOISER_H
#define SPECTRAL_DENOISER_H
#include "shared/noise_estimation/noise_profile.h"
#include "shared/spectral_processor.h"
#include <stdbool.h>
#include <stdint.h>
typedef struct DenoiserParameters {
float reduction_amount;
int noise_scaling_type;
float noise_rescale;
bool residual_listen;
int learn_noise;
int noise_reduction_mode;
float smoothing_factor;
float whitening_factor;
float post_filter_threshold;
} DenoiserParameters;
SpectralProcessorHandle spectral_denoiser_initialize(
uint32_t sample_rate, uint32_t fft_size, uint32_t overlap_factor,
NoiseProfile* noise_profile);
void spectral_denoiser_free(SpectralProcessorHandle instance);
bool load_reduction_parameters(SpectralProcessorHandle instance,
DenoiserParameters parameters);
bool spectral_denoiser_run(SpectralProcessorHandle instance,
float* fft_spectrum);
#endif
+6
View File
@@ -0,0 +1,6 @@
processors_sources = files(
'denoiser/spectral_denoiser.c',
'adaptivedenoiser/adaptive_denoiser.c',
'specbleach_adenoiser.c',
'specbleach_denoiser.c',
)
@@ -0,0 +1,137 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "specbleach_adenoiser.h"
#include "adaptivedenoiser/adaptive_denoiser.h"
#include "shared/configurations.h"
#include "shared/stft/stft_processor.h"
#include "shared/utils/general_utils.h"
#include <math.h>
#include <stdlib.h>
#include <string.h>
typedef struct SbAdaptiveDenoiser {
uint32_t sample_rate;
AdaptiveDenoiserParameters denoise_parameters;
SpectralProcessorHandle adaptive_spectral_denoiser;
StftProcessor* stft_processor;
} SbAdaptiveDenoiser;
SpectralBleachHandle specbleach_adaptive_initialize(const uint32_t sample_rate,
float frame_size) {
SbAdaptiveDenoiser* self =
(SbAdaptiveDenoiser*)calloc(1U, sizeof(SbAdaptiveDenoiser));
if (!self) {
return NULL;
}
self->sample_rate = sample_rate;
self->stft_processor = stft_processor_initialize(
sample_rate, frame_size, OVERLAP_FACTOR_SPEECH,
PADDING_CONFIGURATION_SPEECH, ZEROPADDING_AMOUNT_SPEECH,
INPUT_WINDOW_TYPE_SPEECH, OUTPUT_WINDOW_TYPE_SPEECH);
if (!self->stft_processor) {
specbleach_adaptive_free(self);
return NULL;
}
const uint32_t fft_size = get_stft_fft_size(self->stft_processor);
self->adaptive_spectral_denoiser = spectral_adaptive_denoiser_initialize(
self->sample_rate, fft_size, OVERLAP_FACTOR_SPEECH);
if (!self->adaptive_spectral_denoiser) {
specbleach_adaptive_free(self);
return NULL;
}
return self;
}
void specbleach_adaptive_free(SpectralBleachHandle instance) {
SbAdaptiveDenoiser* self = (SbAdaptiveDenoiser*)instance;
if (!self) {
return;
}
if (self->adaptive_spectral_denoiser) {
spectral_adaptive_denoiser_free(self->adaptive_spectral_denoiser);
}
if (self->stft_processor) {
stft_processor_free(self->stft_processor);
}
free(self);
}
uint32_t specbleach_adaptive_get_latency(SpectralBleachHandle instance) {
SbAdaptiveDenoiser* self = (SbAdaptiveDenoiser*)instance;
return get_stft_latency(self->stft_processor);
}
bool specbleach_adaptive_process(SpectralBleachHandle instance,
const uint32_t number_of_samples,
const float* input, float* output) {
if (!instance || number_of_samples == 0 || !input || !output) {
return false;
}
SbAdaptiveDenoiser* self = (SbAdaptiveDenoiser*)instance;
stft_processor_run(self->stft_processor, number_of_samples, input, output,
&spectral_adaptive_denoiser_run,
self->adaptive_spectral_denoiser);
return true;
}
bool specbleach_adaptive_load_parameters(
SpectralBleachHandle instance,
SpectralBleachAdaptiveParameters parameters) {
if (!instance) {
return false;
}
SbAdaptiveDenoiser* self = (SbAdaptiveDenoiser*)instance;
// clang-format off
self->denoise_parameters = (AdaptiveDenoiserParameters){
.residual_listen = parameters.residual_listen,
.reduction_amount =
from_db_to_coefficient(parameters.reduction_amount * -1.F),
.noise_rescale = from_db_to_coefficient(parameters.noise_rescale),
.noise_scaling_type = parameters.noise_scaling_type,
.smoothing_factor = remap_percentage_log_like_unity(parameters.smoothing_factor / 100.F),
.whitening_factor = parameters.whitening_factor / 100.F,
.post_filter_threshold = from_db_to_coefficient(parameters.post_filter_threshold),
.noise_estimation_method = parameters.noise_estimation_method,
};
// clang-format on
load_adaptive_reduction_parameters(self->adaptive_spectral_denoiser,
self->denoise_parameters);
return true;
}
Binary file not shown.
@@ -0,0 +1,277 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "specbleach_denoiser.h"
#include "denoiser/spectral_denoiser.h"
#include "shared/configurations.h"
#include "shared/noise_estimation/noise_profile.h"
#include "shared/stft/stft_processor.h"
#include "shared/utils/general_utils.h"
#include <stdlib.h>
#include <string.h>
typedef struct SbSpectralDenoiser {
uint32_t sample_rate;
DenoiserParameters denoise_parameters;
NoiseProfile* noise_profile;
SpectralProcessorHandle spectral_denoiser;
StftProcessor* stft_processor;
} SbSpectralDenoiser;
SpectralBleachHandle specbleach_initialize(const uint32_t sample_rate,
float frame_size) {
if (sample_rate < 4000 || sample_rate > 192000 || frame_size <= 0.0f) {
return NULL;
}
SbSpectralDenoiser* self =
(SbSpectralDenoiser*)calloc(1U, sizeof(SbSpectralDenoiser));
if (!self) {
return NULL;
}
self->sample_rate = sample_rate;
self->stft_processor = stft_processor_initialize(
sample_rate, frame_size, OVERLAP_FACTOR_GENERAL,
PADDING_CONFIGURATION_GENERAL, ZEROPADDING_AMOUNT_GENERAL,
INPUT_WINDOW_TYPE_GENERAL, OUTPUT_WINDOW_TYPE_GENERAL);
if (!self->stft_processor) {
specbleach_free(self);
return NULL;
}
const uint32_t fft_size = get_stft_fft_size(self->stft_processor);
const uint32_t real_spectrum_size =
get_stft_real_spectrum_size(self->stft_processor);
self->noise_profile = noise_profile_initialize(real_spectrum_size);
if (!self->noise_profile) {
specbleach_free(self);
return NULL;
}
self->spectral_denoiser = spectral_denoiser_initialize(
self->sample_rate, fft_size, OVERLAP_FACTOR_GENERAL, self->noise_profile);
if (!self->spectral_denoiser) {
specbleach_free(self);
return NULL;
}
return self;
}
void specbleach_free(SpectralBleachHandle instance) {
SbSpectralDenoiser* self = (SbSpectralDenoiser*)instance;
if (!self) {
return;
}
if (self->noise_profile) {
noise_profile_free(self->noise_profile);
}
if (self->spectral_denoiser) {
spectral_denoiser_free(self->spectral_denoiser);
}
if (self->stft_processor) {
stft_processor_free(self->stft_processor);
}
free(self);
}
uint32_t specbleach_get_latency(SpectralBleachHandle instance) {
SbSpectralDenoiser* self = (SbSpectralDenoiser*)instance;
if (!self || !self->stft_processor) {
return 0;
}
return get_stft_latency(self->stft_processor);
}
bool specbleach_process(SpectralBleachHandle instance,
const uint32_t number_of_samples, const float* input,
float* output) {
if (!instance || number_of_samples == 0 || !input || !output) {
return false;
}
SbSpectralDenoiser* self = (SbSpectralDenoiser*)instance;
stft_processor_run(self->stft_processor, number_of_samples, input, output,
&spectral_denoiser_run, self->spectral_denoiser);
return true;
}
uint32_t specbleach_get_noise_profile_size(SpectralBleachHandle instance) {
SbSpectralDenoiser* self = (SbSpectralDenoiser*)instance;
if (!self || !self->noise_profile) {
return 0;
}
return get_noise_profile_size(self->noise_profile);
}
uint32_t specbleach_get_noise_profile_blocks_averaged(
SpectralBleachHandle instance) {
SbSpectralDenoiser* self = (SbSpectralDenoiser*)instance;
if (!self || !self->noise_profile) {
return 0;
}
return get_noise_profile_blocks_averaged(
self->noise_profile, self->denoise_parameters.noise_reduction_mode);
}
float* specbleach_get_noise_profile(SpectralBleachHandle instance) {
SbSpectralDenoiser* self = (SbSpectralDenoiser*)instance;
if (!self || !self->noise_profile) {
return NULL;
}
return get_noise_profile(self->noise_profile,
self->denoise_parameters.noise_reduction_mode);
}
bool specbleach_load_noise_profile(SpectralBleachHandle instance,
const float* restored_profile,
const uint32_t profile_size,
const uint32_t averaged_blocks) {
if (!instance || !restored_profile) {
return false;
}
SbSpectralDenoiser* self = (SbSpectralDenoiser*)instance;
if (profile_size != get_noise_profile_size(self->noise_profile)) {
return false;
}
set_noise_profile(self->noise_profile,
self->denoise_parameters.noise_reduction_mode,
restored_profile, profile_size, averaged_blocks);
return true;
}
bool specbleach_load_noise_profile_for_mode(SpectralBleachHandle instance,
const float* restored_profile,
const uint32_t profile_size,
const uint32_t averaged_blocks,
const int mode) {
if (!instance || !restored_profile || mode < 1 || mode > 3) {
return false;
}
SbSpectralDenoiser* self = (SbSpectralDenoiser*)instance;
if (profile_size != get_noise_profile_size(self->noise_profile)) {
return false;
}
set_noise_profile(self->noise_profile, mode, restored_profile, profile_size,
averaged_blocks);
return true;
}
bool specbleach_reset_noise_profile(SpectralBleachHandle instance) {
if (!instance) {
return false;
}
SbSpectralDenoiser* self = (SbSpectralDenoiser*)instance;
reset_noise_profile(self->noise_profile);
return true;
}
bool specbleach_noise_profile_available(SpectralBleachHandle instance) {
SbSpectralDenoiser* self = (SbSpectralDenoiser*)instance;
return is_noise_estimation_available(
self->noise_profile, self->denoise_parameters.noise_reduction_mode);
}
uint32_t specbleach_get_noise_profile_blocks_averaged_for_mode(
SpectralBleachHandle instance, int mode) {
SbSpectralDenoiser* self = (SbSpectralDenoiser*)instance;
if (!self || mode < 1 || mode > 3) {
return 0;
}
return get_noise_profile_blocks_averaged(self->noise_profile, mode);
}
float* specbleach_get_noise_profile_for_mode(SpectralBleachHandle instance,
int mode) {
SbSpectralDenoiser* self = (SbSpectralDenoiser*)instance;
if (!self || mode < 1 || mode > 3) {
return NULL;
}
return get_noise_profile(self->noise_profile, mode);
}
bool specbleach_noise_profile_available_for_mode(SpectralBleachHandle instance,
int mode) {
SbSpectralDenoiser* self = (SbSpectralDenoiser*)instance;
if (!self || mode < 1 || mode > 3) {
return false;
}
return is_noise_estimation_available(self->noise_profile, mode);
}
bool specbleach_load_parameters(SpectralBleachHandle instance,
SpectralBleachDenoiserParameters parameters) {
if (!instance) {
return false;
}
SbSpectralDenoiser* self = (SbSpectralDenoiser*)instance;
// clang-format off
self->denoise_parameters = (DenoiserParameters){
.learn_noise = parameters.learn_noise,
.noise_reduction_mode = parameters.noise_reduction_mode,
.residual_listen = parameters.residual_listen,
.noise_scaling_type = parameters.noise_scaling_type,
.reduction_amount =
from_db_to_coefficient(parameters.reduction_amount * -1.F),
.noise_rescale = from_db_to_coefficient(parameters.noise_rescale),
.smoothing_factor = remap_percentage_log_like_unity(parameters.smoothing_factor / 100.F),
.whitening_factor = parameters.whitening_factor / 100.F,
.post_filter_threshold = from_db_to_coefficient(parameters.post_filter_threshold),
};
// clang-format on
load_reduction_parameters(self->spectral_denoiser, self->denoise_parameters);
return true;
}
Binary file not shown.
+188
View File
@@ -0,0 +1,188 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef MODULES_CONFIGURATIONS_H
#define MODULES_CONFIGURATIONS_H
#include "utils/spectral_utils.h"
#include <stdbool.h>
#include <stdint.h>
// Compile-time assertions for configuration validity
_Static_assert(HANN_WINDOW >= 0 && HANN_WINDOW <= 3,
"HANN_WINDOW must be between 0 and 3");
_Static_assert(HAMMING_WINDOW >= 0 && HAMMING_WINDOW <= 3,
"HAMMING_WINDOW must be between 0 and 3");
_Static_assert(BLACKMAN_WINDOW >= 0 && BLACKMAN_WINDOW <= 3,
"BLACKMAN_WINDOW must be between 0 and 3");
_Static_assert(VORBIS_WINDOW >= 0 && VORBIS_WINDOW <= 3,
"VORBIS_WINDOW must be between 0 and 3");
// Additional C17 compile-time validations
_Static_assert(sizeof(uint32_t) == 4, "uint32_t must be exactly 32 bits");
#ifndef M_PI
#define M_PI (3.14159265358979323846)
#endif
#ifndef M_PIf
#define M_PIf (3.14159265358979323846F)
#endif
/* --------------------------------------------------------------------- */
/* ------------------- Shared Modules configurations ------------------- */
/* --------------------------------------------------------------------- */
// Absolute hearing thresholds
#define REFERENCE_SINE_WAVE_FREQ (1000.F)
#define REFERENCE_LEVEL (90.F)
#define SINE_AMPLITUDE (1.F)
// Spectral Whitening
#define WHITENING_DECAY_RATE (1000.F)
#define WHITENING_FLOOR (0.01F)
// Masking Thresholds
#define BIAS false
#define HIGH_FREQ_BIAS 20.F
#if BIAS
// clang-format off
#define relative_thresholds \
(float[25]){-16.F, -17.F, -18.F, -19.F, -20.F, -21.F, -22.F, -23.F, -24.F, \
-25.F, -25.F, -25.F, -25.F, -25.F, -25.F, -24.F, -23.F, -22.F, \
-19.F, -18.F, -18.F, -18.F, -18.F, -18.F, -18.F}
// clang-format on
#endif
// Postfilter SNR Threshold
#define POSTFILTER_SCALE (10.0F)
#define PRESERVE_MINIMUN_GAIN (true)
#define POSTFILTER_MIN_GAIN_DB (-15.0F)
// Gain Estimators
#define GSS_EXPONENT \
2.0F // 2 Power Subtraction / 1 Magnitude Subtraxtion / 0.5 Spectral
// Subtraction
// Oversubtraction criteria
#define ALPHA_MAX (6.F)
#define ALPHA_MIN (1.F)
#define BETA_MAX (0.01F)
#define BETA_MIN (0.F)
#define DEFAULT_OVERSUBTRACTION (ALPHA_MIN)
#define DEFAULT_UNDERSUBTRACTION (BETA_MAX)
#define LOWER_SNR (0.F)
#define HIGHER_SNR (20.F)
// Adaptive Estimator
#define N_SMOOTH (0.7F)
#define BETA_AT (0.8F)
#define GAMMA (0.998F)
#define ALPHA_P (0.2F)
#define ALPHA_D (0.85F)
#define CROSSOVER_POINT1 (1000.F)
#define CROSSOVER_POINT2 (3000.F)
#define BAND_1_LEVEL (2.F)
#define BAND_2_LEVEL (2.F)
#define BAND_3_LEVEL (5.F)
// SPP-MMSE Estimator Constants
#define SPP_PRIOR_H1 (0.5F) // P(H1) - Speech present prior
#define SPP_PRIOR_H0 (0.5F) // P(H0) - Speech absent prior
#define SPP_FIXED_XI_H1 (31.62F) // Fixed a priori SNR (15 dB in linear)
#define SPP_ALPHA_POW (0.8F) // Power spectrum smoothing factor
#define SPP_SMOOTH_SPP (0.9F) // SPP smoothing for stagnation control
#define SPP_CURRENT_SPP (0.1F) // Current SPP weighting for stagnation control
#define SPP_STAGNATION_CAP (0.99F) // Maximum SPP value to prevent locking
/* --------------------------------------------------------------- */
/* ------------------- Denoiser configurations ------------------- */
/* --------------------------------------------------------------- */
// STFT configurations - Frame size in milliseconds
#define OVERLAP_FACTOR_GENERAL 4
#define INPUT_WINDOW_TYPE_GENERAL HANN_WINDOW
#define OUTPUT_WINDOW_TYPE_GENERAL HANN_WINDOW
// Fft configuration
#define PADDING_CONFIGURATION_GENERAL NO_PADDING
#define ZEROPADDING_AMOUNT_GENERAL 50 // Even Number
// Spectral Type
#define SPECTRAL_TYPE_GENERAL POWER_SPECTRUM
// Transient protection
#define UPPER_LIMIT (5.F)
#define DEFAULT_TRANSIENT_THRESHOLD (2.F)
// Masking
#define CRITICAL_BANDS_TYPE OPUS_SCALE
// Noise Estimator
#define MIN_NUMBER_OF_WINDOWS_NOISE_AVERAGED 5
#define NUMBER_OF_MEDIAN_SPECTRUM 5
// Noise Scaling strategy
#define NOISE_SCALING_TYPE_GENERAL MASKING_THRESHOLDS
#define GAIN_ESTIMATION_TYPE WIENER
// Time Smoothing
#define TIME_SMOOTHING_TYPE FIXED
// Postfilter
#define POSTFILTER_ENABLED_GENERAL true
// Whitening
#define WHITENING_ENABLED_GENERAL true
/* ------------------------------------------------------------------------ */
/* ------------------- Adaptive Denoiser configurations ------------------- */
/* ------------------------------------------------------------------------ */
// STFT configurations - Frame size in milliseconds
#define OVERLAP_FACTOR_SPEECH 2
#define INPUT_WINDOW_TYPE_SPEECH VORBIS_WINDOW
#define OUTPUT_WINDOW_TYPE_SPEECH VORBIS_WINDOW
// Fft configurations
#define PADDING_CONFIGURATION_SPEECH NO_PADDING
#define ZEROPADDING_AMOUNT_SPEECH 50 // Even Number
// Spectral Type
#define SPECTRAL_TYPE_SPEECH POWER_SPECTRUM
// Masking
#define CRITICAL_BANDS_TYPE_SPEECH OPUS_SCALE
// Noise Scaling strategy
#define NOISE_SCALING_TYPE_SPEECH MASKING_THRESHOLDS
#define GAIN_ESTIMATION_TYPE_SPEECH WIENER
// Time Smoothing
#define TIME_SMOOTHING_TYPE_SPEECH FIXED
// Postfilter
#define POSTFILTER_ENABLED_SPEECH true
// Whitening
#define WHITENING_ENABLED_SPEECH true
#endif // ifndef
@@ -0,0 +1,130 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "gain_estimators.h"
#include "../configurations.h"
#include "../utils/general_utils.h"
#include <float.h>
#include <math.h>
#include <stddef.h>
static void wiener_subtraction(const uint32_t real_spectrum_size,
const uint32_t fft_size, const float* spectrum,
const float* noise_spectrum,
float* gain_spectrum) {
for (uint32_t k = 0U; k < real_spectrum_size; k++) {
if (noise_spectrum[k] > FLT_MIN) {
if (spectrum[k] > noise_spectrum[k]) {
gain_spectrum[k] = (spectrum[k] - (noise_spectrum[k])) / spectrum[k];
} else {
gain_spectrum[k] = 0.F;
}
} else {
gain_spectrum[k] = 1.F;
}
if (k > 0U && k < (fft_size - k)) {
gain_spectrum[fft_size - k] = gain_spectrum[k];
}
}
}
static void spectral_gating(const uint32_t real_spectrum_size,
const uint32_t fft_size, const float* spectrum,
const float* noise_spectrum, float* gain_spectrum) {
for (uint32_t k = 0U; k < real_spectrum_size; k++) {
if (noise_spectrum[k] > FLT_MIN) {
if (spectrum[k] >= noise_spectrum[k]) {
gain_spectrum[k] = 1.F;
} else {
gain_spectrum[k] = 0.F;
}
} else {
gain_spectrum[k] = 1.F;
}
if (k > 0U && k < (fft_size - k)) {
gain_spectrum[fft_size - k] = gain_spectrum[k];
}
}
}
static void generalized_spectral_subtraction(
const uint32_t real_spectrum_size, const uint32_t fft_size,
const float* spectrum, const float* noise_spectrum, float* gain_spectrum,
const float* alpha, const float* beta) {
for (uint32_t k = 0U; k < real_spectrum_size; k++) {
if (spectrum[k] > FLT_MIN) {
if (powf((noise_spectrum[k] / spectrum[k]), GSS_EXPONENT) <
(1.F / (alpha[k] + beta[k]))) {
gain_spectrum[k] =
fmaxf(powf(1.F - (alpha[k] * powf((noise_spectrum[k] / spectrum[k]),
GSS_EXPONENT)),
1.F / GSS_EXPONENT),
0.F);
} else {
gain_spectrum[k] = fmaxf(
powf(
beta[k] * powf((noise_spectrum[k] / spectrum[k]), GSS_EXPONENT),
1.F / GSS_EXPONENT),
0.F);
}
} else {
gain_spectrum[k] = 1.F;
}
if (k > 0U && k < (fft_size - k)) {
gain_spectrum[fft_size - k] = gain_spectrum[k];
}
}
}
static void scale_noise_profile(uint32_t real_spectrum_size,
float* noise_spectrum, const float* alpha) {
for (uint32_t k = 0U; k < real_spectrum_size; k++) {
noise_spectrum[k] *= alpha[k];
}
}
void estimate_gains(uint32_t real_spectrum_size, uint32_t fft_size,
const float* spectrum, float* noise_spectrum,
float* gain_spectrum, const float* alpha, const float* beta,
GainEstimationType type) {
switch (type) {
case GATES:
scale_noise_profile(real_spectrum_size, noise_spectrum, alpha);
spectral_gating(real_spectrum_size, fft_size, spectrum, noise_spectrum,
gain_spectrum);
break;
case WIENER:
scale_noise_profile(real_spectrum_size, noise_spectrum, alpha);
wiener_subtraction(real_spectrum_size, fft_size, spectrum, noise_spectrum,
gain_spectrum);
break;
case GENERALIZED_SPECTRALSUBTRACION:
generalized_spectral_subtraction(real_spectrum_size, fft_size, spectrum,
noise_spectrum, gain_spectrum, alpha,
beta);
break;
default:
break;
}
}
@@ -0,0 +1,38 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef GAIN_ESTIMATORS_H
#define GAIN_ESTIMATORS_H
#include <stdbool.h>
#include <stdint.h>
typedef enum GainEstimationType {
WIENER = 0,
GATES = 1,
GENERALIZED_SPECTRALSUBTRACION = 2,
} GainEstimationType;
void estimate_gains(uint32_t real_spectrum_size, uint32_t fft_size,
const float* spectrum, float* noise_spectrum,
float* gain_spectrum, const float* alpha, const float* beta,
GainEstimationType type);
#endif
@@ -0,0 +1,3 @@
shared_sources += files(
'gain_estimators.c',
)
+7
View File
@@ -0,0 +1,7 @@
shared_sources = []
subdir('gain_estimation')
subdir('noise_estimation')
subdir('post_estimation')
subdir('pre_estimation')
subdir('stft')
subdir('utils')
@@ -0,0 +1,401 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "adaptive_noise_estimator.h"
#include "../configurations.h"
#include "../utils/general_utils.h"
#include "../utils/spectral_utils.h"
#include <float.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
typedef struct FrameSpectrum {
float* smoothed_spectrum;
float* local_minimum_spectrum;
float* speech_present_probability_spectrum;
} FrameSpectrum;
static FrameSpectrum* frame_spectrum_initialize(uint32_t frame_size);
static void frame_spectrum_free(FrameSpectrum* self);
static void compute_auto_thresholds(AdaptiveNoiseEstimator* self,
uint32_t sample_rate,
uint32_t noise_spectrum_size,
uint32_t fft_size);
static void update_frame_spectums(AdaptiveNoiseEstimator* self,
const float* noise_spectrum);
// SPP-MMSE helper functions
static float compute_spp_probability(float observation_power,
float previous_noise_psd);
static float compute_mmse_noise_estimate(float spp_h1, float spp_h0,
float observation_power,
float previous_noise_psd);
struct AdaptiveNoiseEstimator {
uint32_t noise_spectrum_size;
float noisy_speech_ratio;
FrameSpectrum* current;
FrameSpectrum* previous;
float* minimum_detection_thresholds;
float* previous_noise_spectrum;
float* time_frequency_smoothing_constant;
uint32_t* speech_presence_detection;
bool is_first_frame;
// SPP-MMSE specific fields (optional, used when SPP method is selected)
float* spp_previous_noise_psd; // Previous noise PSD estimate
float* spp_smoothed_spp; // Smoothed SPP for stagnation control
};
// SPP-MMSE helper function implementations
static float compute_spp_probability(float observation_power,
float previous_noise_psd) {
// Avoid division by zero and ensure numerical stability
if (previous_noise_psd < 1e-12F) {
previous_noise_psd = 1e-12F;
}
// Compute the exponent: -(|y|^2 / σ_N²(l-1)) * (ξ_H1 / (1 + ξ_H1))
float ratio = observation_power / previous_noise_psd;
float exponent = -ratio * (SPP_FIXED_XI_H1 / (1.F + SPP_FIXED_XI_H1));
// Compute exp(exponent) with numerical stability check
float exp_term = expf(exponent);
if (!isfinite(exp_term)) {
exp_term = (exponent > 0.F) ? FLT_MAX : 0.F;
}
// Compute the ratio: P(H0)/P(H1) * (1 + ξ_H1) * exp(...)
// Since P(H0) = P(H1) = 0.5, P(H0)/P(H1) = 1
float denominator_ratio = (1.F + SPP_FIXED_XI_H1) * exp_term;
// Compute SPP: 1 / (1 + denominator_ratio)
float spp = 1.F / (1.F + denominator_ratio);
// Ensure SPP is in valid range [0, 1]
spp = fmaxf(0.F, fminf(1.F, spp));
return spp;
}
static float compute_mmse_noise_estimate(float spp_h1, float spp_h0,
float observation_power,
float previous_noise_psd) {
// MMSE estimate: E{|N|²|y} = P(H0|y) * |y|² + P(H1|y) * σ_N²(l-1)
return (spp_h0 * observation_power) + (spp_h1 * previous_noise_psd);
}
AdaptiveNoiseEstimator* louizou_estimator_initialize(
const uint32_t noise_spectrum_size, const uint32_t sample_rate,
const uint32_t fft_size) {
AdaptiveNoiseEstimator* self =
(AdaptiveNoiseEstimator*)calloc(1U, sizeof(AdaptiveNoiseEstimator));
if (!self) {
return NULL;
}
self->noise_spectrum_size = noise_spectrum_size;
self->minimum_detection_thresholds =
(float*)calloc(self->noise_spectrum_size, sizeof(float));
self->time_frequency_smoothing_constant =
(float*)calloc(self->noise_spectrum_size, sizeof(float));
self->speech_presence_detection =
(uint32_t*)calloc(self->noise_spectrum_size, sizeof(uint32_t));
self->previous_noise_spectrum =
(float*)calloc(self->noise_spectrum_size, sizeof(float));
self->spp_previous_noise_psd =
(float*)calloc(self->noise_spectrum_size, sizeof(float));
self->spp_smoothed_spp =
(float*)calloc(self->noise_spectrum_size, sizeof(float));
if (!self->minimum_detection_thresholds ||
!self->time_frequency_smoothing_constant ||
!self->speech_presence_detection || !self->previous_noise_spectrum ||
!self->spp_previous_noise_psd || !self->spp_smoothed_spp) {
louizou_estimator_free(self);
return NULL;
}
compute_auto_thresholds(self, sample_rate, noise_spectrum_size, fft_size);
self->current = frame_spectrum_initialize(noise_spectrum_size);
self->previous = frame_spectrum_initialize(noise_spectrum_size);
if (!self->current || !self->previous) {
louizou_estimator_free(self);
return NULL;
}
self->noisy_speech_ratio = 0.F;
self->is_first_frame = true;
return self;
}
AdaptiveNoiseEstimator* spp_mmse_estimator_initialize(
const uint32_t noise_spectrum_size, const uint32_t sample_rate,
const uint32_t fft_size) {
AdaptiveNoiseEstimator* self =
(AdaptiveNoiseEstimator*)calloc(1U, sizeof(AdaptiveNoiseEstimator));
if (!self) {
return NULL;
}
self->noise_spectrum_size = noise_spectrum_size;
self->minimum_detection_thresholds =
(float*)calloc(self->noise_spectrum_size, sizeof(float));
self->time_frequency_smoothing_constant =
(float*)calloc(self->noise_spectrum_size, sizeof(float));
self->speech_presence_detection =
(uint32_t*)calloc(self->noise_spectrum_size, sizeof(uint32_t));
self->previous_noise_spectrum =
(float*)calloc(self->noise_spectrum_size, sizeof(float));
self->spp_previous_noise_psd =
(float*)calloc(self->noise_spectrum_size, sizeof(float));
self->spp_smoothed_spp =
(float*)calloc(self->noise_spectrum_size, sizeof(float));
if (!self->minimum_detection_thresholds ||
!self->time_frequency_smoothing_constant ||
!self->speech_presence_detection || !self->previous_noise_spectrum ||
!self->spp_previous_noise_psd || !self->spp_smoothed_spp) {
spp_mmse_estimator_free(self);
return NULL;
}
compute_auto_thresholds(self, sample_rate, noise_spectrum_size, fft_size);
self->current = frame_spectrum_initialize(noise_spectrum_size);
self->previous = frame_spectrum_initialize(noise_spectrum_size);
if (!self->current || !self->previous) {
spp_mmse_estimator_free(self);
return NULL;
}
self->noisy_speech_ratio = 0.F;
self->is_first_frame = true;
return self;
}
void spp_mmse_estimator_free(AdaptiveNoiseEstimator* self) {
louizou_estimator_free(self); // Reuse the same cleanup logic
}
void louizou_estimator_free(AdaptiveNoiseEstimator* self) {
if (!self) {
return;
}
free(self->minimum_detection_thresholds);
free(self->time_frequency_smoothing_constant);
free(self->speech_presence_detection);
free(self->previous_noise_spectrum);
free(self->spp_previous_noise_psd);
free(self->spp_smoothed_spp);
frame_spectrum_free(self->current);
frame_spectrum_free(self->previous);
free(self);
}
bool louizou_estimator_run(AdaptiveNoiseEstimator* self, const float* spectrum,
float* noise_spectrum) {
if (!self || !spectrum || !noise_spectrum) {
return false;
}
if (self->is_first_frame) {
for (uint32_t k = 0U; k < self->noise_spectrum_size; k++) {
self->current->smoothed_spectrum[k] = spectrum[k];
self->current->local_minimum_spectrum[k] = spectrum[k];
noise_spectrum[k] = spectrum[k];
}
self->is_first_frame = false;
} else {
for (uint32_t k = 0U; k < self->noise_spectrum_size; k++) {
self->current->smoothed_spectrum[k] =
(N_SMOOTH * self->previous->smoothed_spectrum[k]) +
((1.F - N_SMOOTH) * spectrum[k]);
if (self->previous->local_minimum_spectrum[k] <
self->current->smoothed_spectrum[k]) {
self->current->local_minimum_spectrum[k] =
(GAMMA * self->previous->local_minimum_spectrum[k]) +
(((1.F - GAMMA) / (1.F - BETA_AT)) *
(self->current->smoothed_spectrum[k] -
(BETA_AT * self->previous->smoothed_spectrum[k])));
} else {
self->current->local_minimum_spectrum[k] =
self->current->smoothed_spectrum[k];
}
self->noisy_speech_ratio = sanitize_denormal(
self->current->smoothed_spectrum[k] /
(self->current->local_minimum_spectrum[k] + 1e-12F));
if (self->noisy_speech_ratio > self->minimum_detection_thresholds[k]) {
self->speech_presence_detection[k] = 1U;
} else {
self->speech_presence_detection[k] = 0U;
}
self->current->speech_present_probability_spectrum[k] =
(ALPHA_P * self->previous->speech_present_probability_spectrum[k]) +
((1.F - ALPHA_P) * (float)self->speech_presence_detection[k]);
self->time_frequency_smoothing_constant[k] =
ALPHA_D + ((1.F - ALPHA_D) *
self->current->speech_present_probability_spectrum[k]);
noise_spectrum[k] =
(self->time_frequency_smoothing_constant[k] *
self->previous_noise_spectrum[k]) +
((1.F - self->time_frequency_smoothing_constant[k]) * spectrum[k]);
}
}
update_frame_spectums(self, noise_spectrum);
return true;
}
bool spp_mmse_estimator_run(AdaptiveNoiseEstimator* self, const float* spectrum,
float* noise_spectrum) {
if (!self || !spectrum || !noise_spectrum) {
return false;
}
if (self->is_first_frame) {
// Initialize with first frame (assume noise-only)
for (uint32_t k = 0U; k < self->noise_spectrum_size; k++) {
self->spp_previous_noise_psd[k] = spectrum[k];
self->spp_smoothed_spp[k] = 0.F; // Initialize smoothed SPP to 0
noise_spectrum[k] = spectrum[k];
}
self->is_first_frame = false;
} else {
for (uint32_t k = 0U; k < self->noise_spectrum_size; k++) {
// Step 1: Compute A Posteriori Speech Presence Probability
float spp_h1 =
compute_spp_probability(spectrum[k], self->spp_previous_noise_psd[k]);
// Step 2: Apply stagnation control
// If smoothed SPP > 0.99, cap current SPP at 0.99 to allow noise update
if (self->spp_smoothed_spp[k] > SPP_STAGNATION_CAP) {
spp_h1 = fminf(spp_h1, SPP_STAGNATION_CAP);
}
float spp_h0 = 1.F - spp_h1;
// Step 3: Compute MMSE noise periodogram estimate
float mmse_noise_estimate = compute_mmse_noise_estimate(
spp_h1, spp_h0, spectrum[k], self->spp_previous_noise_psd[k]);
// Step 4: Temporal smoothing
// σ_N²(l) = α_pow * σ_N²(l-1) + (1 - α_pow) * E{|N|²|y}
noise_spectrum[k] = (SPP_ALPHA_POW * self->spp_previous_noise_psd[k]) +
((1.F - SPP_ALPHA_POW) * mmse_noise_estimate);
// Step 5: Update smoothed SPP for next frame's stagnation control
// P̄(l) = 0.9 * P̄(l-1) + 0.1 * P(H1|y)
self->spp_smoothed_spp[k] = (SPP_SMOOTH_SPP * self->spp_smoothed_spp[k]) +
(SPP_CURRENT_SPP * spp_h1);
// Step 6: Store current noise estimate for next frame
self->spp_previous_noise_psd[k] = noise_spectrum[k];
}
}
// Update frame spectrums (reuse existing infrastructure for compatibility)
update_frame_spectums(self, noise_spectrum);
return true;
}
static void update_frame_spectums(AdaptiveNoiseEstimator* self,
const float* noise_spectrum) {
memcpy(self->previous_noise_spectrum, noise_spectrum,
sizeof(float) * self->noise_spectrum_size);
memcpy(self->previous->local_minimum_spectrum,
self->current->local_minimum_spectrum,
sizeof(float) * self->noise_spectrum_size);
memcpy(self->previous->smoothed_spectrum, self->current->smoothed_spectrum,
sizeof(float) * self->noise_spectrum_size);
memcpy(self->previous->speech_present_probability_spectrum,
self->current->speech_present_probability_spectrum,
sizeof(float) * self->noise_spectrum_size);
}
static FrameSpectrum* frame_spectrum_initialize(const uint32_t frame_size) {
FrameSpectrum* self = (FrameSpectrum*)calloc(1U, sizeof(FrameSpectrum));
if (!self) {
return NULL;
}
self->smoothed_spectrum = (float*)calloc(frame_size, sizeof(float));
self->local_minimum_spectrum = (float*)calloc(frame_size, sizeof(float));
self->speech_present_probability_spectrum =
(float*)calloc(frame_size, sizeof(float));
if (!self->smoothed_spectrum || !self->local_minimum_spectrum ||
!self->speech_present_probability_spectrum) {
frame_spectrum_free(self);
return NULL;
}
(void)initialize_spectrum_with_value(self->local_minimum_spectrum, frame_size,
FLT_MIN);
return self;
}
static void frame_spectrum_free(FrameSpectrum* self) {
if (!self) {
return;
}
free(self->smoothed_spectrum);
free(self->local_minimum_spectrum);
free(self->speech_present_probability_spectrum);
free(self);
}
static void compute_auto_thresholds(AdaptiveNoiseEstimator* self,
const uint32_t sample_rate,
const uint32_t noise_spectrum_size,
const uint32_t fft_size) {
uint32_t lf = freq_to_fft_bin(CROSSOVER_POINT1, sample_rate, fft_size);
uint32_t mf = freq_to_fft_bin(CROSSOVER_POINT2, sample_rate, fft_size);
for (uint32_t k = 0U; k < noise_spectrum_size; k++) {
if (k <= lf) {
self->minimum_detection_thresholds[k] = BAND_1_LEVEL;
}
if (k > lf && k < mf) {
self->minimum_detection_thresholds[k] = BAND_2_LEVEL;
}
if (k >= mf) {
self->minimum_detection_thresholds[k] = BAND_3_LEVEL;
}
}
}
@@ -0,0 +1,48 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef ADAPTIVE_NOISE_ESTIMATOR_H
#define ADAPTIVE_NOISE_ESTIMATOR_H
#include <stdbool.h>
#include <stdint.h>
typedef enum AdaptiveNoiseEstimationMethod {
LOUIZOU_METHOD = 0, // Original minimum statistics method (default)
SPP_MMSE_METHOD = 1, // Speech Presence Probability - MMSE method
} AdaptiveNoiseEstimationMethod;
typedef struct AdaptiveNoiseEstimator AdaptiveNoiseEstimator;
AdaptiveNoiseEstimator* louizou_estimator_initialize(
uint32_t noise_spectrum_size, uint32_t sample_rate, uint32_t fft_size);
void louizou_estimator_free(AdaptiveNoiseEstimator* self);
bool louizou_estimator_run(AdaptiveNoiseEstimator* self, const float* spectrum,
float* noise_spectrum);
// SPP-MMSE based adaptive noise estimator (Real-Time Unbiased MMSE Noise PSD
// Tracking)
AdaptiveNoiseEstimator* spp_mmse_estimator_initialize(
uint32_t noise_spectrum_size, uint32_t sample_rate, uint32_t fft_size);
void spp_mmse_estimator_free(AdaptiveNoiseEstimator* self);
bool spp_mmse_estimator_run(AdaptiveNoiseEstimator* self, const float* spectrum,
float* noise_spectrum);
#endif
@@ -0,0 +1,5 @@
shared_sources += files(
'adaptive_noise_estimator.c',
'noise_estimator.c',
'noise_profile.c',
)
@@ -0,0 +1,110 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "noise_estimator.h"
#include "../configurations.h"
#include "../utils/spectral_trailing_buffer.h"
#include "../utils/spectral_utils.h"
#include <stdlib.h>
#include <string.h>
struct NoiseEstimator {
uint32_t fft_size;
uint32_t real_spectrum_size;
SpectralTrailingBuffer* median_buffer;
NoiseProfile* noise_profile;
};
NoiseEstimator* noise_estimation_initialize(const uint32_t fft_size,
NoiseProfile* noise_profile) {
NoiseEstimator* self = (NoiseEstimator*)calloc(1U, sizeof(NoiseEstimator));
if (!self) {
return NULL;
}
self->fft_size = fft_size;
self->real_spectrum_size = (self->fft_size / 2U) + 1U;
self->noise_profile = noise_profile;
self->median_buffer = spectral_trailing_buffer_initialize(
self->real_spectrum_size, NUMBER_OF_MEDIAN_SPECTRUM);
if (!self->median_buffer) {
noise_estimation_free(self);
return NULL;
}
return self;
}
void noise_estimation_free(NoiseEstimator* self) {
if (!self) {
return;
}
// Don't free noise profile used as reference here
spectral_trailing_buffer_free(self->median_buffer);
free(self);
}
bool noise_estimation_run(NoiseEstimator* self,
const NoiseEstimatorType noise_estimator_type,
float* signal_spectrum) {
if (!self || !signal_spectrum) {
return false;
}
float* noise_profile =
get_noise_profile(self->noise_profile, noise_estimator_type);
switch (noise_estimator_type) {
case ROLLING_MEAN:
get_rolling_mean_spectrum(noise_profile, signal_spectrum,
get_noise_profile_blocks_averaged(
self->noise_profile, noise_estimator_type),
self->real_spectrum_size);
increment_blocks_averaged(self->noise_profile, noise_estimator_type);
break;
case MEDIAN:
spectral_trailing_buffer_push_back(self->median_buffer, signal_spectrum);
bool is_valid_median = get_rolling_median_spectrum(
noise_profile, get_trailing_spectral_buffer(self->median_buffer),
get_spectrum_buffer_size(self->median_buffer),
get_spectrum_size(self->median_buffer));
if (is_valid_median) {
set_noise_profile_available(self->noise_profile, noise_estimator_type);
}
break;
case MAX:
(void)max_spectrum(noise_profile, signal_spectrum,
self->real_spectrum_size);
set_noise_profile_available(self->noise_profile, noise_estimator_type);
break;
default:
break;
}
return true;
}
@@ -0,0 +1,44 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef NOISE_ESTIMATOR_H
#define NOISE_ESTIMATOR_H
#include "noise_profile.h"
#include <stdbool.h>
#include <stdint.h>
typedef struct NoiseEstimator NoiseEstimator;
typedef enum NoiseEstimatorType {
OFF = 0,
ROLLING_MEAN = 1,
MEDIAN = 2,
MAX = 3,
} NoiseEstimatorType;
NoiseEstimator* noise_estimation_initialize(uint32_t fft_size,
NoiseProfile* noise_profile);
void noise_estimation_free(NoiseEstimator* self);
bool noise_estimation_run(NoiseEstimator* self,
NoiseEstimatorType noise_estimator_type,
float* signal_spectrum);
#endif
@@ -0,0 +1,142 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "noise_profile.h"
#include "../configurations.h"
#include "../utils/spectral_utils.h"
#include <stdlib.h>
#include <string.h>
struct NoiseProfile {
uint32_t noise_profile_size;
uint32_t noise_profile_blocks_averaged[NOISE_PROFILE_MODES];
float* noise_profiles[NOISE_PROFILE_MODES];
bool noise_spectrum_available[NOISE_PROFILE_MODES];
};
NoiseProfile* noise_profile_initialize(const uint32_t size) {
NoiseProfile* self = (NoiseProfile*)calloc(1U, sizeof(NoiseProfile));
if (!self) {
return NULL;
}
self->noise_profile_size = size;
for (int i = 0; i < NOISE_PROFILE_MODES; i++) {
self->noise_profile_blocks_averaged[i] = 0U;
self->noise_spectrum_available[i] = false;
self->noise_profiles[i] = (float*)calloc(size, sizeof(float));
if (!self->noise_profiles[i]) {
noise_profile_free(self);
return NULL;
}
}
return self;
}
void noise_profile_free(NoiseProfile* self) {
if (self) {
for (int i = 0; i < NOISE_PROFILE_MODES; i++) {
if (self->noise_profiles[i]) {
free(self->noise_profiles[i]);
}
}
free(self);
}
}
bool is_noise_estimation_available(NoiseProfile* self, int mode) {
if (mode < 1 || mode > 3) {
return false;
}
return self->noise_spectrum_available[mode - 1];
}
float* get_noise_profile(NoiseProfile* self, int mode) {
if (mode < 1 || mode > 3) {
return NULL;
}
return self->noise_profiles[mode - 1];
}
uint32_t get_noise_profile_size(NoiseProfile* self) {
return self->noise_profile_size;
}
uint32_t get_noise_profile_blocks_averaged(NoiseProfile* self, int mode) {
if (mode < 1 || mode > 3) {
return 0;
}
return self->noise_profile_blocks_averaged[mode - 1];
}
void set_noise_profile_available(NoiseProfile* self, int mode) {
if (mode >= 1 && mode <= 3) {
self->noise_spectrum_available[mode - 1] = true;
}
}
bool set_noise_profile(NoiseProfile* self, int mode, const float* noise_profile,
const uint32_t noise_profile_size,
const uint32_t noise_profile_blocks_averaged) {
if (!self || mode < 1 || mode > 3 || !noise_profile ||
noise_profile_size != self->noise_profile_size) {
return false;
}
int index = mode - 1;
memcpy(self->noise_profiles[index], noise_profile,
noise_profile_size * sizeof(float));
self->noise_profile_blocks_averaged[index] = noise_profile_blocks_averaged;
self->noise_spectrum_available[index] = true;
return true;
}
bool increment_blocks_averaged(NoiseProfile* self, int mode) {
if (!self || mode < 1 || mode > 3) {
return false;
}
int index = mode - 1;
self->noise_profile_blocks_averaged[index]++;
if (self->noise_profile_blocks_averaged[index] >
MIN_NUMBER_OF_WINDOWS_NOISE_AVERAGED &&
!self->noise_spectrum_available[index]) {
self->noise_spectrum_available[index] = true;
}
return true;
}
bool reset_noise_profile(NoiseProfile* self) {
if (!self) {
return false;
}
for (int i = 0; i < NOISE_PROFILE_MODES; i++) {
(void)initialize_spectrum_with_value(self->noise_profiles[i],
self->noise_profile_size, 0.F);
self->noise_profile_blocks_averaged[i] = 0U;
self->noise_spectrum_available[i] = false;
}
return true;
}
@@ -0,0 +1,44 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef NOISE_PROFILE_H
#define NOISE_PROFILE_H
#include <stdbool.h>
#include <stdint.h>
typedef struct NoiseProfile NoiseProfile;
#define NOISE_PROFILE_MODES \
3 // ROLLING_MEAN, MEDIAN, MAX (no OFF storage needed)
NoiseProfile* noise_profile_initialize(uint32_t size);
void noise_profile_free(NoiseProfile* self);
float* get_noise_profile(NoiseProfile* self, int mode);
uint32_t get_noise_profile_size(NoiseProfile* self);
uint32_t get_noise_profile_blocks_averaged(NoiseProfile* self, int mode);
bool increment_blocks_averaged(NoiseProfile* self, int mode);
bool set_noise_profile(NoiseProfile* self, int mode, const float* noise_profile,
uint32_t noise_profile_size, uint32_t averaged_blocks);
void set_noise_profile_available(NoiseProfile* self, int mode);
bool reset_noise_profile(NoiseProfile* self);
bool is_noise_estimation_available(NoiseProfile* self, int mode);
#endif
@@ -0,0 +1,5 @@
shared_sources += files(
'noise_floor_manager.c',
'spectral_whitening.c',
'postfilter.c',
)
@@ -0,0 +1,100 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "noise_floor_manager.h"
#include "spectral_whitening.h"
#include <stdlib.h>
#include <string.h>
struct NoiseFloorManager {
SpectralWhitening* whitening;
float* whitening_weights;
uint32_t real_spectrum_size;
};
NoiseFloorManager* noise_floor_manager_initialize(const uint32_t fft_size,
const uint32_t sample_rate,
const uint32_t hop) {
NoiseFloorManager* self =
(NoiseFloorManager*)calloc(1U, sizeof(NoiseFloorManager));
if (!self) {
return NULL;
}
self->real_spectrum_size = (fft_size / 2U) + 1U;
self->whitening = spectral_whitening_initialize(fft_size);
if (!self->whitening) {
free(self);
return NULL;
}
self->whitening_weights =
(float*)calloc(self->real_spectrum_size, sizeof(float));
if (!self->whitening_weights) {
spectral_whitening_free(self->whitening);
free(self);
return NULL;
}
return self;
}
void noise_floor_manager_free(NoiseFloorManager* self) {
if (!self) {
return;
}
if (self->whitening) {
spectral_whitening_free(self->whitening);
}
if (self->whitening_weights) {
free(self->whitening_weights);
}
free(self);
}
void noise_floor_manager_apply(NoiseFloorManager* self,
uint32_t real_spectrum_size, uint32_t fft_size,
float* gain_spectrum, const float* noise_profile,
float reduction_amount, float whitening_factor) {
if (!self || !gain_spectrum || !noise_profile) {
return;
}
// 1. Calculate whitening weights (including tapering)
spectral_whitening_get_weights(self->whitening, whitening_factor,
noise_profile, self->whitening_weights);
// 2. Apply biasing + frequency-dependent floor
for (uint32_t k = 0U; k < real_spectrum_size; k++) {
float floor = reduction_amount * self->whitening_weights[k];
if (floor > 1.0f) {
floor = 1.0f;
}
float range = 1.0f - floor;
gain_spectrum[k] = floor + (range * gain_spectrum[k]);
}
// 3. Symmetric copy
for (uint32_t k = 1U; k < fft_size - k; k++) {
gain_spectrum[fft_size - k] = gain_spectrum[k];
}
}
@@ -0,0 +1,40 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef NO_FLOOR_MANAGER_H
#define NO_FLOOR_MANAGER_H
#include <stdbool.h>
#include <stdint.h>
typedef struct NoiseFloorManager NoiseFloorManager;
NoiseFloorManager* noise_floor_manager_initialize(uint32_t fft_size,
uint32_t sample_rate,
uint32_t hop);
void noise_floor_manager_free(NoiseFloorManager* self);
void noise_floor_manager_apply(NoiseFloorManager* self,
uint32_t real_spectrum_size, uint32_t fft_size,
float* gain_spectrum, const float* noise_profile,
float reduction_amount, float whitening_factor);
#endif
@@ -0,0 +1,174 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "postfilter.h"
#include "../configurations.h"
#include <math.h>
#include <stdlib.h>
#include <string.h>
struct PostFilter {
float* intermediate_gains;
uint32_t fft_size;
uint32_t real_spectrum_size;
bool preserve_minimum;
float default_postfilter_scale;
float min_gain_coefficient;
};
PostFilter* postfilter_initialize(const uint32_t fft_size) {
PostFilter* self = (PostFilter*)calloc(1U, sizeof(PostFilter));
if (!self) {
return NULL;
}
self->fft_size = fft_size;
self->real_spectrum_size = (self->fft_size / 2U) + 1U;
self->preserve_minimum = (bool)PRESERVE_MINIMUN_GAIN;
self->default_postfilter_scale = POSTFILTER_SCALE;
self->min_gain_coefficient = powf(10.F, (float)POSTFILTER_MIN_GAIN_DB / 20.F);
self->intermediate_gains =
(float*)calloc(self->real_spectrum_size, sizeof(float));
if (!self->intermediate_gains) {
free(self);
return NULL;
}
return self;
}
void postfilter_free(PostFilter* self) {
if (!self) {
return;
}
free(self->intermediate_gains);
free(self);
}
static uint32_t get_adaptive_window_size(const PostFilter* self,
const float* spectrum,
const float snr_threshold,
const float* gain_spectrum) {
float clean_energy = 0.F;
float noisy_energy = 0.F;
for (uint32_t k = 0U; k < self->real_spectrum_size; k++) {
const float noisy = spectrum[k];
const float clean = noisy * gain_spectrum[k];
clean_energy += clean * clean;
noisy_energy += noisy * noisy;
}
if (noisy_energy <= 1e-12F) {
return 1U;
}
const float zeta = clean_energy / noisy_energy;
const float zeta_t = (zeta >= snr_threshold) ? 1.F : zeta;
if (zeta_t >= 1.F) {
return 1U;
}
const float n = (2.F * roundf(self->default_postfilter_scale *
(1.F - (zeta_t / snr_threshold)))) +
1.F;
return (uint32_t)n;
}
static void moving_average(const float* in, float* out, uint32_t size,
uint32_t n) {
if (n <= 1U || n > size) {
memcpy(out, in, size * sizeof(float));
return;
}
const uint32_t half = n / 2U;
double current_sum = 0.0;
// Initial window sum (boundary handling: use clamping for start)
for (int i = -(int)half; i <= (int)half; i++) {
int idx = i;
if (idx < 0) {
idx = 0;
}
if (idx >= (int)size) {
idx = (int)size - 1;
}
current_sum += (double)in[idx];
}
for (uint32_t i = 0U; i < size; i++) {
out[i] = (float)(current_sum / (double)n);
if (i + 1U < size) {
// Move window: subtract oldest, add newest
int old_idx = (int)i - (int)half;
int new_idx = (int)i + (int)half + 1;
if (old_idx < 0) {
old_idx = 0;
}
if (new_idx >= (int)size) {
new_idx = (int)size - 1;
}
current_sum -= (double)in[old_idx];
current_sum += (double)in[new_idx];
}
}
}
bool postfilter_apply(PostFilter* self, const float* spectrum,
float* gain_spectrum,
const PostFiltersParameters parameters) {
if (!self || !spectrum || !gain_spectrum) {
return false;
}
const uint32_t n = get_adaptive_window_size(
self, spectrum, parameters.snr_threshold, gain_spectrum);
if (n > 1U) {
moving_average(gain_spectrum, self->intermediate_gains,
self->real_spectrum_size, n);
if (self->preserve_minimum) {
for (uint32_t k = 0U; k < self->real_spectrum_size; k++) {
gain_spectrum[k] = fminf(gain_spectrum[k], self->intermediate_gains[k]);
}
} else {
memcpy(gain_spectrum, self->intermediate_gains,
self->real_spectrum_size * sizeof(float));
}
}
// Apply gain floor
for (uint32_t k = 0U; k < self->real_spectrum_size; k++) {
if (gain_spectrum[k] < parameters.gain_floor) {
gain_spectrum[k] = parameters.gain_floor;
}
}
return true;
}
@@ -0,0 +1,39 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef POSTFILTER_H
#define POSTFILTER_H
#include <stdbool.h>
#include <stdint.h>
typedef struct PostFilter PostFilter;
typedef struct PostFiltersParameters {
float snr_threshold;
float gain_floor;
} PostFiltersParameters;
PostFilter* postfilter_initialize(uint32_t fft_size);
void postfilter_free(PostFilter* self);
bool postfilter_apply(PostFilter* self, const float* spectrum,
float* gain_spectrum, PostFiltersParameters parameters);
#endif
Binary file not shown.
@@ -0,0 +1,97 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "spectral_whitening.h"
#include "../configurations.h"
#include "../utils/spectral_utils.h"
#include <math.h>
#include <stdlib.h>
#include <string.h>
struct SpectralWhitening {
float* tapering_window;
uint32_t fft_size;
uint32_t real_spectrum_size;
};
SpectralWhitening* spectral_whitening_initialize(const uint32_t fft_size) {
SpectralWhitening* self =
(SpectralWhitening*)calloc(1U, sizeof(SpectralWhitening));
if (!self) {
return NULL;
}
self->fft_size = fft_size;
self->real_spectrum_size = (self->fft_size / 2U) + 1U;
self->tapering_window =
(float*)calloc(self->real_spectrum_size, sizeof(float));
if (!self->tapering_window) {
free(self);
return NULL;
}
// Pre-calculate Right half of Hamming window for HF tapering
uint32_t n_samples = (self->real_spectrum_size * 2U) - 1U;
for (uint32_t k = 0U; k < self->real_spectrum_size; k++) {
uint32_t n = (k + self->real_spectrum_size) - 1U;
self->tapering_window[k] =
0.54f - (0.46f * cosf((2.0f * (float)M_PI * (float)n) /
(float)(n_samples - 1U)));
}
return self;
}
void spectral_whitening_free(SpectralWhitening* self) {
if (!self) {
return;
}
if (self->tapering_window) {
free(self->tapering_window);
}
free(self);
}
void spectral_whitening_get_weights(SpectralWhitening* self,
float whitening_factor,
const float* noise_profile,
float* weights_out) {
if (!self || !weights_out || !noise_profile) {
return;
}
float noise_peak = 1e-12f;
for (uint32_t k = 0U; k < self->real_spectrum_size; k++) {
if (noise_profile[k] > noise_peak) {
noise_peak = noise_profile[k];
}
}
for (uint32_t k = 0U; k < self->real_spectrum_size; k++) {
float weight = 1.0f;
if (whitening_factor > 0.0f && noise_profile[k] > 1e-12f) {
// Power-law valley filling
weight = powf(noise_peak / noise_profile[k], whitening_factor);
}
// Weights include tapering
weights_out[k] = weight * self->tapering_window[k];
}
}
@@ -0,0 +1,37 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef SPECTRAL_WHITENER_H
#define SPECTRAL_WHITENER_H
#include <stdint.h>
typedef struct SpectralWhitening SpectralWhitening;
SpectralWhitening* spectral_whitening_initialize(uint32_t fft_size);
void spectral_whitening_free(SpectralWhitening* self);
void spectral_whitening_get_weights(SpectralWhitening* self,
float whitening_factor,
const float* noise_profile,
float* weights_out);
#endif
@@ -0,0 +1,165 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "absolute_hearing_thresholds.h"
#include "../configurations.h"
#include "../stft/fft_transform.h"
#include "../utils/spectral_utils.h"
#include <math.h>
#include <stdlib.h>
#include <string.h>
static void generate_sinewave(AbsoluteHearingThresholds* self);
static void compute_spl_reference_spectrum(AbsoluteHearingThresholds* self);
static void compute_absolute_thresholds(AbsoluteHearingThresholds* self);
struct AbsoluteHearingThresholds {
float* sinewave;
float* window;
float* spl_reference_values;
float* absolute_thresholds;
SpectralFeatures* spectral_features;
FftTransform* fft_transform;
SpectrumType spectrum_type;
uint32_t fft_size;
uint32_t real_spectrum_size;
uint32_t sample_rate;
float sine_wave_amplitude;
float sine_wave_frequency;
float reference_level;
};
AbsoluteHearingThresholds* absolute_hearing_thresholds_initialize(
const uint32_t sample_rate, const uint32_t fft_size,
SpectrumType spectrum_type) {
AbsoluteHearingThresholds* self =
(AbsoluteHearingThresholds*)calloc(1U, sizeof(AbsoluteHearingThresholds));
if (!self) {
return NULL;
}
self->fft_size = fft_size;
self->real_spectrum_size = (self->fft_size / 2U) + 1U;
self->sample_rate = sample_rate;
self->spectrum_type = spectrum_type;
self->sine_wave_amplitude = SINE_AMPLITUDE;
self->sine_wave_frequency = REFERENCE_SINE_WAVE_FREQ;
self->reference_level = REFERENCE_LEVEL;
self->fft_transform = fft_transform_initialize_bins(self->fft_size);
self->spl_reference_values =
(float*)calloc(self->real_spectrum_size, sizeof(float));
self->absolute_thresholds =
(float*)calloc(self->real_spectrum_size, sizeof(float));
self->sinewave = (float*)calloc(self->fft_size, sizeof(float));
self->window = (float*)calloc(self->fft_size, sizeof(float));
self->spectral_features =
spectral_features_initialize(self->real_spectrum_size);
if (!self->fft_transform || !self->spl_reference_values ||
!self->absolute_thresholds || !self->sinewave || !self->window ||
!self->spectral_features) {
absolute_hearing_thresholds_free(self);
return NULL;
}
generate_sinewave(self);
(void)get_fft_window(self->window, self->fft_size, VORBIS_WINDOW);
compute_spl_reference_spectrum(self);
compute_absolute_thresholds(self);
return self;
}
void absolute_hearing_thresholds_free(AbsoluteHearingThresholds* self) {
if (!self) {
return;
}
fft_transform_free(self->fft_transform);
spectral_features_free(self->spectral_features);
free(self->sinewave);
free(self->window);
free(self->spl_reference_values);
free(self->absolute_thresholds);
free(self);
}
static void generate_sinewave(AbsoluteHearingThresholds* self) {
for (uint32_t k = 0U; k < self->fft_size; k++) {
self->sinewave[k] =
self->sine_wave_amplitude *
sinf((2.F * M_PIf * (float)k * self->sine_wave_frequency) /
(float)self->sample_rate);
}
}
static void compute_spl_reference_spectrum(AbsoluteHearingThresholds* self) {
for (uint32_t k = 0U; k < self->fft_size; k++) {
get_fft_input_buffer(self->fft_transform)[k] =
self->sinewave[k] * self->window[k];
}
compute_forward_fft(self->fft_transform);
float* reference_spectrum = get_spectral_feature(
self->spectral_features, get_fft_output_buffer(self->fft_transform),
self->fft_size, self->spectrum_type);
for (uint32_t k = 0U; k < self->real_spectrum_size; k++) {
self->spl_reference_values[k] =
self->reference_level - (10.F * log10f(reference_spectrum[k] + 1e-12F));
}
}
bool apply_thresholds_as_floor(AbsoluteHearingThresholds* self,
float* spectrum) {
if (!self || !spectrum) {
return false;
}
for (uint32_t k = 0U; k < self->real_spectrum_size; k++) {
const float spl_level =
(10.F * log10f(spectrum[k] + 1e-12F)) + self->spl_reference_values[k];
spectrum[k] =
powf(10.F, fmaxf(spl_level, self->absolute_thresholds[k]) / 10.F);
}
return true;
}
static void compute_absolute_thresholds(AbsoluteHearingThresholds* self) {
for (uint32_t k = 0U; k < self->real_spectrum_size; k++) {
const float frequency =
fmaxf(fft_bin_to_freq(k, self->sample_rate, self->fft_size), 20.F);
self->absolute_thresholds[k] =
(3.64F * powf((frequency / 1000.F), -0.8F)) -
(6.5F * expf(-0.6F * powf(((frequency / 1000.F) - 3.3F), 2.F))) +
(powf(10.F, -3.F) * powf((frequency / 1000.F), 4.F));
}
}
@@ -0,0 +1,36 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef ABSOLUTE_HEARING_THRESHOLDS_H
#define ABSOLUTE_HEARING_THRESHOLDS_H
#include "../utils/spectral_features.h"
#include <stdbool.h>
#include <stdint.h>
typedef struct AbsoluteHearingThresholds AbsoluteHearingThresholds;
AbsoluteHearingThresholds* absolute_hearing_thresholds_initialize(
uint32_t sample_rate, uint32_t fft_size, SpectrumType spectrum_type);
void absolute_hearing_thresholds_free(AbsoluteHearingThresholds* self);
bool apply_thresholds_as_floor(AbsoluteHearingThresholds* self,
float* spectrum);
#endif
@@ -0,0 +1,204 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "critical_bands.h"
#include "../utils/spectral_utils.h"
#include <stdlib.h>
#include <string.h>
static const float bark_bands[24] = {
100.F, 200.F, 300.F, 400.F, 510.F, 630.F, 770.F, 920.F,
1080.F, 1270.F, 1480.F, 1720.F, 2000.F, 2320.F, 2700.F, 3150.F,
3700.F, 4400.F, 5300.F, 6400.F, 7700.F, 9500.F, 12000.F, 15500.F};
static const float opus_bands[20] = {200.F, 400.F, 600.F, 800.F, 1000.F,
1200.F, 1400.F, 1600.F, 2000.F, 2400.F,
2800.F, 3200.F, 4000.F, 4800.F, 5600.F,
6800.F, 8000.F, 9600.F, 12000.F, 15600.F};
static const float mel_bands[33] = {
250.F, 500.F, 750.F, 1000.F, 1250.F, 1500.F, 1750.F, 2000.F,
2250.F, 2500.F, 2750.F, 3000.F, 3250.F, 3500.F, 3750.F, 4000.F,
4250.F, 4500.F, 4750.F, 5000.F, 5250.F, 5500.F, 5750.F, 6000.F,
6250.F, 6500.F, 6750.F, 7000.F, 7250.F, 7500.F, 7750.F, 8000.F};
static const float octave_bands[10] = {31.5F, 63.F, 125.F, 250.F, 500.F,
1000.F, 2000.F, 4000.F, 8000.F, 16000.F};
void set_number_of_bands(CriticalBands* self);
static void compute_mapping_spectrum(CriticalBands* self);
static void compute_band_indexes(CriticalBands* self);
static uint32_t get_last_valid_band_for_samplerate(CriticalBands* self,
uint32_t number_of_bands);
struct CriticalBands {
uint32_t* band_delimiter_bins;
uint32_t* number_bins_per_band;
float* current_critical_bands;
uint32_t fft_size;
uint32_t real_spectrum_size;
uint32_t sample_rate;
uint32_t number_bands;
CriticalBandType type;
CriticalBandIndexes band_indexes;
};
CriticalBands* critical_bands_initialize(const uint32_t sample_rate,
const uint32_t fft_size,
const CriticalBandType type) {
CriticalBands* self = (CriticalBands*)calloc(1U, sizeof(CriticalBands));
if (!self) {
return NULL;
}
self->fft_size = fft_size;
self->real_spectrum_size = (fft_size / 2U) + 1U;
self->sample_rate = sample_rate;
self->type = type;
compute_mapping_spectrum(self);
self->band_delimiter_bins =
(uint32_t*)calloc(self->number_bands, sizeof(uint32_t));
self->number_bins_per_band =
(uint32_t*)calloc(self->number_bands, sizeof(uint32_t));
if (!self->band_delimiter_bins || !self->number_bins_per_band) {
critical_bands_free(self);
return NULL;
}
compute_band_indexes(self);
return self;
}
void critical_bands_free(CriticalBands* self) {
if (!self) {
return;
}
free(self->band_delimiter_bins);
free(self->number_bins_per_band);
free(self);
}
static void compute_band_indexes(CriticalBands* self) {
for (uint32_t k = 0U; k < self->number_bands; k++) {
const uint32_t bin_index =
freq_to_fft_bin(self->current_critical_bands[k], self->sample_rate,
self->real_spectrum_size);
if (k == 0) {
self->number_bins_per_band[k] = bin_index; // Don't include DC bin
self->band_delimiter_bins[k] = bin_index;
} else if (k == self->number_bands - 1U) {
self->band_delimiter_bins[k] = self->real_spectrum_size;
self->number_bins_per_band[k] =
self->band_delimiter_bins[k] - self->band_delimiter_bins[k - 1];
} else {
self->number_bins_per_band[k] =
bin_index - self->band_delimiter_bins[k - 1];
self->band_delimiter_bins[k] = bin_index;
}
}
}
static void compute_mapping_spectrum(CriticalBands* self) {
switch (self->type) {
case BARK_SCALE: {
self->current_critical_bands = (float*)bark_bands;
uint32_t number_of_bark_bands = sizeof(bark_bands) / sizeof(float);
self->number_bands =
get_last_valid_band_for_samplerate(self, number_of_bark_bands);
break;
}
case MEL_SCALE: {
self->current_critical_bands = (float*)mel_bands;
uint32_t number_of_mel_bands = sizeof(mel_bands) / sizeof(float);
self->number_bands =
get_last_valid_band_for_samplerate(self, number_of_mel_bands);
break;
}
case OPUS_SCALE: {
self->current_critical_bands = (float*)opus_bands;
uint32_t number_of_opus_bands = sizeof(opus_bands) / sizeof(float);
self->number_bands =
get_last_valid_band_for_samplerate(self, number_of_opus_bands);
break;
}
case OCTAVE_SCALE: {
self->current_critical_bands = (float*)octave_bands;
uint32_t number_of_octave_bands = sizeof(opus_bands) / sizeof(float);
self->number_bands =
get_last_valid_band_for_samplerate(self, number_of_octave_bands);
break;
}
default:
break;
}
}
static uint32_t get_last_valid_band_for_samplerate(CriticalBands* self,
uint32_t number_of_bands) {
float nyquist_frequency = (float)self->sample_rate / 2.F;
uint32_t last_valid_band = 0U;
for (uint32_t i = 0; i < number_of_bands; i++) {
if (self->current_critical_bands[i] < nyquist_frequency) {
last_valid_band = i;
}
}
return last_valid_band;
}
bool compute_critical_bands_spectrum(CriticalBands* self, const float* spectrum,
float* critical_bands) {
if (!self || !spectrum || !critical_bands) {
return false;
}
memset(critical_bands, 0, self->number_bands * sizeof(float));
for (uint32_t j = 0U; j < self->number_bands; j++) {
self->band_indexes = get_band_indexes(self, j);
for (uint32_t k = self->band_indexes.start_position;
k < self->band_indexes.end_position; k++) {
critical_bands[j] += spectrum[k];
}
}
return true;
}
CriticalBandIndexes get_band_indexes(CriticalBands* self,
const uint32_t band_number) {
return (CriticalBandIndexes){
.start_position = self->band_delimiter_bins[band_number] -
self->number_bins_per_band[band_number],
.end_position = self->band_delimiter_bins[band_number],
};
}
uint32_t get_number_of_critical_bands(CriticalBands* self) {
return self->number_bands;
}
@@ -0,0 +1,50 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef CRITICAL_BANDS_H
#define CRITICAL_BANDS_H
#include <stdbool.h>
#include <stdint.h>
typedef struct CriticalBands CriticalBands;
typedef enum CriticalBandType {
BARK_SCALE = 0,
MEL_SCALE = 1,
OPUS_SCALE = 2,
OCTAVE_SCALE = 3,
} CriticalBandType;
typedef struct CriticalBandIndexes {
uint32_t start_position;
uint32_t end_position;
} CriticalBandIndexes;
CriticalBands* critical_bands_initialize(uint32_t sample_rate,
uint32_t fft_size,
CriticalBandType type);
void critical_bands_free(CriticalBands* self);
bool compute_critical_bands_spectrum(CriticalBands* self, const float* spectrum,
float* critical_bands);
CriticalBandIndexes get_band_indexes(CriticalBands* self, uint32_t band_number);
uint32_t get_number_of_critical_bands(CriticalBands* self);
#endif
@@ -0,0 +1,225 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "masking_estimator.h"
#include "../configurations.h"
#include "../utils/spectral_utils.h"
#include "absolute_hearing_thresholds.h"
#include "critical_bands.h"
#include <math.h>
#include <stdlib.h>
#include <string.h>
static void compute_spectral_spreading_function(MaskingEstimator* self);
static float compute_tonality_factor(MaskingEstimator* self,
const float* spectrum, uint32_t band);
struct MaskingEstimator {
uint32_t fft_size;
uint32_t real_spectrum_size;
uint32_t sample_rate;
uint32_t number_critical_bands;
AbsoluteHearingThresholds* reference_spectrum;
CriticalBands* critical_bands;
CriticalBandIndexes band_indexes;
float* spectral_spreading_function;
float* unity_gain_critical_bands_spectrum;
float* spreaded_unity_gain_critical_bands_spectrum;
float* threshold_j;
float* masking_offset;
float* spreaded_spectrum;
float* critical_bands_reference_spectrum;
};
MaskingEstimator* masking_estimation_initialize(const uint32_t fft_size,
const uint32_t sample_rate,
SpectrumType spectrum_type) {
MaskingEstimator* self =
(MaskingEstimator*)calloc(1U, sizeof(MaskingEstimator));
if (!self) {
return NULL;
}
self->fft_size = fft_size;
self->real_spectrum_size = (self->fft_size / 2U) + 1U;
self->sample_rate = sample_rate;
self->critical_bands = critical_bands_initialize(
self->sample_rate, self->fft_size, CRITICAL_BANDS_TYPE);
if (!self->critical_bands) {
masking_estimation_free(self);
return NULL;
}
self->number_critical_bands =
get_number_of_critical_bands(self->critical_bands);
self->spectral_spreading_function =
(float*)calloc(((size_t)self->number_critical_bands *
(size_t)self->number_critical_bands),
sizeof(float));
self->unity_gain_critical_bands_spectrum =
(float*)calloc(self->number_critical_bands, sizeof(float));
self->spreaded_unity_gain_critical_bands_spectrum =
(float*)calloc(self->number_critical_bands, sizeof(float));
self->threshold_j =
(float*)calloc(self->number_critical_bands, sizeof(float));
self->masking_offset =
(float*)calloc(self->number_critical_bands, sizeof(float));
self->spreaded_spectrum =
(float*)calloc(self->number_critical_bands, sizeof(float));
self->critical_bands_reference_spectrum =
(float*)calloc(self->number_critical_bands, sizeof(float));
self->reference_spectrum = absolute_hearing_thresholds_initialize(
self->sample_rate, self->fft_size, spectrum_type);
if (!self->spectral_spreading_function ||
!self->unity_gain_critical_bands_spectrum ||
!self->spreaded_unity_gain_critical_bands_spectrum ||
!self->threshold_j || !self->masking_offset || !self->spreaded_spectrum ||
!self->critical_bands_reference_spectrum || !self->reference_spectrum) {
masking_estimation_free(self);
return NULL;
}
compute_spectral_spreading_function(self);
(void)initialize_spectrum_with_value(self->unity_gain_critical_bands_spectrum,
self->number_critical_bands, 1.F);
(void)direct_matrix_to_vector_spectral_convolution(
self->spectral_spreading_function,
self->unity_gain_critical_bands_spectrum,
self->spreaded_unity_gain_critical_bands_spectrum,
self->number_critical_bands);
return self;
}
void masking_estimation_free(MaskingEstimator* self) {
if (!self) {
return;
}
absolute_hearing_thresholds_free(self->reference_spectrum);
critical_bands_free(self->critical_bands);
free(self->spectral_spreading_function);
free(self->unity_gain_critical_bands_spectrum);
free(self->spreaded_unity_gain_critical_bands_spectrum);
free(self->threshold_j);
free(self->masking_offset);
free(self->spreaded_spectrum);
free(self->critical_bands_reference_spectrum);
free(self);
}
bool compute_masking_thresholds(MaskingEstimator* self, const float* spectrum,
float* masking_thresholds) {
if (!self || !spectrum || !masking_thresholds) {
return false;
}
compute_critical_bands_spectrum(self->critical_bands, spectrum,
self->critical_bands_reference_spectrum);
(void)direct_matrix_to_vector_spectral_convolution(
self->spectral_spreading_function,
self->critical_bands_reference_spectrum, self->spreaded_spectrum,
self->number_critical_bands);
for (uint32_t j = 0U; j < self->number_critical_bands; j++) {
const float tonality_factor = compute_tonality_factor(self, spectrum, j);
self->masking_offset[j] = (tonality_factor * (14.5F + (float)(j + 1))) +
(5.5F * (1.F - tonality_factor));
#if BIAS
self->masking_offset[j] = relative_thresholds[j];
if (j > 15) {
self->masking_offset[j] += HIGH_FREQ_BIAS;
}
#endif
self->threshold_j[j] = powf(
10.F, (log10f(self->spreaded_spectrum[j] + 1e-12F) -
(self->masking_offset[j] / 10.F) -
log10f(self->spreaded_unity_gain_critical_bands_spectrum[j] +
1e-12F)));
self->band_indexes = get_band_indexes(self->critical_bands, j);
for (uint32_t k = self->band_indexes.start_position;
k < self->band_indexes.end_position; k++) {
masking_thresholds[k] = self->threshold_j[j];
}
}
apply_thresholds_as_floor(self->reference_spectrum, masking_thresholds);
return true;
}
static void compute_spectral_spreading_function(MaskingEstimator* self) {
for (uint32_t i = 0U; i < self->number_critical_bands; i++) {
for (uint32_t j = 0U; j < self->number_critical_bands; j++) {
const uint32_t y = (i + 1) - (j + 1);
self->spectral_spreading_function[(i * self->number_critical_bands) + j] =
15.81F + (7.5F * ((float)y + 0.474F)) -
(17.5F * sqrtf(1.F + (((float)y + 0.474F) * ((float)y + 0.474F))));
self->spectral_spreading_function[(i * self->number_critical_bands) + j] =
powf(10.F, self->spectral_spreading_function
[(i * self->number_critical_bands) + j] /
10.F);
}
}
}
static float compute_tonality_factor(MaskingEstimator* self,
const float* spectrum, uint32_t band) {
float sum_bins = 0.F;
float sum_log_bins = 0.F;
self->band_indexes = get_band_indexes(self->critical_bands, band);
for (uint32_t k = self->band_indexes.start_position;
k < self->band_indexes.end_position; k++) {
const float val = fmaxf(spectrum[k], 1e-12F);
sum_bins += val;
sum_log_bins += log10f(val);
}
float bins_in_band = (float)self->band_indexes.end_position -
(float)self->band_indexes.start_position;
const float sfm =
(10.F * (sum_log_bins / bins_in_band)) - log10f(sum_bins / bins_in_band);
const float tonality_factor = fminf(sfm / -60.F, 1.F);
return tonality_factor;
}
@@ -0,0 +1,37 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef MASKING_ESTIMATOR_H
#define MASKING_ESTIMATOR_H
#include "../utils/spectral_features.h"
#include <stdbool.h>
#include <stdint.h>
typedef struct MaskingEstimator MaskingEstimator;
MaskingEstimator* masking_estimation_initialize(uint32_t fft_size,
uint32_t sample_rate,
SpectrumType spectrum_type);
void masking_estimation_free(MaskingEstimator* self);
bool compute_masking_thresholds(MaskingEstimator* self, const float* spectrum,
float* masking_thresholds);
#endif
@@ -0,0 +1,8 @@
shared_sources += files(
'absolute_hearing_thresholds.c',
'masking_estimator.c',
'critical_bands.c',
'noise_scaling_criterias.c',
'transient_detector.c',
'spectral_smoother.c',
)
@@ -0,0 +1,277 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "noise_scaling_criterias.h"
#include "../configurations.h"
#include "../utils/spectral_utils.h"
#include "critical_bands.h"
#include "masking_estimator.h"
#include <float.h>
#include <math.h>
#include <stdlib.h>
static void a_posteriori_snr_critical_bands(NoiseScalingCriterias* self,
const float* spectrum,
const float* noise_spectrum,
float* alpha,
NoiseScalingParameters parameters);
static void a_posteriori_snr(NoiseScalingCriterias* self, const float* spectrum,
const float* noise_spectrum, float* alpha,
NoiseScalingParameters parameters);
static void masking_thresholds(NoiseScalingCriterias* self,
const float* spectrum,
const float* noise_spectrum, float* alpha,
float* beta, NoiseScalingParameters parameters);
struct NoiseScalingCriterias {
NoiseScalingType noise_scaling_type;
uint32_t fft_size;
uint32_t real_spectrum_size;
uint32_t sample_rate;
SpectrumType spectrum_type;
uint32_t number_critical_bands;
float lower_snr;
float higher_snr;
float alpha_minimun;
float beta_minimun;
CriticalBandIndexes band_indexes;
CriticalBandType critical_band_type;
float* masking_thresholds;
float* clean_signal_estimation;
float* critical_bands_noise_profile;
float* critical_bands_reference_spectrum;
MaskingEstimator* masking_estimation;
CriticalBands* critical_bands;
};
NoiseScalingCriterias* noise_scaling_criterias_initialize(
const uint32_t fft_size, const CriticalBandType critical_band_type,
const uint32_t sample_rate, SpectrumType spectrum_type) {
NoiseScalingCriterias* self =
(NoiseScalingCriterias*)calloc(1U, sizeof(NoiseScalingCriterias));
if (!self) {
return NULL;
}
self->fft_size = fft_size;
self->real_spectrum_size = (self->fft_size / 2U) + 1U;
self->critical_band_type = critical_band_type;
self->sample_rate = sample_rate;
self->spectrum_type = spectrum_type;
self->lower_snr = LOWER_SNR;
self->higher_snr = HIGHER_SNR;
self->alpha_minimun = ALPHA_MIN;
self->beta_minimun = BETA_MIN;
self->critical_bands = critical_bands_initialize(
self->sample_rate, self->fft_size, self->critical_band_type);
self->masking_estimation = masking_estimation_initialize(
self->fft_size, self->sample_rate, self->spectrum_type);
if (!self->critical_bands || !self->masking_estimation) {
noise_scaling_criterias_free(self);
return NULL;
}
self->number_critical_bands =
get_number_of_critical_bands(self->critical_bands);
self->critical_bands_noise_profile =
(float*)calloc(self->number_critical_bands, sizeof(float));
self->critical_bands_reference_spectrum =
(float*)calloc(self->number_critical_bands, sizeof(float));
self->masking_thresholds =
(float*)calloc(self->real_spectrum_size, sizeof(float));
self->clean_signal_estimation =
(float*)calloc(self->real_spectrum_size, sizeof(float));
if (!self->critical_bands_noise_profile ||
!self->critical_bands_reference_spectrum || !self->masking_thresholds ||
!self->clean_signal_estimation) {
noise_scaling_criterias_free(self);
return NULL;
}
return self;
}
void noise_scaling_criterias_free(NoiseScalingCriterias* self) {
if (!self) {
return;
}
critical_bands_free(self->critical_bands);
masking_estimation_free(self->masking_estimation);
free(self->clean_signal_estimation);
free(self->masking_thresholds);
free(self->critical_bands_noise_profile);
free(self->critical_bands_reference_spectrum);
free(self);
}
bool apply_noise_scaling_criteria(NoiseScalingCriterias* self,
const float* spectrum,
const float* noise_spectrum, float* alpha,
float* beta,
NoiseScalingParameters parameters) {
if (!spectrum || !noise_spectrum) {
return false;
}
switch ((NoiseScalingType)parameters.scaling_type) {
case A_POSTERIORI_SNR:
a_posteriori_snr(self, spectrum, noise_spectrum, alpha, parameters);
break;
case A_POSTERIORI_SNR_CRITICAL_BANDS:
a_posteriori_snr_critical_bands(self, spectrum, noise_spectrum, alpha,
parameters);
break;
case MASKING_THRESHOLDS:
masking_thresholds(self, spectrum, noise_spectrum, alpha, beta,
parameters);
break;
case NO_SCALING:
for (uint32_t k = 0U; k < self->real_spectrum_size; k++) {
alpha[k] = self->alpha_minimun;
beta[k] = self->beta_minimun;
}
break;
default:
break;
}
return true;
}
static void a_posteriori_snr_critical_bands(NoiseScalingCriterias* self,
const float* spectrum,
const float* noise_spectrum,
float* alpha,
NoiseScalingParameters parameters) {
compute_critical_bands_spectrum(self->critical_bands, noise_spectrum,
self->critical_bands_noise_profile);
compute_critical_bands_spectrum(self->critical_bands, spectrum,
self->critical_bands_reference_spectrum);
float oversustraction_factor = 1.F;
for (uint32_t j = 0U; j < self->number_critical_bands; j++) {
self->band_indexes = get_band_indexes(self->critical_bands, j);
const float snr_db =
10.F * log10f(self->critical_bands_reference_spectrum[j] /
self->critical_bands_noise_profile[j]);
if (snr_db >= self->lower_snr && snr_db <= self->higher_snr) {
oversustraction_factor = (-0.05F * (snr_db)) + parameters.oversubtraction;
} else if (snr_db < 0.F) {
oversustraction_factor = parameters.oversubtraction;
} else if (snr_db > 20.F) {
oversustraction_factor = 1.F;
}
for (uint32_t k = self->band_indexes.start_position;
k < self->band_indexes.end_position; k++) {
alpha[k] = oversustraction_factor;
}
}
}
static void a_posteriori_snr(NoiseScalingCriterias* self, const float* spectrum,
const float* noise_spectrum, float* alpha,
NoiseScalingParameters parameters) {
float noisy_spectrum_sum = 0.F;
float noise_spectrum_sum = 0.F;
for (uint32_t k = 0U; k < self->real_spectrum_size; k++) {
noisy_spectrum_sum += spectrum[k];
noise_spectrum_sum += noise_spectrum[k];
}
const float snr_db = 10.F * log10f(noisy_spectrum_sum / noise_spectrum_sum);
float oversustraction_factor;
if (snr_db >= self->lower_snr && snr_db <= self->higher_snr) {
oversustraction_factor = (-0.05F * (snr_db)) + parameters.oversubtraction;
} else if (snr_db < 0.F) {
oversustraction_factor = parameters.oversubtraction;
} else {
oversustraction_factor = 1.F;
}
for (uint32_t k = 0U; k < self->real_spectrum_size; k++) {
alpha[k] = oversustraction_factor;
}
}
static void masking_thresholds(NoiseScalingCriterias* self,
const float* spectrum,
const float* noise_spectrum, float* alpha,
float* beta, NoiseScalingParameters parameters) {
for (uint32_t k = 0U; k < self->real_spectrum_size; k++) {
self->clean_signal_estimation[k] =
fmaxf(spectrum[k] - noise_spectrum[k], 0.F);
}
compute_masking_thresholds(self->masking_estimation,
self->clean_signal_estimation,
self->masking_thresholds);
float max_masked_value =
10.F * log10f(max_spectral_value(self->masking_thresholds,
self->real_spectrum_size) +
1e-12F);
float min_masked_value =
10.F * log10f(min_spectral_value(self->masking_thresholds,
self->real_spectrum_size) +
1e-12F);
for (uint32_t k = 0U; k < self->real_spectrum_size; k++) {
const float current_masked_value =
10.F * log10f(self->masking_thresholds[k] + 1e-12F);
if (current_masked_value >= max_masked_value) {
alpha[k] = self->alpha_minimun;
beta[k] = self->beta_minimun;
} else if (current_masked_value <= min_masked_value) {
alpha[k] = parameters.oversubtraction;
beta[k] = parameters.undersubtraction;
} else {
const float normalized_value = (current_masked_value - min_masked_value) /
(max_masked_value - min_masked_value);
alpha[k] = ((1.F - normalized_value) * parameters.oversubtraction) +
(normalized_value * self->alpha_minimun);
beta[k] = ((1.F - normalized_value) * parameters.undersubtraction) +
(normalized_value * self->beta_minimun);
}
}
}
@@ -0,0 +1,54 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef NOISE_SCALING_CRITERIAS_H
#define NOISE_SCALING_CRITERIAS_H
#include "../utils/spectral_features.h"
#include "critical_bands.h"
#include <stdbool.h>
#include <stdint.h>
typedef enum NoiseScalingType {
A_POSTERIORI_SNR = 0,
A_POSTERIORI_SNR_CRITICAL_BANDS = 1,
MASKING_THRESHOLDS = 2,
NO_SCALING = 3,
} NoiseScalingType;
typedef struct NoiseScalingParameters {
float undersubtraction;
float oversubtraction;
int scaling_type;
} NoiseScalingParameters;
typedef struct NoiseScalingCriterias NoiseScalingCriterias;
NoiseScalingCriterias* noise_scaling_criterias_initialize(
uint32_t fft_size, CriticalBandType critical_band_type,
uint32_t sample_rate, SpectrumType spectrum_type);
void noise_scaling_criterias_free(NoiseScalingCriterias* self);
bool apply_noise_scaling_criteria(NoiseScalingCriterias* self,
const float* spectrum,
const float* noise_spectrum, float* alpha,
float* beta,
NoiseScalingParameters parameters);
#endif
@@ -0,0 +1,146 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "spectral_smoother.h"
#include "transient_detector.h"
#include <math.h>
#include <stdlib.h>
#include <string.h>
static void spectrum_time_smoothing(SpectralSmoother* self, float smoothing);
static void spectrum_transient_aware_time_smoothing(SpectralSmoother* self,
float smoothing,
float* spectrum);
struct SpectralSmoother {
uint32_t fft_size;
uint32_t real_spectrum_size;
float adaptive_coefficient;
float previous_adaptive_coefficient;
TimeSmoothingType type;
float* noise_spectrum;
float* smoothed_spectrum;
float* smoothed_spectrum_previous;
TransientDetector* transient_detection;
};
SpectralSmoother* spectral_smoothing_initialize(const uint32_t fft_size,
TimeSmoothingType type) {
SpectralSmoother* self =
(SpectralSmoother*)calloc(1U, sizeof(SpectralSmoother));
if (!self) {
return NULL;
}
self->fft_size = fft_size;
self->real_spectrum_size = (self->fft_size / 2U) + 1U;
self->type = type;
self->previous_adaptive_coefficient = 0.F;
self->adaptive_coefficient = 0.F;
self->noise_spectrum =
(float*)calloc(self->real_spectrum_size, sizeof(float));
self->smoothed_spectrum =
(float*)calloc(self->real_spectrum_size, sizeof(float));
self->smoothed_spectrum_previous =
(float*)calloc(self->real_spectrum_size, sizeof(float));
self->transient_detection = transient_detector_initialize(self->fft_size);
if (!self->noise_spectrum || !self->smoothed_spectrum ||
!self->smoothed_spectrum_previous || !self->transient_detection) {
spectral_smoothing_free(self);
return NULL;
}
return self;
}
void spectral_smoothing_free(SpectralSmoother* self) {
if (!self) {
return;
}
transient_detector_free(self->transient_detection);
free(self->noise_spectrum);
free(self->smoothed_spectrum);
free(self->smoothed_spectrum_previous);
free(self);
}
bool spectral_smoothing_run(SpectralSmoother* self,
TimeSmoothingParameters parameters,
float* signal_spectrum) {
if (!self || !signal_spectrum) {
return false;
}
memcpy(self->smoothed_spectrum, signal_spectrum,
sizeof(float) * self->real_spectrum_size);
switch (self->type) {
case FIXED:
spectrum_time_smoothing(self, parameters.smoothing);
break;
case TRANSIENT_AWARE:
spectrum_transient_aware_time_smoothing(self, parameters.smoothing,
signal_spectrum);
break;
default:
break;
}
memcpy(self->smoothed_spectrum_previous, self->smoothed_spectrum,
sizeof(float) * self->real_spectrum_size);
memcpy(signal_spectrum, self->smoothed_spectrum,
sizeof(float) * self->real_spectrum_size);
return true;
}
static void spectrum_transient_aware_time_smoothing(SpectralSmoother* self,
const float smoothing,
float* spectrum) {
if (!transient_detector_run(self->transient_detection, spectrum)) {
for (uint32_t k = 0U; k < self->real_spectrum_size; k++) {
if (self->smoothed_spectrum[k] > self->smoothed_spectrum_previous[k]) {
self->smoothed_spectrum[k] =
(smoothing * self->smoothed_spectrum_previous[k]) +
((1.F - smoothing) * self->smoothed_spectrum[k]);
}
}
}
}
static void spectrum_time_smoothing(SpectralSmoother* self,
const float smoothing) {
for (uint32_t k = 0U; k < self->real_spectrum_size; k++) {
if (self->smoothed_spectrum[k] > self->smoothed_spectrum_previous[k]) {
self->smoothed_spectrum[k] =
(smoothing * self->smoothed_spectrum_previous[k]) +
((1.F - smoothing) * self->smoothed_spectrum[k]);
}
}
}
@@ -0,0 +1,46 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef SPECTRAL_SMOOTHER_H
#define SPECTRAL_SMOOTHER_H
#include <stdbool.h>
#include <stdint.h>
typedef enum TimeSmoothingType {
NO_SMOOTHING = 0,
FIXED = 1,
TRANSIENT_AWARE = 2,
} TimeSmoothingType;
typedef struct TimeSmoothingParameters {
float smoothing;
} TimeSmoothingParameters;
typedef struct SpectralSmoother SpectralSmoother;
SpectralSmoother* spectral_smoothing_initialize(uint32_t fft_size,
TimeSmoothingType type);
void spectral_smoothing_free(SpectralSmoother* self);
bool spectral_smoothing_run(SpectralSmoother* self,
TimeSmoothingParameters parameters,
float* signal_spectrum);
#endif
@@ -0,0 +1,97 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "transient_detector.h"
#include "../configurations.h"
#include "../utils/spectral_utils.h"
#include <math.h>
#include <stdlib.h>
#include <string.h>
struct TransientDetector {
uint32_t fft_size;
uint32_t real_spectrum_size;
float rolling_mean;
bool transient_present;
uint32_t window_count;
float* previous_spectrum;
};
TransientDetector* transient_detector_initialize(const uint32_t fft_size) {
TransientDetector* self =
(TransientDetector*)calloc(1U, sizeof(TransientDetector));
if (!self) {
return NULL;
}
self->fft_size = fft_size;
self->real_spectrum_size = (self->fft_size / 2U) + 1U;
self->previous_spectrum =
(float*)calloc(self->real_spectrum_size, sizeof(float));
if (!self->previous_spectrum) {
transient_detector_free(self);
return NULL;
}
self->window_count = 0U;
self->rolling_mean = 0.F;
self->transient_present = false;
return self;
}
void transient_detector_free(TransientDetector* self) {
if (!self) {
return;
}
free(self->previous_spectrum);
free(self);
}
bool transient_detector_run(TransientDetector* self, const float* spectrum) {
const float reduction_function = spectral_flux(
spectrum, self->previous_spectrum, self->real_spectrum_size);
self->window_count += 1U;
if (self->window_count > 1U) {
self->rolling_mean +=
((reduction_function - self->rolling_mean) / (float)self->window_count);
} else {
self->rolling_mean = reduction_function;
}
const float adapted_threshold =
((UPPER_LIMIT - DEFAULT_TRANSIENT_THRESHOLD) * self->rolling_mean) +
1e-6F;
memcpy(self->previous_spectrum, spectrum,
sizeof(float) * self->real_spectrum_size);
if (reduction_function > adapted_threshold) {
return true;
}
return false;
}
@@ -0,0 +1,33 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef TRANSIENT_DETECTOR_H
#define TRANSIENT_DETECTOR_H
#include <stdbool.h>
#include <stdint.h>
typedef struct TransientDetector TransientDetector;
TransientDetector* transient_detector_initialize(uint32_t fft_size);
void transient_detector_free(TransientDetector* self);
bool transient_detector_run(TransientDetector* self, const float* spectrum);
#endif
@@ -0,0 +1,36 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef SPECTRAL_PROCESSOR_H
#define SPECTRAL_PROCESSOR_H
#include <stdbool.h>
// Generic Spectral Processing function over an FFT spectrum. Receives any
// spectral processing module handle (void *) and the FFT of a audio block.
// This is to inject any spectral processor and processing function into the
// STFT transform at runtime
typedef void* SpectralProcessorHandle;
// Processing function which deals with the fft spectrum by mutating the array
// with any DSP that operates with the FFT spectrum (1d FFTW spectrum)
typedef bool (*spectral_processing)(SpectralProcessorHandle spectral_processor,
float* fft_spectrum);
#endif
@@ -0,0 +1,229 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "fft_transform.h"
#include "../utils/general_utils.h"
#include <fftw3.h>
#include <stdlib.h>
#include <string.h>
static uint32_t calculate_fft_size(FftTransform* self);
static bool allocate_fftw(FftTransform* self);
struct FftTransform {
fftwf_plan forward;
fftwf_plan backward;
uint32_t fft_size;
uint32_t frame_size;
uint32_t zeropadding_amount;
uint32_t copy_position;
ZeroPaddingType padding_type;
uint32_t padding_amount;
float* input_fft_buffer;
float* output_fft_buffer;
};
FftTransform* fft_transform_initialize(const uint32_t frame_size,
const ZeroPaddingType padding_type,
const uint32_t zeropadding_amount) {
FftTransform* self = (FftTransform*)calloc(1U, sizeof(FftTransform));
if (!self) {
return NULL;
}
self->padding_type = padding_type;
self->zeropadding_amount = zeropadding_amount;
self->frame_size = frame_size;
self->fft_size = calculate_fft_size(self);
self->copy_position = (self->fft_size / 2U) - (self->frame_size / 2U);
if (!allocate_fftw(self)) {
fft_transform_free(self);
return NULL;
}
return self;
}
FftTransform* fft_transform_initialize_bins(const uint32_t fft_size) {
FftTransform* self = (FftTransform*)calloc(1U, sizeof(FftTransform));
if (!self) {
return NULL;
}
self->fft_size = fft_size;
self->frame_size = self->fft_size;
if (!allocate_fftw(self)) {
fft_transform_free(self);
return NULL;
}
return self;
}
static bool allocate_fftw(FftTransform* self) {
self->input_fft_buffer = (float*)fftwf_malloc(self->fft_size * sizeof(float));
self->output_fft_buffer =
(float*)fftwf_malloc(self->fft_size * sizeof(float));
if (!self->input_fft_buffer || !self->output_fft_buffer) {
return false;
}
memset(self->input_fft_buffer, 0, self->fft_size * sizeof(float));
memset(self->output_fft_buffer, 0, self->fft_size * sizeof(float));
self->forward =
fftwf_plan_r2r_1d((int)self->fft_size, self->input_fft_buffer,
self->output_fft_buffer, FFTW_R2HC, FFTW_ESTIMATE);
self->backward =
fftwf_plan_r2r_1d((int)self->fft_size, self->output_fft_buffer,
self->input_fft_buffer, FFTW_HC2R, FFTW_ESTIMATE);
return self->forward && self->backward;
}
static uint32_t calculate_fft_size(FftTransform* self) {
switch (self->padding_type) {
case NO_PADDING: {
self->padding_amount = 0;
return get_next_divisible_two((int)self->frame_size);
}
case NEXT_POWER_OF_TWO: {
uint32_t next_power_of_two = get_next_power_two((int)self->frame_size);
self->padding_amount = next_power_of_two - self->frame_size;
return next_power_of_two;
}
case FIXED_AMOUNT: {
self->padding_amount = self->zeropadding_amount;
return get_next_divisible_two(
(int)(self->frame_size + self->padding_amount));
}
default:
return get_next_divisible_two((int)self->frame_size);
}
}
void fft_transform_free(FftTransform* self) {
if (!self) {
return;
}
if (self->input_fft_buffer) {
fftwf_free(self->input_fft_buffer);
}
if (self->output_fft_buffer) {
fftwf_free(self->output_fft_buffer);
}
// FFTW plans can be NULL if initialization failed
if (self->forward) {
fftwf_destroy_plan(self->forward);
}
if (self->backward) {
fftwf_destroy_plan(self->backward);
}
free(self);
}
uint32_t get_fft_size(FftTransform* self) {
if (!self) {
return 0;
}
return self->fft_size;
}
uint32_t get_fft_real_spectrum_size(FftTransform* self) {
if (!self) {
return 0;
}
return (self->fft_size / 2U) + 1U;
}
bool fft_load_input_samples(FftTransform* self, const float* input) {
if (!self || !input) {
return false;
}
// Ensure buffer bounds are safe
if (self->frame_size + self->copy_position > self->fft_size) {
return false;
}
// Copy centered values only
for (uint32_t i = self->copy_position;
i < (self->frame_size + self->copy_position); i++) {
self->input_fft_buffer[i] = input[i - self->copy_position];
}
return true;
}
bool fft_get_output_samples(FftTransform* self, float* output) {
if (!self || !output) {
return false;
}
// Ensure buffer bounds are safe
if (self->frame_size + self->copy_position > self->fft_size) {
return false;
}
// Copy centered values only
for (uint32_t i = self->copy_position;
i < (self->frame_size + self->copy_position); i++) {
output[i - self->copy_position] = self->input_fft_buffer[i];
}
return true;
}
bool compute_forward_fft(FftTransform* self) {
if (!self) {
return false;
}
fftwf_execute(self->forward);
return true;
}
bool compute_backward_fft(FftTransform* self) {
if (!self) {
return false;
}
fftwf_execute(self->backward);
return true;
}
float* get_fft_input_buffer(FftTransform* self) {
return self->input_fft_buffer;
}
float* get_fft_output_buffer(FftTransform* self) {
return self->output_fft_buffer;
}
@@ -0,0 +1,57 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef FFT_TRANSFORM_H
#define FFT_TRANSFORM_H
#include <stdbool.h>
#include <stdint.h>
// C17 enum validation
enum ZeroPaddingType {
NEXT_POWER_OF_TWO = 0,
FIXED_AMOUNT = 1,
NO_PADDING = 2,
};
// Compile-time validation of enum values
_Static_assert(NEXT_POWER_OF_TWO == 0, "NEXT_POWER_OF_TWO must be 0");
_Static_assert(FIXED_AMOUNT == 1, "FIXED_AMOUNT must be 1");
_Static_assert(NO_PADDING == 2, "NO_PADDING must be 2");
typedef enum ZeroPaddingType ZeroPaddingType;
typedef struct FftTransform FftTransform;
FftTransform* fft_transform_initialize(uint32_t frame_size,
ZeroPaddingType padding_type,
uint32_t zeropadding_amount);
FftTransform* fft_transform_initialize_bins(uint32_t fft_size);
void fft_transform_free(FftTransform* self);
bool fft_load_input_samples(FftTransform* self, const float* input);
bool fft_get_output_samples(FftTransform* self, float* output);
uint32_t get_fft_size(FftTransform* self);
uint32_t get_fft_real_spectrum_size(FftTransform* self);
bool compute_forward_fft(FftTransform* self);
bool compute_backward_fft(FftTransform* self);
float* get_fft_input_buffer(FftTransform* self);
float* get_fft_output_buffer(FftTransform* self);
#endif
Binary file not shown.
+6
View File
@@ -0,0 +1,6 @@
shared_sources += files(
'fft_transform.c',
'stft_windows.c',
'stft_buffer.c',
'stft_processor.c',
)
+107
View File
@@ -0,0 +1,107 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "stft_buffer.h"
#include <stdlib.h>
#include <string.h>
struct StftBuffer {
uint32_t read_position;
uint32_t start_position;
uint32_t stft_frame_size;
uint32_t block_step;
float* in_fifo;
float* out_fifo;
};
StftBuffer* stft_buffer_initialize(const uint32_t stft_frame_size,
const uint32_t start_position,
const uint32_t block_step) {
StftBuffer* self = (StftBuffer*)calloc(1U, sizeof(StftBuffer));
if (!self) {
return NULL;
}
self->stft_frame_size = stft_frame_size;
self->start_position = start_position;
self->block_step = block_step;
self->read_position = self->start_position;
self->in_fifo = (float*)calloc(self->stft_frame_size, sizeof(float));
self->out_fifo = (float*)calloc(self->stft_frame_size, sizeof(float));
if (!self->in_fifo || !self->out_fifo) {
stft_buffer_free(self);
return NULL;
}
return self;
}
void stft_buffer_free(StftBuffer* self) {
if (!self) {
return;
}
free(self->in_fifo);
free(self->out_fifo);
free(self);
}
bool is_buffer_full(StftBuffer* self) {
if (self->read_position == self->stft_frame_size) {
return true;
}
return false;
}
float stft_buffer_fill(StftBuffer* self, const float input_sample) {
float sample_value = 0.F;
self->in_fifo[self->read_position] = input_sample;
sample_value = self->out_fifo[self->read_position - self->start_position];
if (self->read_position < self->stft_frame_size) {
self->read_position++; // Advance
}
return sample_value;
}
bool stft_buffer_advance_block(StftBuffer* self,
const float* reconstructed_signal) {
if (!reconstructed_signal) {
return false;
}
self->read_position = self->start_position; // Reset read
memmove(self->in_fifo, &self->in_fifo[self->block_step],
sizeof(float) * self->start_position);
memcpy(self->out_fifo, reconstructed_signal,
sizeof(float) * self->block_step);
return true;
}
float* get_full_buffer_block(StftBuffer* self) {
return self->in_fifo;
}
+38
View File
@@ -0,0 +1,38 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef STFT_BUFFER_H
#define STFT_BUFFER_H
#include <stdbool.h>
#include <stdint.h>
typedef struct StftBuffer StftBuffer;
StftBuffer* stft_buffer_initialize(uint32_t stft_frame_size,
uint32_t start_position,
uint32_t block_step);
void stft_buffer_free(StftBuffer* self);
bool is_buffer_full(StftBuffer* self);
float stft_buffer_fill(StftBuffer* self, float input_sample);
bool stft_buffer_advance_block(StftBuffer* self,
const float* reconstructed_signal);
float* get_full_buffer_block(StftBuffer* self);
#endif
Binary file not shown.
@@ -0,0 +1,196 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "stft_processor.h"
#include "stft_buffer.h"
#include "stft_windows.h"
#include <stdlib.h>
#include <string.h>
struct StftProcessor {
uint32_t input_latency;
uint32_t hop;
uint32_t overlap_factor;
uint32_t fft_size;
uint32_t frame_size;
float* output_accumulator;
float* tmp_buffer;
FftTransform* fft_transform;
StftBuffer* stft_buffer;
StftWindows* stft_windows;
};
StftProcessor* stft_processor_initialize(const uint32_t sample_rate,
const float stft_frame_size,
const uint32_t overlap_factor,
ZeroPaddingType padding_type,
const uint32_t zeropadding_amount,
WindowTypes input_window,
WindowTypes output_window) {
if (sample_rate == 0 || stft_frame_size <= 0.0f || overlap_factor == 0) {
return NULL;
}
StftProcessor* self = (StftProcessor*)calloc(1U, sizeof(StftProcessor));
if (!self) {
return NULL;
}
self->frame_size =
(uint32_t)((stft_frame_size / 1000.F) * (float)sample_rate);
self->fft_transform = fft_transform_initialize(self->frame_size, padding_type,
zeropadding_amount);
if (!self->fft_transform) {
stft_processor_free(self);
return NULL;
}
self->fft_size = get_fft_size(self->fft_transform);
self->overlap_factor = overlap_factor;
self->hop = self->frame_size / self->overlap_factor;
self->input_latency = self->frame_size;
self->output_accumulator =
(float*)calloc(self->frame_size * 2L, sizeof(float));
if (!self->output_accumulator) {
stft_processor_free(self);
return NULL;
}
self->tmp_buffer = (float*)calloc(self->frame_size, sizeof(float));
if (!self->tmp_buffer) {
stft_processor_free(self);
return NULL;
}
self->stft_buffer = stft_buffer_initialize(
self->frame_size, self->input_latency - self->hop, self->hop);
if (!self->stft_buffer) {
stft_processor_free(self);
return NULL;
}
self->stft_windows = stft_window_initialize(
self->fft_size, self->overlap_factor, input_window, output_window);
if (!self->stft_windows) {
stft_processor_free(self);
return NULL;
}
return self;
}
void stft_processor_free(StftProcessor* self) {
if (!self) {
return;
}
if (self->stft_buffer) {
stft_buffer_free(self->stft_buffer);
}
if (self->stft_windows) {
stft_window_free(self->stft_windows);
}
if (self->fft_transform) {
fft_transform_free(self->fft_transform);
}
if (self->output_accumulator) {
free(self->output_accumulator);
}
if (self->tmp_buffer) {
free(self->tmp_buffer);
}
free(self);
}
bool stft_processor_run(StftProcessor* self, const uint32_t number_of_samples,
const float* input, float* output,
spectral_processing spectral_processing,
SpectralProcessorHandle spectral_processor) {
if (!self || !input || !output || number_of_samples == 0U) {
return false;
}
for (uint32_t k = 0U; k < number_of_samples; k++) {
// Start filling buffer sample per sample
output[k] = stft_buffer_fill(self->stft_buffer, input[k]);
if (is_buffer_full(self->stft_buffer)) {
fft_load_input_samples(self->fft_transform,
get_full_buffer_block(self->stft_buffer));
// STFT Analysis
stft_window_apply(self->stft_windows,
get_fft_input_buffer(self->fft_transform),
INPUT_WINDOW);
compute_forward_fft(self->fft_transform);
// Apply processing
spectral_processing(spectral_processor,
get_fft_output_buffer(self->fft_transform));
// STFT Synthesis
compute_backward_fft(self->fft_transform);
stft_window_apply(self->stft_windows,
get_fft_input_buffer(self->fft_transform),
OUTPUT_WINDOW);
fft_get_output_samples(self->fft_transform, self->tmp_buffer);
// STFT Overlap Add
for (uint32_t j = 0U; j < self->frame_size; j++) {
self->output_accumulator[j] += self->tmp_buffer[j];
}
stft_buffer_advance_block(self->stft_buffer, self->output_accumulator);
memmove(self->output_accumulator, &self->output_accumulator[self->hop],
self->frame_size * sizeof(float));
}
}
return true;
}
uint32_t get_stft_latency(StftProcessor* self) {
if (!self) {
return 0;
}
return self->input_latency;
}
uint32_t get_stft_fft_size(StftProcessor* self) {
if (!self) {
return 0;
}
return self->fft_size;
}
uint32_t get_stft_real_spectrum_size(StftProcessor* self) {
if (!self) {
return 0;
}
return get_fft_real_spectrum_size(self->fft_transform);
}
@@ -0,0 +1,50 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef STFT_PROCESSOR_H
#define STFT_PROCESSOR_H
#include "../spectral_processor.h"
#include "../utils/spectral_utils.h"
#include "fft_transform.h"
#include <stdbool.h>
#include <stdint.h>
typedef struct StftProcessor StftProcessor;
StftProcessor* stft_processor_initialize(
uint32_t sample_rate, float stft_frame_size, uint32_t overlap_factor,
ZeroPaddingType padding_type, uint32_t zeropadding_amount,
WindowTypes input_window, WindowTypes output_window);
void stft_processor_free(StftProcessor* self);
uint32_t get_stft_latency(StftProcessor* self);
uint32_t get_stft_fft_size(StftProcessor* self);
uint32_t get_stft_real_spectrum_size(StftProcessor* self);
// Receives an input and output buffer with a a number_of_samples and does the
// STFT transform applying any spectral_processing. It works similar to qsort,
// because it receives a function pointer of any spectral processing that needs
// to be applied in between the analysis and the synthesis
bool stft_processor_run(StftProcessor* self, uint32_t number_of_samples,
const float* input, float* output,
spectral_processing spectral_processing,
SpectralProcessorHandle spectral_processor);
#endif
Binary file not shown.
+106
View File
@@ -0,0 +1,106 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "stft_windows.h"
#include <stdlib.h>
static float get_windows_scale_factor(StftWindows* self,
uint32_t overlap_factor);
struct StftWindows {
float* input_window;
float* output_window;
uint32_t stft_frame_size;
float scale_factor;
};
StftWindows* stft_window_initialize(const uint32_t stft_frame_size,
const uint32_t overlap_factor,
const WindowTypes input_window,
const WindowTypes output_window) {
StftWindows* self = (StftWindows*)calloc(1U, sizeof(StftWindows));
if (!self) {
return NULL;
}
self->stft_frame_size = stft_frame_size;
self->input_window = (float*)calloc(self->stft_frame_size, sizeof(float));
self->output_window = (float*)calloc(self->stft_frame_size, sizeof(float));
if (!self->input_window || !self->output_window) {
stft_window_free(self);
return NULL;
}
(void)get_fft_window(self->input_window, self->stft_frame_size, input_window);
(void)get_fft_window(self->output_window, self->stft_frame_size,
output_window);
self->scale_factor = get_windows_scale_factor(self, overlap_factor);
return self;
}
void stft_window_free(StftWindows* self) {
if (!self) {
return;
}
free(self->input_window);
free(self->output_window);
free(self);
}
static float get_windows_scale_factor(StftWindows* self,
const uint32_t overlap_factor) {
if (overlap_factor < 2) {
return 0.F;
}
float sum = 0.F;
for (uint32_t i = 0U; i < self->stft_frame_size; i++) {
sum += self->input_window[i] * self->output_window[i];
}
return sum * (float)overlap_factor;
}
bool stft_window_apply(StftWindows* self, float* frame,
const WindowPlace place) {
if (!self || !frame) {
return false;
}
for (uint32_t i = 0U; i < self->stft_frame_size; i++) {
switch (place) {
case INPUT_WINDOW:
frame[i] *= self->input_window[i];
break;
case OUTPUT_WINDOW:
frame[i] *= self->output_window[i] / self->scale_factor;
break;
default:
break;
}
}
return true;
}
@@ -0,0 +1,39 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef STFT_WINDOW_H
#define STFT_WINDOW_H
#include "../utils/spectral_utils.h"
#include <stdbool.h>
#include <stdint.h>
typedef struct StftWindows StftWindows;
typedef enum WindowPlace { INPUT_WINDOW = 1, OUTPUT_WINDOW = 2 } WindowPlace;
StftWindows* stft_window_initialize(uint32_t stft_frame_size,
uint32_t overlap_factor,
WindowTypes input_window,
WindowTypes output_window);
void stft_window_free(StftWindows* self);
bool stft_window_apply(StftWindows* self, float* frame, WindowPlace place);
#endif
Binary file not shown.
@@ -0,0 +1,98 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "denoise_mixer.h"
#include <stdlib.h>
#include <string.h>
struct DenoiseMixer {
float* residual_spectrum;
float* denoised_spectrum;
uint32_t fft_size;
uint32_t real_spectrum_size;
uint32_t sample_rate;
uint32_t hop;
};
DenoiseMixer* denoise_mixer_initialize(uint32_t fft_size, uint32_t sample_rate,
uint32_t hop) {
DenoiseMixer* self = (DenoiseMixer*)calloc(1U, sizeof(DenoiseMixer));
if (!self) {
return NULL;
}
self->fft_size = fft_size;
self->real_spectrum_size = (self->fft_size / 2U) + 1U;
self->sample_rate = sample_rate;
self->hop = hop;
self->residual_spectrum = (float*)calloc((self->fft_size), sizeof(float));
self->denoised_spectrum = (float*)calloc((self->fft_size), sizeof(float));
if (!self->residual_spectrum || !self->denoised_spectrum) {
denoise_mixer_free(self);
return NULL;
}
return self;
}
void denoise_mixer_free(DenoiseMixer* self) {
if (!self) {
return;
}
free(self->residual_spectrum);
free(self->denoised_spectrum);
free(self);
}
bool denoise_mixer_run(DenoiseMixer* self, float* fft_spectrum,
const float* gain_spectrum,
DenoiseMixerParameters parameters) {
if (!fft_spectrum || !gain_spectrum) {
return false;
}
// Get denoised spectrum - Apply to both real and complex parts
for (uint32_t k = 0U; k < self->fft_size; k++) {
self->denoised_spectrum[k] = fft_spectrum[k] * gain_spectrum[k];
}
// Get residual spectrum - Apply to both real and complex parts
for (uint32_t k = 0U; k < self->fft_size; k++) {
self->residual_spectrum[k] = fft_spectrum[k] - self->denoised_spectrum[k];
}
// Mix denoised and residual - Now a simple toggle
if (parameters.residual_listen) {
for (uint32_t k = 0U; k < self->fft_size; k++) {
fft_spectrum[k] = self->residual_spectrum[k];
}
} else {
for (uint32_t k = 0U; k < self->fft_size; k++) {
fft_spectrum[k] = self->denoised_spectrum[k];
}
}
return true;
}
@@ -0,0 +1,42 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef DENOISE_MIXER_H
#define DENOISE_MIXER_H
#include <stdbool.h>
#include <stdint.h>
typedef struct DenoiseMixerParameters {
float noise_level;
bool residual_listen;
float whitening_amount;
} DenoiseMixerParameters;
typedef struct DenoiseMixer DenoiseMixer;
DenoiseMixer* denoise_mixer_initialize(uint32_t fft_size, uint32_t sample_rate,
uint32_t hop);
void denoise_mixer_free(DenoiseMixer* self);
bool denoise_mixer_run(DenoiseMixer* self, float* fft_spectrum,
const float* gain_spectrum,
DenoiseMixerParameters parameters);
#endif
Binary file not shown.
@@ -0,0 +1,54 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "general_utils.h"
#include <float.h>
#include <math.h>
#include <stdlib.h>
float sanitize_denormal(float value) {
if (!isnormal(value)) {
value = 0.F;
}
return value;
}
float from_db_to_coefficient(const float value_db) {
return expf(value_db / 20.F * logf(10.F));
}
float remap_percentage_log_like_unity(const float value) {
return 1.F - expf(-3.F * (value));
}
int get_next_divisible_two(int number) {
int q = number / 2;
int n1 = 2 * q;
int n2 = (number * 2) > 0 ? (2 * (q + 1)) : (2 * (q - 1));
if (abs(number - n1) < abs(number - n2)) {
return n1;
}
return n2;
}
int get_next_power_two(int number) {
return (int)roundf(powf(2.F, ceilf(log2f((float)number))));
}
@@ -0,0 +1,39 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef GENERAL_UTILS_H
#define GENERAL_UTILS_H
#include <stdbool.h>
#include <stdint.h>
// Compile-time validation
_Static_assert(sizeof(float) >= 4, "float must be at least 32 bits");
_Static_assert(sizeof(double) >= 8, "double must be at least 64 bits");
__attribute__((warn_unused_result)) float sanitize_denormal(float value);
__attribute__((warn_unused_result)) float from_db_to_coefficient(
float value_db);
__attribute__((warn_unused_result)) float remap_percentage_log_like_unity(
float value);
__attribute__((warn_unused_result)) int get_next_divisible_two(int number);
__attribute__((warn_unused_result)) int get_next_power_two(int number);
#endif
Binary file not shown.
@@ -0,0 +1,7 @@
shared_sources += files(
'general_utils.c',
'denoise_mixer.c',
'spectral_features.c',
'spectral_utils.c',
'spectral_trailing_buffer.c',
)
@@ -0,0 +1,189 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "spectral_features.h"
#include <math.h>
#include <stdlib.h>
struct SpectralFeatures {
float* power_spectrum;
float* phase_spectrum;
float* magnitude_spectrum;
uint32_t real_spectrum_size;
};
SpectralFeatures* spectral_features_initialize(
const uint32_t real_spectrum_size) {
SpectralFeatures* self =
(SpectralFeatures*)calloc(1U, sizeof(SpectralFeatures));
if (!self) {
return NULL;
}
self->real_spectrum_size = real_spectrum_size;
self->power_spectrum =
(float*)calloc(self->real_spectrum_size, sizeof(float));
self->phase_spectrum =
(float*)calloc(self->real_spectrum_size, sizeof(float));
self->magnitude_spectrum =
(float*)calloc(self->real_spectrum_size, sizeof(float));
if (!self->power_spectrum || !self->phase_spectrum ||
!self->magnitude_spectrum) {
spectral_features_free(self);
return NULL;
}
return self;
}
void spectral_features_free(SpectralFeatures* self) {
if (!self) {
return;
}
free(self->power_spectrum);
free(self->phase_spectrum);
free(self->magnitude_spectrum);
free(self);
}
float* get_power_spectrum(SpectralFeatures* self) {
return self->power_spectrum;
}
float* get_magnitude_spectrum(SpectralFeatures* self) {
return self->magnitude_spectrum;
}
float* get_phase_spectrum(SpectralFeatures* self) {
return self->phase_spectrum;
}
static bool compute_power_spectrum(SpectralFeatures* self,
const float* fft_spectrum,
const uint32_t fft_spectrum_size) {
if (!self || !fft_spectrum || !fft_spectrum_size) {
return false;
}
const uint32_t n = fft_spectrum_size;
const uint32_t n2 = n / 2U;
const bool is_even = (n % 2U == 0);
// DC bin
self->power_spectrum[0] = fft_spectrum[0] * fft_spectrum[0];
// Complex bins
for (uint32_t k = 1U; k < n2; k++) {
float real = fft_spectrum[k];
float imag = fft_spectrum[n - k];
self->power_spectrum[k] = (real * real) + (imag * imag);
}
// Nyquist bin
if (is_even) {
self->power_spectrum[n2] = fft_spectrum[n2] * fft_spectrum[n2];
}
return true;
}
static bool compute_magnitude_spectrum(SpectralFeatures* self,
const float* fft_spectrum,
const uint32_t fft_spectrum_size) {
if (!self || !fft_spectrum || !fft_spectrum_size) {
return false;
}
const uint32_t n = fft_spectrum_size;
const uint32_t n2 = n / 2U;
const bool is_even = (n % 2U == 0);
// DC bin
self->magnitude_spectrum[0] = fabsf(fft_spectrum[0]);
// Complex bins
for (uint32_t k = 1U; k < n2; k++) {
self->magnitude_spectrum[k] = hypotf(fft_spectrum[k], fft_spectrum[n - k]);
}
// Nyquist bin
if (is_even) {
self->magnitude_spectrum[n2] = fabsf(fft_spectrum[n2]);
}
return true;
}
static bool compute_phase_spectrum(SpectralFeatures* self,
const float* fft_spectrum,
const uint32_t fft_spectrum_size) {
if (!self || !fft_spectrum || !fft_spectrum_size) {
return false;
}
const uint32_t n = fft_spectrum_size;
const uint32_t n2 = n / 2U;
const bool is_even = (n % 2U == 0);
// DC bin - purely real
self->phase_spectrum[0] = atan2f(0.F, fft_spectrum[0]);
// Complex bins
for (uint32_t k = 1U; k < n2; k++) {
float real = fft_spectrum[k];
float imag = fft_spectrum[n - k];
self->phase_spectrum[k] = atan2f(imag, real);
}
// Nyquist bin - purely real
if (is_even) {
self->phase_spectrum[n2] = atan2f(0.F, fft_spectrum[n2]);
}
return true;
}
float* get_spectral_feature(SpectralFeatures* self, const float* fft_spectrum,
uint32_t fft_spectrum_size, SpectrumType type) {
if (!self || !fft_spectrum || fft_spectrum_size == 0U) {
return NULL;
}
switch (type) {
case POWER_SPECTRUM:
compute_power_spectrum(self, fft_spectrum, fft_spectrum_size);
return get_power_spectrum(self);
break;
case MAGNITUDE_SPECTRUM:
compute_magnitude_spectrum(self, fft_spectrum, fft_spectrum_size);
return get_magnitude_spectrum(self);
break;
case PHASE_SPECTRUM:
compute_phase_spectrum(self, fft_spectrum, fft_spectrum_size);
return get_phase_spectrum(self);
break;
default:
return NULL;
break;
}
}
@@ -0,0 +1,40 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef SPECTRAL_FEATURES_H
#define SPECTRAL_FEATURES_H
#include <stdbool.h>
#include <stdint.h>
typedef struct SpectralFeatures SpectralFeatures;
typedef enum SpectrumType {
POWER_SPECTRUM = 0,
MAGNITUDE_SPECTRUM = 1,
PHASE_SPECTRUM = 2,
} SpectrumType;
SpectralFeatures* spectral_features_initialize(uint32_t real_spectrum_size);
void spectral_features_free(SpectralFeatures* self);
float* get_spectral_feature(SpectralFeatures* self, const float* fft_spectrum,
uint32_t fft_spectrum_size, SpectrumType type);
#endif
Binary file not shown.
@@ -0,0 +1,90 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "spectral_trailing_buffer.h"
#include <math.h>
#include <stdlib.h>
#include <string.h>
struct SpectralTrailingBuffer {
uint32_t real_spectrum_size;
uint32_t buffer_size;
float* buffer;
};
SpectralTrailingBuffer* spectral_trailing_buffer_initialize(
const uint32_t real_spectrum_size, const uint32_t buffer_size) {
SpectralTrailingBuffer* self =
(SpectralTrailingBuffer*)calloc(1U, sizeof(SpectralTrailingBuffer));
if (!self) {
return NULL;
}
self->real_spectrum_size = real_spectrum_size;
self->buffer_size = buffer_size;
self->buffer = (float*)calloc(
((size_t)self->real_spectrum_size * (size_t)self->buffer_size),
sizeof(float));
if (!self->buffer) {
spectral_trailing_buffer_free(self);
return NULL;
}
return self;
}
void spectral_trailing_buffer_free(SpectralTrailingBuffer* self) {
if (!self) {
return;
}
free(self->buffer);
free(self);
}
bool spectral_trailing_buffer_push_back(SpectralTrailingBuffer* self,
const float* input_spectrum) {
if (!input_spectrum) {
return false;
}
memmove(self->buffer, &self->buffer[self->real_spectrum_size],
sizeof(float) * self->real_spectrum_size * (self->buffer_size - 1U));
memcpy(&self->buffer[(size_t)self->real_spectrum_size *
(size_t)(self->buffer_size - 1U)],
input_spectrum, sizeof(float) * self->real_spectrum_size);
return true;
}
float* get_trailing_spectral_buffer(SpectralTrailingBuffer* self) {
return self->buffer;
}
uint32_t get_spectrum_buffer_size(SpectralTrailingBuffer* self) {
return self->buffer_size;
}
uint32_t get_spectrum_size(SpectralTrailingBuffer* self) {
return self->real_spectrum_size;
}
@@ -0,0 +1,38 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef SPECTRAL_TRAILING_BUFFER_H
#define SPECTRAL_TRAILING_BUFFER_H
#include <stdbool.h>
#include <stdint.h>
typedef struct SpectralTrailingBuffer SpectralTrailingBuffer;
SpectralTrailingBuffer* spectral_trailing_buffer_initialize(
uint32_t real_spectrum_size, uint32_t buffer_size);
void spectral_trailing_buffer_free(SpectralTrailingBuffer* self);
bool spectral_trailing_buffer_push_back(SpectralTrailingBuffer* self,
const float* input_spectrum);
float* get_trailing_spectral_buffer(SpectralTrailingBuffer* self);
uint32_t get_spectrum_buffer_size(SpectralTrailingBuffer* self);
uint32_t get_spectrum_size(SpectralTrailingBuffer* self);
#endif
@@ -0,0 +1,280 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "spectral_utils.h"
#include "../configurations.h"
#include "general_utils.h"
#include <float.h>
#include <math.h>
#include <stdlib.h>
static float blackman(const uint32_t bin_index, const uint32_t fft_size) {
const float p = ((float)(bin_index)) / ((float)(fft_size));
return sanitize_denormal(0.42F - (0.5F * cosf(2.F * M_PIf * p)) +
(0.08F * cosf(4.F * M_PIf * p)));
}
static float hanning(const uint32_t bin_index, const uint32_t fft_size) {
const float p = ((float)(bin_index)) / ((float)(fft_size));
return sanitize_denormal(0.5F - (0.5F * cosf(2.F * M_PIf * p)));
}
static float hamming(const uint32_t bin_index, const uint32_t fft_size) {
const float p = ((float)(bin_index)) / ((float)(fft_size));
return sanitize_denormal(0.54F - (0.46F * cosf(2.F * M_PIf * p)));
}
static float vorbis(const uint32_t bin_index, const uint32_t fft_size) {
const float p = ((float)(bin_index)) / ((float)(fft_size));
return sanitize_denormal(sinf(M_PIf / 2.F * powf(sinf(M_PIf * p), 2.F)));
}
bool get_fft_window(float* window, const uint32_t fft_size,
const WindowTypes window_type) {
if (!window || !fft_size) {
return false;
}
for (uint32_t k = 0; k < fft_size; k++) {
switch (window_type) {
case HANN_WINDOW:
window[k] = hanning(k, fft_size);
break;
case HAMMING_WINDOW:
window[k] = hamming(k, fft_size);
break;
case BLACKMAN_WINDOW:
window[k] = blackman(k, fft_size);
break;
case VORBIS_WINDOW:
window[k] = vorbis(k, fft_size);
break;
default:
break;
}
}
return true;
}
bool initialize_spectrum_with_value(float* spectrum, uint32_t spectrum_size,
const float value) {
if (!spectrum || spectrum_size == 0U) {
return false;
}
for (uint32_t i = 0U; i < spectrum_size; i++) {
spectrum[i] = value;
}
return true;
}
float max_spectral_value(const float* spectrum,
const uint32_t real_spectrum_size) {
if (!spectrum || real_spectrum_size == 0U) {
return 0.F;
}
float max = spectrum[0];
for (uint32_t k = 1U; k < real_spectrum_size; k++) {
max = fmaxf(spectrum[k], max);
}
return max;
}
float min_spectral_value(const float* spectrum,
const uint32_t real_spectrum_size) {
if (!spectrum || real_spectrum_size == 0U) {
return 0.F;
}
float min = spectrum[0];
for (uint32_t k = 1U; k < real_spectrum_size; k++) {
min = fminf(spectrum[k], min);
}
return min;
}
bool min_spectrum_float(float* spectrum_one, const float* spectrum_two,
const uint32_t spectrum_size) {
if (!spectrum_one || !spectrum_two || spectrum_size == 0U) {
return false;
}
for (uint32_t k = 0; k < spectrum_size; k++) {
spectrum_one[k] = fminf(spectrum_one[k], spectrum_two[k]);
}
return true;
}
bool max_spectrum_float(float* spectrum_one, const float* spectrum_two,
const uint32_t spectrum_size) {
if (!spectrum_one || !spectrum_two || spectrum_size == 0U) {
return false;
}
for (uint32_t k = 0; k < spectrum_size; k++) {
spectrum_one[k] = fmaxf(spectrum_one[k], spectrum_two[k]);
}
return true;
}
bool min_spectrum_double(double* spectrum_one, const double* spectrum_two,
const uint32_t spectrum_size) {
if (!spectrum_one || !spectrum_two || spectrum_size == 0U) {
return false;
}
for (uint32_t k = 0; k < spectrum_size; k++) {
spectrum_one[k] = fmin(spectrum_one[k], spectrum_two[k]);
}
return true;
}
bool max_spectrum_double(double* spectrum_one, const double* spectrum_two,
const uint32_t spectrum_size) {
if (!spectrum_one || !spectrum_two || spectrum_size == 0U) {
return false;
}
for (uint32_t k = 0; k < spectrum_size; k++) {
spectrum_one[k] = fmax(spectrum_one[k], spectrum_two[k]);
}
return true;
}
bool direct_matrix_to_vector_spectral_convolution(const float* matrix_spectum,
const float* spectrum,
float* out_spectrum,
uint32_t spectrum_size) {
if (!matrix_spectum || !spectrum || !out_spectrum || spectrum_size == 0U) {
return false;
}
for (uint32_t i = 0U; i < spectrum_size; i++) {
out_spectrum[i] = 0.F;
for (uint32_t j = 0U; j < spectrum_size; j++) {
out_spectrum[i] +=
(matrix_spectum[(i * spectrum_size) + j] * spectrum[j]);
}
}
return true;
}
float fft_bin_to_freq(const uint32_t bin_index, const uint32_t sample_rate,
const uint32_t fft_size) {
return (float)bin_index * ((float)sample_rate / (float)fft_size);
}
uint32_t freq_to_fft_bin(const float freq, const uint32_t sample_rate,
const uint32_t fft_size) {
return (uint32_t)((freq / ((float)sample_rate / (float)fft_size)) + 0.5f);
}
float spectral_flux(const float* spectrum, const float* previous_spectrum,
const uint32_t spectrum_size) {
if (!spectrum || !previous_spectrum || spectrum_size == 0U) {
return 0.F;
}
float spectral_flux = 0.F;
for (uint32_t i = 0U; i < spectrum_size; i++) {
const float temp = sqrtf(spectrum[i]) - sqrtf(previous_spectrum[i]);
spectral_flux += (temp + fabsf(temp)) / 2.F;
}
return spectral_flux;
}
bool get_rolling_mean_spectrum(float* averaged_spectrum,
const float* current_spectrum,
const uint32_t number_of_blocks,
const uint32_t spectrum_size) {
if (!averaged_spectrum || !current_spectrum || spectrum_size == 0U) {
return false;
}
for (uint32_t k = 0U; k < spectrum_size; k++) {
if (number_of_blocks <= 1U) {
averaged_spectrum[k] = current_spectrum[k];
} else {
averaged_spectrum[k] += (current_spectrum[k] - averaged_spectrum[k]) /
(float)number_of_blocks;
}
}
return true;
}
static int min_max_comparator(const void* a, const void* b) {
float x = *(const float*)a;
float y = *(const float*)b;
return x >= y ? 1 : -1;
}
static float find_median(const float* array, uint32_t array_size) {
float median = 0.F;
if (array_size % 2 == 0) {
// if number of elements are even
median = (array[(array_size - 1U) / 2U] + array[array_size / 2U]) / 2.F;
} else {
// if number of elements are odd
median = array[array_size / 2U];
}
return median;
}
bool get_rolling_median_spectrum(float* median_spectrum,
const float* current_spectrum_buffer,
const uint32_t number_of_blocks,
const uint32_t spectrum_size) {
if (!median_spectrum || !current_spectrum_buffer || spectrum_size == 0U) {
return false;
}
float tmp_buffer[number_of_blocks];
for (uint32_t i = 0U; i < spectrum_size; i++) {
for (uint32_t j = 0U; j < number_of_blocks; j++) {
tmp_buffer[j] = current_spectrum_buffer[(j * spectrum_size) + i];
}
// Sorting array
qsort(tmp_buffer, number_of_blocks, sizeof(float), min_max_comparator);
float median_of_buffer = find_median(tmp_buffer, number_of_blocks);
// Taking the max of the median
if (median_of_buffer > median_spectrum[i]) {
median_spectrum[i] = median_of_buffer;
}
}
return true;
}
@@ -0,0 +1,78 @@
/*
libspecbleach - A spectral processing library
Copyright 2022 Luciano Dato <lucianodato@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef SPECTRAL_UTILS_H
#define SPECTRAL_UTILS_H
#include <stdbool.h>
#include <stdint.h>
typedef enum WindowTypes {
HANN_WINDOW = 0,
HAMMING_WINDOW = 1,
BLACKMAN_WINDOW = 2,
VORBIS_WINDOW = 3
} WindowTypes;
bool get_fft_window(float* window, uint32_t fft_size, WindowTypes window_type);
bool initialize_spectrum_with_value(float* spectrum, uint32_t spectrum_size,
float value);
bool direct_matrix_to_vector_spectral_convolution(const float* matrix_spectum,
const float* spectrum,
float* out_spectrum,
uint32_t spectrum_size);
float max_spectral_value(const float* spectrum, uint32_t real_spectrum_size);
float min_spectral_value(const float* spectrum, uint32_t real_spectrum_size);
#define min_spectrum(spectrum_one, spectrum_two, spectrum_size) \
_Generic((spectrum_one), \
float*: min_spectrum_float, \
double*: min_spectrum_double, \
default: min_spectrum_float)(spectrum_one, spectrum_two, spectrum_size)
#define max_spectrum(spectrum_one, spectrum_two, spectrum_size) \
_Generic((spectrum_one), \
float*: max_spectrum_float, \
double*: max_spectrum_double, \
default: max_spectrum_float)(spectrum_one, spectrum_two, spectrum_size)
bool min_spectrum_float(float* spectrum_one, const float* spectrum_two,
uint32_t spectrum_size);
bool max_spectrum_float(float* spectrum_one, const float* spectrum_two,
uint32_t spectrum_size);
bool min_spectrum_double(double* spectrum_one, const double* spectrum_two,
uint32_t spectrum_size);
bool max_spectrum_double(double* spectrum_one, const double* spectrum_two,
uint32_t spectrum_size);
float fft_bin_to_freq(uint32_t bin_index, uint32_t sample_rate,
uint32_t fft_size);
uint32_t freq_to_fft_bin(float freq, uint32_t sample_rate, uint32_t fft_size);
float spectral_flux(const float* spectrum, const float* previous_spectrum,
uint32_t spectrum_size);
bool get_rolling_mean_spectrum(float* averaged_spectrum,
const float* current_spectrum,
uint32_t number_of_blocks,
uint32_t spectrum_size);
bool get_rolling_median_spectrum(float* median_spectrum,
const float* current_spectrum_buffer,
uint32_t number_of_blocks,
uint32_t spectrum_size);
#endif
Binary file not shown.