Compare commits

...

40 Commits

Author SHA1 Message Date
lawl 1aa277446b Add PipeWire support to README 3 years ago
lawl a2a45bc88b UI: Add confirm screen for unload/reload when device in use
Reloading/Unloading destroys the virtual device that an application
is currently using. Add a warning that this may cause strange behaviour.
3 years ago
lawl cb2ee053b6 Handle non-semver versions in audio client gracefully-ish 3 years ago
lawl d1aa44c3c7 pulseaudio: update module with fixed cookie lookup 3 years ago
lawl 077b4763fe CLI: Cleanup temp files on exit
Fixes #135
3 years ago
Andreas Schneider 809f184e87 c:ladspa: Fix linking with libm
Fixes #140
3 years ago
Andreas Schneider a32f73509f Revert "Temporarily force clang if available"
This reverts commit c2bfe0202f.
3 years ago
lawl c2bfe0202f Temporarily force clang if available
GCC10 seems to refuse to link libm. GCC11 and clang seem to work fine.
3 years ago
lawl a57cc20110 display error screen if module load fails 3 years ago
lawl fad3e1d4f4 Add support for PipeWire
Warning: Alpha quality. Please manage your expectations.
3 years ago
Sefa Eyeoglu 9b651eb701
Implement distribution-specific builds
Make more things configurable at compile time so building binaries for distributions is easier while complying with the license.
3 years ago
lawl a0620ae92c Remove 3rd party package warning from README
The distribution that shall not be named (btw) has decided to
comply with the license. (For now?)
3 years ago
lawl 8b11b649ee Don't log to a file anymore. Log to stdout if called with -log
Additionally refactor the CLI a bit more to make that possible
3 years ago
lawl 0ef8229e6f License: Use convey instead of distribute for wording 3 years ago
lawl fb46112181 Add link of a demo to readme
Closes #19
3 years ago
Lorenz 4853c5c8d7
Fix not printing errror message
The CLI should print errors to stderr, and not write to the log file.
3 years ago
lawl 95e82fcbfd CLI: Factor CLI out of main
Our CLI shared some data structures, like the pulseaudio connection with the GUI.
This could lead to issues where the UI would break when code on the CLI was changed
because the the GUI must handle things differently, by nature of working differently.

Add more separation between those, so changes on the CLI are less likely to break the GUI.
3 years ago
lawl 6ea788a0c0 Refactor: Remove old method of removing rlimit for pulseaudio
Previously we had the capability to remove the rlimit on pulseaudio
by calling ourselves via pkexec to elevate privileges.
A while ago we switched to fully using capabilities.
We now remove the old legacy codepath.
3 years ago
lawl 245455d508 Refactor UI: Rename "screen" to "view" 3 years ago
lawl 6f901716ed Refactor UI: Use a stack of views to handle what to display
Previously we dispatched calls to different drawing function with
a bunch of if statements in the main draw function.

Use a stack of drawing functions instead.
3 years ago
lawl fe14585a37 make troubleshooting section more visible 3 years ago
lawl 8b054c20a9 Update License 3 years ago
lawl ee91fea993 Use -O2 for LADSPA module 3 years ago
lawl 3119fc5997 Use go:embed instead of scripts/embedbinary.go 3 years ago
lawl 5a55f0538a Update 'Special thanks to' 3 years ago
lawl 77a8c73436 Update build instructions 3 years ago
lawl fcb4db042c Add information about potentially having to relog for PATH changes 3 years ago
lawl 7172888478 Convert fmt print to log print 3 years ago
lawl 335b8a2567 Fix unloading via CLI (-u)
Squashed commit of the following:

commit 19532d64a7f7337da7f844d52198417ceaf60e80
Author: lawl <github@dumbinter.net>
Date:   Thu Feb 4 10:40:27 2021 +0100

    Fix unloading paClient for UI

commit 66ece81e17589988f92059b44b2659fdca0c4ca1
Merge: 1c97cfe 60d06f2
Author: lawl <github@dumbinter.net>
Date:   Thu Feb 4 10:36:13 2021 +0100

    Merge branch 'fix_cli_unload' of git://github.com/hador/NoiseTorch into hador-fix_cli_unload

commit 60d06f25aa
Author: andreafa <andreafa@amazon.com>
Date:   Thu Feb 4 08:51:48 2021 +0000

    Fix cli unload (-u).

    The findModule method in module.go requires a valid paClient to be
    populated in the context object.
    Added logging if unloadSupressor fails when called via the -u CLI flag.
