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.
+1
View File
@@ -0,0 +1 @@
Jean-Marc Valin <jmvalin@jmvalin.ca>
+32
View File
@@ -0,0 +1,32 @@
Copyright (c) 2007-2017, 2024 Jean-Marc Valin
Copyright (c) 2023 Amazon
Copyright (c) 2017, Mozilla
Copyright (c) 2005-2017, Xiph.Org Foundation
Copyright (c) 2003-2004, Mark Borgerding
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of the Xiph.Org Foundation nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+69
View File
@@ -0,0 +1,69 @@
#
# Makefile for RNNOISE
# This one creates librnnoise.a intended for static linking
#
# DL1YCF NOTE:
# the "full model", rnnoise_data.c, does not compile on RaspPis
# with only 1 GByte of memory, therefore this has now been split
# into 6 files that can be compiled separately.
#
CFLAGS?= -pthread -O3 -D_GNU_SOURCE -Wno-parentheses -Iinclude -Isrc
COMPILE=$(CC) $(CFLAGS)
SOURCES= \
src/denoise.c \
src/celt_lpc.c \
src/kiss_fft.c \
src/nnet.c \
src/nnet_default.c \
src/parse_lpcnet_weights.c \
src/pitch.c \
src/rnn.c \
src/rnnoise_data.c \
src/rnnoise_data_1.c \
src/rnnoise_data_2.c \
src/rnnoise_data_3.c \
src/rnnoise_data_4.c \
src/rnnoise_data_5.c \
src/rnnoise_data_6.c \
src/rnnoise_tables.c
OBJS= \
src/denoise.o \
src/celt_lpc.o \
src/kiss_fft.o \
src/nnet.o \
src/nnet_default.o \
src/parse_lpcnet_weights.o \
src/pitch.o \
src/rnn.o \
src/rnnoise_data.o \
src/rnnoise_data_1.o \
src/rnnoise_data_2.o \
src/rnnoise_data_3.o \
src/rnnoise_data_4.o \
src/rnnoise_data_5.o \
src/rnnoise_data_6.o \
src/rnnoise_tables.o
librnnoise.a: $(OBJS)
ar rv librnnoise.a $(OBJS)
ranlib librnnoise.a
.c.o:
$(COMPILE) -c -o $@ $<
clean:
-rm -f librnnoise.a $(OBJS)
#############################################################################
#
# What follows is automatically generated by the "makedepend" program
#
#############################################################################
# DO NOT DELETE
+125
View File
@@ -0,0 +1,125 @@
RNNoise is a noise suppression library based on a recurrent neural network.
A description of the algorithm is provided in the following paper:
J.-M. Valin, A Hybrid DSP/Deep Learning Approach to Real-Time Full-Band Speech
Enhancement, Proceedings of IEEE Multimedia Signal Processing (MMSP) Workshop,
arXiv:1709.08243, 2018.
https://arxiv.org/pdf/1709.08243.pdf
An interactive demo of version 0.1 is available at: https://jmvalin.ca/demo/rnnoise/
To compile, just type:
% ./autogen.sh
% ./configure
% make
Optionally:
% make install
It is recommended to either set -march= in the CFLAGS to an architecture
with AVX2 support or to add --enable-x86-rtcd to the configure script
so that AVX2 (or SSE4.1) can at least be used as an option.
Note that the autogen.sh script will automatically download the model files
from the Xiph.Org servers, since those are too large to put in Git.
While it is meant to be used as a library, a simple command-line tool is
provided as an example. It operates on RAW 16-bit (machine endian) mono
PCM files sampled at 48 kHz. It can be used as:
% ./examples/rnnoise_demo <noisy speech> <output denoised>
The output is also a 16-bit raw PCM file.
NOTE AGAIN, THE INPUT and OUTPUT ARE IN RAW FORMAT, NOT WAV.
The latest version of the source is available from
https://gitlab.xiph.org/xiph/rnnoise . The GitHub repository
is a convenience copy.
== Training ==
The models distributed with RNNoise are now trained using only the publicly
available datasets listed below and using the training precedure described
here. Exact results will still depend on the the exact mix of data used,
on how long the training is performed and on the various random seeds involved.
To train an RNNoise model, you need both clean speech data, and noise data.
Both need to be sampled at 48 kHz, in 16-bit PCM format (machine endian).
Clean speech data can be obtained from the datasets listed in the datasets.txt
file, or by downloaded the already-concatenation of those files in
https://media.xiph.org/rnnoise/data/tts_speech_48k.sw
For noise data, we suggest the background_noise.sw and foreground_noise.sw
(or later versions) noise files from https://media.xiph.org/rnnoise/data/
The foreground_noise.sw file contains noise signals that are meant to be added
to the background noise (e.g. keyboard sounds). Optionally, the foreground noise
file can even be denoised with a traditional denoiser (e.g. libspeexdsp) to
keep only the transient components. For background noise, the data from the
original RNNoise noise collection have now been sufficiently filtered to
provide good results -- either alone or in combination with the
background_noise.sw file. The dataset can be downloaded (updated Jan 30th 2025)
from: https://media.xiph.org/rnnoise/rnnoise_contributions.tar.gz
The first step is to take the speech and noise, and mix them in a variety of
ways to simulate real life conditions (including pauses, filtering and more).
Assuming the files are called speech.pcm and noise.pcm, start by generating
the training feature data with:
% ./dump_features speech.pcm background_noise.pcm foreground_noise.pcm features.f32 <count>
where <count> is the number of sequences to process. The number of sequences
should be at least 10000, but the more the better (200000 or more is
recommended).
Optionally, training can also simulate reverberation, in which case room impulse
responses (RIR) are also needed. Limited RIR data is available at:
https://media.xiph.org/rnnoise/data/measured_rirs-v2.tar.gz
The format for those is raw 32-bit floating-point (files are little endian).
Assuming a list of all the RIR files is contained in a rir_list.txt file,
the training feature data can be generated with:
% ./dump_features -rir_list rir_list.txt speech.pcm background_noise.pcm foreground_noise.pcm features.f32 <count>
To make the feature generation faster, you can use the script provided in
script/dump_features_parallel.sh (you will need to modify the script if you
want to add RIR augmentation).
To use it:
% script/dump_features_parallel.sh ./dump_features speech.pcm background_noise.pcm foreground_noise.pcm features.f32 <count> rir_list.txt
which will run nb_processes processes, each for count sequences, and
concatenate the output to a single file.
Once the feature file is computed, you can start the training with:
% python3 train_rnnoise.py features.f32 output_directory
Choose a number of epochs (using --epochs) that leads to about 75000 weight
updates. The training will produce .pth files, e.g. rnnoise_50.pth .
The next step is to convert the model to C files using:
% python3 dump_rnnoise_weights.py --quantize rnnoise_50.pth rnnoise_c
which will produce the rnnoise_data.c and rnnoise_data.h files in the
rnnoise_c directory.
Copy these files to src/ and then build RNNoise using the instructions above.
For slightly better results, a trained model can be used to remove any noise
from the "clean" training speech, before restaring the denoising process
again (no need to do that more than once).
== Loadable Models ==
The model format has changed since v0.1.1. Models now use a binary
"machine endian" format. To output a model in that format, build RNNoise
with that model and use the dump_weights_blob executable to output a
weights_blob.bin binary file. That file can then be used with the
rnnoise_model_from_file() API call. Note that the model object MUST NOT
be deleted while the RNNoise state is active and the file MUST NOT
be closed.
To avoid including the default model in the build (e.g. to reduce download
size) and rely only on model loading, add -DUSE_WEIGHTS_FILE to the CFLAGS.
To be able to load different models, the model size (and header file) needs
to patch the size use during build. Otherwise the model will not load
We provide a "little" model with half as an alternative. To use the smaller
model, rename rnnoise_data_little.c to rnnoise_data.c. It is possible
to build both the regular and little binary weights and load any of them
at run time since the little model has the same size as the regular one
(except for the increased sparsity).
+61
View File
@@ -0,0 +1,61 @@
========================================================================================
Notes from DL1YCF (Start)
========================================================================================
The material put into piHPDSR is meant as a minimal model for the ease of the user,
it is generally recommended to use "installed" versions of RNNoise compiled
with CPU-specific optimisations etc.
The RNNoise code within pihpsdr comes from
https://github.com/xiph/rnnoise.git
A new "Makefile" has been provided that just compiles the files needed with standard
optimisation levels. This means, that only the file
rnnoise/Makefile
within the pihpsdr directory differ from those in https://github.com/xiph/rnnoise.git.
The RNNnoise model is not contained in the above repository but downloaded during
installation of rnnoise. The model is originally contained in the files
rnnoise/src/rnnoise_data.c (originally, 80 MByte)
rnnoise/src/rnnoise_data.h (1 KByte)
The file rnnoise_data.c (80 MByte) is not only larger that the recommended
maximum file size in github (50 MByte), its compilation runs out of memory
on RaspPis with only 1 GByte of main memory. Therefore it has been split
into six parts. This means, rnnoise_data.c has been changed (it is now only
16 KByte long) and most of its contents have been moved to the following
six header files:
rnnoise/src/rnnoise_data_1.h (16 MByte)
rnnoise/src/rnnoise_data_2.h (13 MByte)
rnnoise/src/rnnoise_data_3.h (12 MByte)
rnnoise/src/rnnoise_data_4.h (14 MByte)
rnnoise/src/rnnoise_data_5.h (11 MByte)
rnnoise/src/rnnoise_data_6.h ( 9 MByte)
The following six *.c files, each of this is < 100 Bytes long, each include one
of these header files and can be compiled separately:
rnnoise/src/rnnoise_data_1.c
rnnoise/src/rnnoise_data_2.c
rnnoise/src/rnnoise_data_3.c
rnnoise/src/rnnoise_data_4.c
rnnoise/src/rnnoise_data_5.c
rnnoise/src/rnnoise_data_6.c
These changes now make it possible to compile pihpsdr with rnnnoise on a RaspPi
or a RaspPi ComputeModul with only 1 GByte of main memory.
Consequences:
Upgrading the model (that is, adapting a new file rnnoise/src/rnnoise_data.c) is somewhat
involved. Upgrading rnnoise itself (*.h and *.c files in rnnoise/src other than rnnoise_data.c
and rnnoise_data.h) is straightforward.
========================================================================================
Notes from DL1YCF (End)
========================================================================================
+131
View File
@@ -0,0 +1,131 @@
/* Copyright (c) 2018 Gregor Richards
* Copyright (c) 2017 Mozilla */
/*
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef RNNOISE_H
#define RNNOISE_H 1
#include <stdio.h>
#ifdef __cplusplus
extern "C" {
#endif
#ifndef RNNOISE_EXPORT
# if defined(WIN32)
# if defined(RNNOISE_BUILD) && defined(DLL_EXPORT)
# define RNNOISE_EXPORT __declspec(dllexport)
# else
# define RNNOISE_EXPORT
# endif
# elif defined(__GNUC__) && defined(RNNOISE_BUILD)
# define RNNOISE_EXPORT __attribute__ ((visibility ("default")))
# else
# define RNNOISE_EXPORT
# endif
#endif
typedef struct DenoiseState DenoiseState;
typedef struct RNNModel RNNModel;
/**
* Return the size of DenoiseState
*/
RNNOISE_EXPORT int rnnoise_get_size(void);
/**
* Return the number of samples processed by rnnoise_process_frame at a time
*/
RNNOISE_EXPORT int rnnoise_get_frame_size(void);
/**
* Initializes a pre-allocated DenoiseState
*
* If model is NULL the default model is used.
*
* See: rnnoise_create() and rnnoise_model_from_file()
*/
RNNOISE_EXPORT int rnnoise_init(DenoiseState *st, RNNModel *model);
/**
* Allocate and initialize a DenoiseState
*
* If model is NULL the default model is used.
*
* The returned pointer MUST be freed with rnnoise_destroy().
*/
RNNOISE_EXPORT DenoiseState *rnnoise_create(RNNModel *model);
/**
* Free a DenoiseState produced by rnnoise_create.
*
* The optional custom model must be freed by rnnoise_model_free() after.
*/
RNNOISE_EXPORT void rnnoise_destroy(DenoiseState *st);
/**
* Denoise a frame of samples
*
* in and out must be at least rnnoise_get_frame_size() large.
*/
RNNOISE_EXPORT float rnnoise_process_frame(DenoiseState *st, float *out, const float *in);
/**
* Load a model from a memory buffer
*
* It must be deallocated with rnnoise_model_free() and the buffer must remain
* valid until after the returned object is destroyed.
*/
RNNOISE_EXPORT RNNModel *rnnoise_model_from_buffer(const void *ptr, int len);
/**
* Load a model from a file
*
* It must be deallocated with rnnoise_model_free() and the file must not be
* closed until the returned object is destroyed.
*/
RNNOISE_EXPORT RNNModel *rnnoise_model_from_file(FILE *f);
/**
* Load a model from a file name
*
* It must be deallocated with rnnoise_model_free()
*/
RNNOISE_EXPORT RNNModel *rnnoise_model_from_filename(const char *filename);
/**
* Free a custom model
*
* It must be called after all the DenoiseStates referring to it are freed.
*/
RNNOISE_EXPORT void rnnoise_model_free(RNNModel *model);
#ifdef __cplusplus
}
#endif
#endif
Binary file not shown.
+182
View File
@@ -0,0 +1,182 @@
/*Copyright (c) 2003-2004, Mark Borgerding
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.*/
#ifndef KISS_FFT_GUTS_H
#define KISS_FFT_GUTS_H
#define MIN(a,b) ((a)<(b) ? (a):(b))
#define MAX(a,b) ((a)>(b) ? (a):(b))
/* kiss_fft.h
defines kiss_fft_scalar as either short or a float type
and defines
typedef struct { kiss_fft_scalar r; kiss_fft_scalar i; }kiss_fft_cpx; */
#include "kiss_fft.h"
/*
Explanation of macros dealing with complex math:
C_MUL(m,a,b) : m = a*b
C_FIXDIV( c , div ) : if a fixed point impl., c /= div. noop otherwise
C_SUB( res, a,b) : res = a - b
C_SUBFROM( res , a) : res -= a
C_ADDTO( res , a) : res += a
* */
#ifdef FIXED_POINT
#include "arch.h"
#define SAMP_MAX 2147483647
#define TWID_MAX 32767
#define TRIG_UPSCALE 1
#define SAMP_MIN -SAMP_MAX
# define S_MUL(a,b) MULT16_32_Q15(b, a)
# define C_MUL(m,a,b) \
do{ (m).r = SUB32_ovflw(S_MUL((a).r,(b).r) , S_MUL((a).i,(b).i)); \
(m).i = ADD32_ovflw(S_MUL((a).r,(b).i) , S_MUL((a).i,(b).r)); }while(0)
# define C_MULC(m,a,b) \
do{ (m).r = ADD32_ovflw(S_MUL((a).r,(b).r) , S_MUL((a).i,(b).i)); \
(m).i = SUB32_ovflw(S_MUL((a).i,(b).r) , S_MUL((a).r,(b).i)); }while(0)
# define C_MULBYSCALAR( c, s ) \
do{ (c).r = S_MUL( (c).r , s ) ;\
(c).i = S_MUL( (c).i , s ) ; }while(0)
# define DIVSCALAR(x,k) \
(x) = S_MUL( x, (TWID_MAX-((k)>>1))/(k)+1 )
# define C_FIXDIV(c,div) \
do { DIVSCALAR( (c).r , div); \
DIVSCALAR( (c).i , div); }while (0)
#define C_ADD( res, a,b)\
do {(res).r=ADD32_ovflw((a).r,(b).r); (res).i=ADD32_ovflw((a).i,(b).i); \
}while(0)
#define C_SUB( res, a,b)\
do {(res).r=SUB32_ovflw((a).r,(b).r); (res).i=SUB32_ovflw((a).i,(b).i); \
}while(0)
#define C_ADDTO( res , a)\
do {(res).r = ADD32_ovflw((res).r, (a).r); (res).i = ADD32_ovflw((res).i,(a).i);\
}while(0)
#define C_SUBFROM( res , a)\
do {(res).r = ADD32_ovflw((res).r,(a).r); (res).i = SUB32_ovflw((res).i,(a).i); \
}while(0)
#if defined(OPUS_ARM_INLINE_ASM)
#include "arm/kiss_fft_armv4.h"
#endif
#if defined(OPUS_ARM_INLINE_EDSP)
#include "arm/kiss_fft_armv5e.h"
#endif
#if defined(MIPSr1_ASM)
#include "mips/kiss_fft_mipsr1.h"
#endif
#else /* not FIXED_POINT*/
# define S_MUL(a,b) ( (a)*(b) )
#define C_MUL(m,a,b) \
do{ (m).r = (a).r*(b).r - (a).i*(b).i;\
(m).i = (a).r*(b).i + (a).i*(b).r; }while(0)
#define C_MULC(m,a,b) \
do{ (m).r = (a).r*(b).r + (a).i*(b).i;\
(m).i = (a).i*(b).r - (a).r*(b).i; }while(0)
#define C_MUL4(m,a,b) C_MUL(m,a,b)
# define C_FIXDIV(c,div) /* NOOP */
# define C_MULBYSCALAR( c, s ) \
do{ (c).r *= (s);\
(c).i *= (s); }while(0)
#endif
#ifndef CHECK_OVERFLOW_OP
# define CHECK_OVERFLOW_OP(a,op,b) /* noop */
#endif
#ifndef C_ADD
#define C_ADD( res, a,b)\
do { \
CHECK_OVERFLOW_OP((a).r,+,(b).r)\
CHECK_OVERFLOW_OP((a).i,+,(b).i)\
(res).r=(a).r+(b).r; (res).i=(a).i+(b).i; \
}while(0)
#define C_SUB( res, a,b)\
do { \
CHECK_OVERFLOW_OP((a).r,-,(b).r)\
CHECK_OVERFLOW_OP((a).i,-,(b).i)\
(res).r=(a).r-(b).r; (res).i=(a).i-(b).i; \
}while(0)
#define C_ADDTO( res , a)\
do { \
CHECK_OVERFLOW_OP((res).r,+,(a).r)\
CHECK_OVERFLOW_OP((res).i,+,(a).i)\
(res).r += (a).r; (res).i += (a).i;\
}while(0)
#define C_SUBFROM( res , a)\
do {\
CHECK_OVERFLOW_OP((res).r,-,(a).r)\
CHECK_OVERFLOW_OP((res).i,-,(a).i)\
(res).r -= (a).r; (res).i -= (a).i; \
}while(0)
#endif /* C_ADD defined */
#ifdef FIXED_POINT
/*# define KISS_FFT_COS(phase) TRIG_UPSCALE*floor(MIN(32767,MAX(-32767,.5+32768 * cos (phase))))
# define KISS_FFT_SIN(phase) TRIG_UPSCALE*floor(MIN(32767,MAX(-32767,.5+32768 * sin (phase))))*/
# define KISS_FFT_COS(phase) floor(.5+TWID_MAX*cos (phase))
# define KISS_FFT_SIN(phase) floor(.5+TWID_MAX*sin (phase))
# define HALF_OF(x) ((x)>>1)
#elif defined(USE_SIMD)
# define KISS_FFT_COS(phase) _mm_set1_ps( cos(phase) )
# define KISS_FFT_SIN(phase) _mm_set1_ps( sin(phase) )
# define HALF_OF(x) ((x)*_mm_set1_ps(.5f))
#else
# define KISS_FFT_COS(phase) (kiss_fft_scalar) cos(phase)
# define KISS_FFT_SIN(phase) (kiss_fft_scalar) sin(phase)
# define HALF_OF(x) ((x)*.5f)
#endif
#define kf_cexp(x,phase) \
do{ \
(x)->r = KISS_FFT_COS(phase);\
(x)->i = KISS_FFT_SIN(phase);\
}while(0)
#define kf_cexp2(x,phase) \
do{ \
(x)->r = TRIG_UPSCALE*celt_cos_norm((phase));\
(x)->i = TRIG_UPSCALE*celt_cos_norm((phase)-32768);\
}while(0)
#endif /* KISS_FFT_GUTS_H */
+261
View File
@@ -0,0 +1,261 @@
/* Copyright (c) 2003-2008 Jean-Marc Valin
Copyright (c) 2007-2008 CSIRO
Copyright (c) 2007-2009 Xiph.Org Foundation
Written by Jean-Marc Valin */
/**
@file arch.h
@brief Various architecture definitions for CELT
*/
/*
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef ARCH_H
#define ARCH_H
#include "opus_types.h"
#include "common.h"
# if !defined(__GNUC_PREREQ)
# if defined(__GNUC__)&&defined(__GNUC_MINOR__)
# define __GNUC_PREREQ(_maj,_min) \
((__GNUC__<<16)+__GNUC_MINOR__>=((_maj)<<16)+(_min))
# else
# define __GNUC_PREREQ(_maj,_min) 0
# endif
# endif
#define CELT_SIG_SCALE 32768.f
#define celt_fatal(str) _celt_fatal(str, __FILE__, __LINE__);
#ifdef ENABLE_ASSERTIONS
#include <stdio.h>
#include <stdlib.h>
#ifdef __GNUC__
__attribute__((noreturn))
#endif
static OPUS_INLINE void _celt_fatal(const char *str, const char *file, int line)
{
fprintf (stderr, "Fatal (internal) error in %s, line %d: %s\n", file, line, str);
abort();
}
#define celt_assert(cond) {if (!(cond)) {celt_fatal("assertion failed: " #cond);}}
#define celt_assert2(cond, message) {if (!(cond)) {celt_fatal("assertion failed: " #cond "\n" message);}}
#else
#define celt_assert(cond)
#define celt_assert2(cond, message)
#endif
#define IMUL32(a,b) ((a)*(b))
#define MIN16(a,b) ((a) < (b) ? (a) : (b)) /**< Minimum 16-bit value. */
#define MAX16(a,b) ((a) > (b) ? (a) : (b)) /**< Maximum 16-bit value. */
#define MIN32(a,b) ((a) < (b) ? (a) : (b)) /**< Minimum 32-bit value. */
#define MAX32(a,b) ((a) > (b) ? (a) : (b)) /**< Maximum 32-bit value. */
#define IMIN(a,b) ((a) < (b) ? (a) : (b)) /**< Minimum int value. */
#define IMAX(a,b) ((a) > (b) ? (a) : (b)) /**< Maximum int value. */
#define UADD32(a,b) ((a)+(b))
#define USUB32(a,b) ((a)-(b))
/* Set this if opus_int64 is a native type of the CPU. */
/* Assume that all LP64 architectures have fast 64-bit types; also x86_64
(which can be ILP32 for x32) and Win64 (which is LLP64). */
#if defined(__x86_64__) || defined(__LP64__) || defined(_WIN64)
#define OPUS_FAST_INT64 1
#else
#define OPUS_FAST_INT64 0
#endif
#define PRINT_MIPS(file)
#ifdef FIXED_POINT
typedef opus_int16 opus_val16;
typedef opus_int32 opus_val32;
typedef opus_int64 opus_val64;
typedef opus_val32 celt_sig;
typedef opus_val16 celt_norm;
typedef opus_val32 celt_ener;
#define Q15ONE 32767
#define SIG_SHIFT 12
/* Safe saturation value for 32-bit signals. Should be less than
2^31*(1-0.85) to avoid blowing up on DC at deemphasis.*/
#define SIG_SAT (300000000)
#define NORM_SCALING 16384
#define DB_SHIFT 10
#define EPSILON 1
#define VERY_SMALL 0
#define VERY_LARGE16 ((opus_val16)32767)
#define Q15_ONE ((opus_val16)32767)
#define SCALEIN(a) (a)
#define SCALEOUT(a) (a)
#define ABS16(x) ((x) < 0 ? (-(x)) : (x))
#define ABS32(x) ((x) < 0 ? (-(x)) : (x))
static OPUS_INLINE opus_int16 SAT16(opus_int32 x) {
return x > 32767 ? 32767 : x < -32768 ? -32768 : (opus_int16)x;
}
#ifdef FIXED_DEBUG
#include "fixed_debug.h"
#else
#include "fixed_generic.h"
#ifdef OPUS_ARM_PRESUME_AARCH64_NEON_INTR
#include "arm/fixed_arm64.h"
#elif OPUS_ARM_INLINE_EDSP
#include "arm/fixed_armv5e.h"
#elif defined (OPUS_ARM_INLINE_ASM)
#include "arm/fixed_armv4.h"
#elif defined (BFIN_ASM)
#include "fixed_bfin.h"
#elif defined (TI_C5X_ASM)
#include "fixed_c5x.h"
#elif defined (TI_C6X_ASM)
#include "fixed_c6x.h"
#endif
#endif
#else /* FIXED_POINT */
typedef float opus_val16;
typedef float opus_val32;
typedef float opus_val64;
typedef float celt_sig;
typedef float celt_norm;
typedef float celt_ener;
#ifdef FLOAT_APPROX
/* This code should reliably detect NaN/inf even when -ffast-math is used.
Assumes IEEE 754 format. */
static OPUS_INLINE int celt_isnan(float x)
{
union {float f; opus_uint32 i;} in;
in.f = x;
return ((in.i>>23)&0xFF)==0xFF && (in.i&0x007FFFFF)!=0;
}
#else
#ifdef __FAST_MATH__
#error Cannot build libopus with -ffast-math unless FLOAT_APPROX is defined. This could result in crashes on extreme (e.g. NaN) input
#endif
#define celt_isnan(x) ((x)!=(x))
#endif
#define Q15ONE 1.0f
#define NORM_SCALING 1.f
#define EPSILON 1e-15f
#define VERY_SMALL 1e-30f
#define VERY_LARGE16 1e15f
#define Q15_ONE ((opus_val16)1.f)
/* This appears to be the same speed as C99's fabsf() but it's more portable. */
#define ABS16(x) ((float)fabs(x))
#define ABS32(x) ((float)fabs(x))
#define QCONST16(x,bits) (x)
#define QCONST32(x,bits) (x)
#define NEG16(x) (-(x))
#define NEG32(x) (-(x))
#define NEG32_ovflw(x) (-(x))
#define EXTRACT16(x) (x)
#define EXTEND32(x) (x)
#define SHR16(a,shift) (a)
#define SHL16(a,shift) (a)
#define SHR32(a,shift) (a)
#define SHL32(a,shift) (a)
#define PSHR32(a,shift) (a)
#define VSHR32(a,shift) (a)
#define PSHR(a,shift) (a)
#define SHR(a,shift) (a)
#define SHL(a,shift) (a)
#define SATURATE(x,a) (x)
#define SATURATE16(x) (x)
#define ROUND16(a,shift) (a)
#define SROUND16(a,shift) (a)
#define HALF16(x) (.5f*(x))
#define HALF32(x) (.5f*(x))
#define ADD16(a,b) ((a)+(b))
#define SUB16(a,b) ((a)-(b))
#define ADD32(a,b) ((a)+(b))
#define SUB32(a,b) ((a)-(b))
#define ADD32_ovflw(a,b) ((a)+(b))
#define SUB32_ovflw(a,b) ((a)-(b))
#define MULT16_16_16(a,b) ((a)*(b))
#define MULT16_16(a,b) ((opus_val32)(a)*(opus_val32)(b))
#define MAC16_16(c,a,b) ((c)+(opus_val32)(a)*(opus_val32)(b))
#define MULT16_32_Q15(a,b) ((a)*(b))
#define MULT16_32_Q16(a,b) ((a)*(b))
#define MULT32_32_Q31(a,b) ((a)*(b))
#define MAC16_32_Q15(c,a,b) ((c)+(a)*(b))
#define MAC16_32_Q16(c,a,b) ((c)+(a)*(b))
#define MULT16_16_Q11_32(a,b) ((a)*(b))
#define MULT16_16_Q11(a,b) ((a)*(b))
#define MULT16_16_Q13(a,b) ((a)*(b))
#define MULT16_16_Q14(a,b) ((a)*(b))
#define MULT16_16_Q15(a,b) ((a)*(b))
#define MULT16_16_P15(a,b) ((a)*(b))
#define MULT16_16_P13(a,b) ((a)*(b))
#define MULT16_16_P14(a,b) ((a)*(b))
#define MULT16_32_P16(a,b) ((a)*(b))
#define DIV32_16(a,b) (((opus_val32)(a))/(opus_val16)(b))
#define DIV32(a,b) (((opus_val32)(a))/(opus_val32)(b))
#define SCALEIN(a) ((a)*CELT_SIG_SCALE)
#define SCALEOUT(a) ((a)*(1/CELT_SIG_SCALE))
#define SIG2WORD16(x) (x)
#endif /* !FIXED_POINT */
#ifndef GLOBAL_STACK_SIZE
#ifdef FIXED_POINT
#define GLOBAL_STACK_SIZE 120000
#else
#define GLOBAL_STACK_SIZE 120000
#endif
#endif
#endif /* ARCH_H */
+174
View File
@@ -0,0 +1,174 @@
/* Copyright (c) 2009-2010 Xiph.Org Foundation
Written by Jean-Marc Valin */
/*
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "celt_lpc.h"
#include "arch.h"
#include "common.h"
#include "pitch.h"
#include "denoise.h"
void rnn_lpc(
opus_val16 *_lpc, /* out: [0...p-1] LPC coefficients */
const opus_val32 *ac, /* in: [0...p] autocorrelation values */
int p
)
{
int i, j;
opus_val32 r;
opus_val32 error = ac[0];
#ifdef FIXED_POINT
opus_val32 lpc[LPC_ORDER];
#else
float *lpc = _lpc;
#endif
RNN_CLEAR(lpc, p);
if (ac[0] != 0)
{
for (i = 0; i < p; i++) {
/* Sum up this iteration's reflection coefficient */
opus_val32 rr = 0;
for (j = 0; j < i; j++)
rr += MULT32_32_Q31(lpc[j],ac[i - j]);
rr += SHR32(ac[i + 1],3);
r = -SHL32(rr,3)/error;
/* Update LPC coefficients and total error */
lpc[i] = SHR32(r,3);
for (j = 0; j < (i+1)>>1; j++)
{
opus_val32 tmp1, tmp2;
tmp1 = lpc[j];
tmp2 = lpc[i-1-j];
lpc[j] = tmp1 + MULT32_32_Q31(r,tmp2);
lpc[i-1-j] = tmp2 + MULT32_32_Q31(r,tmp1);
}
error = error - MULT32_32_Q31(MULT32_32_Q31(r,r),error);
/* Bail out once we get 30 dB gain */
#ifdef FIXED_POINT
if (error<SHR32(ac[0],10))
break;
#else
if (error<.001f*ac[0])
break;
#endif
}
}
#ifdef FIXED_POINT
for (i=0;i<p;i++)
_lpc[i] = ROUND16(lpc[i],16);
#endif
}
int rnn_autocorr(
const opus_val16 *x, /* in: [0...n-1] samples x */
opus_val32 *ac, /* out: [0...lag-1] ac values */
const opus_val16 *window,
int overlap,
int lag,
int n)
{
opus_val32 d;
int i, k;
int fastN=n-lag;
int shift;
const opus_val16 *xptr;
opus_val16 xx[PITCH_BUF_SIZE/2];
celt_assert(n>0);
celt_assert(n<=PITCH_BUF_SIZE/2)
celt_assert(overlap>=0);
if (overlap == 0)
{
xptr = x;
} else {
for (i=0;i<n;i++)
xx[i] = x[i];
for (i=0;i<overlap;i++)
{
xx[i] = MULT16_16_Q15(x[i],window[i]);
xx[n-i-1] = MULT16_16_Q15(x[n-i-1],window[i]);
}
xptr = xx;
}
shift=0;
#ifdef FIXED_POINT
{
opus_val32 ac0;
ac0 = 1+(n<<7);
if (n&1) ac0 += SHR32(MULT16_16(xptr[0],xptr[0]),9);
for(i=(n&1);i<n;i+=2)
{
ac0 += SHR32(MULT16_16(xptr[i],xptr[i]),9);
ac0 += SHR32(MULT16_16(xptr[i+1],xptr[i+1]),9);
}
shift = celt_ilog2(ac0)-30+10;
shift = (shift)/2;
if (shift>0)
{
for(i=0;i<n;i++)
xx[i] = PSHR32(xptr[i], shift);
xptr = xx;
} else
shift = 0;
}
#endif
rnn_pitch_xcorr(xptr, xptr, ac, fastN, lag+1);
for (k=0;k<=lag;k++)
{
for (i = k+fastN, d = 0; i < n; i++)
d = MAC16_16(d, xptr[i], xptr[i-k]);
ac[k] += d;
}
#ifdef FIXED_POINT
shift = 2*shift;
if (shift<=0)
ac[0] += SHL32((opus_int32)1, -shift);
if (ac[0] < 268435456)
{
int shift2 = 29 - EC_ILOG(ac[0]);
for (i=0;i<=lag;i++)
ac[i] = SHL32(ac[i], shift2);
shift -= shift2;
} else if (ac[0] >= 536870912)
{
int shift2=1;
if (ac[0] >= 1073741824)
shift2++;
for (i=0;i<=lag;i++)
ac[i] = SHR32(ac[i], shift2);
shift += shift2;
}
#endif
return shift;
}
+45
View File
@@ -0,0 +1,45 @@
/* Copyright (c) 2009-2010 Xiph.Org Foundation
Written by Jean-Marc Valin */
/*
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef PLC_H
#define PLC_H
#include "arch.h"
#include "common.h"
#if defined(OPUS_X86_MAY_HAVE_SSE4_1)
#include "x86/celt_lpc_sse.h"
#endif
#define LPC_ORDER 24
void rnn_lpc(opus_val16 *_lpc, const opus_val32 *ac, int p);
int rnn_autocorr(const opus_val16 *x, opus_val32 *ac,
const opus_val16 *window, int overlap, int lag, int n);
#endif /* PLC_H */

Some files were not shown because too many files have changed in this diff Show More