3 years ago
Andrea Fagiani 1c97cfe43c
Use 24-hour clock when adding timestamps to log file names. (#92)
Co-authored-by: andreafa <andreafa@amazon.com>
3 years ago
lawl 47d8188b3a Add warning if FS capabilities don't match process capabilities 3 years ago
lawl febccb207b Tune values for fixed latency devices 3 years ago
lawl 54f9e012e3 Make sure we remove rlimit for unloading too 3 years ago
lawl 730dc35d94 Require capabilities on startup 3 years ago
lawl 4513db1baf use different params for fixed latency devices to hopefully correct latency faster 3 years ago
lawl e7072b2bf9 Add -Wall, -Werr 3 years ago
lawl b747084bb0 Replace noise-suppression-for-voice with own implementation
In preparation for PipeWire support we required our own implementation anyways.
To have a shared code base and potentially enable some other tighter integration
between the LADSPA module and NoiseTorch, we move to our own implementation.
3 years ago
lawl 4f7ac325ab
Update README.md 3 years ago
lawl f3c11be919 remove more commented code (yuck) 3 years ago
lawl 313aca54a2 remove commented out code 3 years ago

3
.gitignore vendored

@ -1,5 +1,2 @@
bin/
librnnoise.go
version.go
licenses.go
patreon.go

4
.gitmodules vendored

@ -1,4 +0,0 @@
[submodule "librnnoise_ladspa"]
path = librnnoise_ladspa
url = https://github.com/werman/noise-suppression-for-voice
ignore = dirty

@ -1,5 +1,15 @@
NoiseTorch (c) 2020 lawl (github.com/lawl)
SPDX-License-Identifier: GPL-3.0-or-later
NoiseTorch (c) 2020-2021 lawl (github.com/lawl)
This software is distributed under the GNU General Public License Version 3 ("GPLv3").
In accordance with Section 7, subsection `c` of the GPLv3 the following additional term(s) apply:
* Conveying modified versions of this program ("NoiseTorch") must be marked as modified in a reasonable way.
Modified versions may not be conveyed to others under same name as the original program.
Package names, source code, user interfaces and other visible appearances of the program name should make it obvious for users
and potential users that the modified version differs from the original version of NoiseTorch it is based upon.
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007

@ -1,7 +1,11 @@
UPDATE_URL=https://noisetorch.epicgamer.org
UPDATE_PUBKEY=3mL+rBi4yBZ1wGimQ/oSQCjxELzgTh+673H4JdzQBOk=
VERSION := $(shell git describe --tags)
dev: rnnoise
mkdir -p bin/
go generate
go build -o bin/noisetorch
go build -ldflags '-X main.version=${VERSION}' -o bin/noisetorch
release: rnnoise
mkdir -p bin/
mkdir -p tmp/
@ -14,7 +18,7 @@ release: rnnoise
mkdir -p tmp/.local/bin/
go generate
CGO_ENABLED=0 GOOS=linux go build -tags release -a -ldflags '-s -w -extldflags "-static"' .
CGO_ENABLED=0 GOOS=linux go build -tags release -a -ldflags '-s -w -extldflags "-static" -X main.version=${VERSION} -X main.distribution=official -X main.updateURL=${UPDATE_URL} -X main.publicKeyString=${UPDATE_PUBKEY}' .
upx noisetorch
mv noisetorch tmp/.local/bin/
cd tmp/; \
@ -23,7 +27,6 @@ release: rnnoise
go run scripts/signer.go -s
git describe --tags > bin/version.txt
rnnoise:
cd librnnoise_ladspa/; \
cmake . -DBUILD_VST_PLUGIN=OFF -DBUILD_LV2_PLUGIN=OFF -DBUILD_LADSPA_PLUGIN=ON; \
cd c/ladspa; \
make

@ -5,7 +5,7 @@
[![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)
[![Last Release](https://img.shields.io/github/v/release/lawl/NoiseTorch?label=latest&style=flat-square)](https://github.com/lawl/NoiseTorch/releases)
NoiseTorch is an easy to use open source application for Linux with PulseAudio. It creates a virtual microphone that suppresses noise, in any application. Use whichever conferencing or VOIP application you like and simply select the NoiseTorch Virtual Microphone as input to torch the sound of your mechanical keyboard, computer fans, trains and the likes.
NoiseTorch is an easy to use open source application for Linux with PulseAudio or PipeWire. It creates a virtual microphone that suppresses noise, in any application. Use whichever conferencing or VOIP application you like and simply select the NoiseTorch Virtual Microphone as input to torch the sound of your mechanical keyboard, computer fans, trains and the likes.
Don't forget to ~~like, comment and subscribe~~ leave a star ⭐ if this sounds useful to you!
@ -17,6 +17,10 @@ Then simply select NoiseTorch as your microphone in any application. OBS, Mumble
![](https://i.imgur.com/nimi7Ne.png)
## Demo
Linux For Everyone has a good demo video [here](https://www.youtube.com/watch?v=DzN9rYNeeIU).
## Features
* Two click setup of your virtual denoising microphone
* A single, small, statically linked, self-contained binary
@ -38,7 +42,11 @@ With gnome this can be done with:
You now have a `noisetorch` binary and desktop entry on your system.
If noisetorch doesn't start after installation, you may also have to make sure that `.local/bin` is in your PATH. On most distributions e.g. Ubuntu, this should be the case by default. If it's not, make sure to append
Give it the required permissions with `setcap`:
sudo setcap 'CAP_SYS_RESOURCE=+ep' ~/.local/bin/noisetorch
If noisetorch doesn't start after installation, you may also have to make sure that `~/.local/bin` is in your PATH. On most distributions e.g. Ubuntu, this should be the case by default. If it's not, make sure to append
```
if [ -d "$HOME/.local/bin" ] ; then
@ -46,7 +54,7 @@ if [ -d "$HOME/.local/bin" ] ; then
fi
```
to your `~/.profile`.
to your `~/.profile`. If you do already have that, you may have to log in and out for it to actually apply if this is the first time you're using `~/.local/bin`.
#### Uninstall
@ -54,11 +62,9 @@ to your `~/.profile`.
rm ~/.local/share/applications/noisetorch.desktop
rm ~/.local/share/icons/hicolor/256x256/apps/noisetorch.png
## Third party packages (like AUR)
Please do not use them. Some are known to mess with configuration files and will break things even if you did install a NoiseTorch package from an official source later on. If you had ever installed NoiseTorch from a non-official source, make sure to *completely* remove every trace, including deleting your `~/.config/noisetorch` directory, before reporting an issue.
## Troubleshooting
If you have been maintaining a third party package for a while and would like it to be blessed as supported, please send me an e-mail.
Please see the [Troubleshooting](https://github.com/lawl/NoiseTorch/wiki/Troubleshooting) section in the wiki.
## Usage
@ -74,10 +80,6 @@ Please keep in mind that you will need to reload NoiseTorch for these changes to
Once NoiseTorch has been loaded, feel free to close the window, the virtual microphone will continue working until you explicitly unload it. The NoiseTorch process is not required anymore once it has been loaded.
## Troubleshooting
Please see the [Troubleshooting](https://github.com/lawl/NoiseTorch/wiki/Troubleshooting) section in the wiki.
## Latency
NoiseTorch may introduce a small amount of latency for microphone filtering. The amount of inherent latency introduced by noise supression is 10ms, this is very low and should not be a problem. Additionally PulseAudio currently introduces a variable amount of latency that depends on your system. Lowering this latency [requires a change in PulseAudio](https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/issues/120).
@ -91,14 +93,12 @@ Install the Go compiler from [golang.org](https://golang.org/). And make sure yo
```shell
git clone https://github.com/lawl/NoiseTorch # Clone the repository
cd NoiseTorch # cd into the cloned repository
git submodule init # Tell git to look at submodules
git submodule update # Update submodules
make # build it
```
## Special thanks to
* [xiph.org](https://xiph.org)/[Mozilla's](https://mozilla.org) excellent [RNNoise](https://jmvalin.ca/demo/rnnoise/).
* [@werman](https://github.com/werman/)'s [LADSPA/VST wrapper](https://github.com/werman/noise-suppression-for-voice/) allowing us to load RNNoise into PulseAudio.
* [@werman](https://github.com/werman/)'s [noise-suppression-for-voice](https://github.com/werman/noise-suppression-for-voice/) for the inspiration
* [@aarzilli](https://github.com/aarzilli/)'s [nucular](https://github.com/aarzilli/nucular) GUI toolkit for Go.
* [Salee Design](https://www.salleedesign.com) (info@salleedesign.com)'s Microphone Icon under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/)
* [Sallee Design](https://www.salleedesign.com) (info@salleedesign.com)'s Microphone Icon under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/)

@ -0,0 +1,2 @@
*.o
rnnoise_ladspa.so

@ -0,0 +1,3 @@
default:
$(CC) -Wall -Werror -O2 -c -fPIC ../ringbuf.c ../rnnoise/*.c module.c
$(CC) -o rnnoise_ladspa.so *.o -shared -Wl,--version-script=export.txt -lm

@ -0,0 +1,4 @@
{
global: *ladspa*;
local: *;
};

@ -0,0 +1,603 @@
/* ladspa.h
Linux Audio Developer's Simple Plugin API Version 1.1[LGPL].
Copyright (C) 2000-2002 Richard W.E. Furse, Paul Barton-Davis,
Stefan Westerfeld.
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA. */
#ifndef LADSPA_INCLUDED
#define LADSPA_INCLUDED
#define LADSPA_VERSION "1.1"
#define LADSPA_VERSION_MAJOR 1
#define LADSPA_VERSION_MINOR 1
#ifdef __cplusplus
extern "C" {
#endif
/*****************************************************************************/
/* Overview:
There is a large number of synthesis packages in use or development
on the Linux platform at this time. This API (`The Linux Audio
Developer's Simple Plugin API') attempts to give programmers the
ability to write simple `plugin' audio processors in C/C++ and link
them dynamically (`plug') into a range of these packages (`hosts').
It should be possible for any host and any plugin to communicate
completely through this interface.
This API is deliberately short and simple. To achieve compatibility
with a range of promising Linux sound synthesis packages it
attempts to find the `greatest common divisor' in their logical
behaviour. Having said this, certain limiting decisions are
implicit, notably the use of a fixed type (LADSPA_Data) for all
data transfer and absence of a parameterised `initialisation'
phase. See below for the LADSPA_Data typedef.
Plugins are expected to distinguish between control and audio
data. Plugins have `ports' that are inputs or outputs for audio or
control data and each plugin is `run' for a `block' corresponding
to a short time interval measured in samples. Audio data is
communicated using arrays of LADSPA_Data, allowing a block of audio
to be processed by the plugin in a single pass. Control data is
communicated using single LADSPA_Data values. Control data has a
single value at the start of a call to the `run()' or `run_adding()'
function, and may be considered to remain this value for its
duration. The plugin may assume that all its input and output ports
have been connected to the relevant data location (see the
`connect_port()' function below) before it is asked to run.
Plugins will reside in shared object files suitable for dynamic
linking by dlopen() and family. The file will provide a number of
`plugin types' that can be used to instantiate actual plugins
(sometimes known as `plugin instances') that can be connected
together to perform tasks.
This API contains very limited error-handling. */
/*****************************************************************************/
/* Fundamental data type passed in and out of plugin. This data type
is used to communicate audio samples and control values. It is
assumed that the plugin will work sensibly given any numeric input
value although it may have a preferred range (see hints below).
For audio it is generally assumed that 1.0f is the `0dB' reference
amplitude and is a `normal' signal level. */
typedef float LADSPA_Data;
/*****************************************************************************/
/* Special Plugin Properties:
Optional features of the plugin type are encapsulated in the
LADSPA_Properties type. This is assembled by ORing individual
properties together. */
typedef int LADSPA_Properties;
/* Property LADSPA_PROPERTY_REALTIME indicates that the plugin has a
real-time dependency (e.g. listens to a MIDI device) and so its
output must not be cached or subject to significant latency. */
#define LADSPA_PROPERTY_REALTIME 0x1
/* Property LADSPA_PROPERTY_INPLACE_BROKEN indicates that the plugin
may cease to work correctly if the host elects to use the same data
location for both input and output (see connect_port()). This
should be avoided as enabling this flag makes it impossible for
hosts to use the plugin to process audio `in-place.' */
#define LADSPA_PROPERTY_INPLACE_BROKEN 0x2
/* Property LADSPA_PROPERTY_HARD_RT_CAPABLE indicates that the plugin
is capable of running not only in a conventional host but also in a
`hard real-time' environment. To qualify for this the plugin must
satisfy all of the following:
(1) The plugin must not use malloc(), free() or other heap memory
management within its run() or run_adding() functions. All new
memory used in run() must be managed via the stack. These
restrictions only apply to the run() function.
(2) The plugin will not attempt to make use of any library
functions with the exceptions of functions in the ANSI standard C
and C maths libraries, which the host is expected to provide.
(3) The plugin will not access files, devices, pipes, sockets, IPC
or any other mechanism that might result in process or thread
blocking.
(4) The plugin will take an amount of time to execute a run() or
run_adding() call approximately of form (A+B*SampleCount) where A
and B depend on the machine and host in use. This amount of time
may not depend on input signals or plugin state. The host is left
the responsibility to perform timings to estimate upper bounds for
A and B. */
#define LADSPA_PROPERTY_HARD_RT_CAPABLE 0x4
#define LADSPA_IS_REALTIME(x) ((x) & LADSPA_PROPERTY_REALTIME)
#define LADSPA_IS_INPLACE_BROKEN(x) ((x) & LADSPA_PROPERTY_INPLACE_BROKEN)
#define LADSPA_IS_HARD_RT_CAPABLE(x) ((x) & LADSPA_PROPERTY_HARD_RT_CAPABLE)
/*****************************************************************************/
/* Plugin Ports:
Plugins have `ports' that are inputs or outputs for audio or
data. Ports can communicate arrays of LADSPA_Data (for audio
inputs/outputs) or single LADSPA_Data values (for control
input/outputs). This information is encapsulated in the
LADSPA_PortDescriptor type which is assembled by ORing individual
properties together.
Note that a port must be an input or an output port but not both
and that a port must be a control or audio port but not both. */
typedef int LADSPA_PortDescriptor;
/* Property LADSPA_PORT_INPUT indicates that the port is an input. */
#define LADSPA_PORT_INPUT 0x1
/* Property LADSPA_PORT_OUTPUT indicates that the port is an output. */
#define LADSPA_PORT_OUTPUT 0x2
/* Property LADSPA_PORT_CONTROL indicates that the port is a control
port. */
#define LADSPA_PORT_CONTROL 0x4
/* Property LADSPA_PORT_AUDIO indicates that the port is a audio
port. */
#define LADSPA_PORT_AUDIO 0x8
#define LADSPA_IS_PORT_INPUT(x) ((x) & LADSPA_PORT_INPUT)
#define LADSPA_IS_PORT_OUTPUT(x) ((x) & LADSPA_PORT_OUTPUT)
#define LADSPA_IS_PORT_CONTROL(x) ((x) & LADSPA_PORT_CONTROL)
#define LADSPA_IS_PORT_AUDIO(x) ((x) & LADSPA_PORT_AUDIO)
/*****************************************************************************/
/* Plugin Port Range Hints:
The host may wish to provide a representation of data entering or
leaving a plugin (e.g. to generate a GUI automatically). To make
this more meaningful, the plugin should provide `hints' to the host
describing the usual values taken by the data.
Note that these are only hints. The host may ignore them and the
plugin must not assume that data supplied to it is meaningful. If
the plugin receives invalid input data it is expected to continue
to run without failure and, where possible, produce a sensible
output (e.g. a high-pass filter given a negative cutoff frequency
might switch to an all-pass mode).
Hints are meaningful for all input and output ports but hints for
input control ports are expected to be particularly useful.
More hint information is encapsulated in the
LADSPA_PortRangeHintDescriptor type which is assembled by ORing
individual hint types together. Hints may require further
LowerBound and UpperBound information.
All the hint information for a particular port is aggregated in the
LADSPA_PortRangeHint structure. */
typedef int LADSPA_PortRangeHintDescriptor;
/* Hint LADSPA_HINT_BOUNDED_BELOW indicates that the LowerBound field
of the LADSPA_PortRangeHint should be considered meaningful. The
value in this field should be considered the (inclusive) lower
bound of the valid range. If LADSPA_HINT_SAMPLE_RATE is also
specified then the value of LowerBound should be multiplied by the
sample rate. */
#define LADSPA_HINT_BOUNDED_BELOW 0x1
/* Hint LADSPA_HINT_BOUNDED_ABOVE indicates that the UpperBound field
of the LADSPA_PortRangeHint should be considered meaningful. The
value in this field should be considered the (inclusive) upper
bound of the valid range. If LADSPA_HINT_SAMPLE_RATE is also
specified then the value of UpperBound should be multiplied by the
sample rate. */
#define LADSPA_HINT_BOUNDED_ABOVE 0x2
/* Hint LADSPA_HINT_TOGGLED indicates that the data item should be
considered a Boolean toggle. Data less than or equal to zero should
be considered `off' or `false,' and data above zero should be
considered `on' or `true.' LADSPA_HINT_TOGGLED may not be used in
conjunction with any other hint except LADSPA_HINT_DEFAULT_0 or
LADSPA_HINT_DEFAULT_1. */
#define LADSPA_HINT_TOGGLED 0x4
/* Hint LADSPA_HINT_SAMPLE_RATE indicates that any bounds specified
should be interpreted as multiples of the sample rate. For
instance, a frequency range from 0Hz to the Nyquist frequency (half
the sample rate) could be requested by this hint in conjunction
with LowerBound = 0 and UpperBound = 0.5. Hosts that support bounds
at all must support this hint to retain meaning. */
#define LADSPA_HINT_SAMPLE_RATE 0x8
/* Hint LADSPA_HINT_LOGARITHMIC indicates that it is likely that the
user will find it more intuitive to view values using a logarithmic
scale. This is particularly useful for frequencies and gains. */
#define LADSPA_HINT_LOGARITHMIC 0x10
/* Hint LADSPA_HINT_INTEGER indicates that a user interface would
probably wish to provide a stepped control taking only integer
values. Any bounds set should be slightly wider than the actual
integer range required to avoid floating point rounding errors. For
instance, the integer set {0,1,2,3} might be described as [-0.1,
3.1]. */
#define LADSPA_HINT_INTEGER 0x20
/* The various LADSPA_HINT_HAS_DEFAULT_* hints indicate a `normal'
value for the port that is sensible as a default. For instance,
this value is suitable for use as an initial value in a user
interface or as a value the host might assign to a control port
when the user has not provided one. Defaults are encoded using a
mask so only one default may be specified for a port. Some of the
hints make use of lower and upper bounds, in which case the
relevant bound or bounds must be available and
LADSPA_HINT_SAMPLE_RATE must be applied as usual. The resulting
default must be rounded if LADSPA_HINT_INTEGER is present. Default
values were introduced in LADSPA v1.1. */
#define LADSPA_HINT_DEFAULT_MASK 0x3C0
/* This default values indicates that no default is provided. */
#define LADSPA_HINT_DEFAULT_NONE 0x0
/* This default hint indicates that the suggested lower bound for the
port should be used. */
#define LADSPA_HINT_DEFAULT_MINIMUM 0x40
/* This default hint indicates that a low value between the suggested
lower and upper bounds should be chosen. For ports with
LADSPA_HINT_LOGARITHMIC, this should be exp(log(lower) * 0.75 +
log(upper) * 0.25). Otherwise, this should be (lower * 0.75 + upper
* 0.25). */
#define LADSPA_HINT_DEFAULT_LOW 0x80
/* This default hint indicates that a middle value between the
suggested lower and upper bounds should be chosen. For ports with
LADSPA_HINT_LOGARITHMIC, this should be exp(log(lower) * 0.5 +
log(upper) * 0.5). Otherwise, this should be (lower * 0.5 + upper *
0.5). */
#define LADSPA_HINT_DEFAULT_MIDDLE 0xC0
/* This default hint indicates that a high value between the suggested
lower and upper bounds should be chosen. For ports with
LADSPA_HINT_LOGARITHMIC, this should be exp(log(lower) * 0.25 +
log(upper) * 0.75). Otherwise, this should be (lower * 0.25 + upper
* 0.75). */
#define LADSPA_HINT_DEFAULT_HIGH 0x100
/* This default hint indicates that the suggested upper bound for the
port should be used. */
#define LADSPA_HINT_DEFAULT_MAXIMUM 0x140
/* This default hint indicates that the number 0 should be used. Note
that this default may be used in conjunction with
LADSPA_HINT_TOGGLED. */
#define LADSPA_HINT_DEFAULT_0 0x200
/* This default hint indicates that the number 1 should be used. Note
that this default may be used in conjunction with
LADSPA_HINT_TOGGLED. */
#define LADSPA_HINT_DEFAULT_1 0x240
/* This default hint indicates that the number 100 should be used. */
#define LADSPA_HINT_DEFAULT_100 0x280
/* This default hint indicates that the Hz frequency of `concert A'
should be used. This will be 440 unless the host uses an unusual
tuning convention, in which case it may be within a few Hz. */
#define LADSPA_HINT_DEFAULT_440 0x2C0
#define LADSPA_IS_HINT_BOUNDED_BELOW(x) ((x) & LADSPA_HINT_BOUNDED_BELOW)
#define LADSPA_IS_HINT_BOUNDED_ABOVE(x) ((x) & LADSPA_HINT_BOUNDED_ABOVE)
#define LADSPA_IS_HINT_TOGGLED(x) ((x) & LADSPA_HINT_TOGGLED)
#define LADSPA_IS_HINT_SAMPLE_RATE(x) ((x) & LADSPA_HINT_SAMPLE_RATE)
#define LADSPA_IS_HINT_LOGARITHMIC(x) ((x) & LADSPA_HINT_LOGARITHMIC)
#define LADSPA_IS_HINT_INTEGER(x) ((x) & LADSPA_HINT_INTEGER)
#define LADSPA_IS_HINT_HAS_DEFAULT(x) ((x) & LADSPA_HINT_DEFAULT_MASK)
#define LADSPA_IS_HINT_DEFAULT_MINIMUM(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \
== LADSPA_HINT_DEFAULT_MINIMUM)
#define LADSPA_IS_HINT_DEFAULT_LOW(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \
== LADSPA_HINT_DEFAULT_LOW)
#define LADSPA_IS_HINT_DEFAULT_MIDDLE(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \
== LADSPA_HINT_DEFAULT_MIDDLE)
#define LADSPA_IS_HINT_DEFAULT_HIGH(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \
== LADSPA_HINT_DEFAULT_HIGH)
#define LADSPA_IS_HINT_DEFAULT_MAXIMUM(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \
== LADSPA_HINT_DEFAULT_MAXIMUM)
#define LADSPA_IS_HINT_DEFAULT_0(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \
== LADSPA_HINT_DEFAULT_0)
#define LADSPA_IS_HINT_DEFAULT_1(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \
== LADSPA_HINT_DEFAULT_1)
#define LADSPA_IS_HINT_DEFAULT_100(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \
== LADSPA_HINT_DEFAULT_100)
#define LADSPA_IS_HINT_DEFAULT_440(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \
== LADSPA_HINT_DEFAULT_440)
typedef struct _LADSPA_PortRangeHint {
/* Hints about the port. */
LADSPA_PortRangeHintDescriptor HintDescriptor;
/* Meaningful when hint LADSPA_HINT_BOUNDED_BELOW is active. When
LADSPA_HINT_SAMPLE_RATE is also active then this value should be
multiplied by the relevant sample rate. */
LADSPA_Data LowerBound;
/* Meaningful when hint LADSPA_HINT_BOUNDED_ABOVE is active. When
LADSPA_HINT_SAMPLE_RATE is also active then this value should be
multiplied by the relevant sample rate. */
LADSPA_Data UpperBound;
} LADSPA_PortRangeHint;
/*****************************************************************************/
/* Plugin Handles:
This plugin handle indicates a particular instance of the plugin
concerned. It is valid to compare this to NULL (0 for C++) but
otherwise the host should not attempt to interpret it. The plugin
may use it to reference internal instance data. */
typedef void * LADSPA_Handle;
/*****************************************************************************/
/* Descriptor for a Type of Plugin:
This structure is used to describe a plugin type. It provides a
number of functions to examine the type, instantiate it, link it to
buffers and workspaces and to run it. */
typedef struct _LADSPA_Descriptor {
/* This numeric identifier indicates the plugin type
uniquely. Plugin programmers may reserve ranges of IDs from a
central body to avoid clashes. Hosts may assume that IDs are
below 0x1000000. */
unsigned long UniqueID;
/* This identifier can be used as a unique, case-sensitive
identifier for the plugin type within the plugin file. Plugin
types should be identified by file and label rather than by index
or plugin name, which may be changed in new plugin
versions. Labels must not contain white-space characters. */
const char * Label;
/* This indicates a number of properties of the plugin. */
LADSPA_Properties Properties;
/* This member points to the null-terminated name of the plugin
(e.g. "Sine Oscillator"). */
const char * Name;
/* This member points to the null-terminated string indicating the
maker of the plugin. This can be an empty string but not NULL. */
const char * Maker;
/* This member points to the null-terminated string indicating any
copyright applying to the plugin. If no Copyright applies the
string "None" should be used. */
const char * Copyright;
/* This indicates the number of ports (input AND output) present on
the plugin. */
unsigned long PortCount;
/* This member indicates an array of port descriptors. Valid indices
vary from 0 to PortCount-1. */
const LADSPA_PortDescriptor * PortDescriptors;
/* This member indicates an array of null-terminated strings
describing ports (e.g. "Frequency (Hz)"). Valid indices vary from
0 to PortCount-1. */
const char * const * PortNames;
/* This member indicates an array of range hints for each port (see
above). Valid indices vary from 0 to PortCount-1. */
const LADSPA_PortRangeHint * PortRangeHints;
/* This may be used by the plugin developer to pass any custom
implementation data into an instantiate call. It must not be used
or interpreted by the host. It is expected that most plugin
writers will not use this facility as LADSPA_Handle should be
used to hold instance data. */
void * ImplementationData;
/* This member is a function pointer that instantiates a plugin. A
handle is returned indicating the new plugin instance. The
instantiation function accepts a sample rate as a parameter. The
plugin descriptor from which this instantiate function was found
must also be passed. This function must return NULL if
instantiation fails.
Note that instance initialisation should generally occur in
activate() rather than here. */
LADSPA_Handle (*instantiate)(const struct _LADSPA_Descriptor * Descriptor,
unsigned long SampleRate);
/* This member is a function pointer that connects a port on an
instantiated plugin to a memory location at which a block of data
for the port will be read/written. The data location is expected
to be an array of LADSPA_Data for audio ports or a single
LADSPA_Data value for control ports. Memory issues will be
managed by the host. The plugin must read/write the data at these
locations every time run() or run_adding() is called and the data
present at the time of this connection call should not be
considered meaningful.
connect_port() may be called more than once for a plugin instance
to allow the host to change the buffers that the plugin is
reading or writing. These calls may be made before or after
activate() or deactivate() calls.
connect_port() must be called at least once for each port before
run() or run_adding() is called. When working with blocks of
LADSPA_Data the plugin should pay careful attention to the block
size passed to the run function as the block allocated may only
just be large enough to contain the block of samples.
Plugin writers should be aware that the host may elect to use the
same buffer for more than one port and even use the same buffer
for both input and output (see LADSPA_PROPERTY_INPLACE_BROKEN).
However, overlapped buffers or use of a single buffer for both
audio and control data may result in unexpected behaviour. */
void (*connect_port)(LADSPA_Handle Instance,
unsigned long Port,
LADSPA_Data * DataLocation);
/* This member is a function pointer that initialises a plugin
instance and activates it for use. This is separated from
instantiate() to aid real-time support and so that hosts can
reinitialise a plugin instance by calling deactivate() and then
activate(). In this case the plugin instance must reset all state
information dependent on the history of the plugin instance
except for any data locations provided by connect_port() and any
gain set by set_run_adding_gain(). If there is nothing for
activate() to do then the plugin writer may provide a NULL rather
than an empty function.
When present, hosts must call this function once before run() (or
run_adding()) is called for the first time. This call should be
made as close to the run() call as possible and indicates to
real-time plugins that they are now live. Plugins should not rely
on a prompt call to run() after activate(). activate() may not be
called again unless deactivate() is called first. Note that
connect_port() may be called before or after a call to
activate(). */
void (*activate)(LADSPA_Handle Instance);
/* This method is a function pointer that runs an instance of a
plugin for a block. Two parameters are required: the first is a
handle to the particular instance to be run and the second
indicates the block size (in samples) for which the plugin
instance may run.
Note that if an activate() function exists then it must be called
before run() or run_adding(). If deactivate() is called for a
plugin instance then the plugin instance may not be reused until
activate() has been called again.
If the plugin has the property LADSPA_PROPERTY_HARD_RT_CAPABLE
then there are various things that the plugin should not do
within the run() or run_adding() functions (see above). */
void (*run)(LADSPA_Handle Instance,
unsigned long SampleCount);
/* This method is a function pointer that runs an instance of a
plugin for a block. This has identical behaviour to run() except
in the way data is output from the plugin. When run() is used,
values are written directly to the memory areas associated with
the output ports. However when run_adding() is called, values
must be added to the values already present in the memory
areas. Furthermore, output values written must be scaled by the
current gain set by set_run_adding_gain() (see below) before
addition.
run_adding() is optional. When it is not provided by a plugin,
this function pointer must be set to NULL. When it is provided,
the function set_run_adding_gain() must be provided also. */
void (*run_adding)(LADSPA_Handle Instance,
unsigned long SampleCount);
/* This method is a function pointer that sets the output gain for
use when run_adding() is called (see above). If this function is
never called the gain is assumed to default to 1. Gain
information should be retained when activate() or deactivate()
are called.
This function should be provided by the plugin if and only if the
run_adding() function is provided. When it is absent this
function pointer must be set to NULL. */
void (*set_run_adding_gain)(LADSPA_Handle Instance,
LADSPA_Data Gain);
/* This is the counterpart to activate() (see above). If there is
nothing for deactivate() to do then the plugin writer may provide
a NULL rather than an empty function.
Hosts must deactivate all activated units after they have been
run() (or run_adding()) for the last time. This call should be
made as close to the last run() call as possible and indicates to
real-time plugins that they are no longer live. Plugins should
not rely on prompt deactivation. Note that connect_port() may be
called before or after a call to deactivate().
Deactivation is not similar to pausing as the plugin instance
will be reinitialised when activate() is called to reuse it. */
void (*deactivate)(LADSPA_Handle Instance);
/* Once an instance of a plugin has been finished with it can be
deleted using the following function. The instance handle passed
ceases to be valid after this call.
If activate() was called for a plugin instance then a
corresponding call to deactivate() must be made before cleanup()
is called. */
void (*cleanup)(LADSPA_Handle Instance);
} LADSPA_Descriptor;
/**********************************************************************/
/* Accessing a Plugin: */
/* The exact mechanism by which plugins are loaded is host-dependent,
however all most hosts will need to know is the name of shared
object file containing the plugin types. To allow multiple hosts to
share plugin types, hosts may wish to check for environment
variable LADSPA_PATH. If present, this should contain a
colon-separated path indicating directories that should be searched
(in order) when loading plugin types.
A plugin programmer must include a function called
"ladspa_descriptor" with the following function prototype within
the shared object file. This function will have C-style linkage (if
you are using C++ this is taken care of by the `extern "C"' clause
at the top of the file).
A host will find the plugin shared object file by one means or
another, find the ladspa_descriptor() function, call it, and
proceed from there.
Plugin types are accessed by index (not ID) using values from 0
upwards. Out of range indexes must result in this function
returning NULL, so the plugin count can be determined by checking
for the least index that results in NULL being returned. */
const LADSPA_Descriptor * ladspa_descriptor(unsigned long Index);
/* Datatype corresponding to the ladspa_descriptor() function. */
typedef const LADSPA_Descriptor *
(*LADSPA_Descriptor_Function)(unsigned long Index);
/**********************************************************************/
#ifdef __cplusplus
}
#endif
#endif /* LADSPA_INCLUDED */
/* EOF */

@ -0,0 +1,233 @@
/*
(c) Copyright 2021 github.com/lawl GPL3+
Free software by Richard W.E. Furse. Do with as you will. No
warranty.
*/
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include "ladspa.h"
#include "utils.h"
#include "../ringbuf.h"
#include "../rnnoise/rnnoise.h"
#define SF_INPUT 0
#define SF_OUTPUT 1
#define SF_VAD 2
#define FRAMESIZE_NSAMPLES 480
#define FRAMESIZE_BYTES (480 * sizeof(float))
#define VAD_GRACE_PERIOD 20
typedef struct {
DenoiseState *st;
ringbuf_t in_buf;
ringbuf_t out_buf;
int32_t remaining_grace_period;
int init;
LADSPA_Data *m_pfVAD;
LADSPA_Data *m_pfInput;
LADSPA_Data *m_pfOutput;
} rnnoiseFilter;
static LADSPA_Handle
instantiateSimpleFilter(const LADSPA_Descriptor *Descriptor,
unsigned long SampleRate) {
rnnoiseFilter *psFilter;
psFilter = (rnnoiseFilter *)malloc(sizeof(rnnoiseFilter));
if (psFilter) {
psFilter->in_buf = ringbuf_new(FRAMESIZE_BYTES * 100);
psFilter->out_buf = ringbuf_new(FRAMESIZE_BYTES * 100);
psFilter->init = 0;
psFilter->remaining_grace_period = VAD_GRACE_PERIOD;
psFilter->st = rnnoise_create(NULL);
}
return psFilter;
}
static void activateSimpleFilter(LADSPA_Handle Instance) {
}
static void connectPortToSimpleFilter(LADSPA_Handle Instance,
unsigned long Port,
LADSPA_Data *DataLocation) {
rnnoiseFilter *psFilter;
psFilter = (rnnoiseFilter *)Instance;
switch (Port) {
case SF_VAD:
psFilter->m_pfVAD = DataLocation;
break;
case SF_INPUT:
psFilter->m_pfInput = DataLocation;
break;
case SF_OUTPUT:
psFilter->m_pfOutput = DataLocation;
break;
}
}
static void runFilter(LADSPA_Handle Instance, unsigned long n_samples) {
rnnoiseFilter *psFilter;
psFilter = (rnnoiseFilter *)Instance;
ringbuf_t in_buf = psFilter->in_buf;
ringbuf_t out_buf = psFilter->out_buf;
float *in, *out, vad_thresh;
in = psFilter->m_pfInput;
out = psFilter->m_pfOutput;
vad_thresh = *psFilter->m_pfVAD / 100;
for (int i = 0; i < n_samples; i++) {
in[i] = in[i] * 32767;
}
ringbuf_memcpy_into(in_buf, in, n_samples * sizeof(float));
const size_t n_frames = ringbuf_bytes_used(in_buf) / FRAMESIZE_BYTES;
float tmpin[n_frames * FRAMESIZE_NSAMPLES];
ringbuf_memcpy_from(tmpin, in_buf, FRAMESIZE_BYTES * n_frames);
for (int i = 0; i < n_frames; i++) {
float tmp[FRAMESIZE_NSAMPLES];
float vad_prob = rnnoise_process_frame(psFilter->st, tmp,
tmpin + (i * FRAMESIZE_NSAMPLES));
if (vad_prob > vad_thresh) {
psFilter->remaining_grace_period = VAD_GRACE_PERIOD;
}
if (psFilter->remaining_grace_period >= 0) {
psFilter->remaining_grace_period--;
} else {
for (int i = 0; i < FRAMESIZE_NSAMPLES; i++) {
tmp[i] = 0.f;
}
}
ringbuf_memcpy_into(out_buf, tmp, FRAMESIZE_BYTES);
}
int frames_avail = ringbuf_bytes_used(out_buf) / FRAMESIZE_BYTES;
int samples_avail = frames_avail * FRAMESIZE_NSAMPLES;
if (samples_avail < n_samples) {
int skip = n_samples - samples_avail;
for (int i = 0; i < skip; i++) {
out[i] = 0.f;
}
ringbuf_memcpy_from(out + skip, out_buf, samples_avail * sizeof(float));
} else {
ringbuf_memcpy_from(out, out_buf, n_samples * sizeof(float));
}
for (int i = 0; i < n_samples; i++) {
out[i] = out[i] / 32767;
}
}
static void cleanupFilter(LADSPA_Handle Instance) {
rnnoiseFilter *psFilter = (rnnoiseFilter *)Instance;
rnnoise_destroy(psFilter->st);
ringbuf_free(&(psFilter->in_buf));
ringbuf_free(&(psFilter->out_buf));
free(Instance);
}
static LADSPA_Descriptor *g_psDescriptor = NULL;
ON_LOAD_ROUTINE {
char **pcPortNames;
LADSPA_PortDescriptor *piPortDescriptors;
LADSPA_PortRangeHint *psPortRangeHints;
g_psDescriptor = (LADSPA_Descriptor *)malloc(sizeof(LADSPA_Descriptor));
if (g_psDescriptor != NULL) {
g_psDescriptor->UniqueID = 16682994;
g_psDescriptor->Label = strdup("noisetorch");
g_psDescriptor->Properties = LADSPA_PROPERTY_HARD_RT_CAPABLE;
g_psDescriptor->Name = strdup("NoiseTorch rnnoise ladspa module");
g_psDescriptor->Maker = strdup("lawl");
g_psDescriptor->Copyright = strdup("GPL3+");
g_psDescriptor->PortCount = 3;
piPortDescriptors =
(LADSPA_PortDescriptor *)calloc(3, sizeof(LADSPA_PortDescriptor));
g_psDescriptor->PortDescriptors =
(const LADSPA_PortDescriptor *)piPortDescriptors;
piPortDescriptors[SF_VAD] = LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL;
piPortDescriptors[SF_INPUT] = LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO;
piPortDescriptors[SF_OUTPUT] = LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO;
pcPortNames = (char **)calloc(3, sizeof(char *));
g_psDescriptor->PortNames = (const char **)pcPortNames;
pcPortNames[SF_VAD] = strdup("VAD %%");
pcPortNames[SF_INPUT] = strdup("Input");
pcPortNames[SF_OUTPUT] = strdup("Output");
psPortRangeHints =
((LADSPA_PortRangeHint *)calloc(3, sizeof(LADSPA_PortRangeHint)));
g_psDescriptor->PortRangeHints =
(const LADSPA_PortRangeHint *)psPortRangeHints;
psPortRangeHints[SF_VAD].HintDescriptor =
(LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE);
psPortRangeHints[SF_VAD].LowerBound = 0;
psPortRangeHints[SF_VAD].UpperBound = 95;
psPortRangeHints[SF_INPUT].HintDescriptor = 0;
psPortRangeHints[SF_OUTPUT].HintDescriptor = 0;
g_psDescriptor->instantiate = instantiateSimpleFilter;
g_psDescriptor->connect_port = connectPortToSimpleFilter;
g_psDescriptor->activate = activateSimpleFilter;
g_psDescriptor->run = runFilter;
g_psDescriptor->run_adding = NULL;
g_psDescriptor->set_run_adding_gain = NULL;
g_psDescriptor->deactivate = NULL;
g_psDescriptor->cleanup = cleanupFilter;
}
}
static void deleteDescriptor(LADSPA_Descriptor *psDescriptor) {
unsigned long lIndex;
if (psDescriptor) {
free((char *)psDescriptor->Label);
free((char *)psDescriptor->Name);
free((char *)psDescriptor->Maker);
free((char *)psDescriptor->Copyright);
free((LADSPA_PortDescriptor *)psDescriptor->PortDescriptors);
for (lIndex = 0; lIndex < psDescriptor->PortCount; lIndex++)
free((char *)(psDescriptor->PortNames[lIndex]));
free((char **)psDescriptor->PortNames);
free((LADSPA_PortRangeHint *)psDescriptor->PortRangeHints);
free(psDescriptor);
}
}
ON_UNLOAD_ROUTINE { deleteDescriptor(g_psDescriptor); }
const LADSPA_Descriptor *ladspa_descriptor(unsigned long Index) {
/* Return the requested descriptor or null if the index is out of
range. */
switch (Index) {
case 0:
return g_psDescriptor;
default:
return NULL;
}
}

@ -0,0 +1,105 @@
/* utils.h
Free software by Richard W.E. Furse. Do with as you will. No
warranty. */
#ifndef LADSPA_SDK_LOAD_PLUGIN_LIB
#define LADSPA_SDK_LOAD_PLUGIN_LIB
/*****************************************************************************/
#include "ladspa.h"
/*****************************************************************************/
/* Functions in load.c: */
/* This function call takes a plugin library filename, searches for
the library along the LADSPA_PATH, loads it with dlopen() and
returns a plugin handle for use with findPluginDescriptor() or
unloadLADSPAPluginLibrary(). Errors are handled by writing a
message to stderr and calling exit(1). It is alright (although
inefficient) to call this more than once for the same file. */
void * loadLADSPAPluginLibrary(const char * pcPluginFilename);
/* This function unloads a LADSPA plugin library. */
void unloadLADSPAPluginLibrary(void * pvLADSPAPluginLibrary);
/* This function locates a LADSPA plugin within a plugin library
loaded with loadLADSPAPluginLibrary(). Errors are handled by
writing a message to stderr and calling exit(1). Note that the
plugin library filename is only included to help provide
informative error messages. */
const LADSPA_Descriptor *
findLADSPAPluginDescriptor(void * pvLADSPAPluginLibrary,
const char * pcPluginLibraryFilename,
const char * pcPluginLabel);
/*****************************************************************************/
/* Functions in search.c: */
/* Callback function for use with LADSPAPluginSearch(). The callback
function passes the filename (full path), a plugin handle (dlopen()
style) and a LADSPA_DescriptorFunction (from which
LADSPA_Descriptors can be acquired). */
typedef void LADSPAPluginSearchCallbackFunction
(const char * pcFullFilename,
void * pvPluginHandle,
LADSPA_Descriptor_Function fDescriptorFunction);
/* Search through the $(LADSPA_PATH) (or a default path) for any
LADSPA plugin libraries. Each plugin library is tested using
dlopen() and dlsym(,"ladspa_descriptor"). After loading each
library, the callback function is called to process it. This
function leaves items passed to the callback function open. */
void LADSPAPluginSearch(LADSPAPluginSearchCallbackFunction fCallbackFunction);
/*****************************************************************************/
/* Function in default.c: */
/* Find the default value for a port. Return 0 if a default is found
and -1 if not. */
int getLADSPADefault(const LADSPA_PortRangeHint * psPortRangeHint,
const unsigned long lSampleRate,
LADSPA_Data * pfResult);
/*****************************************************************************/
/* During C pre-processing, take a string (passed in from the
Makefile) and put quote marks around it. */
#define RAW_STRINGIFY(x) #x
#define EXPAND_AND_STRINGIFY(x) RAW_STRINGIFY(x)
/*****************************************************************************/
#ifndef __cplusplus
/* In C, special incantations are needed to trigger initialisation and
cleanup routines when a dynamic plugin library is loaded or
unloaded (e.g. with dlopen() or dlclose()). _init() and _fini() are
classic exported symbols to achieve this, but these days GNU C
likes to do things a different way. Ideally we would check the GNU
version as older ones will probably expect the classic behaviour,
but for now... */
# if __GNUC__
/* Modern GNU C incantations: */
# define ON_LOAD_ROUTINE static void __attribute__ ((constructor)) init()
# define ON_UNLOAD_ROUTINE static void __attribute__ ((destructor)) fini()
# else
/* Classic incantations: */
# define ON_LOAD_ROUTINE void _init()
# define ON_UNLOAD_ROUTINE void _fini()
# endif
#else
/* In C++, we use the constructor/destructor of a static object to
manage initialisation and cleanup, so we don't need these
routines. */
#endif
/*****************************************************************************/
#endif
/* EOF */

@ -0,0 +1,347 @@
/*
* ringbuf.c - C ring buffer (FIFO) implementation.
*
* Written in 2011 by Drew Hess <dhess-src@bothan.net>.
*
* To the extent possible under law, the author(s) have dedicated all
* copyright and related and neighboring rights to this software to
* the public domain worldwide. This software is distributed without
* any warranty.
*
* You should have received a copy of the CC0 Public Domain Dedication
* along with this software. If not, see
* <http://creativecommons.org/publicdomain/zero/1.0/>.
*/
#include "ringbuf.h"
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/param.h>
#include <assert.h>
/*
* The code is written for clarity, not cleverness or performance, and
* contains many assert()s to enforce invariant assumptions and catch
* bugs. Feel free to optimize the code and to remove asserts for use
* in your own projects, once you're comfortable that it functions as
* intended.
*/
struct ringbuf_t
{
uint8_t *buf;
uint8_t *head, *tail;
size_t size;
};
ringbuf_t
ringbuf_new(size_t capacity)
{
ringbuf_t rb = malloc(sizeof(struct ringbuf_t));
if (rb) {
/* One byte is used for detecting the full condition. */
rb->size = capacity + 1;
rb->buf = malloc(rb->size);
if (rb->buf)
ringbuf_reset(rb);
else {
free(rb);
return 0;
}
}
return rb;
}
size_t
ringbuf_buffer_size(const struct ringbuf_t *rb)
{
return rb->size;
}
void
ringbuf_reset(ringbuf_t rb)
{
rb->head = rb->tail = rb->buf;
}
void
ringbuf_free(ringbuf_t *rb)
{
assert(rb && *rb);
free((*rb)->buf);
free(*rb);
*rb = 0;
}
size_t
ringbuf_capacity(const struct ringbuf_t *rb)
{
return ringbuf_buffer_size(rb) - 1;
}
/*
* Return a pointer to one-past-the-end of the ring buffer's
* contiguous buffer. You shouldn't normally need to use this function
* unless you're writing a new ringbuf_* function.
*/
static const uint8_t *
ringbuf_end(const struct ringbuf_t *rb)
{
return rb->buf + ringbuf_buffer_size(rb);
}
size_t
ringbuf_bytes_free(const struct ringbuf_t *rb)
{
if (rb->head >= rb->tail)
return ringbuf_capacity(rb) - (rb->head - rb->tail);
else
return rb->tail - rb->head - 1;
}
size_t
ringbuf_bytes_used(const struct ringbuf_t *rb)
{
return ringbuf_capacity(rb) - ringbuf_bytes_free(rb);
}
int
ringbuf_is_full(const struct ringbuf_t *rb)
{
return ringbuf_bytes_free(rb) == 0;
}
int
ringbuf_is_empty(const struct ringbuf_t *rb)
{
return ringbuf_bytes_free(rb) == ringbuf_capacity(rb);
}
const void *
ringbuf_tail(const struct ringbuf_t *rb)
{
return rb->tail;
}
const void *
ringbuf_head(const struct ringbuf_t *rb)
{
return rb->head;
}
/*
* Given a ring buffer rb and a pointer to a location within its
* contiguous buffer, return the a pointer to the next logical
* location in the ring buffer.
*/
static uint8_t *
ringbuf_nextp(ringbuf_t rb, const uint8_t *p)
{
/*
* The assert guarantees the expression (++p - rb->buf) is
* non-negative; therefore, the modulus operation is safe and
* portable.
*/
assert((p >= rb->buf) && (p < ringbuf_end(rb)));
return rb->buf + ((++p - rb->buf) % ringbuf_buffer_size(rb));
}
size_t
ringbuf_findchr(const struct ringbuf_t *rb, int c, size_t offset)
{
const uint8_t *bufend = ringbuf_end(rb);
size_t bytes_used = ringbuf_bytes_used(rb);
if (offset >= bytes_used)
return bytes_used;
const uint8_t *start = rb->buf +
(((rb->tail - rb->buf) + offset) % ringbuf_buffer_size(rb));
assert(bufend > start);
size_t n = MIN(bufend - start, bytes_used - offset);
const uint8_t *found = memchr(start, c, n);
if (found)
return offset + (found - start);
else
return ringbuf_findchr(rb, c, offset + n);
}
size_t
ringbuf_memset(ringbuf_t dst, int c, size_t len)
{
const uint8_t *bufend = ringbuf_end(dst);
size_t nwritten = 0;
size_t count = MIN(len, ringbuf_buffer_size(dst));
int overflow = count > ringbuf_bytes_free(dst);
while (nwritten != count) {
/* don't copy beyond the end of the buffer */
assert(bufend > dst->head);
size_t n = MIN(bufend - dst->head, count - nwritten);
memset(dst->head, c, n);
dst->head += n;
nwritten += n;
/* wrap? */
if (dst->head == bufend)
dst->head = dst->buf;
}
if (overflow) {
dst->tail = ringbuf_nextp(dst, dst->head);
assert(ringbuf_is_full(dst));
}
return nwritten;
}
void *
ringbuf_memcpy_into(ringbuf_t dst, const void *src, size_t count)
{
const uint8_t *u8src = src;
const uint8_t *bufend = ringbuf_end(dst);
int overflow = count > ringbuf_bytes_free(dst);
size_t nread = 0;
while (nread != count) {
/* don't copy beyond the end of the buffer */
assert(bufend > dst->head);
size_t n = MIN(bufend - dst->head, count - nread);
memcpy(dst->head, u8src + nread, n);
dst->head += n;
nread += n;
/* wrap? */
if (dst->head == bufend)
dst->head = dst->buf;
}
if (overflow) {
dst->tail = ringbuf_nextp(dst, dst->head);
assert(ringbuf_is_full(dst));
}
return dst->head;
}
ssize_t
ringbuf_read(int fd, ringbuf_t rb, size_t count)
{
const uint8_t *bufend = ringbuf_end(rb);
size_t nfree = ringbuf_bytes_free(rb);
/* don't write beyond the end of the buffer */
assert(bufend > rb->head);
count = MIN(bufend - rb->head, count);
ssize_t n = read(fd, rb->head, count);
if (n > 0) {
assert(rb->head + n <= bufend);
rb->head += n;
/* wrap? */
if (rb->head == bufend)
rb->head = rb->buf;
/* fix up the tail pointer if an overflow occurred */
if (n > nfree) {
rb->tail = ringbuf_nextp(rb, rb->head);
assert(ringbuf_is_full(rb));
}
}
return n;
}
void *
ringbuf_memcpy_from(void *dst, ringbuf_t src, size_t count)
{
size_t bytes_used = ringbuf_bytes_used(src);
if (count > bytes_used)
return 0;
uint8_t *u8dst = dst;
const uint8_t *bufend = ringbuf_end(src);
size_t nwritten = 0;
while (nwritten != count) {
assert(bufend > src->tail);
size_t n = MIN(bufend - src->tail, count - nwritten);
memcpy(u8dst + nwritten, src->tail, n);
src->tail += n;
nwritten += n;
/* wrap ? */
if (src->tail == bufend)
src->tail = src->buf;
}
assert(count + ringbuf_bytes_used(src) == bytes_used);
return src->tail;
}
ssize_t
ringbuf_write(int fd, ringbuf_t rb, size_t count)
{
size_t bytes_used = ringbuf_bytes_used(rb);
if (count > bytes_used)
return 0;
const uint8_t *bufend = ringbuf_end(rb);
assert(bufend > rb->head);
count = MIN(bufend - rb->tail, count);
ssize_t n = write(fd, rb->tail, count);
if (n > 0) {
assert(rb->tail + n <= bufend);
rb->tail += n;
/* wrap? */
if (rb->tail == bufend)
rb->tail = rb->buf;
assert(n + ringbuf_bytes_used(rb) == bytes_used);
}
return n;
}
void *
ringbuf_copy(ringbuf_t dst, ringbuf_t src, size_t count)
{
size_t src_bytes_used = ringbuf_bytes_used(src);
if (count > src_bytes_used)
return 0;
int overflow = count > ringbuf_bytes_free(dst);
const uint8_t *src_bufend = ringbuf_end(src);
const uint8_t *dst_bufend = ringbuf_end(dst);
size_t ncopied = 0;
while (ncopied != count) {
assert(src_bufend > src->tail);
size_t nsrc = MIN(src_bufend - src->tail, count - ncopied);
assert(dst_bufend > dst->head);
size_t n = MIN(dst_bufend - dst->head, nsrc);
memcpy(dst->head, src->tail, n);
src->tail += n;
dst->head += n;
ncopied += n;
/* wrap ? */
if (src->tail == src_bufend)
src->tail = src->buf;
if (dst->head == dst_bufend)
dst->head = dst->buf;
}
assert(count + ringbuf_bytes_used(src) == src_bytes_used);
if (overflow) {
dst->tail = ringbuf_nextp(dst, dst->head);
assert(ringbuf_is_full(dst));
}
return dst->head;
}

@ -0,0 +1,243 @@
#ifndef INCLUDED_RINGBUF_H
#define INCLUDED_RINGBUF_H
/*
* ringbuf.h - C ring buffer (FIFO) interface.
*
* Written in 2011 by Drew Hess <dhess-src@bothan.net>.
*
* To the extent possible under law, the author(s) have dedicated all
* copyright and related and neighboring rights to this software to
* the public domain worldwide. This software is distributed without
* any warranty.
*
* You should have received a copy of the CC0 Public Domain Dedication
* along with this software. If not, see
* <http://creativecommons.org/publicdomain/zero/1.0/>.
*/
/*
* A byte-addressable ring buffer FIFO implementation.
*
* The ring buffer's head pointer points to the starting location
* where data should be written when copying data *into* the buffer
* (e.g., with ringbuf_read). The ring buffer's tail pointer points to
* the starting location where data should be read when copying data
* *from* the buffer (e.g., with ringbuf_write).
*/
#include <stddef.h>
#include <sys/types.h>
typedef struct ringbuf_t *ringbuf_t;
/*
* Create a new ring buffer with the given capacity (usable
* bytes). Note that the actual internal buffer size may be one or
* more bytes larger than the usable capacity, for bookkeeping.
*
* Returns the new ring buffer object, or 0 if there's not enough
* memory to fulfill the request for the given capacity.
*/
ringbuf_t
ringbuf_new(size_t capacity);
/*
* The size of the internal buffer, in bytes. One or more bytes may be
* unusable in order to distinguish the "buffer full" state from the
* "buffer empty" state.
*
* For the usable capacity of the ring buffer, use the
* ringbuf_capacity function.
*/
size_t
ringbuf_buffer_size(const struct ringbuf_t *rb);
/*
* Deallocate a ring buffer, and, as a side effect, set the pointer to
* 0.
*/
void
ringbuf_free(ringbuf_t *rb);
/*
* Reset a ring buffer to its initial state (empty).
*/
void
ringbuf_reset(ringbuf_t rb);
/*
* The usable capacity of the ring buffer, in bytes. Note that this
* value may be less than the ring buffer's internal buffer size, as
* returned by ringbuf_buffer_size.
*/
size_t
ringbuf_capacity(const struct ringbuf_t *rb);
/*
* The number of free/available bytes in the ring buffer. This value
* is never larger than the ring buffer's usable capacity.
*/
size_t
ringbuf_bytes_free(const struct ringbuf_t *rb);
/*
* The number of bytes currently being used in the ring buffer. This
* value is never larger than the ring buffer's usable capacity.
*/
size_t
ringbuf_bytes_used(const struct ringbuf_t *rb);
int
ringbuf_is_full(const struct ringbuf_t *rb);
int
ringbuf_is_empty(const struct ringbuf_t *rb);
/*
* Const access to the head and tail pointers of the ring buffer.
*/
const void *
ringbuf_tail(const struct ringbuf_t *rb);
const void *
ringbuf_head(const struct ringbuf_t *rb);
/*
* Locate the first occurrence of character c (converted to an
* unsigned char) in ring buffer rb, beginning the search at offset
* bytes from the ring buffer's tail pointer. The function returns the
* offset of the character from the ring buffer's tail pointer, if
* found. If c does not occur in the ring buffer, the function returns
* the number of bytes used in the ring buffer.
*
* Note that the offset parameter and the returned offset are logical
* offsets from the tail pointer, not necessarily linear offsets.
*/
size_t
ringbuf_findchr(const struct ringbuf_t *rb, int c, size_t offset);
/*
* Beginning at ring buffer dst's head pointer, fill the ring buffer
* with a repeating sequence of len bytes, each of value c (converted
* to an unsigned char). len can be as large as you like, but the
* function will never write more than ringbuf_buffer_size(dst) bytes
* in a single invocation, since that size will cause all bytes in the
* ring buffer to be written exactly once each.
*
* Note that if len is greater than the number of free bytes in dst,
* the ring buffer will overflow. When an overflow occurs, the state
* of the ring buffer is guaranteed to be consistent, including the
* head and tail pointers; old data will simply be overwritten in FIFO
* fashion, as needed. However, note that, if calling the function
* results in an overflow, the value of the ring buffer's tail pointer
* may be different than it was before the function was called.
*
* Returns the actual number of bytes written to dst: len, if
* len < ringbuf_buffer_size(dst), else ringbuf_buffer_size(dst).
*/
size_t
ringbuf_memset(ringbuf_t dst, int c, size_t len);
/*
* Copy n bytes from a contiguous memory area src into the ring buffer
* dst. Returns the ring buffer's new head pointer.
*
* It is possible to copy more data from src than is available in the
* buffer; i.e., it's possible to overflow the ring buffer using this
* function. When an overflow occurs, the state of the ring buffer is
* guaranteed to be consistent, including the head and tail pointers;
* old data will simply be overwritten in FIFO fashion, as
* needed. However, note that, if calling the function results in an
* overflow, the value of the ring buffer's tail pointer may be
* different than it was before the function was called.
*/
void *
ringbuf_memcpy_into(ringbuf_t dst, const void *src, size_t count);
/*
* This convenience function calls read(2) on the file descriptor fd,
* using the ring buffer rb as the destination buffer for the read,
* and returns the value returned by read(2). It will only call
* read(2) once, and may return a short count.
*
* It is possible to read more data from the file descriptor than is
* available in the buffer; i.e., it's possible to overflow the ring
* buffer using this function. When an overflow occurs, the state of
* the ring buffer is guaranteed to be consistent, including the head
* and tail pointers: old data will simply be overwritten in FIFO
* fashion, as needed. However, note that, if calling the function
* results in an overflow, the value of the ring buffer's tail pointer
* may be different than it was before the function was called.
*/
ssize_t
ringbuf_read(int fd, ringbuf_t rb, size_t count);
/*
* Copy n bytes from the ring buffer src, starting from its tail
* pointer, into a contiguous memory area dst. Returns the value of
* src's tail pointer after the copy is finished.
*
* Note that this copy is destructive with respect to the ring buffer:
* the n bytes copied from the ring buffer are no longer available in
* the ring buffer after the copy is complete, and the ring buffer
* will have n more free bytes than it did before the function was
* called.
*
* This function will *not* allow the ring buffer to underflow. If
* count is greater than the number of bytes used in the ring buffer,
* no bytes are copied, and the function will return 0.
*/
void *
ringbuf_memcpy_from(void *dst, ringbuf_t src, size_t count);
/*
* This convenience function calls write(2) on the file descriptor fd,
* using the ring buffer rb as the source buffer for writing (starting
* at the ring buffer's tail pointer), and returns the value returned
* by write(2). It will only call write(2) once, and may return a
* short count.
*
* Note that this copy is destructive with respect to the ring buffer:
* any bytes written from the ring buffer to the file descriptor are
* no longer available in the ring buffer after the copy is complete,
* and the ring buffer will have N more free bytes than it did before
* the function was called, where N is the value returned by the
* function (unless N is < 0, in which case an error occurred and no
* bytes were written).
*
* This function will *not* allow the ring buffer to underflow. If
* count is greater than the number of bytes used in the ring buffer,
* no bytes are written to the file descriptor, and the function will
* return 0.
*/
ssize_t
ringbuf_write(int fd, ringbuf_t rb, size_t count);
/*
* Copy count bytes from ring buffer src, starting from its tail
* pointer, into ring buffer dst. Returns dst's new head pointer after
* the copy is finished.
*
* Note that this copy is destructive with respect to the ring buffer
* src: any bytes copied from src into dst are no longer available in
* src after the copy is complete, and src will have 'count' more free
* bytes than it did before the function was called.
*
* It is possible to copy more data from src than is available in dst;
* i.e., it's possible to overflow dst using this function. When an
* overflow occurs, the state of dst is guaranteed to be consistent,
* including the head and tail pointers; old data will simply be
* overwritten in FIFO fashion, as needed. However, note that, if
* calling the function results in an overflow, the value dst's tail
* pointer may be different than it was before the function was
* called.
*
* It is *not* possible to underflow src; if count is greater than the
* number of bytes used in src, no bytes are copied, and the function
* returns 0.
*/
void *
ringbuf_copy(ringbuf_t dst, ringbuf_t src, size_t count);
#endif /* INCLUDED_RINGBUF_H */

@ -0,0 +1,31 @@
Copyright (c) 2017, Mozilla
Copyright (c) 2007-2017, Jean-Marc Valin
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.

@ -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 */

@ -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 */

@ -0,0 +1,279 @@
/* 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"
void _celt_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
}
void celt_fir(
const opus_val16 *x,
const opus_val16 *num,
opus_val16 *y,
int N,
int ord)
{
int i,j;
opus_val16 rnum[ord];
for(i=0;i<ord;i++)
rnum[i] = num[ord-i-1];
for (i=0;i<N-3;i+=4)
{
opus_val32 sum[4];
sum[0] = SHL32(EXTEND32(x[i ]), SIG_SHIFT);
sum[1] = SHL32(EXTEND32(x[i+1]), SIG_SHIFT);
sum[2] = SHL32(EXTEND32(x[i+2]), SIG_SHIFT);
sum[3] = SHL32(EXTEND32(x[i+3]), SIG_SHIFT);
xcorr_kernel(rnum, x+i-ord, sum, ord);
y[i ] = ROUND16(sum[0], SIG_SHIFT);
y[i+1] = ROUND16(sum[1], SIG_SHIFT);
y[i+2] = ROUND16(sum[2], SIG_SHIFT);
y[i+3] = ROUND16(sum[3], SIG_SHIFT);
}
for (;i<N;i++)
{
opus_val32 sum = SHL32(EXTEND32(x[i]), SIG_SHIFT);
for (j=0;j<ord;j++)
sum = MAC16_16(sum,rnum[j],x[i+j-ord]);
y[i] = ROUND16(sum, SIG_SHIFT);
}
}
void celt_iir(const opus_val32 *_x,
const opus_val16 *den,
opus_val32 *_y,
int N,
int ord,
opus_val16 *mem)
{
#ifdef SMALL_FOOTPRINT
int i,j;
for (i=0;i<N;i++)
{
opus_val32 sum = _x[i];
for (j=0;j<ord;j++)
{
sum -= MULT16_16(den[j],mem[j]);
}
for (j=ord-1;j>=1;j--)
{
mem[j]=mem[j-1];
}
mem[0] = SROUND16(sum, SIG_SHIFT);
_y[i] = sum;
}
#else
int i,j;
celt_assert((ord&3)==0);
opus_val16 rden[ord];
opus_val16 y[N+ord];
for(i=0;i<ord;i++)
rden[i] = den[ord-i-1];
for(i=0;i<ord;i++)
y[i] = -mem[ord-i-1];
for(;i<N+ord;i++)
y[i]=0;
for (i=0;i<N-3;i+=4)
{
/* Unroll by 4 as if it were an FIR filter */
opus_val32 sum[4];
sum[0]=_x[i];
sum[1]=_x[i+1];
sum[2]=_x[i+2];
sum[3]=_x[i+3];
xcorr_kernel(rden, y+i, sum, ord);
/* Patch up the result to compensate for the fact that this is an IIR */
y[i+ord ] = -SROUND16(sum[0],SIG_SHIFT);
_y[i ] = sum[0];
sum[1] = MAC16_16(sum[1], y[i+ord ], den[0]);
y[i+ord+1] = -SROUND16(sum[1],SIG_SHIFT);
_y[i+1] = sum[1];
sum[2] = MAC16_16(sum[2], y[i+ord+1], den[0]);
sum[2] = MAC16_16(sum[2], y[i+ord ], den[1]);
y[i+ord+2] = -SROUND16(sum[2],SIG_SHIFT);
_y[i+2] = sum[2];
sum[3] = MAC16_16(sum[3], y[i+ord+2], den[0]);
sum[3] = MAC16_16(sum[3], y[i+ord+1], den[1]);
sum[3] = MAC16_16(sum[3], y[i+ord ], den[2]);
y[i+ord+3] = -SROUND16(sum[3],SIG_SHIFT);
_y[i+3] = sum[3];
}
for (;i<N;i++)
{
opus_val32 sum = _x[i];
for (j=0;j<ord;j++)
sum -= MULT16_16(rden[j],y[i+j]);
y[i+ord] = SROUND16(sum,SIG_SHIFT);
_y[i] = sum;
}
for(i=0;i<ord;i++)
mem[i] = _y[N-i-1];
#endif
}
int _celt_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[n];
celt_assert(n>0);
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
celt_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;
}

@ -0,0 +1,59 @@
/* 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 _celt_lpc(opus_val16 *_lpc, const opus_val32 *ac, int p);
void celt_fir(
const opus_val16 *x,
const opus_val16 *num,
opus_val16 *y,
int N,
int ord);
void celt_iir(const opus_val32 *x,
const opus_val16 *den,
opus_val32 *y,
int N,
int ord,
opus_val16 *mem);
int _celt_autocorr(const opus_val16 *x, opus_val32 *ac,
const opus_val16 *window, int overlap, int lag, int n);
#endif /* PLC_H */

@ -0,0 +1,48 @@
#ifndef COMMON_H
#define COMMON_H
#include "stdlib.h"
#include "string.h"
#define RNN_INLINE inline
#define OPUS_INLINE inline
/** RNNoise wrapper for malloc(). To do your own dynamic allocation, all you need t
o do is replace this function and rnnoise_free */
#ifndef OVERRIDE_RNNOISE_ALLOC
static RNN_INLINE void *rnnoise_alloc (size_t size)
{
return malloc(size);
}
#endif
/** RNNoise wrapper for free(). To do your own dynamic allocation, all you need to do is replace this function and rnnoise_alloc */
#ifndef OVERRIDE_RNNOISE_FREE
static RNN_INLINE void rnnoise_free (void *ptr)
{
free(ptr);
}
#endif
/** Copy n elements from src to dst. The 0* term provides compile-time type checking */
#ifndef OVERRIDE_RNN_COPY
#define RNN_COPY(dst, src, n) (memcpy((dst), (src), (n)*sizeof(*(dst)) + 0*((dst)-(src)) ))
#endif
/** Copy n elements from src to dst, allowing overlapping regions. The 0* term
provides compile-time type checking */
#ifndef OVERRIDE_RNN_MOVE
#define RNN_MOVE(dst, src, n) (memmove((dst), (src), (n)*sizeof(*(dst)) + 0*((dst)-(src)) ))
#endif
/** Set n elements of dst to zero */
#ifndef OVERRIDE_RNN_CLEAR
#define RNN_CLEAR(dst, n) (memset((dst), 0, (n)*sizeof(*(dst))))
#endif
#endif

@ -0,0 +1,3 @@
#!/bin/sh
gcc -DTRAINING=1 -Wall -W -O3 -g -I../include denoise.c kiss_fft.c pitch.c celt_lpc.c rnn.c rnn_data.c -o denoise_training -lm

@ -0,0 +1,642 @@
/* 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.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "kiss_fft.h"
#include "common.h"
#include <math.h>
#include "rnnoise.h"
#include "pitch.h"
#include "arch.h"
#include "rnn.h"
#include "rnn_data.h"
#define FRAME_SIZE_SHIFT 2
#define FRAME_SIZE (120<<FRAME_SIZE_SHIFT)
#define WINDOW_SIZE (2*FRAME_SIZE)
#define FREQ_SIZE (FRAME_SIZE + 1)
#define PITCH_MIN_PERIOD 60
#define PITCH_MAX_PERIOD 768
#define PITCH_FRAME_SIZE 960
#define PITCH_BUF_SIZE (PITCH_MAX_PERIOD+PITCH_FRAME_SIZE)
#define SQUARE(x) ((x)*(x))
#define NB_BANDS 22
#define CEPS_MEM 8
#define NB_DELTA_CEPS 6
#define NB_FEATURES (NB_BANDS+3*NB_DELTA_CEPS+2)
#ifndef TRAINING
#define TRAINING 0
#endif
/* The built-in model, used if no file is given as input */
extern const struct RNNModel rnnoise_model_orig;
static const opus_int16 eband5ms[] = {
/*0 200 400 600 800 1k 1.2 1.4 1.6 2k 2.4 2.8 3.2 4k 4.8 5.6 6.8 8k 9.6 12k 15.6 20k*/
0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 34, 40, 48, 60, 78, 100
};
typedef struct {
int init;
kiss_fft_state *kfft;
float half_window[FRAME_SIZE];
float dct_table[NB_BANDS*NB_BANDS];
} CommonState;
struct DenoiseState {
float analysis_mem[FRAME_SIZE];
float cepstral_mem[CEPS_MEM][NB_BANDS];
int memid;
float synthesis_mem[FRAME_SIZE];
float pitch_buf[PITCH_BUF_SIZE];
float pitch_enh_buf[PITCH_BUF_SIZE];
float last_gain;
int last_period;
float mem_hp_x[2];
float lastg[NB_BANDS];
RNNState rnn;
};
void compute_band_energy(float *bandE, const kiss_fft_cpx *X) {
int i;
float sum[NB_BANDS] = {0};
for (i=0;i<NB_BANDS-1;i++)
{
int j;
int band_size;
band_size = (eband5ms[i+1]-eband5ms[i])<<FRAME_SIZE_SHIFT;
for (j=0;j<band_size;j++) {
float tmp;
float frac = (float)j/band_size;
tmp = SQUARE(X[(eband5ms[i]<<FRAME_SIZE_SHIFT) + j].r);
tmp += SQUARE(X[(eband5ms[i]<<FRAME_SIZE_SHIFT) + j].i);
sum[i] += (1-frac)*tmp;
sum[i+1] += frac*tmp;
}
}
sum[0] *= 2;
sum[NB_BANDS-1] *= 2;
for (i=0;i<NB_BANDS;i++)
{
bandE[i] = sum[i];
}
}
void compute_band_corr(float *bandE, const kiss_fft_cpx *X, const kiss_fft_cpx *P) {
int i;
float sum[NB_BANDS] = {0};
for (i=0;i<NB_BANDS-1;i++)
{
int j;
int band_size;
band_size = (eband5ms[i+1]-eband5ms[i])<<FRAME_SIZE_SHIFT;
for (j=0;j<band_size;j++) {
float tmp;
float frac = (float)j/band_size;
tmp = X[(eband5ms[i]<<FRAME_SIZE_SHIFT) + j].r * P[(eband5ms[i]<<FRAME_SIZE_SHIFT) + j].r;
tmp += X[(eband5ms[i]<<FRAME_SIZE_SHIFT) + j].i * P[(eband5ms[i]<<FRAME_SIZE_SHIFT) + j].i;
sum[i] += (1-frac)*tmp;
sum[i+1] += frac*tmp;
}
}
sum[0] *= 2;
sum[NB_BANDS-1] *= 2;
for (i=0;i<NB_BANDS;i++)
{
bandE[i] = sum[i];
}
}
void interp_band_gain(float *g, const float *bandE) {
int i;
memset(g, 0, FREQ_SIZE);
for (i=0;i<NB_BANDS-1;i++)
{
int j;
int band_size;
band_size = (eband5ms[i+1]-eband5ms[i])<<FRAME_SIZE_SHIFT;
for (j=0;j<band_size;j++) {
float frac = (float)j/band_size;
g[(eband5ms[i]<<FRAME_SIZE_SHIFT) + j] = (1-frac)*bandE[i] + frac*bandE[i+1];
}
}
}
CommonState common;
static void check_init() {
int i;
if (common.init) return;
common.kfft = opus_fft_alloc_twiddles(2*FRAME_SIZE, NULL, NULL, NULL, 0);
for (i=0;i<FRAME_SIZE;i++)
common.half_window[i] = sin(.5*M_PI*sin(.5*M_PI*(i+.5)/FRAME_SIZE) * sin(.5*M_PI*(i+.5)/FRAME_SIZE));
for (i=0;i<NB_BANDS;i++) {
int j;
for (j=0;j<NB_BANDS;j++) {
common.dct_table[i*NB_BANDS + j] = cos((i+.5)*j*M_PI/NB_BANDS);
if (j==0) common.dct_table[i*NB_BANDS + j] *= sqrt(.5);
}
}
common.init = 1;
}
static void dct(float *out, const float *in) {
int i;
check_init();
for (i=0;i<NB_BANDS;i++) {
int j;
float sum = 0;
for (j=0;j<NB_BANDS;j++) {
sum += in[j] * common.dct_table[j*NB_BANDS + i];
}
out[i] = sum*sqrt(2./22);
}
}
#if 0
static void idct(float *out, const float *in) {
int i;
check_init();
for (i=0;i<NB_BANDS;i++) {
int j;
float sum = 0;
for (j=0;j<NB_BANDS;j++) {
sum += in[j] * common.dct_table[i*NB_BANDS + j];
}
out[i] = sum*sqrt(2./22);
}
}
#endif
static void forward_transform(kiss_fft_cpx *out, const float *in) {
int i;
kiss_fft_cpx x[WINDOW_SIZE];
kiss_fft_cpx y[WINDOW_SIZE];
check_init();
for (i=0;i<WINDOW_SIZE;i++) {
x[i].r = in[i];
x[i].i = 0;
}
opus_fft(common.kfft, x, y, 0);
for (i=0;i<FREQ_SIZE;i++) {
out[i] = y[i];
}
}
static void inverse_transform(float *out, const kiss_fft_cpx *in) {
int i;
kiss_fft_cpx x[WINDOW_SIZE];
kiss_fft_cpx y[WINDOW_SIZE];
check_init();
for (i=0;i<FREQ_SIZE;i++) {
x[i] = in[i];
}
for (;i<WINDOW_SIZE;i++) {
x[i].r = x[WINDOW_SIZE - i].r;
x[i].i = -x[WINDOW_SIZE - i].i;
}
opus_fft(common.kfft, x, y, 0);
/* output in reverse order for IFFT. */
out[0] = WINDOW_SIZE*y[0].r;
for (i=1;i<WINDOW_SIZE;i++) {
out[i] = WINDOW_SIZE*y[WINDOW_SIZE - i].r;
}
}
static void apply_window(float *x) {
int i;
check_init();
for (i=0;i<FRAME_SIZE;i++) {
x[i] *= common.half_window[i];
x[WINDOW_SIZE - 1 - i] *= common.half_window[i];
}
}
int rnnoise_get_size() {
return sizeof(DenoiseState);
}
int rnnoise_init(DenoiseState *st, RNNModel *model) {
memset(st, 0, sizeof(*st));
if (model)
st->rnn.model = model;
else
st->rnn.model = &rnnoise_model_orig;
st->rnn.vad_gru_state = calloc(sizeof(float), st->rnn.model->vad_gru_size);
st->rnn.noise_gru_state = calloc(sizeof(float), st->rnn.model->noise_gru_size);
st->rnn.denoise_gru_state = calloc(sizeof(float), st->rnn.model->denoise_gru_size);
return 0;
}
DenoiseState *rnnoise_create(RNNModel *model) {
DenoiseState *st;
st = malloc(rnnoise_get_size());
rnnoise_init(st, model);
return st;
}
void rnnoise_destroy(DenoiseState *st) {
free(st->rnn.vad_gru_state);
free(st->rnn.noise_gru_state);
free(st->rnn.denoise_gru_state);
free(st);
}
#if TRAINING
int lowpass = FREQ_SIZE;
int band_lp = NB_BANDS;
#endif
static void frame_analysis(DenoiseState *st, kiss_fft_cpx *X, float *Ex, const float *in) {
int i;
float x[WINDOW_SIZE];
RNN_COPY(x, st->analysis_mem, FRAME_SIZE);
for (i=0;i<FRAME_SIZE;i++) x[FRAME_SIZE + i] = in[i];
RNN_COPY(st->analysis_mem, in, FRAME_SIZE);
apply_window(x);
forward_transform(X, x);
#if TRAINING
for (i=lowpass;i<FREQ_SIZE;i++)
X[i].r = X[i].i = 0;
#endif
compute_band_energy(Ex, X);
}
static int compute_frame_features(DenoiseState *st, kiss_fft_cpx *X, kiss_fft_cpx *P,
float *Ex, float *Ep, float *Exp, float *features, const float *in) {
int i;
float E = 0;
float *ceps_0, *ceps_1, *ceps_2;
float spec_variability = 0;
float Ly[NB_BANDS];
float p[WINDOW_SIZE];
float pitch_buf[PITCH_BUF_SIZE>>1];
int pitch_index;
float gain;
float *(pre[1]);
float tmp[NB_BANDS];
float follow, logMax;
frame_analysis(st, X, Ex, in);
RNN_MOVE(st->pitch_buf, &st->pitch_buf[FRAME_SIZE], PITCH_BUF_SIZE-FRAME_SIZE);
RNN_COPY(&st->pitch_buf[PITCH_BUF_SIZE-FRAME_SIZE], in, FRAME_SIZE);
pre[0] = &st->pitch_buf[0];
pitch_downsample(pre, pitch_buf, PITCH_BUF_SIZE, 1);
pitch_search(pitch_buf+(PITCH_MAX_PERIOD>>1), pitch_buf, PITCH_FRAME_SIZE,
PITCH_MAX_PERIOD-3*PITCH_MIN_PERIOD, &pitch_index);
pitch_index = PITCH_MAX_PERIOD-pitch_index;
gain = remove_doubling(pitch_buf, PITCH_MAX_PERIOD, PITCH_MIN_PERIOD,
PITCH_FRAME_SIZE, &pitch_index, st->last_period, st->last_gain);
st->last_period = pitch_index;
st->last_gain = gain;
for (i=0;i<WINDOW_SIZE;i++)
p[i] = st->pitch_buf[PITCH_BUF_SIZE-WINDOW_SIZE-pitch_index+i];
apply_window(p);
forward_transform(P, p);
compute_band_energy(Ep, P);
compute_band_corr(Exp, X, P);
for (i=0;i<NB_BANDS;i++) Exp[i] = Exp[i]/sqrt(.001+Ex[i]*Ep[i]);
dct(tmp, Exp);
for (i=0;i<NB_DELTA_CEPS;i++) features[NB_BANDS+2*NB_DELTA_CEPS+i] = tmp[i];
features[NB_BANDS+2*NB_DELTA_CEPS] -= 1.3;
features[NB_BANDS+2*NB_DELTA_CEPS+1] -= 0.9;
features[NB_BANDS+3*NB_DELTA_CEPS] = .01*(pitch_index-300);
logMax = -2;
follow = -2;
for (i=0;i<NB_BANDS;i++) {
Ly[i] = log10(1e-2+Ex[i]);
Ly[i] = MAX16(logMax-7, MAX16(follow-1.5, Ly[i]));
logMax = MAX16(logMax, Ly[i]);
follow = MAX16(follow-1.5, Ly[i]);
E += Ex[i];
}
if (!TRAINING && E < 0.04) {
/* If there's no audio, avoid messing up the state. */
RNN_CLEAR(features, NB_FEATURES);
return 1;
}
dct(features, Ly);
features[0] -= 12;
features[1] -= 4;
ceps_0 = st->cepstral_mem[st->memid];
ceps_1 = (st->memid < 1) ? st->cepstral_mem[CEPS_MEM+st->memid-1] : st->cepstral_mem[st->memid-1];
ceps_2 = (st->memid < 2) ? st->cepstral_mem[CEPS_MEM+st->memid-2] : st->cepstral_mem[st->memid-2];
for (i=0;i<NB_BANDS;i++) ceps_0[i] = features[i];
st->memid++;
for (i=0;i<NB_DELTA_CEPS;i++) {
features[i] = ceps_0[i] + ceps_1[i] + ceps_2[i];
features[NB_BANDS+i] = ceps_0[i] - ceps_2[i];
features[NB_BANDS+NB_DELTA_CEPS+i] = ceps_0[i] - 2*ceps_1[i] + ceps_2[i];
}
/* Spectral variability features. */
if (st->memid == CEPS_MEM) st->memid = 0;
for (i=0;i<CEPS_MEM;i++)
{
int j;
float mindist = 1e15f;
for (j=0;j<CEPS_MEM;j++)
{
int k;
float dist=0;
for (k=0;k<NB_BANDS;k++)
{
float tmp;
tmp = st->cepstral_mem[i][k] - st->cepstral_mem[j][k];
dist += tmp*tmp;
}
if (j!=i)
mindist = MIN32(mindist, dist);
}
spec_variability += mindist;
}
features[NB_BANDS+3*NB_DELTA_CEPS+1] = spec_variability/CEPS_MEM-2.1;
return TRAINING && E < 0.1;
}
static void frame_synthesis(DenoiseState *st, float *out, const kiss_fft_cpx *y) {
float x[WINDOW_SIZE];
int i;
inverse_transform(x, y);
apply_window(x);
for (i=0;i<FRAME_SIZE;i++) out[i] = x[i] + st->synthesis_mem[i];
RNN_COPY(st->synthesis_mem, &x[FRAME_SIZE], FRAME_SIZE);
}
static void biquad(float *y, float mem[2], const float *x, const float *b, const float *a, int N) {
int i;
for (i=0;i<N;i++) {
float xi, yi;
xi = x[i];
yi = x[i] + mem[0];
mem[0] = mem[1] + (b[0]*(double)xi - a[0]*(double)yi);
mem[1] = (b[1]*(double)xi - a[1]*(double)yi);
y[i] = yi;
}
}
void pitch_filter(kiss_fft_cpx *X, const kiss_fft_cpx *P, const float *Ex, const float *Ep,
const float *Exp, const float *g) {
int i;
float r[NB_BANDS];
float rf[FREQ_SIZE] = {0};
for (i=0;i<NB_BANDS;i++) {
#if 0
if (Exp[i]>g[i]) r[i] = 1;
else r[i] = Exp[i]*(1-g[i])/(.001 + g[i]*(1-Exp[i]));
r[i] = MIN16(1, MAX16(0, r[i]));
#else
if (Exp[i]>g[i]) r[i] = 1;
else r[i] = SQUARE(Exp[i])*(1-SQUARE(g[i]))/(.001 + SQUARE(g[i])*(1-SQUARE(Exp[i])));
r[i] = sqrt(MIN16(1, MAX16(0, r[i])));
#endif
r[i] *= sqrt(Ex[i]/(1e-8+Ep[i]));
}
interp_band_gain(rf, r);
for (i=0;i<FREQ_SIZE;i++) {
X[i].r += rf[i]*P[i].r;
X[i].i += rf[i]*P[i].i;
}
float newE[NB_BANDS];
compute_band_energy(newE, X);
float norm[NB_BANDS];
float normf[FREQ_SIZE]={0};
for (i=0;i<NB_BANDS;i++) {
norm[i] = sqrt(Ex[i]/(1e-8+newE[i]));
}
interp_band_gain(normf, norm);
for (i=0;i<FREQ_SIZE;i++) {
X[i].r *= normf[i];
X[i].i *= normf[i];
}
}
float rnnoise_process_frame(DenoiseState *st, float *out, const float *in) {
int i;
kiss_fft_cpx X[FREQ_SIZE];
kiss_fft_cpx P[WINDOW_SIZE];
float x[FRAME_SIZE];
float Ex[NB_BANDS], Ep[NB_BANDS];
float Exp[NB_BANDS];
float features[NB_FEATURES];
float g[NB_BANDS];
float gf[FREQ_SIZE]={1};
float vad_prob = 0;
int silence;
static const float a_hp[2] = {-1.99599, 0.99600};
static const float b_hp[2] = {-2, 1};
biquad(x, st->mem_hp_x, in, b_hp, a_hp, FRAME_SIZE);
silence = compute_frame_features(st, X, P, Ex, Ep, Exp, features, x);
if (!silence) {
compute_rnn(&st->rnn, g, &vad_prob, features);
pitch_filter(X, P, Ex, Ep, Exp, g);
for (i=0;i<NB_BANDS;i++) {
float alpha = .6f;
g[i] = MAX16(g[i], alpha*st->lastg[i]);
st->lastg[i] = g[i];
}
interp_band_gain(gf, g);
#if 1
for (i=0;i<FREQ_SIZE;i++) {
X[i].r *= gf[i];
X[i].i *= gf[i];
}
#endif
}
frame_synthesis(st, out, X);
return vad_prob;
}
#if TRAINING
static float uni_rand() {
return rand()/(double)RAND_MAX-.5;
}
static void rand_resp(float *a, float *b) {
a[0] = .75*uni_rand();
a[1] = .75*uni_rand();
b[0] = .75*uni_rand();
b[1] = .75*uni_rand();
}
int main(int argc, char **argv) {
int i;
int count=0;
static const float a_hp[2] = {-1.99599, 0.99600};
static const float b_hp[2] = {-2, 1};
float a_noise[2] = {0};
float b_noise[2] = {0};
float a_sig[2] = {0};
float b_sig[2] = {0};
float mem_hp_x[2]={0};
float mem_hp_n[2]={0};
float mem_resp_x[2]={0};
float mem_resp_n[2]={0};
float x[FRAME_SIZE];
float n[FRAME_SIZE];
float xn[FRAME_SIZE];
int vad_cnt=0;
int gain_change_count=0;
float speech_gain = 1, noise_gain = 1;
FILE *f1, *f2;
int maxCount;
DenoiseState *st;
DenoiseState *noise_state;
DenoiseState *noisy;
st = rnnoise_create(NULL);
noise_state = rnnoise_create(NULL);
noisy = rnnoise_create(NULL);
if (argc!=4) {
fprintf(stderr, "usage: %s <speech> <noise> <count>\n", argv[0]);
return 1;
}
f1 = fopen(argv[1], "r");
f2 = fopen(argv[2], "r");
maxCount = atoi(argv[3]);
for(i=0;i<150;i++) {
short tmp[FRAME_SIZE];
fread(tmp, sizeof(short), FRAME_SIZE, f2);
}
while (1) {
kiss_fft_cpx X[FREQ_SIZE], Y[FREQ_SIZE], N[FREQ_SIZE], P[WINDOW_SIZE];
float Ex[NB_BANDS], Ey[NB_BANDS], En[NB_BANDS], Ep[NB_BANDS];
float Exp[NB_BANDS];
float Ln[NB_BANDS];
float features[NB_FEATURES];
float g[NB_BANDS];
short tmp[FRAME_SIZE];
float vad=0;
float E=0;
if (count==maxCount) break;
if ((count%1000)==0) fprintf(stderr, "%d\r", count);
if (++gain_change_count > 2821) {
speech_gain = pow(10., (-40+(rand()%60))/20.);
noise_gain = pow(10., (-30+(rand()%50))/20.);
if (rand()%10==0) noise_gain = 0;
noise_gain *= speech_gain;
if (rand()%10==0) speech_gain = 0;
gain_change_count = 0;
rand_resp(a_noise, b_noise);
rand_resp(a_sig, b_sig);
lowpass = FREQ_SIZE * 3000./24000. * pow(50., rand()/(double)RAND_MAX);
for (i=0;i<NB_BANDS;i++) {
if (eband5ms[i]<<FRAME_SIZE_SHIFT > lowpass) {
band_lp = i;
break;
}
}
}
if (speech_gain != 0) {
fread(tmp, sizeof(short), FRAME_SIZE, f1);
if (feof(f1)) {
rewind(f1);
fread(tmp, sizeof(short), FRAME_SIZE, f1);
}
for (i=0;i<FRAME_SIZE;i++) x[i] = speech_gain*tmp[i];
for (i=0;i<FRAME_SIZE;i++) E += tmp[i]*(float)tmp[i];
} else {
for (i=0;i<FRAME_SIZE;i++) x[i] = 0;
E = 0;
}
if (noise_gain!=0) {
fread(tmp, sizeof(short), FRAME_SIZE, f2);
if (feof(f2)) {
rewind(f2);
fread(tmp, sizeof(short), FRAME_SIZE, f2);
}
for (i=0;i<FRAME_SIZE;i++) n[i] = noise_gain*tmp[i];
} else {
for (i=0;i<FRAME_SIZE;i++) n[i] = 0;
}
biquad(x, mem_hp_x, x, b_hp, a_hp, FRAME_SIZE);
biquad(x, mem_resp_x, x, b_sig, a_sig, FRAME_SIZE);
biquad(n, mem_hp_n, n, b_hp, a_hp, FRAME_SIZE);
biquad(n, mem_resp_n, n, b_noise, a_noise, FRAME_SIZE);
for (i=0;i<FRAME_SIZE;i++) xn[i] = x[i] + n[i];
if (E > 1e9f) {
vad_cnt=0;
} else if (E > 1e8f) {
vad_cnt -= 5;
} else if (E > 1e7f) {
vad_cnt++;
} else {
vad_cnt+=2;
}
if (vad_cnt < 0) vad_cnt = 0;
if (vad_cnt > 15) vad_cnt = 15;
if (vad_cnt >= 10) vad = 0;
else if (vad_cnt > 0) vad = 0.5f;
else vad = 1.f;
frame_analysis(st, Y, Ey, x);
frame_analysis(noise_state, N, En, n);
for (i=0;i<NB_BANDS;i++) Ln[i] = log10(1e-2+En[i]);
int silence = compute_frame_features(noisy, X, P, Ex, Ep, Exp, features, xn);
pitch_filter(X, P, Ex, Ep, Exp, g);
//printf("%f %d\n", noisy->last_gain, noisy->last_period);
for (i=0;i<NB_BANDS;i++) {
g[i] = sqrt((Ey[i]+1e-3)/(Ex[i]+1e-3));
if (g[i] > 1) g[i] = 1;
if (silence || i > band_lp) g[i] = -1;
if (Ey[i] < 5e-2 && Ex[i] < 5e-2) g[i] = -1;
if (vad==0 && noise_gain==0) g[i] = -1;
}
count++;
#if 1
fwrite(features, sizeof(float), NB_FEATURES, stdout);
fwrite(g, sizeof(float), NB_BANDS, stdout);
fwrite(Ln, sizeof(float), NB_BANDS, stdout);
fwrite(&vad, sizeof(float), 1, stdout);
#endif
}
fprintf(stderr, "matrix size: %d x %d\n", count, NB_FEATURES + 2*NB_BANDS + 1);
fclose(f1);
fclose(f2);
return 0;
}
#endif

@ -0,0 +1,601 @@
/*Copyright (c) 2003-2004, Mark Borgerding
Lots of modifications by Jean-Marc Valin
Copyright (c) 2005-2007, Xiph.Org Foundation
Copyright (c) 2008, Xiph.Org Foundation, CSIRO
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.*/
/* This code is originally from Mark Borgerding's KISS-FFT but has been
heavily modified to better suit Opus */
#ifndef SKIP_CONFIG_H
# ifdef HAVE_CONFIG_H
# include "config.h"
# endif
#endif
#include "_kiss_fft_guts.h"
#define CUSTOM_MODES
/* The guts header contains all the multiplication and addition macros that are defined for
complex numbers. It also delares the kf_ internal functions.
*/
static void kf_bfly2(
kiss_fft_cpx * Fout,
int m,
int N
)
{
kiss_fft_cpx * Fout2;
int i;
(void)m;
#ifdef CUSTOM_MODES
if (m==1)
{
celt_assert(m==1);
for (i=0;i<N;i++)
{
kiss_fft_cpx t;
Fout2 = Fout + 1;
t = *Fout2;
C_SUB( *Fout2 , *Fout , t );
C_ADDTO( *Fout , t );
Fout += 2;
}
} else
#endif
{
opus_val16 tw;
tw = QCONST16(0.7071067812f, 15);
/* We know that m==4 here because the radix-2 is just after a radix-4 */
celt_assert(m==4);
for (i=0;i<N;i++)
{
kiss_fft_cpx t;
Fout2 = Fout + 4;
t = Fout2[0];
C_SUB( Fout2[0] , Fout[0] , t );
C_ADDTO( Fout[0] , t );
t.r = S_MUL(ADD32_ovflw(Fout2[1].r, Fout2[1].i), tw);
t.i = S_MUL(SUB32_ovflw(Fout2[1].i, Fout2[1].r), tw);
C_SUB( Fout2[1] , Fout[1] , t );
C_ADDTO( Fout[1] , t );
t.r = Fout2[2].i;
t.i = -Fout2[2].r;
C_SUB( Fout2[2] , Fout[2] , t );
C_ADDTO( Fout[2] , t );
t.r = S_MUL(SUB32_ovflw(Fout2[3].i, Fout2[3].r), tw);
t.i = S_MUL(NEG32_ovflw(ADD32_ovflw(Fout2[3].i, Fout2[3].r)), tw);
C_SUB( Fout2[3] , Fout[3] , t );
C_ADDTO( Fout[3] , t );
Fout += 8;
}
}
}
static void kf_bfly4(
kiss_fft_cpx * Fout,
const size_t fstride,
const kiss_fft_state *st,
int m,
int N,
int mm
)
{
int i;
if (m==1)
{
/* Degenerate case where all the twiddles are 1. */
for (i=0;i<N;i++)
{
kiss_fft_cpx scratch0, scratch1;
C_SUB( scratch0 , *Fout, Fout[2] );
C_ADDTO(*Fout, Fout[2]);
C_ADD( scratch1 , Fout[1] , Fout[3] );
C_SUB( Fout[2], *Fout, scratch1 );
C_ADDTO( *Fout , scratch1 );
C_SUB( scratch1 , Fout[1] , Fout[3] );
Fout[1].r = ADD32_ovflw(scratch0.r, scratch1.i);
Fout[1].i = SUB32_ovflw(scratch0.i, scratch1.r);
Fout[3].r = SUB32_ovflw(scratch0.r, scratch1.i);
Fout[3].i = ADD32_ovflw(scratch0.i, scratch1.r);
Fout+=4;
}
} else {
int j;
kiss_fft_cpx scratch[6];
const kiss_twiddle_cpx *tw1,*tw2,*tw3;
const int m2=2*m;
const int m3=3*m;
kiss_fft_cpx * Fout_beg = Fout;
for (i=0;i<N;i++)
{
Fout = Fout_beg + i*mm;
tw3 = tw2 = tw1 = st->twiddles;
/* m is guaranteed to be a multiple of 4. */
for (j=0;j<m;j++)
{
C_MUL(scratch[0],Fout[m] , *tw1 );
C_MUL(scratch[1],Fout[m2] , *tw2 );
C_MUL(scratch[2],Fout[m3] , *tw3 );
C_SUB( scratch[5] , *Fout, scratch[1] );
C_ADDTO(*Fout, scratch[1]);
C_ADD( scratch[3] , scratch[0] , scratch[2] );
C_SUB( scratch[4] , scratch[0] , scratch[2] );
C_SUB( Fout[m2], *Fout, scratch[3] );
tw1 += fstride;
tw2 += fstride*2;
tw3 += fstride*3;
C_ADDTO( *Fout , scratch[3] );
Fout[m].r = ADD32_ovflw(scratch[5].r, scratch[4].i);
Fout[m].i = SUB32_ovflw(scratch[5].i, scratch[4].r);
Fout[m3].r = SUB32_ovflw(scratch[5].r, scratch[4].i);
Fout[m3].i = ADD32_ovflw(scratch[5].i, scratch[4].r);
++Fout;
}
}
}
}
#ifndef RADIX_TWO_ONLY
static void kf_bfly3(
kiss_fft_cpx * Fout,
const size_t fstride,
const kiss_fft_state *st,
int m,
int N,
int mm
)
{
int i;
size_t k;
const size_t m2 = 2*m;
const kiss_twiddle_cpx *tw1,*tw2;
kiss_fft_cpx scratch[5];
kiss_twiddle_cpx epi3;
kiss_fft_cpx * Fout_beg = Fout;
#ifdef FIXED_POINT
/*epi3.r = -16384;*/ /* Unused */
epi3.i = -28378;
#else
epi3 = st->twiddles[fstride*m];
#endif
for (i=0;i<N;i++)
{
Fout = Fout_beg + i*mm;
tw1=tw2=st->twiddles;
/* For non-custom modes, m is guaranteed to be a multiple of 4. */
k=m;
do {
C_MUL(scratch[1],Fout[m] , *tw1);
C_MUL(scratch[2],Fout[m2] , *tw2);
C_ADD(scratch[3],scratch[1],scratch[2]);
C_SUB(scratch[0],scratch[1],scratch[2]);
tw1 += fstride;
tw2 += fstride*2;
Fout[m].r = SUB32_ovflw(Fout->r, HALF_OF(scratch[3].r));
Fout[m].i = SUB32_ovflw(Fout->i, HALF_OF(scratch[3].i));
C_MULBYSCALAR( scratch[0] , epi3.i );
C_ADDTO(*Fout,scratch[3]);
Fout[m2].r = ADD32_ovflw(Fout[m].r, scratch[0].i);
Fout[m2].i = SUB32_ovflw(Fout[m].i, scratch[0].r);
Fout[m].r = SUB32_ovflw(Fout[m].r, scratch[0].i);
Fout[m].i = ADD32_ovflw(Fout[m].i, scratch[0].r);
++Fout;
} while(--k);
}
}
#ifndef OVERRIDE_kf_bfly5
static void kf_bfly5(
kiss_fft_cpx * Fout,
const size_t fstride,
const kiss_fft_state *st,
int m,
int N,
int mm
)
{
kiss_fft_cpx *Fout0,*Fout1,*Fout2,*Fout3,*Fout4;
int i, u;
kiss_fft_cpx scratch[13];
const kiss_twiddle_cpx *tw;
kiss_twiddle_cpx ya,yb;
kiss_fft_cpx * Fout_beg = Fout;
#ifdef FIXED_POINT
ya.r = 10126;
ya.i = -31164;
yb.r = -26510;
yb.i = -19261;
#else
ya = st->twiddles[fstride*m];
yb = st->twiddles[fstride*2*m];
#endif
tw=st->twiddles;
for (i=0;i<N;i++)
{
Fout = Fout_beg + i*mm;
Fout0=Fout;
Fout1=Fout0+m;
Fout2=Fout0+2*m;
Fout3=Fout0+3*m;
Fout4=Fout0+4*m;
/* For non-custom modes, m is guaranteed to be a multiple of 4. */
for ( u=0; u<m; ++u ) {
scratch[0] = *Fout0;
C_MUL(scratch[1] ,*Fout1, tw[u*fstride]);
C_MUL(scratch[2] ,*Fout2, tw[2*u*fstride]);
C_MUL(scratch[3] ,*Fout3, tw[3*u*fstride]);
C_MUL(scratch[4] ,*Fout4, tw[4*u*fstride]);
C_ADD( scratch[7],scratch[1],scratch[4]);
C_SUB( scratch[10],scratch[1],scratch[4]);
C_ADD( scratch[8],scratch[2],scratch[3]);
C_SUB( scratch[9],scratch[2],scratch[3]);
Fout0->r = ADD32_ovflw(Fout0->r, ADD32_ovflw(scratch[7].r, scratch[8].r));
Fout0->i = ADD32_ovflw(Fout0->i, ADD32_ovflw(scratch[7].i, scratch[8].i));
scratch[5].r = ADD32_ovflw(scratch[0].r, ADD32_ovflw(S_MUL(scratch[7].r,ya.r), S_MUL(scratch[8].r,yb.r)));
scratch[5].i = ADD32_ovflw(scratch[0].i, ADD32_ovflw(S_MUL(scratch[7].i,ya.r), S_MUL(scratch[8].i,yb.r)));
scratch[6].r = ADD32_ovflw(S_MUL(scratch[10].i,ya.i), S_MUL(scratch[9].i,yb.i));
scratch[6].i = NEG32_ovflw(ADD32_ovflw(S_MUL(scratch[10].r,ya.i), S_MUL(scratch[9].r,yb.i)));
C_SUB(*Fout1,scratch[5],scratch[6]);
C_ADD(*Fout4,scratch[5],scratch[6]);
scratch[11].r = ADD32_ovflw(scratch[0].r, ADD32_ovflw(S_MUL(scratch[7].r,yb.r), S_MUL(scratch[8].r,ya.r)));
scratch[11].i = ADD32_ovflw(scratch[0].i, ADD32_ovflw(S_MUL(scratch[7].i,yb.r), S_MUL(scratch[8].i,ya.r)));
scratch[12].r = SUB32_ovflw(S_MUL(scratch[9].i,ya.i), S_MUL(scratch[10].i,yb.i));
scratch[12].i = SUB32_ovflw(S_MUL(scratch[10].r,yb.i), S_MUL(scratch[9].r,ya.i));
C_ADD(*Fout2,scratch[11],scratch[12]);
C_SUB(*Fout3,scratch[11],scratch[12]);
++Fout0;++Fout1;++Fout2;++Fout3;++Fout4;
}
}
}
#endif /* OVERRIDE_kf_bfly5 */
#endif
#ifdef CUSTOM_MODES
static
void compute_bitrev_table(
int Fout,
opus_int16 *f,
const size_t fstride,
int in_stride,
opus_int16 * factors,
const kiss_fft_state *st
)
{
const int p=*factors++; /* the radix */
const int m=*factors++; /* stage's fft length/p */
/*printf ("fft %d %d %d %d %d %d\n", p*m, m, p, s2, fstride*in_stride, N);*/
if (m==1)
{
int j;
for (j=0;j<p;j++)
{
*f = Fout+j;
f += fstride*in_stride;
}
} else {
int j;
for (j=0;j<p;j++)
{
compute_bitrev_table( Fout , f, fstride*p, in_stride, factors,st);
f += fstride*in_stride;
Fout += m;
}
}
}
/* facbuf is populated by p1,m1,p2,m2, ...
where
p[i] * m[i] = m[i-1]
m0 = n */
static
int kf_factor(int n,opus_int16 * facbuf)
{
int p=4;
int i;
int stages=0;
int nbak = n;
/*factor out powers of 4, powers of 2, then any remaining primes */
do {
while (n % p) {
switch (p) {
case 4: p = 2; break;
case 2: p = 3; break;
default: p += 2; break;
}
if (p>32000 || (opus_int32)p*(opus_int32)p > n)
p = n; /* no more factors, skip to end */
}
n /= p;
#ifdef RADIX_TWO_ONLY
if (p!=2 && p != 4)
#else
if (p>5)
#endif
{
return 0;
}
facbuf[2*stages] = p;
if (p==2 && stages > 1)
{
facbuf[2*stages] = 4;
facbuf[2] = 2;
}
stages++;
} while (n > 1);
n = nbak;
/* Reverse the order to get the radix 4 at the end, so we can use the
fast degenerate case. It turns out that reversing the order also
improves the noise behaviour. */
for (i=0;i<stages/2;i++)
{
int tmp;
tmp = facbuf[2*i];
facbuf[2*i] = facbuf[2*(stages-i-1)];
facbuf[2*(stages-i-1)] = tmp;
}
for (i=0;i<stages;i++)
{
n /= facbuf[2*i];
facbuf[2*i+1] = n;
}
return 1;
}
static void compute_twiddles(kiss_twiddle_cpx *twiddles, int nfft)
{
int i;
#ifdef FIXED_POINT
for (i=0;i<nfft;++i) {
opus_val32 phase = -i;
kf_cexp2(twiddles+i, DIV32(SHL32(phase,17),nfft));
}
#else
for (i=0;i<nfft;++i) {
const double pi=3.14159265358979323846264338327;
double phase = ( -2*pi /nfft ) * i;
kf_cexp(twiddles+i, phase );
}
#endif
}
int opus_fft_alloc_arch_c(kiss_fft_state *st) {
(void)st;
return 0;
}
/*
*
* Allocates all necessary storage space for the fft and ifft.
* The return value is a contiguous block of memory. As such,
* It can be freed with free().
* */
kiss_fft_state *opus_fft_alloc_twiddles(int nfft,void * mem,size_t * lenmem,
const kiss_fft_state *base, int arch)
{
kiss_fft_state *st=NULL;
size_t memneeded = sizeof(struct kiss_fft_state); /* twiddle factors*/
if ( lenmem==NULL ) {
st = ( kiss_fft_state*)KISS_FFT_MALLOC( memneeded );
}else{
if (mem != NULL && *lenmem >= memneeded)
st = (kiss_fft_state*)mem;
*lenmem = memneeded;
}
if (st) {
opus_int16 *bitrev;
kiss_twiddle_cpx *twiddles;
st->nfft=nfft;
#ifdef FIXED_POINT
st->scale_shift = celt_ilog2(st->nfft);
if (st->nfft == 1<<st->scale_shift)
st->scale = Q15ONE;
else
st->scale = (1073741824+st->nfft/2)/st->nfft>>(15-st->scale_shift);
#else
st->scale = 1.f/nfft;
#endif
if (base != NULL)
{
st->twiddles = base->twiddles;
st->shift = 0;
while (st->shift < 32 && nfft<<st->shift != base->nfft)
st->shift++;
if (st->shift>=32)
goto fail;
} else {
st->twiddles = twiddles = (kiss_twiddle_cpx*)KISS_FFT_MALLOC(sizeof(kiss_twiddle_cpx)*nfft);
compute_twiddles(twiddles, nfft);
st->shift = -1;
}
if (!kf_factor(nfft,st->factors))
{
goto fail;
}
/* bitrev */
st->bitrev = bitrev = (opus_int16*)KISS_FFT_MALLOC(sizeof(opus_int16)*nfft);
if (st->bitrev==NULL)
goto fail;
compute_bitrev_table(0, bitrev, 1,1, st->factors,st);
/* Initialize architecture specific fft parameters */
if (opus_fft_alloc_arch(st, arch))
goto fail;
}
return st;
fail:
opus_fft_free(st, arch);
return NULL;
}
kiss_fft_state *opus_fft_alloc(int nfft,void * mem,size_t * lenmem, int arch)
{
return opus_fft_alloc_twiddles(nfft, mem, lenmem, NULL, arch);
}
void opus_fft_free_arch_c(kiss_fft_state *st) {
(void)st;
}
void opus_fft_free(const kiss_fft_state *cfg, int arch)
{
if (cfg)
{
opus_fft_free_arch((kiss_fft_state *)cfg, arch);
opus_free((opus_int16*)cfg->bitrev);
if (cfg->shift < 0)
opus_free((kiss_twiddle_cpx*)cfg->twiddles);
opus_free((kiss_fft_state*)cfg);
}
}
#endif /* CUSTOM_MODES */
void opus_fft_impl(const kiss_fft_state *st,kiss_fft_cpx *fout)
{
int m2, m;
int p;
int L;
int fstride[MAXFACTORS];
int i;
int shift;
/* st->shift can be -1 */
shift = st->shift>0 ? st->shift : 0;
fstride[0] = 1;
L=0;
do {
p = st->factors[2*L];
m = st->factors[2*L+1];
fstride[L+1] = fstride[L]*p;
L++;
} while(m!=1);
m = st->factors[2*L-1];
for (i=L-1;i>=0;i--)
{
if (i!=0)
m2 = st->factors[2*i-1];
else
m2 = 1;
switch (st->factors[2*i])
{
case 2:
kf_bfly2(fout, m, fstride[i]);
break;
case 4:
kf_bfly4(fout,fstride[i]<<shift,st,m, fstride[i], m2);
break;
#ifndef RADIX_TWO_ONLY
case 3:
kf_bfly3(fout,fstride[i]<<shift,st,m, fstride[i], m2);
break;
case 5:
kf_bfly5(fout,fstride[i]<<shift,st,m, fstride[i], m2);
break;
#endif
}
m = m2;
}
}
void opus_fft_c(const kiss_fft_state *st,const kiss_fft_cpx *fin,kiss_fft_cpx *fout)
{
int i;
opus_val16 scale;
#ifdef FIXED_POINT
/* Allows us to scale with MULT16_32_Q16(), which is faster than
MULT16_32_Q15() on ARM. */
int scale_shift = st->scale_shift-1;
#endif
scale = st->scale;
celt_assert2 (fin != fout, "In-place FFT not supported");
/* Bit-reverse the input */
for (i=0;i<st->nfft;i++)
{
kiss_fft_cpx x = fin[i];
fout[st->bitrev[i]].r = SHR32(MULT16_32_Q16(scale, x.r), scale_shift);
fout[st->bitrev[i]].i = SHR32(MULT16_32_Q16(scale, x.i), scale_shift);
}
opus_fft_impl(st, fout);
}
void opus_ifft_c(const kiss_fft_state *st,const kiss_fft_cpx *fin,kiss_fft_cpx *fout)
{
int i;
celt_assert2 (fin != fout, "In-place FFT not supported");
/* Bit-reverse the input */
for (i=0;i<st->nfft;i++)
fout[st->bitrev[i]] = fin[i];
for (i=0;i<st->nfft;i++)
fout[i].i = -fout[i].i;
opus_fft_impl(st, fout);
for (i=0;i<st->nfft;i++)
fout[i].i = -fout[i].i;
}

@ -0,0 +1,203 @@
/*Copyright (c) 2003-2004, Mark Borgerding
Lots of modifications by Jean-Marc Valin
Copyright (c) 2005-2007, Xiph.Org Foundation
Copyright (c) 2008, Xiph.Org Foundation, CSIRO
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_H
#define KISS_FFT_H
#include <stdlib.h>
#include <math.h>
#include "arch.h"
#include <stdlib.h>
#define opus_alloc(x) malloc(x)
#define opus_free(x) free(x)
#ifdef __cplusplus
extern "C" {
#endif
#ifdef USE_SIMD
# include <xmmintrin.h>
# define kiss_fft_scalar __m128
#define KISS_FFT_MALLOC(nbytes) memalign(16,nbytes)
#else
#define KISS_FFT_MALLOC opus_alloc
#endif
#ifdef FIXED_POINT
#include "arch.h"
# define kiss_fft_scalar opus_int32
# define kiss_twiddle_scalar opus_int16
#else
# ifndef kiss_fft_scalar
/* default is float */
# define kiss_fft_scalar float
# define kiss_twiddle_scalar float
# define KF_SUFFIX _celt_single
# endif
#endif
typedef struct {
kiss_fft_scalar r;
kiss_fft_scalar i;
}kiss_fft_cpx;
typedef struct {
kiss_twiddle_scalar r;
kiss_twiddle_scalar i;
}kiss_twiddle_cpx;
#define MAXFACTORS 8
/* e.g. an fft of length 128 has 4 factors
as far as kissfft is concerned
4*4*4*2
*/
typedef struct arch_fft_state{
int is_supported;
void *priv;
} arch_fft_state;
typedef struct kiss_fft_state{
int nfft;
opus_val16 scale;
#ifdef FIXED_POINT
int scale_shift;
#endif
int shift;
opus_int16 factors[2*MAXFACTORS];
const opus_int16 *bitrev;
const kiss_twiddle_cpx *twiddles;
arch_fft_state *arch_fft;
} kiss_fft_state;
#if defined(HAVE_ARM_NE10)
#include "arm/fft_arm.h"
#endif
/*typedef struct kiss_fft_state* kiss_fft_cfg;*/
/**
* opus_fft_alloc
*
* Initialize a FFT (or IFFT) algorithm's cfg/state buffer.
*
* typical usage: kiss_fft_cfg mycfg=opus_fft_alloc(1024,0,NULL,NULL);
*
* The return value from fft_alloc is a cfg buffer used internally
* by the fft routine or NULL.
*
* If lenmem is NULL, then opus_fft_alloc will allocate a cfg buffer using malloc.
* The returned value should be free()d when done to avoid memory leaks.
*
* The state can be placed in a user supplied buffer 'mem':
* If lenmem is not NULL and mem is not NULL and *lenmem is large enough,
* then the function places the cfg in mem and the size used in *lenmem
* and returns mem.
*
* If lenmem is not NULL and ( mem is NULL or *lenmem is not large enough),
* then the function returns NULL and places the minimum cfg
* buffer size in *lenmem.
* */
kiss_fft_state *opus_fft_alloc_twiddles(int nfft,void * mem,size_t * lenmem, const kiss_fft_state *base, int arch);
kiss_fft_state *opus_fft_alloc(int nfft,void * mem,size_t * lenmem, int arch);
/**
* opus_fft(cfg,in_out_buf)
*
* Perform an FFT on a complex input buffer.
* for a forward FFT,
* fin should be f[0] , f[1] , ... ,f[nfft-1]
* fout will be F[0] , F[1] , ... ,F[nfft-1]
* Note that each element is complex and can be accessed like
f[k].r and f[k].i
* */
void opus_fft_c(const kiss_fft_state *cfg,const kiss_fft_cpx *fin,kiss_fft_cpx *fout);
void opus_ifft_c(const kiss_fft_state *cfg,const kiss_fft_cpx *fin,kiss_fft_cpx *fout);
void opus_fft_impl(const kiss_fft_state *st,kiss_fft_cpx *fout);
void opus_ifft_impl(const kiss_fft_state *st,kiss_fft_cpx *fout);
void opus_fft_free(const kiss_fft_state *cfg, int arch);
void opus_fft_free_arch_c(kiss_fft_state *st);
int opus_fft_alloc_arch_c(kiss_fft_state *st);
#if !defined(OVERRIDE_OPUS_FFT)
/* Is run-time CPU detection enabled on this platform? */
#if defined(OPUS_HAVE_RTCD) && (defined(HAVE_ARM_NE10))
extern int (*const OPUS_FFT_ALLOC_ARCH_IMPL[OPUS_ARCHMASK+1])(
kiss_fft_state *st);
#define opus_fft_alloc_arch(_st, arch) \
((*OPUS_FFT_ALLOC_ARCH_IMPL[(arch)&OPUS_ARCHMASK])(_st))
extern void (*const OPUS_FFT_FREE_ARCH_IMPL[OPUS_ARCHMASK+1])(
kiss_fft_state *st);
#define opus_fft_free_arch(_st, arch) \
((*OPUS_FFT_FREE_ARCH_IMPL[(arch)&OPUS_ARCHMASK])(_st))
extern void (*const OPUS_FFT[OPUS_ARCHMASK+1])(const kiss_fft_state *cfg,
const kiss_fft_cpx *fin, kiss_fft_cpx *fout);
#define opus_fft(_cfg, _fin, _fout, arch) \
((*OPUS_FFT[(arch)&OPUS_ARCHMASK])(_cfg, _fin, _fout))
extern void (*const OPUS_IFFT[OPUS_ARCHMASK+1])(const kiss_fft_state *cfg,
const kiss_fft_cpx *fin, kiss_fft_cpx *fout);
#define opus_ifft(_cfg, _fin, _fout, arch) \
((*OPUS_IFFT[(arch)&OPUS_ARCHMASK])(_cfg, _fin, _fout))
#else /* else for if defined(OPUS_HAVE_RTCD) && (defined(HAVE_ARM_NE10)) */
#define opus_fft_alloc_arch(_st, arch) \
((void)(arch), opus_fft_alloc_arch_c(_st))
#define opus_fft_free_arch(_st, arch) \
((void)(arch), opus_fft_free_arch_c(_st))
#define opus_fft(_cfg, _fin, _fout, arch) \
((void)(arch), opus_fft_c(_cfg, _fin, _fout))
#define opus_ifft(_cfg, _fin, _fout, arch) \
((void)(arch), opus_ifft_c(_cfg, _fin, _fout))
#endif /* end if defined(OPUS_HAVE_RTCD) && (defined(HAVE_ARM_NE10)) */
#endif /* end if !defined(OVERRIDE_OPUS_FFT) */
#ifdef __cplusplus
}
#endif
#endif

@ -0,0 +1,159 @@
/* (C) COPYRIGHT 1994-2002 Xiph.Org Foundation */
/* Modified 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.
*/
/* opus_types.h based on ogg_types.h from libogg */
/**
@file opus_types.h
@brief Opus reference implementation types
*/
#ifndef OPUS_TYPES_H
#define OPUS_TYPES_H
/* Use the real stdint.h if it's there (taken from Paul Hsieh's pstdint.h) */
#if (defined(__STDC__) && __STDC__ && defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || (defined(__GNUC__) && (defined(_STDINT_H) || defined(_STDINT_H_)) || defined (HAVE_STDINT_H))
#include <stdint.h>
typedef int16_t opus_int16;
typedef uint16_t opus_uint16;
typedef int32_t opus_int32;
typedef uint32_t opus_uint32;
#elif defined(_WIN32)
# if defined(__CYGWIN__)
# include <_G_config.h>
typedef _G_int32_t opus_int32;
typedef _G_uint32_t opus_uint32;
typedef _G_int16 opus_int16;
typedef _G_uint16 opus_uint16;
# elif defined(__MINGW32__)
typedef short opus_int16;
typedef unsigned short opus_uint16;
typedef int opus_int32;
typedef unsigned int opus_uint32;
# elif defined(__MWERKS__)
typedef int opus_int32;
typedef unsigned int opus_uint32;
typedef short opus_int16;
typedef unsigned short opus_uint16;
# else
/* MSVC/Borland */
typedef __int32 opus_int32;
typedef unsigned __int32 opus_uint32;
typedef __int16 opus_int16;
typedef unsigned __int16 opus_uint16;
# endif
#elif defined(__MACOS__)
# include <sys/types.h>
typedef SInt16 opus_int16;
typedef UInt16 opus_uint16;
typedef SInt32 opus_int32;
typedef UInt32 opus_uint32;
#elif (defined(__APPLE__) && defined(__MACH__)) /* MacOS X Framework build */
# include <sys/types.h>
typedef int16_t opus_int16;
typedef u_int16_t opus_uint16;
typedef int32_t opus_int32;
typedef u_int32_t opus_uint32;
#elif defined(__BEOS__)
/* Be */
# include <inttypes.h>
typedef int16 opus_int16;
typedef u_int16 opus_uint16;
typedef int32_t opus_int32;
typedef u_int32_t opus_uint32;
#elif defined (__EMX__)
/* OS/2 GCC */
typedef short opus_int16;
typedef unsigned short opus_uint16;
typedef int opus_int32;
typedef unsigned int opus_uint32;
#elif defined (DJGPP)
/* DJGPP */
typedef short opus_int16;
typedef unsigned short opus_uint16;
typedef int opus_int32;
typedef unsigned int opus_uint32;
#elif defined(R5900)
/* PS2 EE */
typedef int opus_int32;
typedef unsigned opus_uint32;
typedef short opus_int16;
typedef unsigned short opus_uint16;
#elif defined(__SYMBIAN32__)
/* Symbian GCC */
typedef signed short opus_int16;
typedef unsigned short opus_uint16;
typedef signed int opus_int32;
typedef unsigned int opus_uint32;
#elif defined(CONFIG_TI_C54X) || defined (CONFIG_TI_C55X)
typedef short opus_int16;
typedef unsigned short opus_uint16;
typedef long opus_int32;
typedef unsigned long opus_uint32;
#elif defined(CONFIG_TI_C6X)
typedef short opus_int16;
typedef unsigned short opus_uint16;
typedef int opus_int32;
typedef unsigned int opus_uint32;
#else
/* Give up, take a reasonable guess */
typedef short opus_int16;
typedef unsigned short opus_uint16;
typedef int opus_int32;
typedef unsigned int opus_uint32;
#endif
#define opus_int int /* used for counters etc; at least 16 bits */
#define opus_int64 long long
#define opus_int8 signed char
#define opus_uint unsigned int /* used for counters etc; at least 16 bits */
#define opus_uint64 unsigned long long
#define opus_uint8 unsigned char
#endif /* OPUS_TYPES_H */

@ -0,0 +1,526 @@
/* Copyright (c) 2007-2008 CSIRO
Copyright (c) 2007-2009 Xiph.Org Foundation
Written by Jean-Marc Valin */
/**
@file pitch.c
@brief Pitch analysis
*/
/*
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 "pitch.h"
#include "common.h"
//#include "modes.h"
//#include "stack_alloc.h"
//#include "mathops.h"
#include "celt_lpc.h"
#include "math.h"
static void find_best_pitch(opus_val32 *xcorr, opus_val16 *y, int len,
int max_pitch, int *best_pitch
#ifdef FIXED_POINT
, int yshift, opus_val32 maxcorr
#endif
)
{
int i, j;
opus_val32 Syy=1;
opus_val16 best_num[2];
opus_val32 best_den[2];
#ifdef FIXED_POINT
int xshift;
xshift = celt_ilog2(maxcorr)-14;
#endif
best_num[0] = -1;
best_num[1] = -1;
best_den[0] = 0;
best_den[1] = 0;
best_pitch[0] = 0;
best_pitch[1] = 1;
for (j=0;j<len;j++)
Syy = ADD32(Syy, SHR32(MULT16_16(y[j],y[j]), yshift));
for (i=0;i<max_pitch;i++)
{
if (xcorr[i]>0)
{
opus_val16 num;
opus_val32 xcorr16;
xcorr16 = EXTRACT16(VSHR32(xcorr[i], xshift));
#ifndef FIXED_POINT
/* Considering the range of xcorr16, this should avoid both underflows
and overflows (inf) when squaring xcorr16 */
xcorr16 *= 1e-12f;
#endif
num = MULT16_16_Q15(xcorr16,xcorr16);
if (MULT16_32_Q15(num,best_den[1]) > MULT16_32_Q15(best_num[1],Syy))
{
if (MULT16_32_Q15(num,best_den[0]) > MULT16_32_Q15(best_num[0],Syy))
{
best_num[1] = best_num[0];
best_den[1] = best_den[0];
best_pitch[1] = best_pitch[0];
best_num[0] = num;
best_den[0] = Syy;
best_pitch[0] = i;
} else {
best_num[1] = num;
best_den[1] = Syy;
best_pitch[1] = i;
}
}
}
Syy += SHR32(MULT16_16(y[i+len],y[i+len]),yshift) - SHR32(MULT16_16(y[i],y[i]),yshift);
Syy = MAX32(1, Syy);
}
}
static void celt_fir5(const opus_val16 *x,
const opus_val16 *num,
opus_val16 *y,
int N,
opus_val16 *mem)
{
int i;
opus_val16 num0, num1, num2, num3, num4;
opus_val32 mem0, mem1, mem2, mem3, mem4;
num0=num[0];
num1=num[1];
num2=num[2];
num3=num[3];
num4=num[4];
mem0=mem[0];
mem1=mem[1];
mem2=mem[2];
mem3=mem[3];
mem4=mem[4];
for (i=0;i<N;i++)
{
opus_val32 sum = SHL32(EXTEND32(x[i]), SIG_SHIFT);
sum = MAC16_16(sum,num0,mem0);
sum = MAC16_16(sum,num1,mem1);
sum = MAC16_16(sum,num2,mem2);
sum = MAC16_16(sum,num3,mem3);
sum = MAC16_16(sum,num4,mem4);
mem4 = mem3;
mem3 = mem2;
mem2 = mem1;
mem1 = mem0;
mem0 = x[i];
y[i] = ROUND16(sum, SIG_SHIFT);
}
mem[0]=mem0;
mem[1]=mem1;
mem[2]=mem2;
mem[3]=mem3;
mem[4]=mem4;
}
void pitch_downsample(celt_sig *x[], opus_val16 *x_lp,
int len, int C)
{
int i;
opus_val32 ac[5];
opus_val16 tmp=Q15ONE;
opus_val16 lpc[4], mem[5]={0,0,0,0,0};
opus_val16 lpc2[5];
opus_val16 c1 = QCONST16(.8f,15);
#ifdef FIXED_POINT
int shift;
opus_val32 maxabs = celt_maxabs32(x[0], len);
if (C==2)
{
opus_val32 maxabs_1 = celt_maxabs32(x[1], len);
maxabs = MAX32(maxabs, maxabs_1);
}
if (maxabs<1)
maxabs=1;
shift = celt_ilog2(maxabs)-10;
if (shift<0)
shift=0;
if (C==2)
shift++;
#endif
for (i=1;i<len>>1;i++)
x_lp[i] = SHR32(HALF32(HALF32(x[0][(2*i-1)]+x[0][(2*i+1)])+x[0][2*i]), shift);
x_lp[0] = SHR32(HALF32(HALF32(x[0][1])+x[0][0]), shift);
if (C==2)
{
for (i=1;i<len>>1;i++)
x_lp[i] += SHR32(HALF32(HALF32(x[1][(2*i-1)]+x[1][(2*i+1)])+x[1][2*i]), shift);
x_lp[0] += SHR32(HALF32(HALF32(x[1][1])+x[1][0]), shift);
}
_celt_autocorr(x_lp, ac, NULL, 0,
4, len>>1);
/* Noise floor -40 dB */
#ifdef FIXED_POINT
ac[0] += SHR32(ac[0],13);
#else
ac[0] *= 1.0001f;
#endif
/* Lag windowing */
for (i=1;i<=4;i++)
{
/*ac[i] *= exp(-.5*(2*M_PI*.002*i)*(2*M_PI*.002*i));*/
#ifdef FIXED_POINT
ac[i] -= MULT16_32_Q15(2*i*i, ac[i]);
#else
ac[i] -= ac[i]*(.008f*i)*(.008f*i);
#endif
}
_celt_lpc(lpc, ac, 4);
for (i=0;i<4;i++)
{
tmp = MULT16_16_Q15(QCONST16(.9f,15), tmp);
lpc[i] = MULT16_16_Q15(lpc[i], tmp);
}
/* Add a zero */
lpc2[0] = lpc[0] + QCONST16(.8f,SIG_SHIFT);
lpc2[1] = lpc[1] + MULT16_16_Q15(c1,lpc[0]);
lpc2[2] = lpc[2] + MULT16_16_Q15(c1,lpc[1]);
lpc2[3] = lpc[3] + MULT16_16_Q15(c1,lpc[2]);
lpc2[4] = MULT16_16_Q15(c1,lpc[3]);
celt_fir5(x_lp, lpc2, x_lp, len>>1, mem);
}
void celt_pitch_xcorr(const opus_val16 *_x, const opus_val16 *_y,
opus_val32 *xcorr, int len, int max_pitch)
{
#if 0 /* This is a simple version of the pitch correlation that should work
well on DSPs like Blackfin and TI C5x/C6x */
int i, j;
#ifdef FIXED_POINT
opus_val32 maxcorr=1;
#endif
for (i=0;i<max_pitch;i++)
{
opus_val32 sum = 0;
for (j=0;j<len;j++)
sum = MAC16_16(sum, _x[j], _y[i+j]);
xcorr[i] = sum;
#ifdef FIXED_POINT
maxcorr = MAX32(maxcorr, sum);
#endif
}
#ifdef FIXED_POINT
return maxcorr;
#endif
#else /* Unrolled version of the pitch correlation -- runs faster on x86 and ARM */
int i;
/*The EDSP version requires that max_pitch is at least 1, and that _x is
32-bit aligned.
Since it's hard to put asserts in assembly, put them here.*/
#ifdef FIXED_POINT
opus_val32 maxcorr=1;
#endif
celt_assert(max_pitch>0);
celt_assert((((unsigned char *)_x-(unsigned char *)NULL)&3)==0);
for (i=0;i<max_pitch-3;i+=4)
{
opus_val32 sum[4]={0,0,0,0};
xcorr_kernel(_x, _y+i, sum, len);
xcorr[i]=sum[0];
xcorr[i+1]=sum[1];
xcorr[i+2]=sum[2];
xcorr[i+3]=sum[3];
#ifdef FIXED_POINT
sum[0] = MAX32(sum[0], sum[1]);
sum[2] = MAX32(sum[2], sum[3]);
sum[0] = MAX32(sum[0], sum[2]);
maxcorr = MAX32(maxcorr, sum[0]);
#endif
}
/* In case max_pitch isn't a multiple of 4, do non-unrolled version. */
for (;i<max_pitch;i++)
{
opus_val32 sum;
sum = celt_inner_prod(_x, _y+i, len);
xcorr[i] = sum;
#ifdef FIXED_POINT
maxcorr = MAX32(maxcorr, sum);
#endif
}
#ifdef FIXED_POINT
return maxcorr;
#endif
#endif
}
void pitch_search(const opus_val16 *x_lp, opus_val16 *y,
int len, int max_pitch, int *pitch)
{
int i, j;
int lag;
int best_pitch[2]={0,0};
#ifdef FIXED_POINT
opus_val32 maxcorr;
opus_val32 xmax, ymax;
int shift=0;
#endif
int offset;
celt_assert(len>0);
celt_assert(max_pitch>0);
lag = len+max_pitch;
opus_val16 x_lp4[len>>2];
opus_val16 y_lp4[lag>>2];
opus_val32 xcorr[max_pitch>>1];
/* Downsample by 2 again */
for (j=0;j<len>>2;j++)
x_lp4[j] = x_lp[2*j];
for (j=0;j<lag>>2;j++)
y_lp4[j] = y[2*j];
#ifdef FIXED_POINT
xmax = celt_maxabs16(x_lp4, len>>2);
ymax = celt_maxabs16(y_lp4, lag>>2);
shift = celt_ilog2(MAX32(1, MAX32(xmax, ymax)))-11;
if (shift>0)
{
for (j=0;j<len>>2;j++)
x_lp4[j] = SHR16(x_lp4[j], shift);
for (j=0;j<lag>>2;j++)
y_lp4[j] = SHR16(y_lp4[j], shift);
/* Use double the shift for a MAC */
shift *= 2;
} else {
shift = 0;
}
#endif
/* Coarse search with 4x decimation */
#ifdef FIXED_POINT
maxcorr =
#endif
celt_pitch_xcorr(x_lp4, y_lp4, xcorr, len>>2, max_pitch>>2);
find_best_pitch(xcorr, y_lp4, len>>2, max_pitch>>2, best_pitch
#ifdef FIXED_POINT
, 0, maxcorr
#endif
);
/* Finer search with 2x decimation */
#ifdef FIXED_POINT
maxcorr=1;
#endif
for (i=0;i<max_pitch>>1;i++)
{
opus_val32 sum;
xcorr[i] = 0;
if (abs(i-2*best_pitch[0])>2 && abs(i-2*best_pitch[1])>2)
continue;
#ifdef FIXED_POINT
sum = 0;
for (j=0;j<len>>1;j++)
sum += SHR32(MULT16_16(x_lp[j],y[i+j]), shift);
#else
sum = celt_inner_prod(x_lp, y+i, len>>1);
#endif
xcorr[i] = MAX32(-1, sum);
#ifdef FIXED_POINT
maxcorr = MAX32(maxcorr, sum);
#endif
}
find_best_pitch(xcorr, y, len>>1, max_pitch>>1, best_pitch
#ifdef FIXED_POINT
, shift+1, maxcorr
#endif
);
/* Refine by pseudo-interpolation */
if (best_pitch[0]>0 && best_pitch[0]<(max_pitch>>1)-1)
{
opus_val32 a, b, c;
a = xcorr[best_pitch[0]-1];
b = xcorr[best_pitch[0]];
c = xcorr[best_pitch[0]+1];
if ((c-a) > MULT16_32_Q15(QCONST16(.7f,15),b-a))
offset = 1;
else if ((a-c) > MULT16_32_Q15(QCONST16(.7f,15),b-c))
offset = -1;
else
offset = 0;
} else {
offset = 0;
}
*pitch = 2*best_pitch[0]-offset;
}
#ifdef FIXED_POINT
static opus_val16 compute_pitch_gain(opus_val32 xy, opus_val32 xx, opus_val32 yy)
{
opus_val32 x2y2;
int sx, sy, shift;
opus_val32 g;
opus_val16 den;
if (xy == 0 || xx == 0 || yy == 0)
return 0;
sx = celt_ilog2(xx)-14;
sy = celt_ilog2(yy)-14;
shift = sx + sy;
x2y2 = SHR32(MULT16_16(VSHR32(xx, sx), VSHR32(yy, sy)), 14);
if (shift & 1) {
if (x2y2 < 32768)
{
x2y2 <<= 1;
shift--;
} else {
x2y2 >>= 1;
shift++;
}
}
den = celt_rsqrt_norm(x2y2);
g = MULT16_32_Q15(den, xy);
g = VSHR32(g, (shift>>1)-1);
return EXTRACT16(MIN32(g, Q15ONE));
}
#else
static opus_val16 compute_pitch_gain(opus_val32 xy, opus_val32 xx, opus_val32 yy)
{
return xy/sqrt(1+xx*yy);
}
#endif
static const int second_check[16] = {0, 0, 3, 2, 3, 2, 5, 2, 3, 2, 3, 2, 5, 2, 3, 2};
opus_val16 remove_doubling(opus_val16 *x, int maxperiod, int minperiod,
int N, int *T0_, int prev_period, opus_val16 prev_gain)
{
int k, i, T, T0;
opus_val16 g, g0;
opus_val16 pg;
opus_val32 xy,xx,yy,xy2;
opus_val32 xcorr[3];
opus_val32 best_xy, best_yy;
int offset;
int minperiod0;
minperiod0 = minperiod;
maxperiod /= 2;
minperiod /= 2;
*T0_ /= 2;
prev_period /= 2;
N /= 2;
x += maxperiod;
if (*T0_>=maxperiod)
*T0_=maxperiod-1;
T = T0 = *T0_;
opus_val32 yy_lookup[maxperiod+1];
dual_inner_prod(x, x, x-T0, N, &xx, &xy);
yy_lookup[0] = xx;
yy=xx;
for (i=1;i<=maxperiod;i++)
{
yy = yy+MULT16_16(x[-i],x[-i])-MULT16_16(x[N-i],x[N-i]);
yy_lookup[i] = MAX32(0, yy);
}
yy = yy_lookup[T0];
best_xy = xy;
best_yy = yy;
g = g0 = compute_pitch_gain(xy, xx, yy);
/* Look for any pitch at T/k */
for (k=2;k<=15;k++)
{
int T1, T1b;
opus_val16 g1;
opus_val16 cont=0;
opus_val16 thresh;
T1 = (2*T0+k)/(2*k);
if (T1 < minperiod)
break;
/* Look for another strong correlation at T1b */
if (k==2)
{
if (T1+T0>maxperiod)
T1b = T0;
else
T1b = T0+T1;
} else
{
T1b = (2*second_check[k]*T0+k)/(2*k);
}
dual_inner_prod(x, &x[-T1], &x[-T1b], N, &xy, &xy2);
xy = HALF32(xy + xy2);
yy = HALF32(yy_lookup[T1] + yy_lookup[T1b]);
g1 = compute_pitch_gain(xy, xx, yy);
if (abs(T1-prev_period)<=1)
cont = prev_gain;
else if (abs(T1-prev_period)<=2 && 5*k*k < T0)
cont = HALF16(prev_gain);
else
cont = 0;
thresh = MAX16(QCONST16(.3f,15), MULT16_16_Q15(QCONST16(.7f,15),g0)-cont);
/* Bias against very high pitch (very short period) to avoid false-positives
due to short-term correlation */
if (T1<3*minperiod)
thresh = MAX16(QCONST16(.4f,15), MULT16_16_Q15(QCONST16(.85f,15),g0)-cont);
else if (T1<2*minperiod)
thresh = MAX16(QCONST16(.5f,15), MULT16_16_Q15(QCONST16(.9f,15),g0)-cont);
if (g1 > thresh)
{
best_xy = xy;
best_yy = yy;
T = T1;
g = g1;
}
}
best_xy = MAX32(0, best_xy);
if (best_yy <= best_xy)
pg = Q15ONE;
else
pg = best_xy/(best_yy+1);
for (k=0;k<3;k++)
xcorr[k] = celt_inner_prod(x, x-(T+k-1), N);
if ((xcorr[2]-xcorr[0]) > MULT16_32_Q15(QCONST16(.7f,15),xcorr[1]-xcorr[0]))
offset = 1;
else if ((xcorr[0]-xcorr[2]) > MULT16_32_Q15(QCONST16(.7f,15),xcorr[1]-xcorr[2]))
offset = -1;
else
offset = 0;
if (pg > g)
pg = g;
*T0_ = 2*T+offset;
if (*T0_<minperiod0)
*T0_=minperiod0;
return pg;
}

@ -0,0 +1,149 @@
/* Copyright (c) 2007-2008 CSIRO
Copyright (c) 2007-2009 Xiph.Org Foundation
Written by Jean-Marc Valin */
/**
@file pitch.h
@brief Pitch analysis
*/
/*
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 PITCH_H
#define PITCH_H
//#include "modes.h"
//#include "cpu_support.h"
#include "arch.h"
void pitch_downsample(celt_sig *x[], opus_val16 *x_lp,
int len, int C);
void pitch_search(const opus_val16 *x_lp, opus_val16 *y,
int len, int max_pitch, int *pitch);
opus_val16 remove_doubling(opus_val16 *x, int maxperiod, int minperiod,
int N, int *T0, int prev_period, opus_val16 prev_gain);
/* OPT: This is the kernel you really want to optimize. It gets used a lot
by the prefilter and by the PLC. */
static OPUS_INLINE void xcorr_kernel(const opus_val16 * x, const opus_val16 * y, opus_val32 sum[4], int len)
{
int j;
opus_val16 y_0, y_1, y_2, y_3;
celt_assert(len>=3);
y_3=0; /* gcc doesn't realize that y_3 can't be used uninitialized */
y_0=*y++;
y_1=*y++;
y_2=*y++;
for (j=0;j<len-3;j+=4)
{
opus_val16 tmp;
tmp = *x++;
y_3=*y++;
sum[0] = MAC16_16(sum[0],tmp,y_0);
sum[1] = MAC16_16(sum[1],tmp,y_1);
sum[2] = MAC16_16(sum[2],tmp,y_2);
sum[3] = MAC16_16(sum[3],tmp,y_3);
tmp=*x++;
y_0=*y++;
sum[0] = MAC16_16(sum[0],tmp,y_1);
sum[1] = MAC16_16(sum[1],tmp,y_2);
sum[2] = MAC16_16(sum[2],tmp,y_3);
sum[3] = MAC16_16(sum[3],tmp,y_0);
tmp=*x++;
y_1=*y++;
sum[0] = MAC16_16(sum[0],tmp,y_2);
sum[1] = MAC16_16(sum[1],tmp,y_3);
sum[2] = MAC16_16(sum[2],tmp,y_0);
sum[3] = MAC16_16(sum[3],tmp,y_1);
tmp=*x++;
y_2=*y++;
sum[0] = MAC16_16(sum[0],tmp,y_3);
sum[1] = MAC16_16(sum[1],tmp,y_0);
sum[2] = MAC16_16(sum[2],tmp,y_1);
sum[3] = MAC16_16(sum[3],tmp,y_2);
}
if (j++<len)
{
opus_val16 tmp = *x++;
y_3=*y++;
sum[0] = MAC16_16(sum[0],tmp,y_0);
sum[1] = MAC16_16(sum[1],tmp,y_1);
sum[2] = MAC16_16(sum[2],tmp,y_2);
sum[3] = MAC16_16(sum[3],tmp,y_3);
}
if (j++<len)
{
opus_val16 tmp=*x++;
y_0=*y++;
sum[0] = MAC16_16(sum[0],tmp,y_1);
sum[1] = MAC16_16(sum[1],tmp,y_2);
sum[2] = MAC16_16(sum[2],tmp,y_3);
sum[3] = MAC16_16(sum[3],tmp,y_0);
}
if (j<len)
{
opus_val16 tmp=*x++;
y_1=*y++;
sum[0] = MAC16_16(sum[0],tmp,y_2);
sum[1] = MAC16_16(sum[1],tmp,y_3);
sum[2] = MAC16_16(sum[2],tmp,y_0);
sum[3] = MAC16_16(sum[3],tmp,y_1);
}
}
static OPUS_INLINE void dual_inner_prod(const opus_val16 *x, const opus_val16 *y01, const opus_val16 *y02,
int N, opus_val32 *xy1, opus_val32 *xy2)
{
int i;
opus_val32 xy01=0;
opus_val32 xy02=0;
for (i=0;i<N;i++)
{
xy01 = MAC16_16(xy01, x[i], y01[i]);
xy02 = MAC16_16(xy02, x[i], y02[i]);
}
*xy1 = xy01;
*xy2 = xy02;
}
/*We make sure a C version is always available for cases where the overhead of
vectorization and passing around an arch flag aren't worth it.*/
static OPUS_INLINE opus_val32 celt_inner_prod(const opus_val16 *x,
const opus_val16 *y, int N)
{
int i;
opus_val32 xy=0;
for (i=0;i<N;i++)
xy = MAC16_16(xy, x[i], y[i]);
return xy;
}
void celt_pitch_xcorr(const opus_val16 *_x, const opus_val16 *_y,
opus_val32 *xcorr, int len, int max_pitch);
#endif

@ -0,0 +1,178 @@
/* Copyright (c) 2008-2011 Octasic Inc.
2012-2017 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 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.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <math.h>
#include "opus_types.h"
#include "common.h"
#include "arch.h"
#include "tansig_table.h"
#include "rnn.h"
#include "rnn_data.h"
#include <stdio.h>
static OPUS_INLINE float tansig_approx(float x)
{
int i;
float y, dy;
float sign=1;
/* Tests are reversed to catch NaNs */
if (!(x<8))
return 1;
if (!(x>-8))
return -1;
#ifndef FIXED_POINT
/* Another check in case of -ffast-math */
if (celt_isnan(x))
return 0;
#endif
if (x<0)
{
x=-x;
sign=-1;
}
i = (int)floor(.5f+25*x);
x -= .04f*i;
y = tansig_table[i];
dy = 1-y*y;
y = y + x*dy*(1 - y*x);
return sign*y;
}
static OPUS_INLINE float sigmoid_approx(float x)
{
return .5 + .5*tansig_approx(.5*x);
}
static OPUS_INLINE float relu(float x)
{
return x < 0 ? 0 : x;
}
void compute_dense(const DenseLayer *layer, float *output, const float *input)
{
int i, j;
int N, M;
int stride;
M = layer->nb_inputs;
N = layer->nb_neurons;
stride = N;
for (i=0;i<N;i++)
{
/* Compute update gate. */
float sum = layer->bias[i];
for (j=0;j<M;j++)
sum += layer->input_weights[j*stride + i]*input[j];
output[i] = WEIGHTS_SCALE*sum;
}
if (layer->activation == ACTIVATION_SIGMOID) {
for (i=0;i<N;i++)
output[i] = sigmoid_approx(output[i]);
} else if (layer->activation == ACTIVATION_TANH) {
for (i=0;i<N;i++)
output[i] = tansig_approx(output[i]);
} else if (layer->activation == ACTIVATION_RELU) {
for (i=0;i<N;i++)
output[i] = relu(output[i]);
} else {
*(int*)0=0;
}
}
void compute_gru(const GRULayer *gru, float *state, const float *input)
{
int i, j;
int N, M;
int stride;
float z[MAX_NEURONS];
float r[MAX_NEURONS];
float h[MAX_NEURONS];
M = gru->nb_inputs;
N = gru->nb_neurons;
stride = 3*N;
for (i=0;i<N;i++)
{
/* Compute update gate. */
float sum = gru->bias[i];
for (j=0;j<M;j++)
sum += gru->input_weights[j*stride + i]*input[j];
for (j=0;j<N;j++)
sum += gru->recurrent_weights[j*stride + i]*state[j];
z[i] = sigmoid_approx(WEIGHTS_SCALE*sum);
}
for (i=0;i<N;i++)
{
/* Compute reset gate. */
float sum = gru->bias[N + i];
for (j=0;j<M;j++)
sum += gru->input_weights[N + j*stride + i]*input[j];
for (j=0;j<N;j++)
sum += gru->recurrent_weights[N + j*stride + i]*state[j];
r[i] = sigmoid_approx(WEIGHTS_SCALE*sum);
}
for (i=0;i<N;i++)
{
/* Compute output. */
float sum = gru->bias[2*N + i];
for (j=0;j<M;j++)
sum += gru->input_weights[2*N + j*stride + i]*input[j];
for (j=0;j<N;j++)
sum += gru->recurrent_weights[2*N + j*stride + i]*state[j]*r[j];
if (gru->activation == ACTIVATION_SIGMOID) sum = sigmoid_approx(WEIGHTS_SCALE*sum);
else if (gru->activation == ACTIVATION_TANH) sum = tansig_approx(WEIGHTS_SCALE*sum);
else if (gru->activation == ACTIVATION_RELU) sum = relu(WEIGHTS_SCALE*sum);
else *(int*)0=0;
h[i] = z[i]*state[i] + (1-z[i])*sum;
}
for (i=0;i<N;i++)
state[i] = h[i];
}
#define INPUT_SIZE 42
void compute_rnn(RNNState *rnn, float *gains, float *vad, const float *input) {
int i;
float dense_out[MAX_NEURONS];
float noise_input[MAX_NEURONS*3];
float denoise_input[MAX_NEURONS*3];
compute_dense(rnn->model->input_dense, dense_out, input);
compute_gru(rnn->model->vad_gru, rnn->vad_gru_state, dense_out);
compute_dense(rnn->model->vad_output, vad, rnn->vad_gru_state);
for (i=0;i<rnn->model->input_dense_size;i++) noise_input[i] = dense_out[i];
for (i=0;i<rnn->model->vad_gru_size;i++) noise_input[i+rnn->model->input_dense_size] = rnn->vad_gru_state[i];
for (i=0;i<INPUT_SIZE;i++) noise_input[i+rnn->model->input_dense_size+rnn->model->vad_gru_size] = input[i];
compute_gru(rnn->model->noise_gru, rnn->noise_gru_state, noise_input);
for (i=0;i<rnn->model->vad_gru_size;i++) denoise_input[i] = rnn->vad_gru_state[i];
for (i=0;i<rnn->model->noise_gru_size;i++) denoise_input[i+rnn->model->vad_gru_size] = rnn->noise_gru_state[i];
for (i=0;i<INPUT_SIZE;i++) denoise_input[i+rnn->model->vad_gru_size+rnn->model->noise_gru_size] = input[i];
compute_gru(rnn->model->denoise_gru, rnn->denoise_gru_state, denoise_input);
compute_dense(rnn->model->denoise_output, gains, rnn->denoise_gru_state);
}

@ -0,0 +1,69 @@
/* Copyright (c) 2017 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 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 RNN_H_
#define RNN_H_
#include "rnnoise.h"
#include "opus_types.h"
#define WEIGHTS_SCALE (1.f/256)
#define MAX_NEURONS 128
#define ACTIVATION_TANH 0
#define ACTIVATION_SIGMOID 1
#define ACTIVATION_RELU 2
typedef signed char rnn_weight;
typedef struct {
const rnn_weight *bias;
const rnn_weight *input_weights;
int nb_inputs;
int nb_neurons;
int activation;
} DenseLayer;
typedef struct {
const rnn_weight *bias;
const rnn_weight *input_weights;
const rnn_weight *recurrent_weights;
int nb_inputs;
int nb_neurons;
int activation;
} GRULayer;
typedef struct RNNState RNNState;
void compute_dense(const DenseLayer *layer, float *output, const float *input);
void compute_gru(const GRULayer *gru, float *state, const float *input);
void compute_rnn(RNNState *rnn, float *gains, float *vad, const float *input);
#endif /* _MLP_H_ */

File diff suppressed because it is too large Load Diff

@ -0,0 +1,34 @@
#ifndef RNN_DATA_H
#define RNN_DATA_H
#include "rnn.h"
struct RNNModel {
int input_dense_size;
const DenseLayer *input_dense;
int vad_gru_size;
const GRULayer *vad_gru;
int noise_gru_size;
const GRULayer *noise_gru;
int denoise_gru_size;
const GRULayer *denoise_gru;
int denoise_output_size;
const DenseLayer *denoise_output;
int vad_output_size;
const DenseLayer *vad_output;
};
struct RNNState {
const RNNModel *model;
float *vad_gru_state;
float *noise_gru_state;
float *denoise_gru_state;
};
#endif

@ -0,0 +1,168 @@
/* Copyright (c) 2018 Gregor Richards */
/*
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.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include "rnn.h"
#include "rnn_data.h"
#include "rnnoise.h"
/* Although these values are the same as in rnn.h, we make them separate to
* avoid accidentally burning internal values into a file format */
#define F_ACTIVATION_TANH 0
#define F_ACTIVATION_SIGMOID 1
#define F_ACTIVATION_RELU 2
RNNModel *rnnoise_model_from_file(FILE *f)
{
int i, in;
if (fscanf(f, "rnnoise-nu model file version %d\n", &in) != 1 || in != 1)
return NULL;
RNNModel *ret = calloc(1, sizeof(RNNModel));
if (!ret)
return NULL;
#define ALLOC_LAYER(type, name) \
type *name; \
name = calloc(1, sizeof(type)); \
if (!name) { \
rnnoise_model_free(ret); \
return NULL; \
} \
ret->name = name
ALLOC_LAYER(DenseLayer, input_dense);
ALLOC_LAYER(GRULayer, vad_gru);
ALLOC_LAYER(GRULayer, noise_gru);
ALLOC_LAYER(GRULayer, denoise_gru);
ALLOC_LAYER(DenseLayer, denoise_output);
ALLOC_LAYER(DenseLayer, vad_output);
#define INPUT_VAL(name) do { \
if (fscanf(f, "%d", &in) != 1 || in < 0 || in > 128) { \
rnnoise_model_free(ret); \
return NULL; \
} \
name = in; \
} while (0)
#define INPUT_ACTIVATION(name) do { \
int activation; \
INPUT_VAL(activation); \
switch (activation) { \
case F_ACTIVATION_SIGMOID: \
name = ACTIVATION_SIGMOID; \
break; \
case F_ACTIVATION_RELU: \
name = ACTIVATION_RELU; \
break; \
default: \
name = ACTIVATION_TANH; \
} \
} while (0)
#define INPUT_ARRAY(name, len) do { \
rnn_weight *values = malloc((len) * sizeof(rnn_weight)); \
if (!values) { \
rnnoise_model_free(ret); \
return NULL; \
} \
name = values; \
for (i = 0; i < (len); i++) { \
if (fscanf(f, "%d", &in) != 1) { \
rnnoise_model_free(ret); \
return NULL; \
} \
values[i] = in; \
} \
} while (0)
#define INPUT_DENSE(name) do { \
INPUT_VAL(name->nb_inputs); \
INPUT_VAL(name->nb_neurons); \
ret->name ## _size = name->nb_neurons; \
INPUT_ACTIVATION(name->activation); \
INPUT_ARRAY(name->input_weights, name->nb_inputs * name->nb_neurons); \
INPUT_ARRAY(name->bias, name->nb_neurons); \
} while (0)
#define INPUT_GRU(name) do { \
INPUT_VAL(name->nb_inputs); \
INPUT_VAL(name->nb_neurons); \
ret->name ## _size = name->nb_neurons; \
INPUT_ACTIVATION(name->activation); \
INPUT_ARRAY(name->input_weights, name->nb_inputs * name->nb_neurons * 3); \
INPUT_ARRAY(name->recurrent_weights, name->nb_neurons * name->nb_neurons * 3); \
INPUT_ARRAY(name->bias, name->nb_neurons * 3); \
} while (0)
INPUT_DENSE(input_dense);
INPUT_GRU(vad_gru);
INPUT_GRU(noise_gru);
INPUT_GRU(denoise_gru);
INPUT_DENSE(denoise_output);
INPUT_DENSE(vad_output);
return ret;
}
void rnnoise_model_free(RNNModel *model)
{
#define FREE_MAYBE(ptr) do { if (ptr) free(ptr); } while (0)
#define FREE_DENSE(name) do { \
if (model->name) { \
free((void *) model->name->input_weights); \
free((void *) model->name->bias); \
free((void *) model->name); \
} \
} while (0)
#define FREE_GRU(name) do { \
if (model->name) { \
free((void *) model->name->input_weights); \
free((void *) model->name->recurrent_weights); \
free((void *) model->name->bias); \
free((void *) model->name); \
} \
} while (0)
if (!model)
return;
FREE_DENSE(input_dense);
FREE_GRU(vad_gru);
FREE_GRU(noise_gru);
FREE_GRU(denoise_gru);
FREE_DENSE(denoise_output);
FREE_DENSE(vad_output);
free(model);
}

@ -0,0 +1,66 @@
#!/usr/bin/python
from __future__ import print_function
from keras.models import Sequential
from keras.models import Model
from keras.layers import Input
from keras.layers import Dense
from keras.layers import LSTM
from keras.layers import GRU
from keras.layers import SimpleRNN
from keras.layers import Dropout
from keras import losses
import h5py
from keras import backend as K
import numpy as np
print('Build model...')
main_input = Input(shape=(None, 22), name='main_input')
#x = Dense(44, activation='relu')(main_input)
#x = GRU(44, dropout=0.0, recurrent_dropout=0.0, activation='tanh', recurrent_activation='sigmoid', return_sequences=True)(x)
x=main_input
x = GRU(128, activation='tanh', recurrent_activation='sigmoid', return_sequences=True)(x)
#x = GRU(128, return_sequences=True)(x)
#x = GRU(22, activation='relu', return_sequences=True)(x)
x = Dense(22, activation='sigmoid')(x)
#x = Dense(22, activation='softplus')(x)
model = Model(inputs=main_input, outputs=x)
batch_size = 32
print('Loading data...')
with h5py.File('denoise_data.h5', 'r') as hf:
all_data = hf['denoise_data'][:]
print('done.')
window_size = 500
nb_sequences = len(all_data)//window_size
print(nb_sequences, ' sequences')
x_train = all_data[:nb_sequences*window_size, :-22]
x_train = np.reshape(x_train, (nb_sequences, window_size, 22))
y_train = np.copy(all_data[:nb_sequences*window_size, -22:])
y_train = np.reshape(y_train, (nb_sequences, window_size, 22))
#y_train = -20*np.log10(np.add(y_train, .03));
all_data = 0;
x_train = x_train.astype('float32')
y_train = y_train.astype('float32')
print(len(x_train), 'train sequences. x shape =', x_train.shape, 'y shape = ', y_train.shape)
# try using different optimizers and different optimizer configs
model.compile(loss='mean_squared_error',
optimizer='adam',
metrics=['binary_accuracy'])
print('Train...')
model.fit(x_train, y_train,
batch_size=batch_size,
epochs=200,
validation_data=(x_train, y_train))
model.save("newweights.hdf5")

@ -0,0 +1,65 @@
/* 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>
#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;
RNNOISE_EXPORT int rnnoise_get_size();
RNNOISE_EXPORT int rnnoise_init(DenoiseState *st, RNNModel *model);
RNNOISE_EXPORT DenoiseState *rnnoise_create(RNNModel *model);
RNNOISE_EXPORT void rnnoise_destroy(DenoiseState *st);
RNNOISE_EXPORT float rnnoise_process_frame(DenoiseState *st, float *out, const float *in);
RNNOISE_EXPORT RNNModel *rnnoise_model_from_file(FILE *f);
RNNOISE_EXPORT void rnnoise_model_free(RNNModel *model);
#endif

@ -0,0 +1,45 @@
/* This file is auto-generated by gen_tables */
static const float tansig_table[201] = {
0.000000f, 0.039979f, 0.079830f, 0.119427f, 0.158649f,
0.197375f, 0.235496f, 0.272905f, 0.309507f, 0.345214f,
0.379949f, 0.413644f, 0.446244f, 0.477700f, 0.507977f,
0.537050f, 0.564900f, 0.591519f, 0.616909f, 0.641077f,
0.664037f, 0.685809f, 0.706419f, 0.725897f, 0.744277f,
0.761594f, 0.777888f, 0.793199f, 0.807569f, 0.821040f,
0.833655f, 0.845456f, 0.856485f, 0.866784f, 0.876393f,
0.885352f, 0.893698f, 0.901468f, 0.908698f, 0.915420f,
0.921669f, 0.927473f, 0.932862f, 0.937863f, 0.942503f,
0.946806f, 0.950795f, 0.954492f, 0.957917f, 0.961090f,
0.964028f, 0.966747f, 0.969265f, 0.971594f, 0.973749f,
0.975743f, 0.977587f, 0.979293f, 0.980869f, 0.982327f,
0.983675f, 0.984921f, 0.986072f, 0.987136f, 0.988119f,
0.989027f, 0.989867f, 0.990642f, 0.991359f, 0.992020f,
0.992631f, 0.993196f, 0.993718f, 0.994199f, 0.994644f,
0.995055f, 0.995434f, 0.995784f, 0.996108f, 0.996407f,
0.996682f, 0.996937f, 0.997172f, 0.997389f, 0.997590f,
0.997775f, 0.997946f, 0.998104f, 0.998249f, 0.998384f,
0.998508f, 0.998623f, 0.998728f, 0.998826f, 0.998916f,
0.999000f, 0.999076f, 0.999147f, 0.999213f, 0.999273f,
0.999329f, 0.999381f, 0.999428f, 0.999472f, 0.999513f,
0.999550f, 0.999585f, 0.999617f, 0.999646f, 0.999673f,
0.999699f, 0.999722f, 0.999743f, 0.999763f, 0.999781f,
0.999798f, 0.999813f, 0.999828f, 0.999841f, 0.999853f,
0.999865f, 0.999875f, 0.999885f, 0.999893f, 0.999902f,
0.999909f, 0.999916f, 0.999923f, 0.999929f, 0.999934f,
0.999939f, 0.999944f, 0.999948f, 0.999952f, 0.999956f,
0.999959f, 0.999962f, 0.999965f, 0.999968f, 0.999970f,
0.999973f, 0.999975f, 0.999977f, 0.999978f, 0.999980f,
0.999982f, 0.999983f, 0.999984f, 0.999986f, 0.999987f,
0.999988f, 0.999989f, 0.999990f, 0.999990f, 0.999991f,
0.999992f, 0.999992f, 0.999993f, 0.999994f, 0.999994f,
0.999994f, 0.999995f, 0.999995f, 0.999996f, 0.999996f,
0.999996f, 0.999997f, 0.999997f, 0.999997f, 0.999997f,
0.999997f, 0.999998f, 0.999998f, 0.999998f, 0.999998f,
0.999998f, 0.999998f, 0.999999f, 0.999999f, 0.999999f,
0.999999f, 0.999999f, 0.999999f, 0.999999f, 0.999999f,
0.999999f, 0.999999f, 0.999999f, 0.999999f, 0.999999f,
1.000000f, 1.000000f, 1.000000f, 1.000000f, 1.000000f,
1.000000f, 1.000000f, 1.000000f, 1.000000f, 1.000000f,
1.000000f,
};

@ -1,7 +1,6 @@
package main
import (
"fmt"
"log"
"os"
"os/exec"
@ -25,7 +24,7 @@ func getCurrentCaps() *capability.Capabilities {
func getSelfFileCaps() *capability.Capabilities {
self, err := os.Executable()
fmt.Printf("Getting caps for: %s\n", self)
log.Printf("Getting caps for: %s\n", self)
if err != nil {
log.Fatalf("Could not get path to own executable: %+v\n", err)
}
@ -58,10 +57,11 @@ func makeBinarySetcapped() error {
return nil
}
func pkexecSetcapSelf() {
func pkexecSetcapSelf() error {
self, err := os.Executable()
if err != nil {
log.Fatalf("Couldn't find path to own binary\n")
return err
}
cmd := exec.Command("pkexec", self, "-setcap")
@ -69,5 +69,8 @@ func pkexecSetcapSelf() {
err = cmd.Run()
if err != nil {
log.Printf("Couldn't setcap self as root: %v\n", err)
return err
}
return nil
}

158
cli.go

@ -0,0 +1,158 @@
package main
import (
"flag"
"fmt"
"os"
"github.com/lawl/pulseaudio"
)
type CLIOpts struct {
doLog bool
setcap bool
sinkName string
unload bool
loadInput bool
loadOutput bool
threshold int
list bool
}
func parseCLIOpts() CLIOpts {
var opt CLIOpts
flag.BoolVar(&opt.doLog, "log", false, "Print debugging output to stdout")
flag.BoolVar(&opt.setcap, "setcap", false, "for internal use only")
flag.StringVar(&opt.sinkName, "s", "", "Use the specified source/sink device ID")
flag.BoolVar(&opt.loadInput, "i", false, "Load supressor for input. If no source device ID is specified the default pulse audio source is used.")
flag.BoolVar(&opt.loadOutput, "o", false, "Load supressor for output. If no source device ID is specified the default pulse audio source is used.")
flag.BoolVar(&opt.unload, "u", false, "Unload supressor")
flag.IntVar(&opt.threshold, "t", -1, "Voice activation threshold")
flag.BoolVar(&opt.list, "l", false, "List available PulseAudio devices")
flag.Parse()
return opt
}
func doCLI(opt CLIOpts, config *config, librnnoise string) {
if opt.setcap {
err := makeBinarySetcapped()
if err != nil {
cleanupExit(librnnoise, 1)
}
cleanupExit(librnnoise, 0)
}
paClient, err := pulseaudio.NewClient()
if err != nil {
fmt.Fprintf(os.Stderr, "Couldn't create pulseaudio client: %v\n", err)
cleanupExit(librnnoise, 1)
}
defer paClient.Close()
ctx := ntcontext{}
info, err := serverInfo(paClient)
if err != nil {
fmt.Fprintf(os.Stderr, "Couldn't fetch audio server info: %s\n", err)
}
ctx.serverInfo = info
ctx.config = config
ctx.librnnoise = librnnoise
ctx.paClient = paClient
if opt.list {
fmt.Println("Sources:")
sources := getSources(paClient)
for i := range sources {
fmt.Printf("\tDevice Name: %s\n\tDevice ID: %s\n\n", sources[i].Name, sources[i].ID)
}
fmt.Println("Sinks:")
sinks := getSinks(paClient)
for i := range sinks {
fmt.Printf("\tDevice Name: %s\n\tDevice ID: %s\n\n", sinks[i].Name, sinks[i].ID)
}
cleanupExit(librnnoise, 0)
}
if opt.threshold > 0 {
if opt.threshold > 95 {
fmt.Fprintf(os.Stderr, "Threshold of '%d' too high, setting to maximum of 95.\n", opt.threshold)
ctx.config.Threshold = 95
} else {
ctx.config.Threshold = opt.threshold
}
}
if opt.unload {
err := unloadSupressor(&ctx)
if err != nil {
fmt.Fprintf(os.Stderr, "Error unloading PulseAudio Module: %+v\n", err)
cleanupExit(librnnoise, 1)
}
cleanupExit(librnnoise, 0)
}
if opt.loadInput {
sources := getSources(paClient)
if opt.sinkName == "" {
defaultSource, err := getDefaultSourceID(paClient)
if err != nil {
fmt.Fprintf(os.Stderr, "No source specified to load and failed to load default source: %+v\n", err)
cleanupExit(librnnoise, 1)
}
opt.sinkName = defaultSource
}
for i := range sources {
if sources[i].ID == opt.sinkName {
sources[i].checked = true
err := loadSupressor(&ctx, &sources[i], &device{})
if err != nil {
fmt.Fprintf(os.Stderr, "Error loading PulseAudio Module: %+v\n", err)
cleanupExit(librnnoise, 1)
}
cleanupExit(librnnoise, 0)
}
}
fmt.Fprintf(os.Stderr, "PulseAudio source not found: %s\n", opt.sinkName)
cleanupExit(librnnoise, 1)
}
if opt.loadOutput {
sinks := getSinks(paClient)
if opt.sinkName == "" {
defaultSink, err := getDefaultSinkID(paClient)
if err != nil {
fmt.Fprintf(os.Stderr, "No sink specified to load and failed to load default sink: %+v\n", err)
cleanupExit(librnnoise, 1)
}
opt.sinkName = defaultSink
}
for i := range sinks {
if sinks[i].ID == opt.sinkName {
sinks[i].checked = true
err := loadSupressor(&ctx, &device{}, &sinks[i])
if err != nil {
fmt.Fprintf(os.Stderr, "Error loading PulseAudio Module: %+v\n", err)
cleanupExit(librnnoise, 1)
}
cleanupExit(librnnoise, 0)
}
}
fmt.Fprintf(os.Stderr, "PulseAudio sink not found: %s\n", opt.sinkName)
cleanupExit(librnnoise, 1)
}
}
func cleanupExit(librnnoise string, exitCode int) {
removeLib(librnnoise)
os.Exit(exitCode)
}

@ -1,13 +1,13 @@
module noisetorch
go 1.14
go 1.16
require (
gioui.org v0.0.0-20200630184602-223f8fd40ae4 // indirect
github.com/BurntSushi/toml v0.3.1
github.com/BurntSushi/xgbutil v0.0.0-20190907113008-ad855c713046
github.com/aarzilli/nucular v0.0.0-20200615134801-81910c722bba
github.com/lawl/pulseaudio v0.0.0-20200802093727-ab0735955fd0
github.com/lawl/pulseaudio v0.0.0-20210604102109-cb2596d6a8ef
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635
golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899
)

@ -21,6 +21,8 @@ github.com/hashicorp/golang-lru v0.0.0-20160813221303-0a025b7e63ad h1:eMxs9EL0Pv
github.com/hashicorp/golang-lru v0.0.0-20160813221303-0a025b7e63ad/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/lawl/pulseaudio v0.0.0-20200802093727-ab0735955fd0 h1:JrvOwrr1teFiqsp0EQxgEPJsm0pet+YLTL+HdYmnMx0=
github.com/lawl/pulseaudio v0.0.0-20200802093727-ab0735955fd0/go.mod h1:9h36x4KH7r2V8DOCKoPMt87IXZ++X90y8D5nnuwq290=
github.com/lawl/pulseaudio v0.0.0-20210604102109-cb2596d6a8ef h1:mpCJg3O6C+B8mh5xoO147NG3Z70GBcFNgqgz2DH/rLQ=
github.com/lawl/pulseaudio v0.0.0-20210604102109-cb2596d6a8ef/go.mod h1:9h36x4KH7r2V8DOCKoPMt87IXZ++X90y8D5nnuwq290=
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 h1:kdXcSzyDtseVEc4yCz2qF8ZrQvIDBJLl4S1c3GCXmoI=
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=

@ -1 +0,0 @@
Subproject commit 453a8af82a31a5361f6a13bf95c97686f0a2acd1

@ -1,15 +1,14 @@
package main
import (
"flag"
"fmt"
"image"
"io/ioutil"
"log"
"os"
"path/filepath"
"regexp"
"strconv"
"strings"
"syscall"
"time"
"github.com/BurntSushi/xgbutil"
@ -19,15 +18,20 @@ import (
"github.com/lawl/pulseaudio"
_ "embed"
"github.com/aarzilli/nucular"
"github.com/aarzilli/nucular/style"
)
//go:generate go run scripts/embedbinary.go librnnoise_ladspa/bin/ladspa/librnnoise_ladspa.so librnnoise.go libRNNoise
//go:generate go run scripts/embedbinary.go assets/patreon.png patreon.go patreonPNG
//go:generate go run scripts/embedversion.go
//go:generate go run scripts/embedlicenses.go
//go:embed c/ladspa/rnnoise_ladspa.so
var libRNNoise []byte
//go:embed assets/patreon.png
var patreonPNG []byte
type device struct {
ID string
Name string
@ -39,59 +43,20 @@ type device struct {
const appName = "NoiseTorch"
func main() {
var pulsepid int
var setcap bool
var sinkName string
var unload bool
var loadInput bool
var loadOutput bool
var threshold int
var list bool
flag.IntVar(&pulsepid, "removerlimit", -1, "for internal use only")
flag.BoolVar(&setcap, "setcap", false, "for internal use only")
flag.StringVar(&sinkName, "s", "", "Use the specified source/sink device ID")
flag.BoolVar(&loadInput, "i", false, "Load supressor for input. If no source device ID is specified the default pulse audio source is used.")
flag.BoolVar(&loadOutput, "o", false, "Load supressor for output. If no source device ID is specified the default pulse audio source is used.")
flag.BoolVar(&unload, "u", false, "Unload supressor")
flag.IntVar(&threshold, "t", -1, "Voice activation threshold")
flag.BoolVar(&list, "l", false, "List available PulseAudio devices")
flag.Parse()
// we also execute this opportunistically on pulsepid since that's also called as root, but need to do so silently, so no os.Exit()'s
if setcap || pulsepid > 0 {
err := makeBinarySetcapped()
if err != nil && !(pulsepid > 0) {
os.Exit(1)
}
var version = "unknown" // will be changed by build
var distribution = "custom" // ditto
var updateURL = "" // ditto
var publicKeyString = "" // ditto
if !(pulsepid > 0) {
os.Exit(0)
}
}
if pulsepid > 0 {
const MaxUint = ^uint64(0)
new := syscall.Rlimit{Cur: MaxUint, Max: MaxUint}
err := setRlimit(pulsepid, &new)
if err != nil {
os.Exit(1)
}
os.Exit(0)
}
func main() {
opt := parseCLIOpts()
date := time.Now().Format("2006_01_02_03_04_05")
tmpdir := os.TempDir()
f, err := os.OpenFile(filepath.Join(tmpdir, fmt.Sprintf("noisetorch-%s.log", date)), os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644)
if err != nil {
log.Fatalf("error opening file: %v\n", err)
if opt.doLog {
log.SetOutput(os.Stdout)
} else {
log.SetOutput(ioutil.Discard)
}
defer f.Close()
log.SetOutput(f)
log.Printf("Application starting. Version: %s\n", version)
log.Printf("Application starting. Version: %s (%s)\n", version, distribution)
log.Printf("CAP_SYS_RESOURCE: %t\n", hasCapSysResource(getCurrentCaps()))
initializeConfigIfNot()
@ -102,109 +67,26 @@ func main() {
ctx.config = readConfig()
ctx.librnnoise = rnnoisefile
paClient, err := pulseaudio.NewClient()
if err != nil {
log.Printf("Couldn't create pulseaudio client: %v\n", err)
os.Exit(1)
}
if list {
fmt.Println("Sources:")
sources := getSources(paClient)
for i := range sources {
fmt.Printf("\tDevice Name: %s\n\tDevice ID: %s\n\n", sources[i].Name, sources[i].ID)
}
fmt.Println("Sinks:")
sinks := getSinks(paClient)
for i := range sinks {
fmt.Printf("\tDevice Name: %s\n\tDevice ID: %s\n\n", sinks[i].Name, sinks[i].ID)
}
os.Exit(0)
}
if threshold > 0 {
if threshold > 95 {
fmt.Fprintf(os.Stderr, "Threshold of '%d' too high, setting to maximum of 95.\n", threshold)
ctx.config.Threshold = 95
} else {
ctx.config.Threshold = threshold
}
}
if unload {
unloadSupressor(&ctx)
os.Exit(0)
}
if loadInput {
ctx.paClient = paClient
sources := getSources(paClient)
if sinkName == "" {
defaultSource, err := getDefaultSourceID(paClient)
if err != nil {
fmt.Fprintf(os.Stderr, "No source specified to load and failed to load default source: %+v\n", err)
os.Exit(1)
}
sinkName = defaultSource
}
for i := range sources {
if sources[i].ID == sinkName {
sources[i].checked = true
err := loadSupressor(&ctx, &sources[i], &device{})
if err != nil {
fmt.Fprintf(os.Stderr, "Error loading PulseAudio Module: %+v\n", err)
os.Exit(1)
}
os.Exit(0)
}
}
fmt.Fprintf(os.Stderr, "PulseAudio source not found: %s\n", sinkName)
os.Exit(1)
}
if loadOutput {
ctx.paClient = paClient
sinks := getSinks(paClient)
if sinkName == "" {
defaultSink, err := getDefaultSinkID(paClient)
if err != nil {
fmt.Fprintf(os.Stderr, "No sink specified to load and failed to load default sink: %+v\n", err)
os.Exit(1)
}
sinkName = defaultSink
}
for i := range sinks {
if sinks[i].ID == sinkName {
sinks[i].checked = true
err := loadSupressor(&ctx, &device{}, &sinks[i])
if err != nil {
fmt.Fprintf(os.Stderr, "Error loading PulseAudio Module: %+v\n", err)
os.Exit(1)
}
os.Exit(0)
}
}
fmt.Fprintf(os.Stderr, "PulseAudio sink not found: %s\n", sinkName)
os.Exit(1)
}
doCLI(opt, ctx.config, ctx.librnnoise)
if ctx.config.EnableUpdates {
go updateCheck(&ctx)
}
go paConnectionWatchdog(&ctx)
ctx.haveCapabilities = hasCapSysResource(getCurrentCaps())
ctx.capsMismatch = hasCapSysResource(getCurrentCaps()) != hasCapSysResource(getSelfFileCaps())
resetUI(&ctx)
wnd := nucular.NewMasterWindowSize(0, appName, image.Point{600, 400}, func(w *nucular.Window) {
updatefn(&ctx, w)
})
ctx.masterWindow = &wnd
(*ctx.masterWindow).Changed()
go paConnectionWatchdog(&ctx)
style := style.FromTheme(style.DarkTheme, 2.0)
style.Font = font.DefaultFont(16, 1)
wnd.SetStyle(style)
@ -243,12 +125,10 @@ func getSources(client *pulseaudio.Client) []device {
outputs := make([]device, 0)
for i := range sources {
if strings.Contains(sources[i].Name, "nui_") {
if strings.Contains(sources[i].Name, "nui_") || strings.Contains(sources[i].Name, "NoiseTorch") {
continue
}
log.Printf("Input %s, %+v\n", sources[i].Name, sources[i])
var inp device
inp.ID = sources[i].Name
@ -273,7 +153,7 @@ func getSinks(client *pulseaudio.Client) []device {
inputs := make([]device, 0)
for i := range sources {
if strings.Contains(sources[i].Name, "nui_") {
if strings.Contains(sources[i].Name, "nui_") || strings.Contains(sources[i].Name, "NoiseTorch") {
continue
}
@ -301,12 +181,23 @@ func paConnectionWatchdog(ctx *ntcontext) {
continue
}
ctx.views.Push(connectView)
(*ctx.masterWindow).Changed()
paClient, err := pulseaudio.NewClient()
if err != nil {
log.Printf("Couldn't create pulseaudio client: %v\n", err)
fmt.Fprintf(os.Stderr, "Couldn't create pulseaudio client: %v\n", err)
}
info, err := serverInfo(paClient)
if err != nil {
log.Printf("Couldn't fetch audio server info: %s\n", err)
}
ctx.serverInfo = info
log.Printf("Connected to audio server. Server name '%s'\n", info.name)
ctx.paClient = paClient
go updateNoiseSupressorLoaded(ctx)
@ -314,11 +205,77 @@ func paConnectionWatchdog(ctx *ntcontext) {
ctx.outputList = preselectDevice(ctx, getSinks(paClient), ctx.config.LastUsedOutput, getDefaultSinkID)
resetUI(ctx)
(*ctx.masterWindow).Changed()
time.Sleep(500 * time.Millisecond)
}
}
func serverInfo(paClient *pulseaudio.Client) (audioserverinfo, error) {
info, err := paClient.ServerInfo()
if err != nil {
log.Printf("Couldn't fetch pulse server info: %v\n", err)
fmt.Fprintf(os.Stderr, "Couldn't fetch pulse server info: %v\n", err)
}
pkgname := info.PackageName
log.Printf("Audioserver package name: %s\n", pkgname)
log.Printf("Audioserver package version: %s\n", info.PackageVersion)
isPipewire := strings.Contains(pkgname, "PipeWire")
var servername string
var servertype uint
var major, minor, patch int
var versionRegex *regexp.Regexp
var versionString string
var outdatedPipeWire bool
if isPipewire {
servername = "PipeWire"
servertype = servertype_pipewire
versionRegex = regexp.MustCompile(`.*?on PipeWire (\d+)\.(\d+)\.(\d+).*?`)
versionString = pkgname
log.Printf("Detected PipeWire\n")
} else {
servername = "PulseAudio"
servertype = servertype_pulse
versionRegex = regexp.MustCompile(`.*?(\d+)\.(\d+)\.(\d+).*?`)
versionString = info.PackageVersion
log.Printf("Detected PulseAudio\n")
}
res := versionRegex.FindStringSubmatch(versionString)
if len(res) != 4 {
log.Printf("couldn't parse server version, regexp didn't match version: %s\n", versionString)
return audioserverinfo{servertype: servertype}, nil
}
major, err = strconv.Atoi(res[1])
if err != nil {
return audioserverinfo{servertype: servertype}, err
}
minor, err = strconv.Atoi(res[2])
if err != nil {
return audioserverinfo{servertype: servertype}, err
}
patch, err = strconv.Atoi(res[3])
if err != nil {
return audioserverinfo{servertype: servertype}, err
}
if isPipewire && major <= 0 && minor <= 3 && patch < 28 {
log.Printf("pipewire version %d.%d.%d too old.\n", major, minor, patch)
outdatedPipeWire = true
}
return audioserverinfo{
servertype: servertype,
name: servername,
major: major,
minor: minor,
patch: patch,
outdatedPipeWire: outdatedPipeWire}, nil
}
func preselectDevice(ctx *ntcontext, devices []device, preselectID string,
fallbackFunc func(client *pulseaudio.Client) (string, error)) []device {

@ -24,8 +24,7 @@ func updateNoiseSupressorLoaded(ctx *ntcontext) {
}
for {
ctx.noiseSupressorState = supressorState(ctx)
ctx.noiseSupressorState, ctx.virtualDeviceInUse = supressorState(ctx)
if !c.Connected() {
break
}
@ -34,189 +33,309 @@ func updateNoiseSupressorLoaded(ctx *ntcontext) {
}
}
func supressorState(ctx *ntcontext) int {
func supressorState(ctx *ntcontext) (int, bool) {
//perform some checks to see if it looks like the noise supressor is loaded
c := ctx.paClient
var inpLoaded, outLoaded, inputInc, outputInc bool
var virtualDeviceInUse bool = false
if ctx.config.FilterInput {
_, nullsink, err := findModule(c, "module-null-sink", "sink_name=nui_mic_denoised_out")
if err != nil {
log.Printf("Couldn't fetch module list to check for module-null-sink: %v\n", err)
}
_, ladspasink, err := findModule(c, "module-ladspa-sink", "sink_name=nui_mic_raw_in sink_master=nui_mic_denoised_out")
if err != nil {
log.Printf("Couldn't fetch module list to check for module-ladspa-sink: %v\n", err)
}
_, loopback, err := findModule(c, "module-loopback", "sink=nui_mic_raw_in")
if err != nil {
log.Printf("Couldn't fetch module list to check for module-loopback: %v\n", err)
}
_, remap, err := findModule(c, "module-remap-source", "master=nui_mic_denoised_out.monitor source_name=nui_mic_remap")
if err != nil {
log.Printf("Couldn't fetch module list to check for module-remap-source: %v\n", err)
}
if nullsink && ladspasink && loopback && remap {
inpLoaded = true
} else if nullsink || ladspasink || loopback || remap {
inputInc = true
if ctx.serverInfo.servertype == servertype_pipewire {
module, ladspasource, err := findModule(c, "module-ladspa-source", "source_name='NoiseTorch Microphone'")
if err != nil {
log.Printf("Couldn't fetch module list to check for module-ladspa-source: %v\n", err)
}
virtualDeviceInUse = virtualDeviceInUse || (module.NUsed != 0)
inpLoaded = ladspasource
inputInc = false
} else {
_, nullsink, err := findModule(c, "module-null-sink", "sink_name=nui_mic_denoised_out")
if err != nil {
log.Printf("Couldn't fetch module list to check for module-null-sink: %v\n", err)
}
_, ladspasink, err := findModule(c, "module-ladspa-sink", "sink_name=nui_mic_raw_in sink_master=nui_mic_denoised_out")
if err != nil {
log.Printf("Couldn't fetch module list to check for module-ladspa-sink: %v\n", err)
}
_, loopback, err := findModule(c, "module-loopback", "sink=nui_mic_raw_in")
if err != nil {
log.Printf("Couldn't fetch module list to check for module-loopback: %v\n", err)
}
module, remap, err := findModule(c, "module-remap-source", "master=nui_mic_denoised_out.monitor source_name=nui_mic_remap")
if err != nil {
log.Printf("Couldn't fetch module list to check for module-remap-source: %v\n", err)
}
virtualDeviceInUse = virtualDeviceInUse || (module.NUsed != 0)
if nullsink && ladspasink && loopback && remap {
inpLoaded = true
} else if nullsink || ladspasink || loopback || remap {
inputInc = true
}
}
} else {
inpLoaded = true
}
if ctx.config.FilterOutput {
_, out, err := findModule(c, "module-null-sink", "sink_name=nui_out_out_sink")
if err != nil {
log.Printf("Couldn't fetch module list to check for output module-ladspa-sink: %v\n", err)
if ctx.serverInfo.servertype == servertype_pipewire {
module, ladspasink, err := findModule(c, "module-ladspa-sink", "sink_name='NoiseTorch Headphones'")
if err != nil {
log.Printf("Couldn't fetch module list to check for module-ladspa-sink: %v\n", err)
}
virtualDeviceInUse = virtualDeviceInUse || (module.NUsed != 0)
outLoaded = ladspasink
outputInc = false
} else {
_, out, err := findModule(c, "module-null-sink", "sink_name=nui_out_out_sink")
if err != nil {
log.Printf("Couldn't fetch module list to check for output module-ladspa-sink: %v\n", err)
}
_, lad, err := findModule(c, "module-ladspa-sink", "sink_name=nui_out_ladspa")
if err != nil {
log.Printf("Couldn't fetch module list to check for output module-ladspa-sink: %v\n", err)
}
_, loop, err := findModule(c, "module-loopback", "source=nui_out_out_sink.monitor")
if err != nil {
log.Printf("Couldn't fetch module list to check for output module-ladspa-sink: %v\n", err)
}
module, outin, err := findModule(c, "module-null-sink", "sink_name=nui_out_in_sink")
if err != nil {
log.Printf("Couldn't fetch module list to check for output module-ladspa-sink: %v\n", err)
}
virtualDeviceInUse = virtualDeviceInUse || (module.NUsed != 0)
_, loop2, err := findModule(c, "module-loopback", "source=nui_out_in_sink.monitor")
if err != nil {
log.Printf("Couldn't fetch module list to check for output module-ladspa-sink: %v\n", err)
}
outLoaded = out && lad && loop && outin && loop2
outputInc = out || lad || loop || outin || loop2
}
_, lad, err := findModule(c, "module-ladspa-sink", "sink_name=nui_out_ladspa")
if err != nil {
log.Printf("Couldn't fetch module list to check for output module-ladspa-sink: %v\n", err)
}
_, loop, err := findModule(c, "module-loopback", "source=nui_out_out_sink.monitor")
if err != nil {
log.Printf("Couldn't fetch module list to check for output module-ladspa-sink: %v\n", err)
}
_, outin, err := findModule(c, "module-null-sink", "sink_name=nui_out_in_sink")
if err != nil {
log.Printf("Couldn't fetch module list to check for output module-ladspa-sink: %v\n", err)
}
_, loop2, err := findModule(c, "module-loopback", "source=nui_out_in_sink.monitor")
if err != nil {
log.Printf("Couldn't fetch module list to check for output module-ladspa-sink: %v\n", err)
}
outLoaded = out && lad && loop && outin && loop2
outputInc = out || lad || loop || outin || loop2
} else {
outLoaded = true
}
if (inpLoaded || !ctx.config.FilterInput) && (outLoaded || !ctx.config.FilterOutput) && !inputInc {
return loaded
return loaded, virtualDeviceInUse
}
if (inpLoaded && ctx.config.FilterInput) || (outLoaded && ctx.config.FilterOutput) || inputInc || outputInc {
return inconsistent
return inconsistent, virtualDeviceInUse
}
return unloaded
return unloaded, virtualDeviceInUse
}
func loadSupressor(ctx *ntcontext, inp *device, out *device) error {
log.Printf("Querying pulse rlimit\n")
c := ctx.paClient
pid, err := getPulsePid()
if err != nil {
return err
}
lim, err := getRlimit(pid)
if err != nil {
return err
}
log.Printf("Rlimit: %+v. Trying to remove.\n", lim)
if hasCapSysResource(getCurrentCaps()) {
log.Printf("Have capabilities\n")
removeRlimit(pid)
} else {
log.Printf("Capabilities missing, removing via pkexec\n")
removeRlimitAsRoot(pid)
}
defer setRlimit(pid, &lim) // lowering RLIMIT doesn't require root
newLim, err := getRlimit(pid)
if err != nil {
return err
}
log.Printf("Rlimit: %+v\n", newLim)
if inp.checked {
log.Printf("Loading supressor\n")
idx, err := c.LoadModule("module-null-sink", "sink_name=nui_mic_denoised_out rate=48000")
if ctx.serverInfo.servertype == servertype_pulse {
log.Printf("Querying pulse rlimit\n")
pid, err := getPulsePid()
if err != nil {
return err
}
log.Printf("Loaded null sink as idx: %d\n", idx)
//time.Sleep(time.Millisecond * 500) // pulseaudio gets SIGKILL'd because of RLIMITS if we send these too fast
idx, err = c.LoadModule("module-ladspa-sink",
fmt.Sprintf("sink_name=nui_mic_raw_in sink_master=nui_mic_denoised_out "+
"label=noise_suppressor_mono plugin=%s control=%d", ctx.librnnoise, ctx.config.Threshold))
lim, err := getRlimit(pid)
if err != nil {
return err
}
log.Printf("Loaded ladspa sink as idx: %d\n", idx)
log.Printf("Rlimit: %+v. Trying to remove.\n", lim)
removeRlimit(pid)
//time.Sleep(time.Millisecond * 1000) // pulseaudio gets SIGKILL'd because of RLIMITS if we send these too fast
defer setRlimit(pid, &lim) // lowering RLIMIT doesn't require root
idx, err = c.LoadModule("module-loopback",
fmt.Sprintf("source=%s sink=nui_mic_raw_in channels=1 latency_msec=1 source_dont_move=true sink_dont_move=true", inp.ID))
newLim, err := getRlimit(pid)
if err != nil {
return err
}
log.Printf("Loaded loopback as idx: %d\n", idx)
//time.Sleep(time.Millisecond * 500) // pulseaudio gets SIGKILL'd because of RLIMITS if we send these too fast
log.Printf("Rlimit: %+v\n", newLim)
}
idx, err = c.LoadModule("module-remap-source", `master=nui_mic_denoised_out.monitor `+
`source_name=nui_mic_remap source_properties="device.description='NoiseTorch Microphone'"`)
if inp.checked {
var err error
if ctx.serverInfo.servertype == servertype_pipewire {
err = loadPipeWireInput(ctx, inp)
} else {
err = loadPulseInput(ctx, inp)
}
if err != nil {
log.Printf("Error loading input: %v\n", err)
return err
}
log.Printf("Loaded remap source as idx: %d\n", idx)
}
if out.checked {
_, err := c.LoadModule("module-null-sink", `sink_name=nui_out_out_sink`)
if err != nil {
return err
var err error
if ctx.serverInfo.servertype == servertype_pipewire {
err = loadPipeWireOutput(ctx, out)
} else {
err = loadPulseOutput(ctx, out)
}
_, err = c.LoadModule("module-null-sink", `sink_name=nui_out_in_sink sink_properties="device.description='NoiseTorch Headphones'"`)
if err != nil {
log.Printf("Error loading output: %v\n", err)
return err
}
}
_, err = c.LoadModule("module-ladspa-sink", fmt.Sprintf(`sink_name=nui_out_ladspa sink_master=nui_out_out_sink `+
`label=noise_suppressor_mono channels=1 plugin=%s control=%d rate=%d`,
/*out.ID,*/ ctx.librnnoise, ctx.config.Threshold /*out.rate*/, 48000))
if err != nil {
return err
}
return nil
}
func loadModule(ctx *ntcontext, module, args string) (uint32, error) {
idx, err := ctx.paClient.LoadModule(module, args)
//14 = module initialisation failed
if paErr, ok := err.(*pulseaudio.Error); ok && paErr.Code == 14 {
resetUI(ctx)
ctx.views.Push(makeErrorView(ctx, fmt.Sprintf("Could not load module '%s'. This is likely a problem with your system or distribution.", module)))
}
return idx, err
}
func loadPipeWireInput(ctx *ntcontext, inp *device) error {
log.Printf("Loading supressor for pipewire\n")
idx, err := loadModule(ctx, "module-ladspa-source",
fmt.Sprintf("source_name='NoiseTorch Microphone' master=%s "+
"rate=48000 channels=1 "+
"label=noisetorch plugin=%s control=%d", inp.ID, ctx.librnnoise, ctx.config.Threshold))
if err != nil {
return err
}
log.Printf("Loaded ladspa source as idx: %d\n", idx)
return nil
}
_, err = c.LoadModule("module-loopback",
fmt.Sprintf("source=nui_out_out_sink.monitor sink=%s channels=2 latency_msec=50 source_dont_move=true sink_dont_move=true", out.ID))
func loadPipeWireOutput(ctx *ntcontext, out *device) error {
log.Printf("Loading supressor for pipewire\n")
idx, err := loadModule(ctx, "module-ladspa-sink",
fmt.Sprintf("sink_name='NoiseTorch Headphones' master=%s "+
"rate=48000 channels=1 "+
"label=noisetorch plugin=%s control=%d", out.ID, ctx.librnnoise, ctx.config.Threshold))
if err != nil {
return err
}
log.Printf("Loaded ladspa source as idx: %d\n", idx)
return nil
}
func loadPulseInput(ctx *ntcontext, inp *device) error {
log.Printf("Loading supressor for pulse\n")
idx, err := loadModule(ctx, "module-null-sink", "sink_name=nui_mic_denoised_out rate=48000")
if err != nil {
return err
}
log.Printf("Loaded null sink as idx: %d\n", idx)
idx, err = loadModule(ctx, "module-ladspa-sink",
fmt.Sprintf("sink_name=nui_mic_raw_in sink_master=nui_mic_denoised_out "+
"label=noisetorch plugin=%s control=%d", ctx.librnnoise, ctx.config.Threshold))
if err != nil {
return err
}
log.Printf("Loaded ladspa sink as idx: %d\n", idx)
if inp.dynamicLatency {
idx, err = loadModule(ctx, "module-loopback",
fmt.Sprintf("source=%s sink=nui_mic_raw_in channels=1 latency_msec=1 source_dont_move=true sink_dont_move=true", inp.ID))
if err != nil {
return err
}
_, err = c.LoadModule("module-loopback",
fmt.Sprintf("source=nui_out_in_sink.monitor sink=nui_out_ladspa channels=1 latency_msec=50 source_dont_move=true sink_dont_move=true"))
log.Printf("Loaded loopback as idx: %d\n", idx)
} else {
idx, err = loadModule(ctx, "module-loopback",
fmt.Sprintf("source=%s sink=nui_mic_raw_in channels=1 latency_msec=50 source_dont_move=true sink_dont_move=true adjust_time=1", inp.ID))
if err != nil {
return err
}
log.Printf("Loaded fixed latency loopback as idx: %d\n", idx)
}
idx, err = loadModule(ctx, "module-remap-source", `master=nui_mic_denoised_out.monitor `+
`source_name=nui_mic_remap source_properties="device.description='NoiseTorch Microphone'"`)
if err != nil {
return err
}
log.Printf("Loaded remap source as idx: %d\n", idx)
return nil
}
func loadPulseOutput(ctx *ntcontext, out *device) error {
_, err := loadModule(ctx, "module-null-sink", `sink_name=nui_out_out_sink`)
if err != nil {
return err
}
_, err = loadModule(ctx, "module-null-sink", `sink_name=nui_out_in_sink sink_properties="device.description='NoiseTorch Headphones'"`)
if err != nil {
return err
}
_, err = loadModule(ctx, "module-ladspa-sink", fmt.Sprintf(`sink_name=nui_out_ladspa sink_master=nui_out_out_sink `+
`label=noisetorch channels=1 plugin=%s control=%d rate=%d`,
ctx.librnnoise, ctx.config.Threshold, 48000))
if err != nil {
return err
}
_, err = loadModule(ctx, "module-loopback",
fmt.Sprintf("source=nui_out_out_sink.monitor sink=%s channels=2 latency_msec=50 source_dont_move=true sink_dont_move=true", out.ID))
if err != nil {
return err
}
_, err = loadModule(ctx, "module-loopback",
fmt.Sprintf("source=nui_out_in_sink.monitor sink=nui_out_ladspa channels=1 latency_msec=50 source_dont_move=true sink_dont_move=true"))
if err != nil {
return err
}
return nil
}
func unloadSupressor(ctx *ntcontext) error {
log.Printf("Unloading pulseaudio modules\n")
if ctx.serverInfo.servertype == servertype_pipewire {
return unloadSupressorPipeWire(ctx)
} else {
return unloadSupressorPulse(ctx)
}
}
func unloadSupressorPipeWire(ctx *ntcontext) error {
log.Printf("Unloading modules for pipewire\n")
log.Printf("Searching for module-ladspa-source\n")
c := ctx.paClient
m, found, err := findModule(c, "module-ladspa-source", "source_name='NoiseTorch Microphone'")
if err != nil {
return err
}
if found {
log.Printf("Found module-ladspa-source at id [%d], sending unload command\n", m.Index)
c.UnloadModule(m.Index)
}
log.Printf("Searching for module-ladspa-sink\n")
m, found, err = findModule(c, "module-ladspa-sink", "sink_name='NoiseTorch Headphones'")
if err != nil {
return err
}
if found {
log.Printf("Found module-ladspa-sink at id [%d], sending unload command\n", m.Index)
c.UnloadModule(m.Index)
}
return nil
}
func unloadSupressorPulse(ctx *ntcontext) error {
log.Printf("Unloading modules for pulseaudio\n")
// we ignore errors here on purpose, since NT didn't use to do this for unloading anyways
// and we don't want to prompt for root again
// so this only suceeds with CAP_SYS_RESOURCE, which we want to make the new default anyways.
if pid, err := getPulsePid(); err == nil {
if lim, err := getRlimit(pid); err == nil {
log.Printf("Trying to remove rlimit. Limit is: %+v\n", lim)
removeRlimit(pid)
newLim, _ := getRlimit(pid)
log.Printf("Rlimit: %+v\n", newLim)
defer setRlimit(pid, &lim)
}
@ -232,7 +351,7 @@ func unloadSupressor(ctx *ntcontext) error {
log.Printf("Found null-sink at id [%d], sending unload command\n", m.Index)
c.UnloadModule(m.Index)
}
//time.Sleep(time.Millisecond * 500) // pulseaudio gets SIGKILL'd because of RLIMITS if we send these too fast
log.Printf("Searching for ladspa-sink\n")
m, found, err = findModule(c, "module-ladspa-sink", "sink_name=nui_mic_raw_in sink_master=nui_mic_denoised_out")
if err != nil {
@ -242,7 +361,7 @@ func unloadSupressor(ctx *ntcontext) error {
log.Printf("Found ladspa-sink at id [%d], sending unload command\n", m.Index)
c.UnloadModule(m.Index)
}
//time.Sleep(time.Millisecond * 500) // pulseaudio gets SIGKILL'd because of RLIMITS if we send these too fast
log.Printf("Searching for loopback\n")
m, found, err = findModule(c, "module-loopback", "sink=nui_mic_raw_in")
if err != nil {
@ -252,7 +371,7 @@ func unloadSupressor(ctx *ntcontext) error {
log.Printf("Found loopback at id [%d], sending unload command\n", m.Index)
c.UnloadModule(m.Index)
}
//time.Sleep(time.Millisecond * 500) // pulseaudio gets SIGKILL'd because of RLIMITS if we send these too fast
log.Printf("Searching for remap-source\n")
m, found, err = findModule(c, "module-remap-source", "master=nui_mic_denoised_out.monitor source_name=nui_mic_remap")
if err != nil {
@ -263,7 +382,6 @@ func unloadSupressor(ctx *ntcontext) error {
c.UnloadModule(m.Index)
}
//time.Sleep(time.Millisecond * 500) // pulseaudio gets SIGKILL'd because of RLIMITS if we send these too fast
log.Printf("Searching for output module-null-sink\n")
m, found, err = findModule(c, "module-null-sink", "sink_name=nui_out_out_sink")
if err != nil {
@ -274,7 +392,6 @@ func unloadSupressor(ctx *ntcontext) error {
c.UnloadModule(m.Index)
}
//time.Sleep(time.Millisecond * 500) // pulseaudio gets SIGKILL'd because of RLIMITS if we send these too fast
log.Printf("Searching for output module-null-sink\n")
m, found, err = findModule(c, "module-null-sink", "sink_name=nui_out_in_sink")
if err != nil {
@ -285,7 +402,6 @@ func unloadSupressor(ctx *ntcontext) error {
c.UnloadModule(m.Index)
}
//time.Sleep(time.Millisecond * 500) // pulseaudio gets SIGKILL'd because of RLIMITS if we send these too fast
log.Printf("Searching for output module-ladspa-sink\n")
m, found, err = findModule(c, "module-ladspa-sink", "sink_name=nui_out_ladspa")
if err != nil {
@ -296,7 +412,6 @@ func unloadSupressor(ctx *ntcontext) error {
c.UnloadModule(m.Index)
}
//time.Sleep(time.Millisecond * 500) // pulseaudio gets SIGKILL'd because of RLIMITS if we send these too fast
log.Printf("Searching for output module-loopback\n")
m, found, err = findModule(c, "module-loopback", "source=nui_out_out_sink.monitor")
if err != nil {
@ -307,7 +422,6 @@ func unloadSupressor(ctx *ntcontext) error {
c.UnloadModule(m.Index)
}
//time.Sleep(time.Millisecond * 500) // pulseaudio gets SIGKILL'd because of RLIMITS if we send these too fast
log.Printf("Searching for output module-loopback\n")
m, found, err = findModule(c, "module-loopback", "source=nui_out_in_sink.monitor")
if err != nil {
@ -324,6 +438,7 @@ func unloadSupressor(ctx *ntcontext) error {
// Finds a module by exactly matching the module name, and checking if the second string is a substring of the argument
func findModule(c *pulseaudio.Client, name string, argMatch string) (module pulseaudio.Module, found bool, err error) {
lst, err := c.ModuleList()
if err != nil {
return pulseaudio.Module{}, false, err
}

@ -3,8 +3,6 @@ package main
import (
"io/ioutil"
"log"
"os"
"os/exec"
"strconv"
"strings"
"syscall"
@ -52,20 +50,6 @@ func removeRlimit(pid int) {
}
}
func removeRlimitAsRoot(pid int) {
self, err := os.Executable()
if err != nil {
log.Fatalf("Couldn't find path to own binary\n")
}
cmd := exec.Command("pkexec", self, "-removerlimit", strconv.Itoa(pid))
log.Printf("Calling: %s\n", cmd.String())
err = cmd.Run()
if err != nil {
log.Printf("Couldn't remove rlimit as root: %v\n", err)
}
}
func pRlimit(pid int, limit uintptr, new *syscall.Rlimit, old *syscall.Rlimit) error {
_, _, errno := syscall.RawSyscall6(syscall.SYS_PRLIMIT64,
uintptr(pid),

@ -1,31 +0,0 @@
package main
import (
"flag"
"fmt"
"io/ioutil"
"os"
"strconv"
)
func main() {
flag.Parse()
args := flag.Args()
b, err := ioutil.ReadFile(args[0])
if err != nil {
fmt.Printf("Couldn't read '%s': %v\n", args[0], err)
os.Exit(1)
}
out, _ := os.Create(args[1])
defer out.Close()
out.Write([]byte("package main \n\n//THIS FILE IS AUTOMATICALLY GENERATED BY `go generate` DO NOT EDIT!\n\nvar " + args[2] + " = []byte{\n"))
for i, c := range b {
out.Write([]byte(strconv.Itoa(int(c))))
out.Write([]byte(","))
if i%32 == 0 && i != 0 {
out.Write([]byte("\n"))
}
}
out.Write([]byte("}\n"))
}

@ -1,24 +0,0 @@
package main
import (
"os"
"os/exec"
"strings"
)
func main() {
cmd := exec.Command("git", "describe", "--tags")
ret, err := cmd.Output()
if err != nil {
panic("Couldn't read git tags to embed version number")
}
version := strings.TrimSpace(string(ret))
out, _ := os.Create("version.go")
defer out.Close()
out.Write([]byte("package main\n\n//THIS FILE IS AUTOMATICALLY GENERATED BY `go generate` DO NOT EDIT!\n\nvar version=\""))
out.Write([]byte(version))
out.Write([]byte("\"\n"))
}

@ -22,8 +22,8 @@ func main() {
var doSign bool
flag.BoolVar(&doSign, "s", false, "Sign the release tar")
var doVerify bool
flag.BoolVar(&doVerify, "v", false, "Verify the signature of the tar")
var publicKeyString string
flag.StringVar(&publicKeyString, "k", "", "Public key to verify against (runs verifier if set)")
flag.Parse()
@ -56,9 +56,8 @@ func main() {
os.Exit(0)
}
if doVerify {
pubStr := "3mL+rBi4yBZ1wGimQ/oSQCjxELzgTh+673H4JdzQBOk="
pub, err := base64.StdEncoding.DecodeString(pubStr)
if publicKeyString != "" {
pub, err := base64.StdEncoding.DecodeString(publicKeyString)
if err != nil {
panic(err)
}

270
ui.go

@ -8,7 +8,9 @@ import (
"image/draw"
"image/png"
"log"
"os"
"os/exec"
"syscall"
"time"
"github.com/aarzilli/nucular"
@ -24,15 +26,32 @@ type ntcontext struct {
librnnoise string
sourceListColdWidthIndex int
config *config
loadingScreen bool
licenseScreen bool
versionScreen bool
licenseTextArea nucular.TextEditor
masterWindow *nucular.MasterWindow
update updateui
reloadRequired bool
haveCapabilities bool
capsMismatch bool
views *ViewStack
serverInfo audioserverinfo
virtualDeviceInUse bool
}
//TODO pull some of these strucs out of UI, they don't belong here
type audioserverinfo struct {
servertype uint
name string
major int
minor int
patch int
outdatedPipeWire bool
}
const (
servertype_pulse = iota
servertype_pipewire
)
var green = color.RGBA{34, 187, 69, 255}
var red = color.RGBA{255, 70, 70, 255}
var orange = color.RGBA{255, 140, 0, 255}
@ -40,26 +59,11 @@ var orange = color.RGBA{255, 140, 0, 255}
var patreonImg *image.RGBA
func updatefn(ctx *ntcontext, w *nucular.Window) {
currView := ctx.views.Peek()
currView(ctx, w)
}
if !ctx.paClient.Connected() {
connectScreen(ctx, w)
return
}
if ctx.loadingScreen {
loadingScreen(ctx, w)
return
}
if ctx.licenseScreen {
licenseScreen(ctx, w)
return
}
if ctx.versionScreen {
versionScreen(ctx, w)
return
}
func mainView(ctx *ntcontext, w *nucular.Window) {
w.MenubarBegin()
@ -67,14 +71,14 @@ func updatefn(ctx *ntcontext, w *nucular.Window) {
if w := w.Menu(label.TA("About", "LC"), 120, nil); w != nil {
w.Row(10).Dynamic(1)
if w.MenuItem(label.T("Licenses")) {
ctx.licenseScreen = true
ctx.views.Push(licenseView)
}
w.Row(10).Dynamic(1)
if w.MenuItem(label.T("Source code")) {
exec.Command("xdg-open", "https://github.com/lawl/NoiseTorch").Run()
}
if w.MenuItem(label.T("Version")) {
ctx.versionScreen = true
ctx.views.Push(versionView)
}
}
@ -97,6 +101,11 @@ func updatefn(ctx *ntcontext, w *nucular.Window) {
w.LabelColored("Inconsistent state, please unload first.", "RC", orange)
}
if ctx.serverInfo.servertype == servertype_pipewire {
w.Row(20).Dynamic(1)
w.Label("Running in PipeWire mode. PipeWire support is currently alpha quality. Please report bugs.", "LC")
}
if ctx.update.available && !ctx.update.triggered {
w.Row(20).Ratio(0.9, 0.1)
w.LabelColored("Update available! Click to install version: "+ctx.update.serverVersion, "LC", green)
@ -141,13 +150,13 @@ func updatefn(ctx *ntcontext, w *nucular.Window) {
if w.CheckboxText("Filter Microphone", &ctx.config.FilterInput) {
ctx.sourceListColdWidthIndex++ //recompute the with because of new elements
go writeConfig(ctx.config)
go (func() { ctx.noiseSupressorState = supressorState(ctx) })()
go (func() { ctx.noiseSupressorState, _ = supressorState(ctx) })()
}
if w.CheckboxText("Filter Headphones", &ctx.config.FilterOutput) {
ctx.sourceListColdWidthIndex++ //recompute the with because of new elements
go writeConfig(ctx.config)
go (func() { ctx.noiseSupressorState = supressorState(ctx) })()
go (func() { ctx.noiseSupressorState, _ = supressorState(ctx) })()
}
w.TreePop()
@ -231,21 +240,19 @@ func updatefn(ctx *ntcontext, w *nucular.Window) {
w.Row(25).Dynamic(2)
if ctx.noiseSupressorState != unloaded {
if w.ButtonText("Unload NoiseTorch") {
ctx.loadingScreen = true
ctx.reloadRequired = false
go func() { // don't block the UI thread, just display a working screen so user can't run multiple loads/unloads
if err := unloadSupressor(ctx); err != nil {
log.Println(err)
}
//wait until PA reports it has actually loaded it, timeout at 10s
for i := 0; i < 20; i++ {
if supressorState(ctx) != unloaded {
time.Sleep(time.Millisecond * 500)
}
}
ctx.loadingScreen = false
(*ctx.masterWindow).Changed()
}()
if ctx.virtualDeviceInUse {
confirm := makeConfirmView(ctx,
"Virtual Device in Use",
"Some applications may behave weirdly when you remove a device they're currently using",
"Unload",
"Go back",
func() { uiUnloadNoisetorch(ctx) },
func() {})
ctx.views.Push(confirm)
} else {
go uiUnloadNoisetorch(ctx)
}
}
} else {
w.Spacing(1)
@ -263,30 +270,20 @@ func updatefn(ctx *ntcontext, w *nucular.Window) {
((ctx.config.FilterOutput && ctx.config.GuiltTripped) || !ctx.config.FilterOutput) &&
ctx.noiseSupressorState != inconsistent {
if w.ButtonText(txt) {
ctx.loadingScreen = true
ctx.reloadRequired = false
go func() { // don't block the UI thread, just display a working screen so user can't run multiple loads/unloads
if ctx.noiseSupressorState == loaded {
if err := unloadSupressor(ctx); err != nil {
log.Println(err)
}
}
if err := loadSupressor(ctx, &inp, &out); err != nil {
log.Println(err)
}
//wait until PA reports it has actually loaded it, timeout at 10s
for i := 0; i < 20; i++ {
if supressorState(ctx) != loaded {
time.Sleep(time.Millisecond * 500)
}
}
ctx.config.LastUsedInput = inp.ID
ctx.config.LastUsedOutput = out.ID
go writeConfig(ctx.config)
ctx.loadingScreen = false
(*ctx.masterWindow).Changed()
}()
if ctx.virtualDeviceInUse {
confirm := makeConfirmView(ctx,
"Virtual Device in Use",
"Some applications may behave weirdly when you reload a device they're currently using",
"Reload",
"Go back",
func() { uiReloadNoisetorch(ctx, inp, out) },
func() {})
ctx.views.Push(confirm)
} else {
go uiReloadNoisetorch(ctx, inp, out)
}
}
} else {
w.Spacing(1)
@ -294,6 +291,45 @@ func updatefn(ctx *ntcontext, w *nucular.Window) {
}
func uiUnloadNoisetorch(ctx *ntcontext) {
ctx.views.Push(loadingView)
if err := unloadSupressor(ctx); err != nil {
log.Println(err)
}
//wait until PA reports it has actually loaded it, timeout at 10s
for i := 0; i < 20; i++ {
if state, _ := supressorState(ctx); state != unloaded {
time.Sleep(time.Millisecond * 500)
}
}
ctx.views.Pop()
(*ctx.masterWindow).Changed()
}
func uiReloadNoisetorch(ctx *ntcontext, inp, out device) {
ctx.views.Push(loadingView)
if ctx.noiseSupressorState == loaded {
if err := unloadSupressor(ctx); err != nil {
log.Println(err)
}
}
if err := loadSupressor(ctx, &inp, &out); err != nil {
log.Println(err)
}
//wait until PA reports it has actually loaded it, timeout at 10s
for i := 0; i < 20; i++ {
if state, _ := supressorState(ctx); state != loaded {
time.Sleep(time.Millisecond * 500)
}
}
ctx.config.LastUsedInput = inp.ID
ctx.config.LastUsedOutput = out.ID
go writeConfig(ctx.config)
ctx.views.Pop()
(*ctx.masterWindow).Changed()
}
func ensureOnlyOneInputSelected(inps *[]device, current *device) {
if current.checked != true {
return
@ -331,14 +367,14 @@ func outputSelection(ctx *ntcontext) (device, bool) {
return device{}, false
}
func loadingScreen(ctx *ntcontext, w *nucular.Window) {
func loadingView(ctx *ntcontext, w *nucular.Window) {
w.Row(50).Dynamic(1)
w.Label("Working...", "CB")
w.Row(50).Dynamic(1)
w.Label("(this may take a few seconds)", "CB")
}
func licenseScreen(ctx *ntcontext, w *nucular.Window) {
func licenseView(ctx *ntcontext, w *nucular.Window) {
w.Row(255).Dynamic(1)
field := &ctx.licenseTextArea
field.Flags |= nucular.EditMultiline
@ -350,34 +386,124 @@ func licenseScreen(ctx *ntcontext, w *nucular.Window) {
w.Row(20).Dynamic(2)
w.Spacing(1)
if w.ButtonText("OK") {
ctx.licenseScreen = false
ctx.views.Pop()
}
}
func versionScreen(ctx *ntcontext, w *nucular.Window) {
func versionView(ctx *ntcontext, w *nucular.Window) {
w.Row(50).Dynamic(1)
w.Label("Version", "CB")
w.Row(50).Dynamic(1)
w.Label(version, "CB")
w.Label(fmt.Sprintf("%s (%s)", version, distribution), "CB")
w.Row(50).Dynamic(1)
w.Spacing(1)
w.Row(20).Dynamic(2)
w.Spacing(1)
if w.ButtonText("OK") {
ctx.versionScreen = false
ctx.views.Pop()
}
}
func connectScreen(ctx *ntcontext, w *nucular.Window) {
func connectView(ctx *ntcontext, w *nucular.Window) {
w.Row(50).Dynamic(1)
w.Label("Connecting to pulseaudio...", "CB")
}
func capabilitiesView(ctx *ntcontext, w *nucular.Window) {
w.Row(15).Dynamic(1)
w.Label("NoiseTorch currently does not have the capabilities to function properly.", "CB")
w.Row(15).Dynamic(1)
w.Label("We require CAP_SYS_RESOURCE. If that doesn't mean anything to you, don't worry. I'll fix it for you.", "CB")
if ctx.capsMismatch {
w.Row(15).Dynamic(1)
w.LabelColored("Warning: File has CAP_SYS_RESOURCE but our process doesn't.", "CB", orange)
w.Row(15).Dynamic(1)
w.LabelColored("Check if your filesystem has nosuid set or check the troubleshooting page.", "CB", orange)
}
w.Row(40).Dynamic(1)
w.Row(25).Dynamic(1)
if w.ButtonText("Grant capability (requires root)") {
err := pkexecSetcapSelf()
if err != nil {
ctx.views.Push(makeErrorView(ctx, err.Error()))
return
}
self, err := os.Executable()
if err != nil {
ctx.views.Push(makeErrorView(ctx, err.Error()))
return
}
err = syscall.Exec(self, []string{""}, os.Environ())
if err != nil {
ctx.views.Push(makeErrorView(ctx, err.Error()))
return
}
}
}
func makeErrorView(ctx *ntcontext, errorMsg string) ViewFunc {
return func(ctx *ntcontext, w *nucular.Window) {
w.Row(15).Dynamic(1)
w.Label("Error", "CB")
w.Row(15).Dynamic(1)
w.Label(errorMsg, "CB")
w.Row(40).Dynamic(1)
w.Row(25).Dynamic(1)
if w.ButtonText("OK") {
ctx.views.Pop()
return
}
}
}
func makeFatalErrorView(ctx *ntcontext, errorMsg string) ViewFunc {
return func(ctx *ntcontext, w *nucular.Window) {
w.Row(15).Dynamic(1)
w.Label("Fatal Error", "CB")
w.Row(15).Dynamic(1)
w.Label(errorMsg, "CB")
w.Row(40).Dynamic(1)
w.Row(25).Dynamic(1)
if w.ButtonText("Quit") {
os.Exit(1)
return
}
}
}
func makeConfirmView(ctx *ntcontext, title, text, confirmText, denyText string, confirmfunc, denyfunc func()) ViewFunc {
return func(ctx *ntcontext, w *nucular.Window) {
w.Row(15).Dynamic(1)
w.Label(title, "CB")
w.Row(15).Dynamic(1)
w.Label(text, "CB")
w.Row(40).Dynamic(1)
w.Row(25).Dynamic(2)
if w.ButtonText(denyText) {
ctx.views.Pop()
go denyfunc()
return
}
if w.ButtonText(confirmText) {
ctx.views.Pop()
go confirmfunc()
return
}
}
}
func resetUI(ctx *ntcontext) {
ctx.loadingScreen = false
ctx.views = NewViewStack()
ctx.views.Push(mainView)
if !ctx.haveCapabilities {
ctx.views.Push(capabilitiesView)
}
if ctx.masterWindow != nil {
(*ctx.masterWindow).Changed()
if ctx.serverInfo.outdatedPipeWire {
ctx.views.Push(makeFatalErrorView(ctx,
fmt.Sprintf("Your PipeWire version is too old. Detected %d.%d.%d. Require at least 0.3.28.",
ctx.serverInfo.major, ctx.serverInfo.minor, ctx.serverInfo.patch)))
}
}

@ -1,5 +1,3 @@
// +build release
package main
import (
@ -14,9 +12,6 @@ import (
"strings"
)
var updateURL = "https://noisetorch.epicgamer.org"
var publicKeyString = "3mL+rBi4yBZ1wGimQ/oSQCjxELzgTh+673H4JdzQBOk="
type updateui struct {
serverVersion string
available bool
@ -24,7 +19,14 @@ type updateui struct {
updatingText string
}
func updateable() bool {
return updateURL != "" && publicKeyString != ""
}
func updateCheck(ctx *ntcontext) {
if !updateable() {
return
}
log.Println("Checking for updates")
bodybuf, err := fetchFile("version.txt")
if err != nil {
@ -41,6 +43,9 @@ func updateCheck(ctx *ntcontext) {
}
func update(ctx *ntcontext) {
if !updateable() {
return
}
sig, err := fetchFile("NoiseTorch_x64.tgz.sig")
if err != nil {
log.Println("Couldn't fetch signature", err)
@ -94,8 +99,9 @@ func fetchFile(file string) ([]byte, error) {
func publickey() []byte {
pub, err := base64.StdEncoding.DecodeString(publicKeyString)
if err != nil {
panic(err) // it's hardcoded, we should never hit this, panic if we do
if err != nil { // Should only happen when distributor ships an invalid public key
log.Fatalf("Error while reading public key: %s\nContact the distribution '%s' about this error.\n", err, distribution)
os.Exit(1)
}
return pub
}

@ -1,18 +0,0 @@
// +build !release
package main
type updateui struct {
serverVersion string
available bool
triggered bool
updatingText string
}
func updateCheck(ctx *ntcontext) {
// noop for non-release versions
}
func update(ctx *ntcontext) {
// noop for non-release versions
}

@ -383,7 +383,7 @@ func cookiePath() (string, error) {
return p, nil
}
p = filepath.Join(os.Getenv("HOME"), "/.pulse_cookie")
p = filepath.Join(os.Getenv("HOME"), "/.pulse-cookie")
if exists(p) {
return p, nil
}

@ -71,7 +71,7 @@ github.com/golang/freetype/truetype
# github.com/hashicorp/golang-lru v0.0.0-20160813221303-0a025b7e63ad
github.com/hashicorp/golang-lru
github.com/hashicorp/golang-lru/simplelru
# github.com/lawl/pulseaudio v0.0.0-20200802093727-ab0735955fd0
# github.com/lawl/pulseaudio v0.0.0-20210604102109-cb2596d6a8ef
## explicit
github.com/lawl/pulseaudio
# github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635

@ -0,0 +1,50 @@
package main
import (
"fmt"
"sync"
"github.com/aarzilli/nucular"
)
type ViewFunc func(ctx *ntcontext, w *nucular.Window)
type ViewStack struct {
stack [100]ViewFunc
sp int8
mu sync.Mutex
}
func NewViewStack() *ViewStack {
return &ViewStack{sp: -1}
}
func (v *ViewStack) Push(f ViewFunc) {
v.mu.Lock()
defer v.mu.Unlock()
v.stack[v.sp+1] = f
v.sp++
}
func (v *ViewStack) Pop() (ViewFunc, error) {
v.mu.Lock()
if v.sp <= 0 {
return nil, fmt.Errorf("Cannot pop root element from ViewStack")
}
defer (func() {
v.stack[v.sp] = nil
v.sp--
v.mu.Unlock()
})()
return v.stack[v.sp], nil
}
func (v *ViewStack) Peek() ViewFunc {
v.mu.Lock()
defer v.mu.Unlock()
return v.stack[v.sp]
}
Loading…
Cancel
Save