Compare commits
1 Commits
Author | SHA1 | Date |
---|---|---|
lawl | 981278e747 | 4 years ago |
@ -1,2 +1,4 @@
|
||||
bin/
|
||||
licenses.go
|
||||
librnnoise.go
|
||||
version.go
|
||||
licenses.go
|
@ -0,0 +1,4 @@
|
||||
[submodule "librnnoise_ladspa"]
|
||||
path = librnnoise_ladspa
|
||||
url = https://github.com/werman/noise-suppression-for-voice
|
||||
ignore = dirty
|
Binary file not shown.
Before Width: | Height: | Size: 3.3 KiB |
@ -1,2 +0,0 @@
|
||||
*.o
|
||||
rnnoise_ladspa.so
|
@ -1,3 +0,0 @@
|
||||
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
|
@ -1,4 +0,0 @@
|
||||
{
|
||||
global: *ladspa*;
|
||||
local: *;
|
||||
};
|
@ -1,603 +0,0 @@
|
||||
/* 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 */
|
@ -1,233 +0,0 @@
|
||||
/*
|
||||
(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;
|
||||
}
|
||||
}
|
@ -1,105 +0,0 @@
|
||||
/* 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 */
|
@ -1,347 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
@ -1,243 +0,0 @@
|
||||
#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 */
|
@ -1,31 +0,0 @@
|
||||
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.
|
@ -1,182 +0,0 @@
|
||||
/*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 */
|
@ -1,261 +0,0 @@
|
||||
/* 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 */
|
@ -1,279 +0,0 @@
|
||||
/* 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;
|
||||
}
|
@ -1,59 +0,0 @@
|
||||
/* 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 */
|
@ -1,48 +0,0 @@
|
||||
|
||||
|
||||
#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
|
@ -1,3 +0,0 @@
|
||||
#!/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
|
@ -1,642 +0,0 @@
|
||||
/* 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
|
@ -1,601 +0,0 @@
|
||||
/*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;
|
||||
}
|
@ -1,203 +0,0 @@
|
||||
/*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
|
@ -1,159 +0,0 @@
|
||||
/* (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 */
|
@ -1,526 +0,0 @@
|
||||
/* 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;
|
||||
}
|
@ -1,149 +0,0 @@
|
||||
/* 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
|
@ -1,178 +0,0 @@
|
||||
/* 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);
|
||||
}
|
@ -1,69 +0,0 @@
|
||||
/* 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
@ -1,34 +0,0 @@
|
||||
#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
|
@ -1,168 +0,0 @@
|
||||
/* 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);
|
||||
}
|
@ -1,66 +0,0 @@
|
||||
#!/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")
|
@ -1,65 +0,0 @@
|
||||
/* 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
|
@ -1,45 +0,0 @@
|
||||
/* 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,158 +0,0 @@
|
||||
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,12 @@
|
||||
module noisetorch
|
||||
|
||||
go 1.16
|
||||
go 1.14
|
||||
|
||||
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-20210604102109-cb2596d6a8ef
|
||||
github.com/lawl/pulseaudio v0.0.0-20200802093727-ab0735955fd0
|
||||
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635
|
||||
golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899
|
||||
)
|
||||
|
@ -0,0 +1 @@
|
||||
Subproject commit 453a8af82a31a5361f6a13bf95c97686f0a2acd1
|
@ -0,0 +1,28 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func main() {
|
||||
b, err := ioutil.ReadFile("librnnoise_ladspa/bin/ladspa/librnnoise_ladspa.so")
|
||||
if err != nil {
|
||||
fmt.Printf("Couldn't read librnnoise_ladspa.so: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
out, _ := os.Create("librnnoise.go")
|
||||
defer out.Close()
|
||||
|
||||
out.Write([]byte("package main \n\n//THIS FILE IS AUTOMATICALLY GENERATED BY `go generate` DO NOT EDIT!\n\nvar libRNNoise = []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"))
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
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"))
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
// +build !release
|
||||
|
||||
package main
|
||||
|
||||
import "errors"
|
||||
|
||||
type updateui struct {
|
||||
serverVersion string
|
||||
available bool
|
||||
triggered bool
|
||||
updatingText string
|
||||
}
|
||||
|
||||
func updateCheck(ctx *ntcontext) {
|
||||
|
||||
}
|
||||
|
||||
func update(ctx *ntcontext) {
|
||||
|
||||
}
|
||||
|
||||
func fetchFile(file string) ([]byte, error) {
|
||||
return make([]byte, 0), errors.New("Disabled by build flags")
|
||||
}
|
||||
|
||||
func publickey() []byte {
|
||||
return make([]byte, 0)
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,718 +0,0 @@
|
||||
// Package xinerama is the X client API for the XINERAMA extension.
|
||||
package xinerama
|
||||
|
||||
// This file is automatically generated from xinerama.xml. Edit at your peril!
|
||||
|
||||
import (
|
||||
"github.com/BurntSushi/xgb"
|
||||
|
||||
"github.com/BurntSushi/xgb/xproto"
|
||||
)
|
||||
|
||||
// Init must be called before using the XINERAMA extension.
|
||||
func Init(c *xgb.Conn) error {
|
||||
reply, err := xproto.QueryExtension(c, 8, "XINERAMA").Reply()
|
||||
switch {
|
||||
case err != nil:
|
||||
return err
|
||||
case !reply.Present:
|
||||
return xgb.Errorf("No extension named XINERAMA could be found on on the server.")
|
||||
}
|
||||
|
||||
c.ExtLock.Lock()
|
||||
c.Extensions["XINERAMA"] = reply.MajorOpcode
|
||||
c.ExtLock.Unlock()
|
||||
for evNum, fun := range xgb.NewExtEventFuncs["XINERAMA"] {
|
||||
xgb.NewEventFuncs[int(reply.FirstEvent)+evNum] = fun
|
||||
}
|
||||
for errNum, fun := range xgb.NewExtErrorFuncs["XINERAMA"] {
|
||||
xgb.NewErrorFuncs[int(reply.FirstError)+errNum] = fun
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
xgb.NewExtEventFuncs["XINERAMA"] = make(map[int]xgb.NewEventFun)
|
||||
xgb.NewExtErrorFuncs["XINERAMA"] = make(map[int]xgb.NewErrorFun)
|
||||
}
|
||||
|
||||
type ScreenInfo struct {
|
||||
XOrg int16
|
||||
YOrg int16
|
||||
Width uint16
|
||||
Height uint16
|
||||
}
|
||||
|
||||
// ScreenInfoRead reads a byte slice into a ScreenInfo value.
|
||||
func ScreenInfoRead(buf []byte, v *ScreenInfo) int {
|
||||
b := 0
|
||||
|
||||
v.XOrg = int16(xgb.Get16(buf[b:]))
|
||||
b += 2
|
||||
|
||||
v.YOrg = int16(xgb.Get16(buf[b:]))
|
||||
b += 2
|
||||
|
||||
v.Width = xgb.Get16(buf[b:])
|
||||
b += 2
|
||||
|
||||
v.Height = xgb.Get16(buf[b:])
|
||||
b += 2
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
// ScreenInfoReadList reads a byte slice into a list of ScreenInfo values.
|
||||
func ScreenInfoReadList(buf []byte, dest []ScreenInfo) int {
|
||||
b := 0
|
||||
for i := 0; i < len(dest); i++ {
|
||||
dest[i] = ScreenInfo{}
|
||||
b += ScreenInfoRead(buf[b:], &dest[i])
|
||||
}
|
||||
return xgb.Pad(b)
|
||||
}
|
||||
|
||||
// Bytes writes a ScreenInfo value to a byte slice.
|
||||
func (v ScreenInfo) Bytes() []byte {
|
||||
buf := make([]byte, 8)
|
||||
b := 0
|
||||
|
||||
xgb.Put16(buf[b:], uint16(v.XOrg))
|
||||
b += 2
|
||||
|
||||
xgb.Put16(buf[b:], uint16(v.YOrg))
|
||||
b += 2
|
||||
|
||||
xgb.Put16(buf[b:], v.Width)
|
||||
b += 2
|
||||
|
||||
xgb.Put16(buf[b:], v.Height)
|
||||
b += 2
|
||||
|
||||
return buf[:b]
|
||||
}
|
||||
|
||||
// ScreenInfoListBytes writes a list of ScreenInfo values to a byte slice.
|
||||
func ScreenInfoListBytes(buf []byte, list []ScreenInfo) int {
|
||||
b := 0
|
||||
var structBytes []byte
|
||||
for _, item := range list {
|
||||
structBytes = item.Bytes()
|
||||
copy(buf[b:], structBytes)
|
||||
b += len(structBytes)
|
||||
}
|
||||
return xgb.Pad(b)
|
||||
}
|
||||
|
||||
// Skipping definition for base type 'Bool'
|
||||
|
||||
// Skipping definition for base type 'Byte'
|
||||
|
||||
// Skipping definition for base type 'Card8'
|
||||
|
||||
// Skipping definition for base type 'Char'
|
||||
|
||||
// Skipping definition for base type 'Void'
|
||||
|
||||
// Skipping definition for base type 'Double'
|
||||
|
||||
// Skipping definition for base type 'Float'
|
||||
|
||||
// Skipping definition for base type 'Int16'
|
||||
|
||||
// Skipping definition for base type 'Int32'
|
||||
|
||||
// Skipping definition for base type 'Int8'
|
||||
|
||||
// Skipping definition for base type 'Card16'
|
||||
|
||||
// Skipping definition for base type 'Card32'
|
||||
|
||||
// GetScreenCountCookie is a cookie used only for GetScreenCount requests.
|
||||
type GetScreenCountCookie struct {
|
||||
*xgb.Cookie
|
||||
}
|
||||
|
||||
// GetScreenCount sends a checked request.
|
||||
// If an error occurs, it will be returned with the reply by calling GetScreenCountCookie.Reply()
|
||||
func GetScreenCount(c *xgb.Conn, Window xproto.Window) GetScreenCountCookie {
|
||||
c.ExtLock.RLock()
|
||||
defer c.ExtLock.RUnlock()
|
||||
if _, ok := c.Extensions["XINERAMA"]; !ok {
|
||||
panic("Cannot issue request 'GetScreenCount' using the uninitialized extension 'XINERAMA'. xinerama.Init(connObj) must be called first.")
|
||||
}
|
||||
cookie := c.NewCookie(true, true)
|
||||
c.NewRequest(getScreenCountRequest(c, Window), cookie)
|
||||
return GetScreenCountCookie{cookie}
|
||||
}
|
||||
|
||||
// GetScreenCountUnchecked sends an unchecked request.
|
||||
// If an error occurs, it can only be retrieved using xgb.WaitForEvent or xgb.PollForEvent.
|
||||
func GetScreenCountUnchecked(c *xgb.Conn, Window xproto.Window) GetScreenCountCookie {
|
||||
c.ExtLock.RLock()
|
||||
defer c.ExtLock.RUnlock()
|
||||
if _, ok := c.Extensions["XINERAMA"]; !ok {
|
||||
panic("Cannot issue request 'GetScreenCount' using the uninitialized extension 'XINERAMA'. xinerama.Init(connObj) must be called first.")
|
||||
}
|
||||
cookie := c.NewCookie(false, true)
|
||||
c.NewRequest(getScreenCountRequest(c, Window), cookie)
|
||||
return GetScreenCountCookie{cookie}
|
||||
}
|
||||
|
||||
// GetScreenCountReply represents the data returned from a GetScreenCount request.
|
||||
type GetScreenCountReply struct {
|
||||
Sequence uint16 // sequence number of the request for this reply
|
||||
Length uint32 // number of bytes in this reply
|
||||
ScreenCount byte
|
||||
Window xproto.Window
|
||||
}
|
||||
|
||||
// Reply blocks and returns the reply data for a GetScreenCount request.
|
||||
func (cook GetScreenCountCookie) Reply() (*GetScreenCountReply, error) {
|
||||
buf, err := cook.Cookie.Reply()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if buf == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return getScreenCountReply(buf), nil
|
||||
}
|
||||
|
||||
// getScreenCountReply reads a byte slice into a GetScreenCountReply value.
|
||||
func getScreenCountReply(buf []byte) *GetScreenCountReply {
|
||||
v := new(GetScreenCountReply)
|
||||
b := 1 // skip reply determinant
|
||||
|
||||
v.ScreenCount = buf[b]
|
||||
b += 1
|
||||
|
||||
v.Sequence = xgb.Get16(buf[b:])
|
||||
b += 2
|
||||
|
||||
v.Length = xgb.Get32(buf[b:]) // 4-byte units
|
||||
b += 4
|
||||
|
||||
v.Window = xproto.Window(xgb.Get32(buf[b:]))
|
||||
b += 4
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
// Write request to wire for GetScreenCount
|
||||
// getScreenCountRequest writes a GetScreenCount request to a byte slice.
|
||||
func getScreenCountRequest(c *xgb.Conn, Window xproto.Window) []byte {
|
||||
size := 8
|
||||
b := 0
|
||||
buf := make([]byte, size)
|
||||
|
||||
c.ExtLock.RLock()
|
||||
buf[b] = c.Extensions["XINERAMA"]
|
||||
c.ExtLock.RUnlock()
|
||||
b += 1
|
||||
|
||||
buf[b] = 2 // request opcode
|
||||
b += 1
|
||||
|
||||
xgb.Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
|
||||
b += 2
|
||||
|
||||
xgb.Put32(buf[b:], uint32(Window))
|
||||
b += 4
|
||||
|
||||
return buf
|
||||
}
|
||||
|
||||
// GetScreenSizeCookie is a cookie used only for GetScreenSize requests.
|
||||
type GetScreenSizeCookie struct {
|
||||
*xgb.Cookie
|
||||
}
|
||||
|
||||
// GetScreenSize sends a checked request.
|
||||
// If an error occurs, it will be returned with the reply by calling GetScreenSizeCookie.Reply()
|
||||
func GetScreenSize(c *xgb.Conn, Window xproto.Window, Screen uint32) GetScreenSizeCookie {
|
||||
c.ExtLock.RLock()
|
||||
defer c.ExtLock.RUnlock()
|
||||
if _, ok := c.Extensions["XINERAMA"]; !ok {
|
||||
panic("Cannot issue request 'GetScreenSize' using the uninitialized extension 'XINERAMA'. xinerama.Init(connObj) must be called first.")
|
||||
}
|
||||
cookie := c.NewCookie(true, true)
|
||||
c.NewRequest(getScreenSizeRequest(c, Window, Screen), cookie)
|
||||
return GetScreenSizeCookie{cookie}
|
||||
}
|
||||
|
||||
// GetScreenSizeUnchecked sends an unchecked request.
|
||||
// If an error occurs, it can only be retrieved using xgb.WaitForEvent or xgb.PollForEvent.
|
||||
func GetScreenSizeUnchecked(c *xgb.Conn, Window xproto.Window, Screen uint32) GetScreenSizeCookie {
|
||||
c.ExtLock.RLock()
|
||||
defer c.ExtLock.RUnlock()
|
||||
if _, ok := c.Extensions["XINERAMA"]; !ok {
|
||||
panic("Cannot issue request 'GetScreenSize' using the uninitialized extension 'XINERAMA'. xinerama.Init(connObj) must be called first.")
|
||||
}
|
||||
cookie := c.NewCookie(false, true)
|
||||
c.NewRequest(getScreenSizeRequest(c, Window, Screen), cookie)
|
||||
return GetScreenSizeCookie{cookie}
|
||||
}
|
||||
|
||||
// GetScreenSizeReply represents the data returned from a GetScreenSize request.
|
||||
type GetScreenSizeReply struct {
|
||||
Sequence uint16 // sequence number of the request for this reply
|
||||
Length uint32 // number of bytes in this reply
|
||||
// padding: 1 bytes
|
||||
Width uint32
|
||||
Height uint32
|
||||
Window xproto.Window
|
||||
Screen uint32
|
||||
}
|
||||
|
||||
// Reply blocks and returns the reply data for a GetScreenSize request.
|
||||
func (cook GetScreenSizeCookie) Reply() (*GetScreenSizeReply, error) {
|
||||
buf, err := cook.Cookie.Reply()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if buf == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return getScreenSizeReply(buf), nil
|
||||
}
|
||||
|
||||
// getScreenSizeReply reads a byte slice into a GetScreenSizeReply value.
|
||||
func getScreenSizeReply(buf []byte) *GetScreenSizeReply {
|
||||
v := new(GetScreenSizeReply)
|
||||
b := 1 // skip reply determinant
|
||||
|
||||
b += 1 // padding
|
||||
|
||||
v.Sequence = xgb.Get16(buf[b:])
|
||||
b += 2
|
||||
|
||||
v.Length = xgb.Get32(buf[b:]) // 4-byte units
|
||||
b += 4
|
||||
|
||||
v.Width = xgb.Get32(buf[b:])
|
||||
b += 4
|
||||
|
||||
v.Height = xgb.Get32(buf[b:])
|
||||
b += 4
|
||||
|
||||
v.Window = xproto.Window(xgb.Get32(buf[b:]))
|
||||
b += 4
|
||||
|
||||
v.Screen = xgb.Get32(buf[b:])
|
||||
b += 4
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
// Write request to wire for GetScreenSize
|
||||
// getScreenSizeRequest writes a GetScreenSize request to a byte slice.
|
||||
func getScreenSizeRequest(c *xgb.Conn, Window xproto.Window, Screen uint32) []byte {
|
||||
size := 12
|
||||
b := 0
|
||||
buf := make([]byte, size)
|
||||
|
||||
c.ExtLock.RLock()
|
||||
buf[b] = c.Extensions["XINERAMA"]
|
||||
c.ExtLock.RUnlock()
|
||||
b += 1
|
||||
|
||||
buf[b] = 3 // request opcode
|
||||
b += 1
|
||||
|
||||
xgb.Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
|
||||
b += 2
|
||||
|
||||
xgb.Put32(buf[b:], uint32(Window))
|
||||
b += 4
|
||||
|
||||
xgb.Put32(buf[b:], Screen)
|
||||
b += 4
|
||||
|
||||
return buf
|
||||
}
|
||||
|
||||
// GetStateCookie is a cookie used only for GetState requests.
|
||||
type GetStateCookie struct {
|
||||
*xgb.Cookie
|
||||
}
|
||||
|
||||
// GetState sends a checked request.
|
||||
// If an error occurs, it will be returned with the reply by calling GetStateCookie.Reply()
|
||||
func GetState(c *xgb.Conn, Window xproto.Window) GetStateCookie {
|
||||
c.ExtLock.RLock()
|
||||
defer c.ExtLock.RUnlock()
|
||||
if _, ok := c.Extensions["XINERAMA"]; !ok {
|
||||
panic("Cannot issue request 'GetState' using the uninitialized extension 'XINERAMA'. xinerama.Init(connObj) must be called first.")
|
||||
}
|
||||
cookie := c.NewCookie(true, true)
|
||||
c.NewRequest(getStateRequest(c, Window), cookie)
|
||||
return GetStateCookie{cookie}
|
||||
}
|
||||
|
||||
// GetStateUnchecked sends an unchecked request.
|
||||
// If an error occurs, it can only be retrieved using xgb.WaitForEvent or xgb.PollForEvent.
|
||||
func GetStateUnchecked(c *xgb.Conn, Window xproto.Window) GetStateCookie {
|
||||
c.ExtLock.RLock()
|
||||
defer c.ExtLock.RUnlock()
|
||||
if _, ok := c.Extensions["XINERAMA"]; !ok {
|
||||
panic("Cannot issue request 'GetState' using the uninitialized extension 'XINERAMA'. xinerama.Init(connObj) must be called first.")
|
||||
}
|
||||
cookie := c.NewCookie(false, true)
|
||||
c.NewRequest(getStateRequest(c, Window), cookie)
|
||||
return GetStateCookie{cookie}
|
||||
}
|
||||
|
||||
// GetStateReply represents the data returned from a GetState request.
|
||||
type GetStateReply struct {
|
||||
Sequence uint16 // sequence number of the request for this reply
|
||||
Length uint32 // number of bytes in this reply
|
||||
State byte
|
||||
Window xproto.Window
|
||||
}
|
||||
|
||||
// Reply blocks and returns the reply data for a GetState request.
|
||||
func (cook GetStateCookie) Reply() (*GetStateReply, error) {
|
||||
buf, err := cook.Cookie.Reply()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if buf == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return getStateReply(buf), nil
|
||||
}
|
||||
|
||||
// getStateReply reads a byte slice into a GetStateReply value.
|
||||
func getStateReply(buf []byte) *GetStateReply {
|
||||
v := new(GetStateReply)
|
||||
b := 1 // skip reply determinant
|
||||
|
||||
v.State = buf[b]
|
||||
b += 1
|
||||
|
||||
v.Sequence = xgb.Get16(buf[b:])
|
||||
b += 2
|
||||
|
||||
v.Length = xgb.Get32(buf[b:]) // 4-byte units
|
||||
b += 4
|
||||
|
||||
v.Window = xproto.Window(xgb.Get32(buf[b:]))
|
||||
b += 4
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
// Write request to wire for GetState
|
||||
// getStateRequest writes a GetState request to a byte slice.
|
||||
func getStateRequest(c *xgb.Conn, Window xproto.Window) []byte {
|
||||
size := 8
|
||||
b := 0
|
||||
buf := make([]byte, size)
|
||||
|
||||
c.ExtLock.RLock()
|
||||
buf[b] = c.Extensions["XINERAMA"]
|
||||
c.ExtLock.RUnlock()
|
||||
b += 1
|
||||
|
||||
buf[b] = 1 // request opcode
|
||||
b += 1
|
||||
|
||||
xgb.Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
|
||||
b += 2
|
||||
|
||||
xgb.Put32(buf[b:], uint32(Window))
|
||||
b += 4
|
||||
|
||||
return buf
|
||||
}
|
||||
|
||||
// IsActiveCookie is a cookie used only for IsActive requests.
|
||||
type IsActiveCookie struct {
|
||||
*xgb.Cookie
|
||||
}
|
||||
|
||||
// IsActive sends a checked request.
|
||||
// If an error occurs, it will be returned with the reply by calling IsActiveCookie.Reply()
|
||||
func IsActive(c *xgb.Conn) IsActiveCookie {
|
||||
c.ExtLock.RLock()
|
||||
defer c.ExtLock.RUnlock()
|
||||
if _, ok := c.Extensions["XINERAMA"]; !ok {
|
||||
panic("Cannot issue request 'IsActive' using the uninitialized extension 'XINERAMA'. xinerama.Init(connObj) must be called first.")
|
||||
}
|
||||
cookie := c.NewCookie(true, true)
|
||||
c.NewRequest(isActiveRequest(c), cookie)
|
||||
return IsActiveCookie{cookie}
|
||||
}
|
||||
|
||||
// IsActiveUnchecked sends an unchecked request.
|
||||
// If an error occurs, it can only be retrieved using xgb.WaitForEvent or xgb.PollForEvent.
|
||||
func IsActiveUnchecked(c *xgb.Conn) IsActiveCookie {
|
||||
c.ExtLock.RLock()
|
||||
defer c.ExtLock.RUnlock()
|
||||
if _, ok := c.Extensions["XINERAMA"]; !ok {
|
||||
panic("Cannot issue request 'IsActive' using the uninitialized extension 'XINERAMA'. xinerama.Init(connObj) must be called first.")
|
||||
}
|
||||
cookie := c.NewCookie(false, true)
|
||||
c.NewRequest(isActiveRequest(c), cookie)
|
||||
return IsActiveCookie{cookie}
|
||||
}
|
||||
|
||||
// IsActiveReply represents the data returned from a IsActive request.
|
||||
type IsActiveReply struct {
|
||||
Sequence uint16 // sequence number of the request for this reply
|
||||
Length uint32 // number of bytes in this reply
|
||||
// padding: 1 bytes
|
||||
State uint32
|
||||
}
|
||||
|
||||
// Reply blocks and returns the reply data for a IsActive request.
|
||||
func (cook IsActiveCookie) Reply() (*IsActiveReply, error) {
|
||||
buf, err := cook.Cookie.Reply()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if buf == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return isActiveReply(buf), nil
|
||||
}
|
||||
|
||||
// isActiveReply reads a byte slice into a IsActiveReply value.
|
||||
func isActiveReply(buf []byte) *IsActiveReply {
|
||||
v := new(IsActiveReply)
|
||||
b := 1 // skip reply determinant
|
||||
|
||||
b += 1 // padding
|
||||
|
||||
v.Sequence = xgb.Get16(buf[b:])
|
||||
b += 2
|
||||
|
||||
v.Length = xgb.Get32(buf[b:]) // 4-byte units
|
||||
b += 4
|
||||
|
||||
v.State = xgb.Get32(buf[b:])
|
||||
b += 4
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
// Write request to wire for IsActive
|
||||
// isActiveRequest writes a IsActive request to a byte slice.
|
||||
func isActiveRequest(c *xgb.Conn) []byte {
|
||||
size := 4
|
||||
b := 0
|
||||
buf := make([]byte, size)
|
||||
|
||||
c.ExtLock.RLock()
|
||||
buf[b] = c.Extensions["XINERAMA"]
|
||||
c.ExtLock.RUnlock()
|
||||
b += 1
|
||||
|
||||
buf[b] = 4 // request opcode
|
||||
b += 1
|
||||
|
||||
xgb.Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
|
||||
b += 2
|
||||
|
||||
return buf
|
||||
}
|
||||
|
||||
// QueryScreensCookie is a cookie used only for QueryScreens requests.
|
||||
type QueryScreensCookie struct {
|
||||
*xgb.Cookie
|
||||
}
|
||||
|
||||
// QueryScreens sends a checked request.
|
||||
// If an error occurs, it will be returned with the reply by calling QueryScreensCookie.Reply()
|
||||
func QueryScreens(c *xgb.Conn) QueryScreensCookie {
|
||||
c.ExtLock.RLock()
|
||||
defer c.ExtLock.RUnlock()
|
||||
if _, ok := c.Extensions["XINERAMA"]; !ok {
|
||||
panic("Cannot issue request 'QueryScreens' using the uninitialized extension 'XINERAMA'. xinerama.Init(connObj) must be called first.")
|
||||
}
|
||||
cookie := c.NewCookie(true, true)
|
||||
c.NewRequest(queryScreensRequest(c), cookie)
|
||||
return QueryScreensCookie{cookie}
|
||||
}
|
||||
|
||||
// QueryScreensUnchecked sends an unchecked request.
|
||||
// If an error occurs, it can only be retrieved using xgb.WaitForEvent or xgb.PollForEvent.
|
||||
func QueryScreensUnchecked(c *xgb.Conn) QueryScreensCookie {
|
||||
c.ExtLock.RLock()
|
||||
defer c.ExtLock.RUnlock()
|
||||
if _, ok := c.Extensions["XINERAMA"]; !ok {
|
||||
panic("Cannot issue request 'QueryScreens' using the uninitialized extension 'XINERAMA'. xinerama.Init(connObj) must be called first.")
|
||||
}
|
||||
cookie := c.NewCookie(false, true)
|
||||
c.NewRequest(queryScreensRequest(c), cookie)
|
||||
return QueryScreensCookie{cookie}
|
||||
}
|
||||
|
||||
// QueryScreensReply represents the data returned from a QueryScreens request.
|
||||
type QueryScreensReply struct {
|
||||
Sequence uint16 // sequence number of the request for this reply
|
||||
Length uint32 // number of bytes in this reply
|
||||
// padding: 1 bytes
|
||||
Number uint32
|
||||
// padding: 20 bytes
|
||||
ScreenInfo []ScreenInfo // size: xgb.Pad((int(Number) * 8))
|
||||
}
|
||||
|
||||
// Reply blocks and returns the reply data for a QueryScreens request.
|
||||
func (cook QueryScreensCookie) Reply() (*QueryScreensReply, error) {
|
||||
buf, err := cook.Cookie.Reply()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if buf == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return queryScreensReply(buf), nil
|
||||
}
|
||||
|
||||
// queryScreensReply reads a byte slice into a QueryScreensReply value.
|
||||
func queryScreensReply(buf []byte) *QueryScreensReply {
|
||||
v := new(QueryScreensReply)
|
||||
b := 1 // skip reply determinant
|
||||
|
||||
b += 1 // padding
|
||||
|
||||
v.Sequence = xgb.Get16(buf[b:])
|
||||
b += 2
|
||||
|
||||
v.Length = xgb.Get32(buf[b:]) // 4-byte units
|
||||
b += 4
|
||||
|
||||
v.Number = xgb.Get32(buf[b:])
|
||||
b += 4
|
||||
|
||||
b += 20 // padding
|
||||
|
||||
v.ScreenInfo = make([]ScreenInfo, v.Number)
|
||||
b += ScreenInfoReadList(buf[b:], v.ScreenInfo)
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
// Write request to wire for QueryScreens
|
||||
// queryScreensRequest writes a QueryScreens request to a byte slice.
|
||||
func queryScreensRequest(c *xgb.Conn) []byte {
|
||||
size := 4
|
||||
b := 0
|
||||
buf := make([]byte, size)
|
||||
|
||||
c.ExtLock.RLock()
|
||||
buf[b] = c.Extensions["XINERAMA"]
|
||||
c.ExtLock.RUnlock()
|
||||
b += 1
|
||||
|
||||
buf[b] = 5 // request opcode
|
||||
b += 1
|
||||
|
||||
xgb.Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
|
||||
b += 2
|
||||
|
||||
return buf
|
||||
}
|
||||
|
||||
// QueryVersionCookie is a cookie used only for QueryVersion requests.
|
||||
type QueryVersionCookie struct {
|
||||
*xgb.Cookie
|
||||
}
|
||||
|
||||
// QueryVersion sends a checked request.
|
||||
// If an error occurs, it will be returned with the reply by calling QueryVersionCookie.Reply()
|
||||
func QueryVersion(c *xgb.Conn, Major byte, Minor byte) QueryVersionCookie {
|
||||
c.ExtLock.RLock()
|
||||
defer c.ExtLock.RUnlock()
|
||||
if _, ok := c.Extensions["XINERAMA"]; !ok {
|
||||
panic("Cannot issue request 'QueryVersion' using the uninitialized extension 'XINERAMA'. xinerama.Init(connObj) must be called first.")
|
||||
}
|
||||
cookie := c.NewCookie(true, true)
|
||||
c.NewRequest(queryVersionRequest(c, Major, Minor), cookie)
|
||||
return QueryVersionCookie{cookie}
|
||||
}
|
||||
|
||||
// QueryVersionUnchecked sends an unchecked request.
|
||||
// If an error occurs, it can only be retrieved using xgb.WaitForEvent or xgb.PollForEvent.
|
||||
func QueryVersionUnchecked(c *xgb.Conn, Major byte, Minor byte) QueryVersionCookie {
|
||||
c.ExtLock.RLock()
|
||||
defer c.ExtLock.RUnlock()
|
||||
if _, ok := c.Extensions["XINERAMA"]; !ok {
|
||||
panic("Cannot issue request 'QueryVersion' using the uninitialized extension 'XINERAMA'. xinerama.Init(connObj) must be called first.")
|
||||
}
|
||||
cookie := c.NewCookie(false, true)
|
||||
c.NewRequest(queryVersionRequest(c, Major, Minor), cookie)
|
||||
return QueryVersionCookie{cookie}
|
||||
}
|
||||
|
||||
// QueryVersionReply represents the data returned from a QueryVersion request.
|
||||
type QueryVersionReply struct {
|
||||
Sequence uint16 // sequence number of the request for this reply
|
||||
Length uint32 // number of bytes in this reply
|
||||
// padding: 1 bytes
|
||||
Major uint16
|
||||
Minor uint16
|
||||
}
|
||||
|
||||
// Reply blocks and returns the reply data for a QueryVersion request.
|
||||
func (cook QueryVersionCookie) Reply() (*QueryVersionReply, error) {
|
||||
buf, err := cook.Cookie.Reply()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if buf == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return queryVersionReply(buf), nil
|
||||
}
|
||||
|
||||
// queryVersionReply reads a byte slice into a QueryVersionReply value.
|
||||
func queryVersionReply(buf []byte) *QueryVersionReply {
|
||||
v := new(QueryVersionReply)
|
||||
b := 1 // skip reply determinant
|
||||
|
||||
b += 1 // padding
|
||||
|
||||
v.Sequence = xgb.Get16(buf[b:])
|
||||
b += 2
|
||||
|
||||
v.Length = xgb.Get32(buf[b:]) // 4-byte units
|
||||
b += 4
|
||||
|
||||
v.Major = xgb.Get16(buf[b:])
|
||||
b += 2
|
||||
|
||||
v.Minor = xgb.Get16(buf[b:])
|
||||
b += 2
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
// Write request to wire for QueryVersion
|
||||
// queryVersionRequest writes a QueryVersion request to a byte slice.
|
||||
func queryVersionRequest(c *xgb.Conn, Major byte, Minor byte) []byte {
|
||||
size := 8
|
||||
b := 0
|
||||
buf := make([]byte, size)
|
||||
|
||||
c.ExtLock.RLock()
|
||||
buf[b] = c.Extensions["XINERAMA"]
|
||||
c.ExtLock.RUnlock()
|
||||
b += 1
|
||||
|
||||
buf[b] = 0 // request opcode
|
||||
b += 1
|
||||
|
||||
xgb.Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
|
||||
b += 2
|
||||
|
||||
buf[b] = Major
|
||||
b += 1
|
||||
|
||||
buf[b] = Minor
|
||||
b += 1
|
||||
|
||||
return buf
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
*.swp
|
||||
*.png
|
||||
tst_first
|
||||
tst_graphics
|
||||
TAGS
|
||||
|
@ -1,13 +0,0 @@
|
||||
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
Version 2, December 2004
|
||||
|
||||
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
|
||||
|
||||
Everyone is permitted to copy and distribute verbatim or modified
|
||||
copies of this license document, and changing it is allowed as long
|
||||
as the name is changed.
|
||||
|
||||
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. You just DO WHAT THE FUCK YOU WANT TO.
|
@ -1,36 +0,0 @@
|
||||
all: callback.go types_auto.go gofmt
|
||||
|
||||
install:
|
||||
go install -p 6 . ./ewmh ./gopher ./icccm ./keybind ./motif ./mousebind \
|
||||
./xcursor ./xevent ./xgraphics ./xinerama ./xprop ./xrect ./xwindow
|
||||
|
||||
push:
|
||||
git push origin master
|
||||
git push github master
|
||||
|
||||
build-ex:
|
||||
find ./_examples/ -type d -wholename './_examples/[a-z]*' -print0 \
|
||||
| xargs -0 go build -p 6
|
||||
|
||||
gofmt:
|
||||
gofmt -w *.go */*.go _examples/*/*.go
|
||||
colcheck *.go */*.go _examples/*/*.go
|
||||
|
||||
callback.go:
|
||||
scripts/write-events callbacks > xevent/callback.go
|
||||
|
||||
types_auto.go:
|
||||
scripts/write-events evtypes > xevent/types_auto.go
|
||||
|
||||
tags:
|
||||
find ./ \( -name '*.go' -and -not -wholename './tests/*' -and -not -wholename './_examples/*' \) -print0 | xargs -0 gotags > TAGS
|
||||
|
||||
loc:
|
||||
find ./ -name '*.go' -and -not -wholename './tests*' -and -not -name '*keysymdef.go' -and -not -name '*gopher.go' -print | sort | xargs wc -l
|
||||
|
||||
ex-%:
|
||||
go run _examples/$*/main.go
|
||||
|
||||
gopherimg:
|
||||
go-bindata -f GopherPng -p gopher -i gopher/gophercolor-small.png -o gopher/gopher.go
|
||||
|
@ -1,55 +0,0 @@
|
||||
xgbutil is a utility library designed to work with the X Go Binding. This
|
||||
project's main goal is to make various X related tasks easier. For example,
|
||||
binding keys, using the EWMH or ICCCM specs with the window manager,
|
||||
moving/resizing windows, assigning function callbacks to particular events,
|
||||
drawing images to a window, etc.
|
||||
|
||||
xgbutil attempts to be thread safe, but it has not been completely tested in
|
||||
this regard. In general, the X event loop implemented in the xevent package is
|
||||
sequential. The idea is to be sequential by default, and let the user spawn
|
||||
concurrent code at their discretion. (i.e., the complexity of making the main
|
||||
event loop generally concurrent is vast.)
|
||||
|
||||
You may sleep safely at night by assuming that XGB is thread safe, though.
|
||||
|
||||
To start using xgbutil, you should have at least a passing familiarity with X.
|
||||
Your first stop should be the examples directory.
|
||||
|
||||
Installation
|
||||
============
|
||||
go get github.com/BurntSushi/xgbutil
|
||||
|
||||
Dependencies
|
||||
============
|
||||
XGB is the main dependency. Use of the xgraphics packages requires graphics-go
|
||||
and freetype-go.
|
||||
|
||||
XGB project URL: https://github.com/BurntSushi/xgb
|
||||
|
||||
Quick Example
|
||||
=============
|
||||
go get github.com/BurntSushi/xgbutil/_examples/window-name-sizes
|
||||
"$GOPATH"/bin/window-name-sizes
|
||||
|
||||
The output will be a list of names of all top-level windows and their geometry
|
||||
including window manager decorations. (Assuming your window manager supports
|
||||
some basic EWMH properties.)
|
||||
|
||||
Documentation
|
||||
=============
|
||||
https://godoc.org/github.com/BurntSushi/xgbutil
|
||||
|
||||
Examples
|
||||
========
|
||||
There are several examples in the examples directory covering common use cases.
|
||||
They are heavily documented and should run out of the box.
|
||||
|
||||
Python
|
||||
======
|
||||
An older project of mine, xpybutil, served as inspiration for xgbutil. If you
|
||||
want to use Python, xpybutil should help quite a bit. Please note though, that
|
||||
at this point, xgbutil provides a lot more functionality and is much better
|
||||
documented.
|
||||
|
||||
xpybutil project URL: https://github.com/BurntSushi/xpybutil
|
||||
|
@ -1,29 +0,0 @@
|
||||
I like to keep all my code to 80 columns or less. I have plenty of screen real
|
||||
estate, but enjoy 80 columns so that I can have multiple code windows open side
|
||||
to side and not be plagued by the ugly auto-wrapping of a text editor.
|
||||
|
||||
If you don't oblige me, I will fix any patch you submit to abide 80 columns.
|
||||
|
||||
Note that this style restriction does not preclude gofmt, but introduces a few
|
||||
peculiarities. The first is that gofmt will occasionally add spacing (typically
|
||||
to comments) that ends up going over 80 columns. Either shorten the comment or
|
||||
put it on its own line.
|
||||
|
||||
The second and more common hiccup is when a function definition extends beyond
|
||||
80 columns. If one adds line breaks to keep it below 80 columns, gofmt will
|
||||
indent all subsequent lines in a function definition to the same indentation
|
||||
level of the function body. This results in a less-than-ideal separation
|
||||
between function definition and function body. To remedy this, simply add a
|
||||
line break like so:
|
||||
|
||||
func RestackWindowExtra(xu *xgbutil.XUtil, win xproto.Window, stackMode int,
|
||||
sibling xproto.Window, source int) error {
|
||||
|
||||
return ClientEvent(xu, win, "_NET_RESTACK_WINDOW", source, int(sibling),
|
||||
stackMode)
|
||||
}
|
||||
|
||||
Something similar should also be applied to long 'if' or 'for' conditionals,
|
||||
although it would probably be preferrable to break up the conditional to
|
||||
smaller chunks with a few helper variables.
|
||||
|
@ -1,67 +0,0 @@
|
||||
/*
|
||||
Package xgbutil is a utility library designed to make common tasks with the X
|
||||
server easier. The central design choice that has driven development is to hide
|
||||
the complexity of X wherever possible but expose it when necessary.
|
||||
|
||||
For example, the xevent package provides an implementation of an X event loop
|
||||
that acts as a dispatcher to event handlers set up with the xevent, keybind and
|
||||
mousebind packages. At the same time, the event queue is exposed and can be
|
||||
modified using xevent.Peek and xevent.DequeueAt.
|
||||
|
||||
Sub-packages
|
||||
|
||||
The xgbutil package is considerably small, and only contains some type
|
||||
definitions and the initial setup for an X connection. Much of the
|
||||
functionality of xgbutil comes from its sub-packages. Each sub-package is
|
||||
appropriately documented.
|
||||
|
||||
Installation
|
||||
|
||||
xgbutil is go-gettable:
|
||||
|
||||
go get github.com/BurntSushi/xgbutil
|
||||
|
||||
Dependencies
|
||||
|
||||
XGB is the main dependency, and is required for all packages inside xgbutil.
|
||||
|
||||
graphics-go and freetype-go are also required if using the xgraphics package.
|
||||
|
||||
Quick Example
|
||||
|
||||
A quick example to demonstrate that xgbutil is working correctly:
|
||||
|
||||
go get github.com/BurntSushi/xgbutil/examples/window-name-sizes
|
||||
GO/PATH/bin/window-name-sizes
|
||||
|
||||
The output will be a list of names of all top-level windows and their geometry
|
||||
including window manager decorations. (Assuming your window manager supports
|
||||
some basic EWMH properties.)
|
||||
|
||||
Examples
|
||||
|
||||
The examples directory contains a sizable number of examples demonstrating
|
||||
common tasks with X. They are intended to demonstrate a single thing each,
|
||||
although a few that require setup are necessarily long. Each example is
|
||||
heavily documented.
|
||||
|
||||
The examples directory should be your first stop when learning how to use
|
||||
xgbutil.
|
||||
|
||||
xgbutil is also used heavily throughout my window manager, Wingo. It may be
|
||||
useful reference material.
|
||||
|
||||
Wingo project page: https://github.com/BurntSushi/wingo
|
||||
|
||||
Thread Safety
|
||||
|
||||
While I am fairly confident that XGB is thread safe, I am only somewhat
|
||||
confident that xgbutil is thread safe. It simply has not been tested enough for
|
||||
my confidence to be higher.
|
||||
|
||||
Note that the xevent package's X event loop is not concurrent. Namely,
|
||||
designing a generally concurrent X event loop is extremely complex. Instead,
|
||||
the onus is on you, the user, to design concurrent callback functions if
|
||||
concurrency is desired.
|
||||
*/
|
||||
package xgbutil
|
@ -1,56 +0,0 @@
|
||||
/*
|
||||
Package ewmh provides a comprehensive API to get and set properties specified
|
||||
by the EWMH spec, as well as perform actions specified by the EWMH spec.
|
||||
|
||||
Since there are so many functions and they adhere to an existing spec,
|
||||
this package file does not contain much documentation. Indeed, each
|
||||
method has only a single comment associated with it: the EWMH property name.
|
||||
|
||||
The idea is to
|
||||
provide a consistent interface to use all facilities described in the EWMH
|
||||
spec: http://standards.freedesktop.org/wm-spec/wm-spec-latest.html.
|
||||
|
||||
Naming scheme
|
||||
|
||||
Using "_NET_ACTIVE_WINDOW" as an example,
|
||||
functions "ActiveWindowGet" and "ActiveWindowSet" get and set the
|
||||
property, respectively. Both of these functions exist for most EWMH
|
||||
properties. Additionally, some EWMH properties support sending a client
|
||||
message event to request the window manager to perform some action. In the
|
||||
case of "_NET_ACTIVE_WINDOW", this request is used to set the active
|
||||
window.
|
||||
|
||||
These sorts of functions end in "Req". So for "_NET_ACTIVE_WINDOW",
|
||||
the method name is "ActiveWindowReq". Moreover, most requests include
|
||||
various parameters that don't need to be changed often (like the source
|
||||
indication). Thus, by default, functions ending in "Req" force these to
|
||||
sensible defaults. If you need access to all of the parameters, use the
|
||||
corresponding "ReqExtra" method. So for "_NET_ACTIVE_WINDOW", that would
|
||||
be "ActiveWindowReqExtra". (If no "ReqExtra" method exists, then the
|
||||
"Req" method covers all available parameters.)
|
||||
|
||||
This naming scheme has one exception: if a property's only use is through
|
||||
sending an event (like "_NET_CLOSE_WINDOW"), then the name will be
|
||||
"CloseWindow" for the short-hand version and "CloseWindowExtra"
|
||||
for access to all of the parameters. (Since there is no "_NET_CLOSE_WINDOW"
|
||||
property, there is no need for "CloseWindowGet" and "CloseWindowSet"
|
||||
functions.)
|
||||
|
||||
For properties that store more than just a simple integer, name or list
|
||||
of integers, structs have been created and exposed to organize the
|
||||
information returned in a sensible manner. For example, the
|
||||
"_NET_DESKTOP_GEOMETRY" property would typically return a slice of integers
|
||||
of length 2, where the first integer is the width and the second is the
|
||||
height. Xgbutil will wrap this in a struct with the obvious members. These
|
||||
structs are documented.
|
||||
|
||||
Finally, functions ending in "*Set" are typically only used when setting
|
||||
properties on clients *you've* created or when the window manager sets
|
||||
properties. Thus, it's unlikely that you should use them unless you're
|
||||
creating a top-level client or building a window manager.
|
||||
|
||||
Functions ending in "Get" or "Req[Extra]" are commonly used.
|
||||
|
||||
N.B. Not all properties have "*Req" functions.
|
||||
*/
|
||||
package ewmh
|
File diff suppressed because it is too large
Load Diff
@ -1,31 +0,0 @@
|
||||
package ewmh
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/BurntSushi/xgbutil"
|
||||
)
|
||||
|
||||
// GetEwmhWM uses the EWMH spec to find if a conforming window manager
|
||||
// is currently running or not. If it is, then its name will be returned.
|
||||
// Otherwise, an error will be returned explaining why one couldn't be found.
|
||||
func GetEwmhWM(xu *xgbutil.XUtil) (string, error) {
|
||||
childCheck, err := SupportingWmCheckGet(xu, xu.RootWin())
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("GetEwmhWM: Failed because: %s", err)
|
||||
}
|
||||
|
||||
childCheck2, err := SupportingWmCheckGet(xu, childCheck)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("GetEwmhWM: Failed because: %s", err)
|
||||
}
|
||||
|
||||
if childCheck != childCheck2 {
|
||||
return "", fmt.Errorf(
|
||||
"GetEwmhWM: _NET_SUPPORTING_WM_CHECK value on the root window "+
|
||||
"(%x) does not match _NET_SUPPORTING_WM_CHECK value "+
|
||||
"on the child window (%x).", childCheck, childCheck2)
|
||||
}
|
||||
|
||||
return WmNameGet(xu, childCheck)
|
||||
}
|
@ -1,61 +0,0 @@
|
||||
/*
|
||||
Package icccm provides an API for a portion of the ICCCM, namely, getters
|
||||
and setters for many of the properties specified in the ICCCM. There is also a
|
||||
smattering of support for other protocols specified by ICCCM. For example, to
|
||||
satisfy the WM_DELETE_WINDOW protocol, package icccm provides 'IsDeleteProtocol'
|
||||
which returns whether a ClientMessage event satisfies the WM_DELETE_WINDOW
|
||||
protocol.
|
||||
|
||||
If a property has values that aren't simple strings or integers, struct types
|
||||
are provided to organize the data. In particular, WM_NORMAL_HINTS and WM_HINTS.
|
||||
|
||||
Also note that properties like WM_NORMAL_HINTS and WM_HINTS contain a 'Flags'
|
||||
field (a bit mask) that specifies which values are "active". This is of
|
||||
importance when setting and reading WM_NORMAL_HINTS and WM_HINTS; one must make
|
||||
sure the appropriate bit is set in Flags.
|
||||
|
||||
For example, you might want to check if a window has specified a resize
|
||||
increment in the WM_NORMAL_HINTS property. The values in the corresponding
|
||||
NormalHints struct are WidthInc and HeightInc. So to check if such values exist
|
||||
*and* should be used:
|
||||
|
||||
normalHints, err := icccm.WmNormalHintsGet(XUtilValue, window-id)
|
||||
if err != nil {
|
||||
// handle error
|
||||
}
|
||||
if normalHints.Flags&icccm.SizeHintPResizeInc > 0 {
|
||||
// Use normalHints.WidthInc and normalHints.HeightInc
|
||||
}
|
||||
|
||||
When you should use icccm
|
||||
|
||||
Although the ICCCM is extremely old, a lot of it is still used. In fact, the
|
||||
EWMH spec itself specifically states that the ICCCM should still be used unless
|
||||
otherwise noted by the EWMH. For example, WM_HINTS and WM_NORMAL_HINTS are
|
||||
still used, but _NET_WM_NAME replaces WM_NAME.
|
||||
|
||||
With that said, many applications (like xterm or LibreOffice) have not been
|
||||
updated to be fully EWMH compliant. Therefore, code that finds a window's name
|
||||
often looks like this:
|
||||
|
||||
winName, err := ewmh.WmNameGet(XUtilValue, window-id)
|
||||
if err != nil || winName == "" {
|
||||
winName, err = icccm.WmNameGet(XUtilValue, window-id)
|
||||
if err != nill || winName == "" {
|
||||
winName = "N/A"
|
||||
}
|
||||
}
|
||||
|
||||
Something similar can be said for the _NET_WM_ICON and the IconPixmap field
|
||||
in WM_HINTS.
|
||||
|
||||
Naming scheme
|
||||
|
||||
The naming scheme is precisely the same as the one found in the ewmh package.
|
||||
The documentation for the ewmh package describes the naming scheme in more
|
||||
detail. The only difference (currently) is that the icccm package only contains
|
||||
functions ending in "Get" and "Set". It is planned to add "Req" functions. (An
|
||||
example of a Req function would be to send a ClientMessage implementing the
|
||||
WM_DELETE_WINDOW protocol to a client window.)
|
||||
*/
|
||||
package icccm
|
@ -1,358 +0,0 @@
|
||||
package icccm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/BurntSushi/xgb/xproto"
|
||||
|
||||
"github.com/BurntSushi/xgbutil"
|
||||
"github.com/BurntSushi/xgbutil/xprop"
|
||||
)
|
||||
|
||||
const (
|
||||
HintInput = (1 << iota)
|
||||
HintState
|
||||
HintIconPixmap
|
||||
HintIconWindow
|
||||
HintIconPosition
|
||||
HintIconMask
|
||||
HintWindowGroup
|
||||
HintMessage
|
||||
HintUrgency
|
||||
)
|
||||
|
||||
const (
|
||||
SizeHintUSPosition = (1 << iota)
|
||||
SizeHintUSSize
|
||||
SizeHintPPosition
|
||||
SizeHintPSize
|
||||
SizeHintPMinSize
|
||||
SizeHintPMaxSize
|
||||
SizeHintPResizeInc
|
||||
SizeHintPAspect
|
||||
SizeHintPBaseSize
|
||||
SizeHintPWinGravity
|
||||
)
|
||||
|
||||
const (
|
||||
StateWithdrawn = iota
|
||||
StateNormal
|
||||
StateZoomed
|
||||
StateIconic
|
||||
StateInactive
|
||||
)
|
||||
|
||||
// WM_NAME get
|
||||
func WmNameGet(xu *xgbutil.XUtil, win xproto.Window) (string, error) {
|
||||
return xprop.PropValStr(xprop.GetProperty(xu, win, "WM_NAME"))
|
||||
}
|
||||
|
||||
// WM_NAME set
|
||||
func WmNameSet(xu *xgbutil.XUtil, win xproto.Window, name string) error {
|
||||
return xprop.ChangeProp(xu, win, 8, "WM_NAME", "STRING", ([]byte)(name))
|
||||
}
|
||||
|
||||
// WM_ICON_NAME get
|
||||
func WmIconNameGet(xu *xgbutil.XUtil, win xproto.Window) (string, error) {
|
||||
return xprop.PropValStr(xprop.GetProperty(xu, win, "WM_ICON_NAME"))
|
||||
}
|
||||
|
||||
// WM_ICON_NAME set
|
||||
func WmIconNameSet(xu *xgbutil.XUtil, win xproto.Window, name string) error {
|
||||
return xprop.ChangeProp(xu, win, 8, "WM_ICON_NAME", "STRING",
|
||||
([]byte)(name))
|
||||
}
|
||||
|
||||
// NormalHints is a struct that organizes the information related to the
|
||||
// WM_NORMAL_HINTS property. Please see the ICCCM spec for more details.
|
||||
type NormalHints struct {
|
||||
Flags uint
|
||||
X, Y int
|
||||
Width, Height, MinWidth, MinHeight, MaxWidth, MaxHeight uint
|
||||
WidthInc, HeightInc uint
|
||||
MinAspectNum, MinAspectDen, MaxAspectNum, MaxAspectDen uint
|
||||
BaseWidth, BaseHeight, WinGravity uint
|
||||
}
|
||||
|
||||
// WM_NORMAL_HINTS get
|
||||
func WmNormalHintsGet(xu *xgbutil.XUtil,
|
||||
win xproto.Window) (nh *NormalHints, err error) {
|
||||
|
||||
lenExpect := 18
|
||||
hints, err := xprop.PropValNums(xprop.GetProperty(xu, win,
|
||||
"WM_NORMAL_HINTS"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(hints) != lenExpect {
|
||||
return nil,
|
||||
fmt.Errorf("WmNormalHint: There are %d fields in WM_NORMAL_HINTS, "+
|
||||
"but xgbutil expects %d.", len(hints), lenExpect)
|
||||
}
|
||||
|
||||
nh = &NormalHints{}
|
||||
nh.Flags = hints[0]
|
||||
nh.X = int(hints[1])
|
||||
nh.Y = int(hints[2])
|
||||
nh.Width = hints[3]
|
||||
nh.Height = hints[4]
|
||||
nh.MinWidth = hints[5]
|
||||
nh.MinHeight = hints[6]
|
||||
nh.MaxWidth = hints[7]
|
||||
nh.MaxHeight = hints[8]
|
||||
nh.WidthInc = hints[9]
|
||||
nh.HeightInc = hints[10]
|
||||
nh.MinAspectNum = hints[11]
|
||||
nh.MinAspectDen = hints[12]
|
||||
nh.MaxAspectNum = hints[13]
|
||||
nh.MaxAspectDen = hints[14]
|
||||
nh.BaseWidth = hints[15]
|
||||
nh.BaseHeight = hints[16]
|
||||
nh.WinGravity = hints[17]
|
||||
|
||||
if nh.WinGravity <= 0 {
|
||||
nh.WinGravity = xproto.GravityNorthWest
|
||||
}
|
||||
|
||||
return nh, nil
|
||||
}
|
||||
|
||||
// WM_NORMAL_HINTS set
|
||||
// Make sure to set the flags in the NormalHints struct correctly!
|
||||
func WmNormalHintsSet(xu *xgbutil.XUtil, win xproto.Window,
|
||||
nh *NormalHints) error {
|
||||
|
||||
raw := []uint{
|
||||
nh.Flags,
|
||||
uint(nh.X), uint(nh.Y), nh.Width, nh.Height,
|
||||
nh.MinWidth, nh.MinHeight,
|
||||
nh.MaxWidth, nh.MaxHeight,
|
||||
nh.WidthInc, nh.HeightInc,
|
||||
nh.MinAspectNum, nh.MinAspectDen,
|
||||
nh.MaxAspectNum, nh.MaxAspectDen,
|
||||
nh.BaseWidth, nh.BaseHeight,
|
||||
nh.WinGravity,
|
||||
}
|
||||
return xprop.ChangeProp32(xu, win, "WM_NORMAL_HINTS", "WM_SIZE_HINTS",
|
||||
raw...)
|
||||
}
|
||||
|
||||
// Hints is a struct that organizes information related to the WM_HINTS
|
||||
// property. Once again, I refer you to the ICCCM spec for documentation.
|
||||
type Hints struct {
|
||||
Flags uint
|
||||
Input, InitialState uint
|
||||
IconX, IconY int
|
||||
IconPixmap, IconMask xproto.Pixmap
|
||||
WindowGroup, IconWindow xproto.Window
|
||||
}
|
||||
|
||||
// WM_HINTS get
|
||||
func WmHintsGet(xu *xgbutil.XUtil,
|
||||
win xproto.Window) (hints *Hints, err error) {
|
||||
|
||||
lenExpect := 9
|
||||
raw, err := xprop.PropValNums(xprop.GetProperty(xu, win, "WM_HINTS"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(raw) != lenExpect {
|
||||
return nil,
|
||||
fmt.Errorf("WmHints: There are %d fields in "+
|
||||
"WM_HINTS, but xgbutil expects %d.", len(raw), lenExpect)
|
||||
}
|
||||
|
||||
hints = &Hints{}
|
||||
hints.Flags = raw[0]
|
||||
hints.Input = raw[1]
|
||||
hints.InitialState = raw[2]
|
||||
hints.IconPixmap = xproto.Pixmap(raw[3])
|
||||
hints.IconWindow = xproto.Window(raw[4])
|
||||
hints.IconX = int(raw[5])
|
||||
hints.IconY = int(raw[6])
|
||||
hints.IconMask = xproto.Pixmap(raw[7])
|
||||
hints.WindowGroup = xproto.Window(raw[8])
|
||||
|
||||
return hints, nil
|
||||
}
|
||||
|
||||
// WM_HINTS set
|
||||
// Make sure to set the flags in the Hints struct correctly!
|
||||
func WmHintsSet(xu *xgbutil.XUtil, win xproto.Window, hints *Hints) error {
|
||||
raw := []uint{
|
||||
hints.Flags, hints.Input, hints.InitialState,
|
||||
uint(hints.IconPixmap), uint(hints.IconWindow),
|
||||
uint(hints.IconX), uint(hints.IconY),
|
||||
uint(hints.IconMask),
|
||||
uint(hints.WindowGroup),
|
||||
}
|
||||
return xprop.ChangeProp32(xu, win, "WM_HINTS", "WM_HINTS", raw...)
|
||||
}
|
||||
|
||||
// WmClass struct contains two data points:
|
||||
// the instance and a class of a window.
|
||||
type WmClass struct {
|
||||
Instance, Class string
|
||||
}
|
||||
|
||||
// WM_CLASS get
|
||||
func WmClassGet(xu *xgbutil.XUtil, win xproto.Window) (*WmClass, error) {
|
||||
raw, err := xprop.PropValStrs(xprop.GetProperty(xu, win, "WM_CLASS"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(raw) != 2 {
|
||||
return nil,
|
||||
fmt.Errorf("WmClass: Two string make up WM_CLASS, but "+
|
||||
"xgbutil found %d in '%v'.", len(raw), raw)
|
||||
}
|
||||
|
||||
return &WmClass{
|
||||
Instance: raw[0],
|
||||
Class: raw[1],
|
||||
}, nil
|
||||
}
|
||||
|
||||
// WM_CLASS set
|
||||
func WmClassSet(xu *xgbutil.XUtil, win xproto.Window, class *WmClass) error {
|
||||
raw := make([]byte, len(class.Instance)+len(class.Class)+2)
|
||||
copy(raw, class.Instance)
|
||||
copy(raw[(len(class.Instance)+1):], class.Class)
|
||||
|
||||
return xprop.ChangeProp(xu, win, 8, "WM_CLASS", "STRING", raw)
|
||||
}
|
||||
|
||||
// WM_TRANSIENT_FOR get
|
||||
func WmTransientForGet(xu *xgbutil.XUtil,
|
||||
win xproto.Window) (xproto.Window, error) {
|
||||
|
||||
return xprop.PropValWindow(xprop.GetProperty(xu, win, "WM_TRANSIENT_FOR"))
|
||||
}
|
||||
|
||||
// WM_TRANSIENT_FOR set
|
||||
func WmTransientForSet(xu *xgbutil.XUtil, win xproto.Window,
|
||||
transient xproto.Window) error {
|
||||
|
||||
return xprop.ChangeProp32(xu, win, "WM_TRANSIENT_FOR", "WINDOW",
|
||||
uint(transient))
|
||||
}
|
||||
|
||||
// WM_PROTOCOLS get
|
||||
func WmProtocolsGet(xu *xgbutil.XUtil, win xproto.Window) ([]string, error) {
|
||||
raw, err := xprop.GetProperty(xu, win, "WM_PROTOCOLS")
|
||||
return xprop.PropValAtoms(xu, raw, err)
|
||||
}
|
||||
|
||||
// WM_PROTOCOLS set
|
||||
func WmProtocolsSet(xu *xgbutil.XUtil, win xproto.Window,
|
||||
atomNames []string) error {
|
||||
|
||||
atoms, err := xprop.StrToAtoms(xu, atomNames)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return xprop.ChangeProp32(xu, win, "WM_PROTOCOLS", "ATOM", atoms...)
|
||||
}
|
||||
|
||||
// WM_COLORMAP_WINDOWS get
|
||||
func WmColormapWindowsGet(xu *xgbutil.XUtil,
|
||||
win xproto.Window) ([]xproto.Window, error) {
|
||||
|
||||
return xprop.PropValWindows(xprop.GetProperty(xu, win,
|
||||
"WM_COLORMAP_WINDOWS"))
|
||||
}
|
||||
|
||||
// WM_COLORMAP_WINDOWS set
|
||||
func WmColormapWindowsSet(xu *xgbutil.XUtil, win xproto.Window,
|
||||
windows []xproto.Window) error {
|
||||
|
||||
return xprop.ChangeProp32(xu, win, "WM_COLORMAP_WINDOWS", "WINDOW",
|
||||
xprop.WindowToInt(windows)...)
|
||||
}
|
||||
|
||||
// WM_CLIENT_MACHINE get
|
||||
func WmClientMachineGet(xu *xgbutil.XUtil, win xproto.Window) (string, error) {
|
||||
return xprop.PropValStr(xprop.GetProperty(xu, win, "WM_CLIENT_MACHINE"))
|
||||
}
|
||||
|
||||
// WM_CLIENT_MACHINE set
|
||||
func WmClientMachineSet(xu *xgbutil.XUtil, win xproto.Window,
|
||||
client string) error {
|
||||
|
||||
return xprop.ChangeProp(xu, win, 8, "WM_CLIENT_MACHINE", "STRING",
|
||||
([]byte)(client))
|
||||
}
|
||||
|
||||
// WmState is a struct that organizes information related to the WM_STATE
|
||||
// property. Namely, the state (corresponding to a State* constant in this file)
|
||||
// and the icon window (probably not used).
|
||||
type WmState struct {
|
||||
State uint
|
||||
Icon xproto.Window
|
||||
}
|
||||
|
||||
// WM_STATE get
|
||||
func WmStateGet(xu *xgbutil.XUtil, win xproto.Window) (*WmState, error) {
|
||||
raw, err := xprop.PropValNums(xprop.GetProperty(xu, win, "WM_STATE"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(raw) != 2 {
|
||||
return nil,
|
||||
fmt.Errorf("WmState: Expected two integers in WM_STATE property "+
|
||||
"but xgbutil found %d in '%v'.", len(raw), raw)
|
||||
}
|
||||
|
||||
return &WmState{
|
||||
State: raw[0],
|
||||
Icon: xproto.Window(raw[1]),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// WM_STATE set
|
||||
func WmStateSet(xu *xgbutil.XUtil, win xproto.Window, state *WmState) error {
|
||||
raw := []uint{
|
||||
state.State,
|
||||
uint(state.Icon),
|
||||
}
|
||||
|
||||
return xprop.ChangeProp32(xu, win, "WM_STATE", "WM_STATE", raw...)
|
||||
}
|
||||
|
||||
// IconSize is a struct the organizes information related to the WM_ICON_SIZE
|
||||
// property. Mostly info about its dimensions.
|
||||
type IconSize struct {
|
||||
MinWidth, MinHeight, MaxWidth, MaxHeight, WidthInc, HeightInc uint
|
||||
}
|
||||
|
||||
// WM_ICON_SIZE get
|
||||
func WmIconSizeGet(xu *xgbutil.XUtil, win xproto.Window) (*IconSize, error) {
|
||||
raw, err := xprop.PropValNums(xprop.GetProperty(xu, win, "WM_ICON_SIZE"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(raw) != 6 {
|
||||
return nil,
|
||||
fmt.Errorf("WmIconSize: Expected six integers in WM_ICON_SIZE "+
|
||||
"property, but xgbutil found "+"%d in '%v'.", len(raw), raw)
|
||||
}
|
||||
|
||||
return &IconSize{
|
||||
MinWidth: raw[0], MinHeight: raw[1],
|
||||
MaxWidth: raw[2], MaxHeight: raw[3],
|
||||
WidthInc: raw[4], HeightInc: raw[5],
|
||||
}, nil
|
||||
}
|
||||
|
||||
// WM_ICON_SIZE set
|
||||
func WmIconSizeSet(xu *xgbutil.XUtil, win xproto.Window,
|
||||
icondim *IconSize) error {
|
||||
|
||||
raw := []uint{
|
||||
icondim.MinWidth, icondim.MinHeight,
|
||||
icondim.MaxWidth, icondim.MaxHeight,
|
||||
icondim.WidthInc, icondim.HeightInc,
|
||||
}
|
||||
|
||||
return xprop.ChangeProp32(xu, win, "WM_ICON_SIZE", "WM_ICON_SIZE", raw...)
|
||||
}
|
@ -1,64 +0,0 @@
|
||||
package icccm
|
||||
|
||||
import (
|
||||
"github.com/BurntSushi/xgb/xproto"
|
||||
|
||||
"github.com/BurntSushi/xgbutil"
|
||||
"github.com/BurntSushi/xgbutil/xevent"
|
||||
"github.com/BurntSushi/xgbutil/xprop"
|
||||
)
|
||||
|
||||
// IsDeleteProtocol checks whether a ClientMessage event satisfies the
|
||||
// WM_DELETE_WINDOW protocol. Namely, the format must be 32, the type must
|
||||
// be the WM_PROTOCOLS atom, and the first data item must be the atom
|
||||
// WM_DELETE_WINDOW.
|
||||
//
|
||||
// Note that if you're using the xwindow package, you should use the
|
||||
// WMGracefulClose method instead of directly using IsDeleteProtocol.
|
||||
func IsDeleteProtocol(X *xgbutil.XUtil, ev xevent.ClientMessageEvent) bool {
|
||||
// Make sure the Format is 32. (Meaning that each data item is
|
||||
// 32 bits.)
|
||||
if ev.Format != 32 {
|
||||
return false
|
||||
}
|
||||
|
||||
// Check to make sure the Type atom is WM_PROTOCOLS.
|
||||
typeName, err := xprop.AtomName(X, ev.Type)
|
||||
if err != nil || typeName != "WM_PROTOCOLS" { // not what we want
|
||||
return false
|
||||
}
|
||||
|
||||
// Check to make sure the first data item is WM_DELETE_WINDOW.
|
||||
protocolType, err := xprop.AtomName(X,
|
||||
xproto.Atom(ev.Data.Data32[0]))
|
||||
if err != nil || protocolType != "WM_DELETE_WINDOW" {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// IsFocusProtocol checks whether a ClientMessage event satisfies the
|
||||
// WM_TAKE_FOCUS protocol.
|
||||
func IsFocusProtocol(X *xgbutil.XUtil, ev xevent.ClientMessageEvent) bool {
|
||||
// Make sure the Format is 32. (Meaning that each data item is
|
||||
// 32 bits.)
|
||||
if ev.Format != 32 {
|
||||
return false
|
||||
}
|
||||
|
||||
// Check to make sure the Type atom is WM_PROTOCOLS.
|
||||
typeName, err := xprop.AtomName(X, ev.Type)
|
||||
if err != nil || typeName != "WM_PROTOCOLS" { // not what we want
|
||||
return false
|
||||
}
|
||||
|
||||
// Check to make sure the first data item is WM_TAKE_FOCUS.
|
||||
protocolType, err := xprop.AtomName(X,
|
||||
xproto.Atom(ev.Data.Data32[0]))
|
||||
if err != nil || protocolType != "WM_TAKE_FOCUS" {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
@ -1 +0,0 @@
|
||||
au BufWritePost *.go silent!make tags > /dev/null 2>&1
|
@ -1,167 +0,0 @@
|
||||
package xgbutil
|
||||
|
||||
/*
|
||||
types.go contains several types used in the XUtil structure. In an ideal world,
|
||||
they would be defined in their appropriate packages, but must be defined here
|
||||
(and exported) for use in some sub-packages. (Namely, xevent, keybind and
|
||||
mousebind.)
|
||||
*/
|
||||
|
||||
import (
|
||||
"github.com/BurntSushi/xgb"
|
||||
"github.com/BurntSushi/xgb/xproto"
|
||||
)
|
||||
|
||||
// Callback is an interface that should be implemented by event callback
|
||||
// functions. Namely, to assign a function to a particular event/window
|
||||
// combination, simply define a function with type 'SomeEventFun' (pre-defined
|
||||
// in xevent/callback.go), and call the 'Connect' method.
|
||||
// The 'Run' method is used inside the Main event loop, and shouldn't be used
|
||||
// by the user.
|
||||
// Also, it is perfectly legitimate to connect to events that don't specify
|
||||
// a window (like MappingNotify and KeymapNotify). In this case, simply
|
||||
// use 'xgbutil.NoWindow' as the window id.
|
||||
//
|
||||
// Example to respond to ConfigureNotify events on window 0x1
|
||||
//
|
||||
// xevent.ConfigureNotifyFun(
|
||||
// func(X *xgbutil.XUtil, e xevent.ConfigureNotifyEvent) {
|
||||
// fmt.Printf("(%d, %d) %dx%d\n", e.X, e.Y, e.Width, e.Height)
|
||||
// }).Connect(X, 0x1)
|
||||
type Callback interface {
|
||||
// Connect modifies XUtil's state to attach an event handler to a
|
||||
// particular event.
|
||||
Connect(xu *XUtil, win xproto.Window)
|
||||
|
||||
// Run is exported for use in the xevent package but should not be
|
||||
// used by the user. (It is used to run the callback function in the
|
||||
// main event loop.)
|
||||
Run(xu *XUtil, ev interface{})
|
||||
}
|
||||
|
||||
// CallbackHook works similarly to the more general Callback, but it is
|
||||
// for hooks into the main xevent loop. As such it does not get attached
|
||||
// to a window.
|
||||
type CallbackHook interface {
|
||||
// Connect connects this hook to the main loop of the passed XUtil
|
||||
// instance.
|
||||
Connect(xu *XUtil)
|
||||
|
||||
// Run is exported for use in the xevent package, but should not be
|
||||
// used by the user. It should return true if it's ok to process
|
||||
// the event as usual, or false if it should be suppressed.
|
||||
Run(xu *XUtil, ev interface{}) bool
|
||||
}
|
||||
|
||||
// CallbackKey works similarly to the more general Callback, but it adds
|
||||
// parameters specific to key bindings.
|
||||
type CallbackKey interface {
|
||||
// Connect modifies XUtil's state to attach an event handler to a
|
||||
// particular key press. If grab is true, connect will request a passive
|
||||
// grab.
|
||||
Connect(xu *XUtil, win xproto.Window, keyStr string, grab bool) error
|
||||
|
||||
// Run is exported for use in the keybind package but should not be
|
||||
// used by the user. (It is used to run the callback function in the
|
||||
// main event loop.
|
||||
Run(xu *XUtil, ev interface{})
|
||||
}
|
||||
|
||||
// CallbackMouse works similarly to the more general Callback, but it adds
|
||||
// parameters specific to mouse bindings.
|
||||
type CallbackMouse interface {
|
||||
// Connect modifies XUtil's state to attach an event handler to a
|
||||
// particular button press.
|
||||
// If sync is true, the grab will be synchronous. (This will require a
|
||||
// call to xproto.AllowEvents in response, otherwise no further events
|
||||
// will be processed and your program will lock.)
|
||||
// If grab is true, connect will request a passive grab.
|
||||
Connect(xu *XUtil, win xproto.Window, buttonStr string,
|
||||
sync bool, grab bool) error
|
||||
|
||||
// Run is exported for use in the mousebind package but should not be
|
||||
// used by the user. (It is used to run the callback function in the
|
||||
// main event loop.)
|
||||
Run(xu *XUtil, ev interface{})
|
||||
}
|
||||
|
||||
// KeyKey is the type of the key in the map of keybindings.
|
||||
// It essentially represents the tuple
|
||||
// (event type, window id, modifier, keycode).
|
||||
// It is exported for use in the keybind package. It should not be used.
|
||||
type KeyKey struct {
|
||||
Evtype int
|
||||
Win xproto.Window
|
||||
Mod uint16
|
||||
Code xproto.Keycode
|
||||
}
|
||||
|
||||
// KeyString is the type of a key binding string used to connect to particular
|
||||
// key combinations. A list of all such key strings is maintained in order to
|
||||
// rebind keys when the keyboard mapping has been changed.
|
||||
type KeyString struct {
|
||||
Str string
|
||||
Callback CallbackKey
|
||||
Evtype int
|
||||
Win xproto.Window
|
||||
Grab bool
|
||||
}
|
||||
|
||||
// MouseKey is the type of the key in the map of mouse bindings.
|
||||
// It essentially represents the tuple
|
||||
// (event type, window id, modifier, button).
|
||||
// It is exported for use in the mousebind package. It should not be used.
|
||||
type MouseKey struct {
|
||||
Evtype int
|
||||
Win xproto.Window
|
||||
Mod uint16
|
||||
Button xproto.Button
|
||||
}
|
||||
|
||||
// KeyboardMapping embeds a keyboard mapping reply from XGB.
|
||||
// It should be retrieved using keybind.KeyMapGet, if necessary.
|
||||
// xgbutil tries quite hard to absolve you from ever having to use this.
|
||||
// A keyboard mapping is a table that maps keycodes to one or more keysyms.
|
||||
type KeyboardMapping struct {
|
||||
*xproto.GetKeyboardMappingReply
|
||||
}
|
||||
|
||||
// ModifierMapping embeds a modifier mapping reply from XGB.
|
||||
// It should be retrieved using keybind.ModMapGet, if necessary.
|
||||
// xgbutil tries quite hard to absolve you from ever having to use this.
|
||||
// A modifier mapping is a table that maps modifiers to one or more keycodes.
|
||||
type ModifierMapping struct {
|
||||
*xproto.GetModifierMappingReply
|
||||
}
|
||||
|
||||
// ErrorHandlerFun is the type of function required to handle errors that
|
||||
// come in through the main event loop.
|
||||
// For example, to set a new error handler, use:
|
||||
//
|
||||
// xevent.ErrorHandlerSet(xgbutil.ErrorHandlerFun(
|
||||
// func(err xgb.Error) {
|
||||
// // do something with err
|
||||
// }))
|
||||
type ErrorHandlerFun func(err xgb.Error)
|
||||
|
||||
// EventOrError is a struct that contains either an event value or an error
|
||||
// value. It is an error to contain both. Containing neither indicates an
|
||||
// error too.
|
||||
// This is exported for use in the xevent package. You shouldn't have any
|
||||
// direct contact with values of this type, unless you need to inspect the
|
||||
// queue directly with xevent.Peek.
|
||||
type EventOrError struct {
|
||||
Event xgb.Event
|
||||
Err xgb.Error
|
||||
}
|
||||
|
||||
// MouseDragFun is the kind of function used on each dragging step
|
||||
// and at the end of a drag.
|
||||
type MouseDragFun func(xu *XUtil, rootX, rootY, eventX, eventY int)
|
||||
|
||||
// MouseDragBeginFun is the kind of function used to initialize a drag.
|
||||
// The difference between this and MouseDragFun is that the begin function
|
||||
// returns a bool (of whether or not to cancel the drag) and an X resource
|
||||
// identifier corresponding to a cursor.
|
||||
type MouseDragBeginFun func(xu *XUtil, rootX, rootY,
|
||||
eventX, eventY int) (bool, xproto.Cursor)
|
@ -1,389 +0,0 @@
|
||||
package xevent
|
||||
|
||||
/*
|
||||
Does all the plumbing to allow a simple callback interface for users.
|
||||
|
||||
This file is automatically generated using `scripts/write-events callbacks`.
|
||||
|
||||
Edit it at your peril.
|
||||
*/
|
||||
|
||||
import (
|
||||
"github.com/BurntSushi/xgb/xproto"
|
||||
|
||||
"github.com/BurntSushi/xgbutil"
|
||||
)
|
||||
|
||||
type KeyPressFun func(xu *xgbutil.XUtil, event KeyPressEvent)
|
||||
|
||||
func (callback KeyPressFun) Connect(xu *xgbutil.XUtil,
|
||||
win xproto.Window) {
|
||||
attachCallback(xu, KeyPress, win, callback)
|
||||
}
|
||||
|
||||
func (callback KeyPressFun) Run(xu *xgbutil.XUtil, event interface{}) {
|
||||
callback(xu, event.(KeyPressEvent))
|
||||
}
|
||||
|
||||
type KeyReleaseFun func(xu *xgbutil.XUtil, event KeyReleaseEvent)
|
||||
|
||||
func (callback KeyReleaseFun) Connect(xu *xgbutil.XUtil,
|
||||
win xproto.Window) {
|
||||
attachCallback(xu, KeyRelease, win, callback)
|
||||
}
|
||||
|
||||
func (callback KeyReleaseFun) Run(xu *xgbutil.XUtil, event interface{}) {
|
||||
callback(xu, event.(KeyReleaseEvent))
|
||||
}
|
||||
|
||||
type ButtonPressFun func(xu *xgbutil.XUtil, event ButtonPressEvent)
|
||||
|
||||
func (callback ButtonPressFun) Connect(xu *xgbutil.XUtil,
|
||||
win xproto.Window) {
|
||||
attachCallback(xu, ButtonPress, win, callback)
|
||||
}
|
||||
|
||||
func (callback ButtonPressFun) Run(xu *xgbutil.XUtil, event interface{}) {
|
||||
callback(xu, event.(ButtonPressEvent))
|
||||
}
|
||||
|
||||
type ButtonReleaseFun func(xu *xgbutil.XUtil, event ButtonReleaseEvent)
|
||||
|
||||
func (callback ButtonReleaseFun) Connect(xu *xgbutil.XUtil,
|
||||
win xproto.Window) {
|
||||
attachCallback(xu, ButtonRelease, win, callback)
|
||||
}
|
||||
|
||||
func (callback ButtonReleaseFun) Run(xu *xgbutil.XUtil, event interface{}) {
|
||||
callback(xu, event.(ButtonReleaseEvent))
|
||||
}
|
||||
|
||||
type MotionNotifyFun func(xu *xgbutil.XUtil, event MotionNotifyEvent)
|
||||
|
||||
func (callback MotionNotifyFun) Connect(xu *xgbutil.XUtil,
|
||||
win xproto.Window) {
|
||||
attachCallback(xu, MotionNotify, win, callback)
|
||||
}
|
||||
|
||||
func (callback MotionNotifyFun) Run(xu *xgbutil.XUtil, event interface{}) {
|
||||
callback(xu, event.(MotionNotifyEvent))
|
||||
}
|
||||
|
||||
type EnterNotifyFun func(xu *xgbutil.XUtil, event EnterNotifyEvent)
|
||||
|
||||
func (callback EnterNotifyFun) Connect(xu *xgbutil.XUtil,
|
||||
win xproto.Window) {
|
||||
attachCallback(xu, EnterNotify, win, callback)
|
||||
}
|
||||
|
||||
func (callback EnterNotifyFun) Run(xu *xgbutil.XUtil, event interface{}) {
|
||||
callback(xu, event.(EnterNotifyEvent))
|
||||
}
|
||||
|
||||
type LeaveNotifyFun func(xu *xgbutil.XUtil, event LeaveNotifyEvent)
|
||||
|
||||
func (callback LeaveNotifyFun) Connect(xu *xgbutil.XUtil,
|
||||
win xproto.Window) {
|
||||
attachCallback(xu, LeaveNotify, win, callback)
|
||||
}
|
||||
|
||||
func (callback LeaveNotifyFun) Run(xu *xgbutil.XUtil, event interface{}) {
|
||||
callback(xu, event.(LeaveNotifyEvent))
|
||||
}
|
||||
|
||||
type FocusInFun func(xu *xgbutil.XUtil, event FocusInEvent)
|
||||
|
||||
func (callback FocusInFun) Connect(xu *xgbutil.XUtil,
|
||||
win xproto.Window) {
|
||||
attachCallback(xu, FocusIn, win, callback)
|
||||
}
|
||||
|
||||
func (callback FocusInFun) Run(xu *xgbutil.XUtil, event interface{}) {
|
||||
callback(xu, event.(FocusInEvent))
|
||||
}
|
||||
|
||||
type FocusOutFun func(xu *xgbutil.XUtil, event FocusOutEvent)
|
||||
|
||||
func (callback FocusOutFun) Connect(xu *xgbutil.XUtil,
|
||||
win xproto.Window) {
|
||||
attachCallback(xu, FocusOut, win, callback)
|
||||
}
|
||||
|
||||
func (callback FocusOutFun) Run(xu *xgbutil.XUtil, event interface{}) {
|
||||
callback(xu, event.(FocusOutEvent))
|
||||
}
|
||||
|
||||
type KeymapNotifyFun func(xu *xgbutil.XUtil, event KeymapNotifyEvent)
|
||||
|
||||
func (callback KeymapNotifyFun) Connect(xu *xgbutil.XUtil,
|
||||
win xproto.Window) {
|
||||
attachCallback(xu, KeymapNotify, win, callback)
|
||||
}
|
||||
|
||||
func (callback KeymapNotifyFun) Run(xu *xgbutil.XUtil, event interface{}) {
|
||||
callback(xu, event.(KeymapNotifyEvent))
|
||||
}
|
||||
|
||||
type ExposeFun func(xu *xgbutil.XUtil, event ExposeEvent)
|
||||
|
||||
func (callback ExposeFun) Connect(xu *xgbutil.XUtil,
|
||||
win xproto.Window) {
|
||||
attachCallback(xu, Expose, win, callback)
|
||||
}
|
||||
|
||||
func (callback ExposeFun) Run(xu *xgbutil.XUtil, event interface{}) {
|
||||
callback(xu, event.(ExposeEvent))
|
||||
}
|
||||
|
||||
type GraphicsExposureFun func(xu *xgbutil.XUtil, event GraphicsExposureEvent)
|
||||
|
||||
func (callback GraphicsExposureFun) Connect(xu *xgbutil.XUtil,
|
||||
win xproto.Window) {
|
||||
attachCallback(xu, GraphicsExposure, win, callback)
|
||||
}
|
||||
|
||||
func (callback GraphicsExposureFun) Run(xu *xgbutil.XUtil, event interface{}) {
|
||||
callback(xu, event.(GraphicsExposureEvent))
|
||||
}
|
||||
|
||||
type NoExposureFun func(xu *xgbutil.XUtil, event NoExposureEvent)
|
||||
|
||||
func (callback NoExposureFun) Connect(xu *xgbutil.XUtil,
|
||||
win xproto.Window) {
|
||||
attachCallback(xu, NoExposure, win, callback)
|
||||
}
|
||||
|
||||
func (callback NoExposureFun) Run(xu *xgbutil.XUtil, event interface{}) {
|
||||
callback(xu, event.(NoExposureEvent))
|
||||
}
|
||||
|
||||
type VisibilityNotifyFun func(xu *xgbutil.XUtil, event VisibilityNotifyEvent)
|
||||
|
||||
func (callback VisibilityNotifyFun) Connect(xu *xgbutil.XUtil,
|
||||
win xproto.Window) {
|
||||
attachCallback(xu, VisibilityNotify, win, callback)
|
||||
}
|
||||
|
||||
func (callback VisibilityNotifyFun) Run(xu *xgbutil.XUtil, event interface{}) {
|
||||
callback(xu, event.(VisibilityNotifyEvent))
|
||||
}
|
||||
|
||||
type CreateNotifyFun func(xu *xgbutil.XUtil, event CreateNotifyEvent)
|
||||
|
||||
func (callback CreateNotifyFun) Connect(xu *xgbutil.XUtil,
|
||||
win xproto.Window) {
|
||||
attachCallback(xu, CreateNotify, win, callback)
|
||||
}
|
||||
|
||||
func (callback CreateNotifyFun) Run(xu *xgbutil.XUtil, event interface{}) {
|
||||
callback(xu, event.(CreateNotifyEvent))
|
||||
}
|
||||
|
||||
type DestroyNotifyFun func(xu *xgbutil.XUtil, event DestroyNotifyEvent)
|
||||
|
||||
func (callback DestroyNotifyFun) Connect(xu *xgbutil.XUtil,
|
||||
win xproto.Window) {
|
||||
attachCallback(xu, DestroyNotify, win, callback)
|
||||
}
|
||||
|
||||
func (callback DestroyNotifyFun) Run(xu *xgbutil.XUtil, event interface{}) {
|
||||
callback(xu, event.(DestroyNotifyEvent))
|
||||
}
|
||||
|
||||
type UnmapNotifyFun func(xu *xgbutil.XUtil, event UnmapNotifyEvent)
|
||||
|
||||
func (callback UnmapNotifyFun) Connect(xu *xgbutil.XUtil,
|
||||
win xproto.Window) {
|
||||
attachCallback(xu, UnmapNotify, win, callback)
|
||||
}
|
||||
|
||||
func (callback UnmapNotifyFun) Run(xu *xgbutil.XUtil, event interface{}) {
|
||||
callback(xu, event.(UnmapNotifyEvent))
|
||||
}
|
||||
|
||||
type MapNotifyFun func(xu *xgbutil.XUtil, event MapNotifyEvent)
|
||||
|
||||
func (callback MapNotifyFun) Connect(xu *xgbutil.XUtil,
|
||||
win xproto.Window) {
|
||||
attachCallback(xu, MapNotify, win, callback)
|
||||
}
|
||||
|
||||
func (callback MapNotifyFun) Run(xu *xgbutil.XUtil, event interface{}) {
|
||||
callback(xu, event.(MapNotifyEvent))
|
||||
}
|
||||
|
||||
type MapRequestFun func(xu *xgbutil.XUtil, event MapRequestEvent)
|
||||
|
||||
func (callback MapRequestFun) Connect(xu *xgbutil.XUtil,
|
||||
win xproto.Window) {
|
||||
attachCallback(xu, MapRequest, win, callback)
|
||||
}
|
||||
|
||||
func (callback MapRequestFun) Run(xu *xgbutil.XUtil, event interface{}) {
|
||||
callback(xu, event.(MapRequestEvent))
|
||||
}
|
||||
|
||||
type ReparentNotifyFun func(xu *xgbutil.XUtil, event ReparentNotifyEvent)
|
||||
|
||||
func (callback ReparentNotifyFun) Connect(xu *xgbutil.XUtil,
|
||||
win xproto.Window) {
|
||||
attachCallback(xu, ReparentNotify, win, callback)
|
||||
}
|
||||
|
||||
func (callback ReparentNotifyFun) Run(xu *xgbutil.XUtil, event interface{}) {
|
||||
callback(xu, event.(ReparentNotifyEvent))
|
||||
}
|
||||
|
||||
type ConfigureNotifyFun func(xu *xgbutil.XUtil, event ConfigureNotifyEvent)
|
||||
|
||||
func (callback ConfigureNotifyFun) Connect(xu *xgbutil.XUtil,
|
||||
win xproto.Window) {
|
||||
attachCallback(xu, ConfigureNotify, win, callback)
|
||||
}
|
||||
|
||||
func (callback ConfigureNotifyFun) Run(xu *xgbutil.XUtil, event interface{}) {
|
||||
callback(xu, event.(ConfigureNotifyEvent))
|
||||
}
|
||||
|
||||
type ConfigureRequestFun func(xu *xgbutil.XUtil, event ConfigureRequestEvent)
|
||||
|
||||
func (callback ConfigureRequestFun) Connect(xu *xgbutil.XUtil,
|
||||
win xproto.Window) {
|
||||
attachCallback(xu, ConfigureRequest, win, callback)
|
||||
}
|
||||
|
||||
func (callback ConfigureRequestFun) Run(xu *xgbutil.XUtil, event interface{}) {
|
||||
callback(xu, event.(ConfigureRequestEvent))
|
||||
}
|
||||
|
||||
type GravityNotifyFun func(xu *xgbutil.XUtil, event GravityNotifyEvent)
|
||||
|
||||
func (callback GravityNotifyFun) Connect(xu *xgbutil.XUtil,
|
||||
win xproto.Window) {
|
||||
attachCallback(xu, GravityNotify, win, callback)
|
||||
}
|
||||
|
||||
func (callback GravityNotifyFun) Run(xu *xgbutil.XUtil, event interface{}) {
|
||||
callback(xu, event.(GravityNotifyEvent))
|
||||
}
|
||||
|
||||
type ResizeRequestFun func(xu *xgbutil.XUtil, event ResizeRequestEvent)
|
||||
|
||||
func (callback ResizeRequestFun) Connect(xu *xgbutil.XUtil,
|
||||
win xproto.Window) {
|
||||
attachCallback(xu, ResizeRequest, win, callback)
|
||||
}
|
||||
|
||||
func (callback ResizeRequestFun) Run(xu *xgbutil.XUtil, event interface{}) {
|
||||
callback(xu, event.(ResizeRequestEvent))
|
||||
}
|
||||
|
||||
type CirculateNotifyFun func(xu *xgbutil.XUtil, event CirculateNotifyEvent)
|
||||
|
||||
func (callback CirculateNotifyFun) Connect(xu *xgbutil.XUtil,
|
||||
win xproto.Window) {
|
||||
attachCallback(xu, CirculateNotify, win, callback)
|
||||
}
|
||||
|
||||
func (callback CirculateNotifyFun) Run(xu *xgbutil.XUtil, event interface{}) {
|
||||
callback(xu, event.(CirculateNotifyEvent))
|
||||
}
|
||||
|
||||
type CirculateRequestFun func(xu *xgbutil.XUtil, event CirculateRequestEvent)
|
||||
|
||||
func (callback CirculateRequestFun) Connect(xu *xgbutil.XUtil,
|
||||
win xproto.Window) {
|
||||
attachCallback(xu, CirculateRequest, win, callback)
|
||||
}
|
||||
|
||||
func (callback CirculateRequestFun) Run(xu *xgbutil.XUtil, event interface{}) {
|
||||
callback(xu, event.(CirculateRequestEvent))
|
||||
}
|
||||
|
||||
type PropertyNotifyFun func(xu *xgbutil.XUtil, event PropertyNotifyEvent)
|
||||
|
||||
func (callback PropertyNotifyFun) Connect(xu *xgbutil.XUtil,
|
||||
win xproto.Window) {
|
||||
attachCallback(xu, PropertyNotify, win, callback)
|
||||
}
|
||||
|
||||
func (callback PropertyNotifyFun) Run(xu *xgbutil.XUtil, event interface{}) {
|
||||
callback(xu, event.(PropertyNotifyEvent))
|
||||
}
|
||||
|
||||
type SelectionClearFun func(xu *xgbutil.XUtil, event SelectionClearEvent)
|
||||
|
||||
func (callback SelectionClearFun) Connect(xu *xgbutil.XUtil,
|
||||
win xproto.Window) {
|
||||
attachCallback(xu, SelectionClear, win, callback)
|
||||
}
|
||||
|
||||
func (callback SelectionClearFun) Run(xu *xgbutil.XUtil, event interface{}) {
|
||||
callback(xu, event.(SelectionClearEvent))
|
||||
}
|
||||
|
||||
type SelectionRequestFun func(xu *xgbutil.XUtil, event SelectionRequestEvent)
|
||||
|
||||
func (callback SelectionRequestFun) Connect(xu *xgbutil.XUtil,
|
||||
win xproto.Window) {
|
||||
attachCallback(xu, SelectionRequest, win, callback)
|
||||
}
|
||||
|
||||
func (callback SelectionRequestFun) Run(xu *xgbutil.XUtil, event interface{}) {
|
||||
callback(xu, event.(SelectionRequestEvent))
|
||||
}
|
||||
|
||||
type SelectionNotifyFun func(xu *xgbutil.XUtil, event SelectionNotifyEvent)
|
||||
|
||||
func (callback SelectionNotifyFun) Connect(xu *xgbutil.XUtil,
|
||||
win xproto.Window) {
|
||||
attachCallback(xu, SelectionNotify, win, callback)
|
||||
}
|
||||
|
||||
func (callback SelectionNotifyFun) Run(xu *xgbutil.XUtil, event interface{}) {
|
||||
callback(xu, event.(SelectionNotifyEvent))
|
||||
}
|
||||
|
||||
type ColormapNotifyFun func(xu *xgbutil.XUtil, event ColormapNotifyEvent)
|
||||
|
||||
func (callback ColormapNotifyFun) Connect(xu *xgbutil.XUtil,
|
||||
win xproto.Window) {
|
||||
attachCallback(xu, ColormapNotify, win, callback)
|
||||
}
|
||||
|
||||
func (callback ColormapNotifyFun) Run(xu *xgbutil.XUtil, event interface{}) {
|
||||
callback(xu, event.(ColormapNotifyEvent))
|
||||
}
|
||||
|
||||
type ClientMessageFun func(xu *xgbutil.XUtil, event ClientMessageEvent)
|
||||
|
||||
func (callback ClientMessageFun) Connect(xu *xgbutil.XUtil,
|
||||
win xproto.Window) {
|
||||
attachCallback(xu, ClientMessage, win, callback)
|
||||
}
|
||||
|
||||
func (callback ClientMessageFun) Run(xu *xgbutil.XUtil, event interface{}) {
|
||||
callback(xu, event.(ClientMessageEvent))
|
||||
}
|
||||
|
||||
type MappingNotifyFun func(xu *xgbutil.XUtil, event MappingNotifyEvent)
|
||||
|
||||
func (callback MappingNotifyFun) Connect(xu *xgbutil.XUtil,
|
||||
win xproto.Window) {
|
||||
attachCallback(xu, MappingNotify, win, callback)
|
||||
}
|
||||
|
||||
func (callback MappingNotifyFun) Run(xu *xgbutil.XUtil, event interface{}) {
|
||||
callback(xu, event.(MappingNotifyEvent))
|
||||
}
|
||||
|
||||
type ShapeNotifyFun func(xu *xgbutil.XUtil, event ShapeNotifyEvent)
|
||||
|
||||
func (callback ShapeNotifyFun) Connect(xu *xgbutil.XUtil,
|
||||
win xproto.Window) {
|
||||
attachCallback(xu, ShapeNotify, win, callback)
|
||||
}
|
||||
|
||||
func (callback ShapeNotifyFun) Run(xu *xgbutil.XUtil, event interface{}) {
|
||||
callback(xu, event.(ShapeNotifyEvent))
|
||||
}
|
@ -1,76 +0,0 @@
|
||||
/*
|
||||
Package xevent provides an event handler interface for attaching callback
|
||||
functions to X events, and an implementation of an X event loop.
|
||||
|
||||
The X event loop
|
||||
|
||||
One of the biggest conveniences offered by xgbutil is its event handler system.
|
||||
That is, the ability to attach an arbitrary callback function to any X event.
|
||||
In order for such things to work, xgbutil needs to control the main X event
|
||||
loop and act as a dispatcher for all event handlers created by you.
|
||||
|
||||
To run the X event loop, use xevent.Main or xevent.MainPing. The former
|
||||
runs a normal event loop in the current goroutine and processes events. The
|
||||
latter runs the event loop in a new goroutine and returns a pingBefore and
|
||||
a pingAfter channel. The pingBefore channel is sent a benign value right before
|
||||
an event is dequeued, and the pingAfter channel is sent a benign value right
|
||||
after after all callbacks for that event have finished execution. These
|
||||
synchronization points in the main event loop can be combined with a 'select'
|
||||
statement to process data from other input sources. An example of this is given
|
||||
in the documentation for the MainPing function. A complete example called
|
||||
multiple-source-event-loop can also be found in the examples directory of the
|
||||
xgbutil package.
|
||||
|
||||
To quit the main event loop, you may use xevent.Quit, but there is nothing
|
||||
inherently wrong with stopping dead using os.Exit. xevent.Quit is provided for
|
||||
your convenience should you need to run any clean-up code after the main event
|
||||
loop returns.
|
||||
|
||||
The X event queue
|
||||
|
||||
xgbutil's event queue contains values that are either events or errors. (Never
|
||||
both and never neither.) Namely, errors are received in the event loop from
|
||||
unchecked requests. (Errors generated by checked requests are guaranteed to be
|
||||
returned to the caller and are never received in the event loop.) Also, a
|
||||
default error handler function can be set with xevent.ErrorHandlerSet.
|
||||
|
||||
To this end, xgbutil's event queue can be inspected. This is advantageous when
|
||||
information about what events will be processed in the future could be helpful
|
||||
(i.e., if there is an UnmapNotify event waiting to be processed.) The event
|
||||
queue can also be manipulated to facilitate event compression. (Two events that
|
||||
are common candidates for compression are ConfigureNotify and MotionNotify.)
|
||||
|
||||
Detach events
|
||||
|
||||
Whenever a window can no longer receive events (i.e., when it is destroyed),
|
||||
all event handlers related to that window should be detached. (If this is
|
||||
omitted, then Go's garbage collector will not be able to reuse memory occupied
|
||||
by the now-unused event handlers for that window.) Moreover, its possible that
|
||||
a window id can be reused after it has been discarded, which could result in
|
||||
odd behavior in your application.
|
||||
|
||||
To detach a window from all event handlers in the xevent package, use
|
||||
xevent.Detach. If you're also using the keybind and mousebind packages, you'll
|
||||
need to call keybind.Detach and mousebind.Detach too. So to detach your window
|
||||
from all possible event handlers in xgbutil, use something like:
|
||||
|
||||
xevent.Detach(XUtilValue, your-window-id)
|
||||
keybind.Detach(XUtilValue, your-window-id)
|
||||
mousebind.Detach(XUtilValue, your-window-id)
|
||||
|
||||
Quick example
|
||||
|
||||
A small example that shows how to respond to ConfigureNotify events sent to
|
||||
your-window-id.
|
||||
|
||||
xevent.ConfigureNotifyFun(
|
||||
func(X *xgbutil.XUtil, e xevent.ConfigureNotifyEvent) {
|
||||
fmt.Printf("(%d, %d) %dx%d\n", e.X, e.Y, e.Width, e.Height)
|
||||
}).Connect(XUtilValue, your-window-id)
|
||||
|
||||
More examples
|
||||
|
||||
The xevent package is used in several of the examples in the examples directory
|
||||
in the xgbutil package.
|
||||
*/
|
||||
package xevent
|
@ -1,291 +0,0 @@
|
||||
package xevent
|
||||
|
||||
/*
|
||||
xevent/eventloop.go contains code that implements a main X event loop.
|
||||
|
||||
Namely, it provides facilities to read new events into xevent's event queue,
|
||||
run a normal main event loop and run a main event loop that pings a channel
|
||||
each time an event is about to be dequeued. The latter facility allows one to
|
||||
easily include other input sources for processing in a program's main event
|
||||
loop.
|
||||
*/
|
||||
|
||||
import (
|
||||
"github.com/BurntSushi/xgb/shape"
|
||||
"github.com/BurntSushi/xgb/xproto"
|
||||
|
||||
"github.com/BurntSushi/xgbutil"
|
||||
)
|
||||
|
||||
// Read reads one or more events and queues them in XUtil.
|
||||
// If 'block' is True, then call 'WaitForEvent' before sucking up
|
||||
// all events that have been queued by XGB.
|
||||
func Read(xu *xgbutil.XUtil, block bool) {
|
||||
if block {
|
||||
ev, err := xu.Conn().WaitForEvent()
|
||||
if ev == nil && err == nil {
|
||||
xgbutil.Logger.Fatal("BUG: Could not read an event or an error.")
|
||||
}
|
||||
Enqueue(xu, ev, err)
|
||||
}
|
||||
|
||||
// Clean up anything that's in the queue
|
||||
for {
|
||||
ev, err := xu.Conn().PollForEvent()
|
||||
|
||||
// No events left...
|
||||
if ev == nil && err == nil {
|
||||
break
|
||||
}
|
||||
|
||||
// We're good, queue it up
|
||||
Enqueue(xu, ev, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Main starts the main X event loop. It will read events and call appropriate
|
||||
// callback functions.
|
||||
// N.B. If you have multiple X connections in the same program, you should be
|
||||
// able to run this in different goroutines concurrently. However, only
|
||||
// *one* of these should run for *each* connection.
|
||||
func Main(xu *xgbutil.XUtil) {
|
||||
mainEventLoop(xu, nil, nil, nil)
|
||||
}
|
||||
|
||||
// MainPing starts the main X event loop, and returns three "ping" channels:
|
||||
// the first is pinged before an event is dequeued, the second is pinged
|
||||
// after all callbacks for a particular event have been called and the last
|
||||
// is pinged when the event loop stops (e.g., after a call to xevent.Quit).
|
||||
// pingAfter channel.
|
||||
//
|
||||
// This is useful if your event loop needs to draw from other sources. e.g.,
|
||||
//
|
||||
// pingBefore, pingAfter, pingQuit := xevent.MainPing()
|
||||
// for {
|
||||
// select {
|
||||
// case <-pingBefore:
|
||||
// // Wait for event processing to finish.
|
||||
// <-pingAfter
|
||||
// case val <- someOtherChannel:
|
||||
// // do some work with val
|
||||
// case <-pingQuit:
|
||||
// fmt.Printf("xevent loop has quit")
|
||||
// return
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// Note that an unbuffered channel is returned, which implies that any work
|
||||
// done with 'val' will delay further X event processing.
|
||||
//
|
||||
// A complete example using MainPing can be found in the examples directory in
|
||||
// the xgbutil package under the name multiple-source-event-loop.
|
||||
func MainPing(xu *xgbutil.XUtil) (chan struct{}, chan struct{}, chan struct{}) {
|
||||
pingBefore := make(chan struct{}, 0)
|
||||
pingAfter := make(chan struct{}, 0)
|
||||
pingQuit := make(chan struct{}, 0)
|
||||
go func() {
|
||||
mainEventLoop(xu, pingBefore, pingAfter, pingQuit)
|
||||
}()
|
||||
return pingBefore, pingAfter, pingQuit
|
||||
}
|
||||
|
||||
// mainEventLoop runs the main event loop with an optional ping channel.
|
||||
func mainEventLoop(xu *xgbutil.XUtil,
|
||||
pingBefore, pingAfter, pingQuit chan struct{}) {
|
||||
for {
|
||||
if Quitting(xu) {
|
||||
if pingQuit != nil {
|
||||
pingQuit <- struct{}{}
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
// Gobble up as many events as possible (into the queue).
|
||||
// If there are no events, we block.
|
||||
Read(xu, true)
|
||||
|
||||
// Now process every event/error in the queue.
|
||||
processEventQueue(xu, pingBefore, pingAfter)
|
||||
}
|
||||
}
|
||||
|
||||
// processEventQueue processes every item in the event/error queue.
|
||||
func processEventQueue(xu *xgbutil.XUtil, pingBefore, pingAfter chan struct{}) {
|
||||
for !Empty(xu) {
|
||||
if Quitting(xu) {
|
||||
return
|
||||
}
|
||||
|
||||
// We send the ping *before* the next event is dequeued.
|
||||
// This is so the queue doesn't present a misrepresentation of which
|
||||
// events haven't been processed yet.
|
||||
if pingBefore != nil && pingAfter != nil {
|
||||
pingBefore <- struct{}{}
|
||||
}
|
||||
ev, err := Dequeue(xu)
|
||||
|
||||
// If we gobbled up an error, send it to the error event handler
|
||||
// and move on the next event/error.
|
||||
if err != nil {
|
||||
ErrorHandlerGet(xu)(err)
|
||||
if pingBefore != nil && pingAfter != nil {
|
||||
pingAfter <- struct{}{}
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// We know there isn't an error. If there isn't an event either,
|
||||
// then there's a bug somewhere.
|
||||
if ev == nil {
|
||||
xgbutil.Logger.Fatal("BUG: Expected an event but got nil.")
|
||||
}
|
||||
|
||||
hooks := getHooks(xu)
|
||||
for _, hook := range hooks {
|
||||
if !hook.Run(xu, ev) {
|
||||
goto END
|
||||
}
|
||||
}
|
||||
|
||||
switch event := ev.(type) {
|
||||
case xproto.KeyPressEvent:
|
||||
e := KeyPressEvent{&event}
|
||||
|
||||
// If we're redirecting key events, this is the place to do it!
|
||||
if wid := RedirectKeyGet(xu); wid > 0 {
|
||||
e.Event = wid
|
||||
}
|
||||
|
||||
xu.TimeSet(e.Time)
|
||||
runCallbacks(xu, e, KeyPress, e.Event)
|
||||
case xproto.KeyReleaseEvent:
|
||||
e := KeyReleaseEvent{&event}
|
||||
|
||||
// If we're redirecting key events, this is the place to do it!
|
||||
if wid := RedirectKeyGet(xu); wid > 0 {
|
||||
e.Event = wid
|
||||
}
|
||||
|
||||
xu.TimeSet(e.Time)
|
||||
runCallbacks(xu, e, KeyRelease, e.Event)
|
||||
case xproto.ButtonPressEvent:
|
||||
e := ButtonPressEvent{&event}
|
||||
xu.TimeSet(e.Time)
|
||||
runCallbacks(xu, e, ButtonPress, e.Event)
|
||||
case xproto.ButtonReleaseEvent:
|
||||
e := ButtonReleaseEvent{&event}
|
||||
xu.TimeSet(e.Time)
|
||||
runCallbacks(xu, e, ButtonRelease, e.Event)
|
||||
case xproto.MotionNotifyEvent:
|
||||
e := MotionNotifyEvent{&event}
|
||||
xu.TimeSet(e.Time)
|
||||
runCallbacks(xu, e, MotionNotify, e.Event)
|
||||
case xproto.EnterNotifyEvent:
|
||||
e := EnterNotifyEvent{&event}
|
||||
xu.TimeSet(e.Time)
|
||||
runCallbacks(xu, e, EnterNotify, e.Event)
|
||||
case xproto.LeaveNotifyEvent:
|
||||
e := LeaveNotifyEvent{&event}
|
||||
xu.TimeSet(e.Time)
|
||||
runCallbacks(xu, e, LeaveNotify, e.Event)
|
||||
case xproto.FocusInEvent:
|
||||
e := FocusInEvent{&event}
|
||||
runCallbacks(xu, e, FocusIn, e.Event)
|
||||
case xproto.FocusOutEvent:
|
||||
e := FocusOutEvent{&event}
|
||||
runCallbacks(xu, e, FocusOut, e.Event)
|
||||
case xproto.KeymapNotifyEvent:
|
||||
e := KeymapNotifyEvent{&event}
|
||||
runCallbacks(xu, e, KeymapNotify, NoWindow)
|
||||
case xproto.ExposeEvent:
|
||||
e := ExposeEvent{&event}
|
||||
runCallbacks(xu, e, Expose, e.Window)
|
||||
case xproto.GraphicsExposureEvent:
|
||||
e := GraphicsExposureEvent{&event}
|
||||
runCallbacks(xu, e, GraphicsExposure, xproto.Window(e.Drawable))
|
||||
case xproto.NoExposureEvent:
|
||||
e := NoExposureEvent{&event}
|
||||
runCallbacks(xu, e, NoExposure, xproto.Window(e.Drawable))
|
||||
case xproto.VisibilityNotifyEvent:
|
||||
e := VisibilityNotifyEvent{&event}
|
||||
runCallbacks(xu, e, VisibilityNotify, e.Window)
|
||||
case xproto.CreateNotifyEvent:
|
||||
e := CreateNotifyEvent{&event}
|
||||
runCallbacks(xu, e, CreateNotify, e.Parent)
|
||||
case xproto.DestroyNotifyEvent:
|
||||
e := DestroyNotifyEvent{&event}
|
||||
runCallbacks(xu, e, DestroyNotify, e.Window)
|
||||
case xproto.UnmapNotifyEvent:
|
||||
e := UnmapNotifyEvent{&event}
|
||||
runCallbacks(xu, e, UnmapNotify, e.Window)
|
||||
case xproto.MapNotifyEvent:
|
||||
e := MapNotifyEvent{&event}
|
||||
runCallbacks(xu, e, MapNotify, e.Event)
|
||||
case xproto.MapRequestEvent:
|
||||
e := MapRequestEvent{&event}
|
||||
runCallbacks(xu, e, MapRequest, e.Window)
|
||||
runCallbacks(xu, e, MapRequest, e.Parent)
|
||||
case xproto.ReparentNotifyEvent:
|
||||
e := ReparentNotifyEvent{&event}
|
||||
runCallbacks(xu, e, ReparentNotify, e.Window)
|
||||
case xproto.ConfigureNotifyEvent:
|
||||
e := ConfigureNotifyEvent{&event}
|
||||
runCallbacks(xu, e, ConfigureNotify, e.Window)
|
||||
case xproto.ConfigureRequestEvent:
|
||||
e := ConfigureRequestEvent{&event}
|
||||
runCallbacks(xu, e, ConfigureRequest, e.Window)
|
||||
runCallbacks(xu, e, ConfigureRequest, e.Parent)
|
||||
case xproto.GravityNotifyEvent:
|
||||
e := GravityNotifyEvent{&event}
|
||||
runCallbacks(xu, e, GravityNotify, e.Window)
|
||||
case xproto.ResizeRequestEvent:
|
||||
e := ResizeRequestEvent{&event}
|
||||
runCallbacks(xu, e, ResizeRequest, e.Window)
|
||||
case xproto.CirculateNotifyEvent:
|
||||
e := CirculateNotifyEvent{&event}
|
||||
runCallbacks(xu, e, CirculateNotify, e.Window)
|
||||
case xproto.CirculateRequestEvent:
|
||||
e := CirculateRequestEvent{&event}
|
||||
runCallbacks(xu, e, CirculateRequest, e.Window)
|
||||
case xproto.PropertyNotifyEvent:
|
||||
e := PropertyNotifyEvent{&event}
|
||||
xu.TimeSet(e.Time)
|
||||
runCallbacks(xu, e, PropertyNotify, e.Window)
|
||||
case xproto.SelectionClearEvent:
|
||||
e := SelectionClearEvent{&event}
|
||||
xu.TimeSet(e.Time)
|
||||
runCallbacks(xu, e, SelectionClear, e.Owner)
|
||||
case xproto.SelectionRequestEvent:
|
||||
e := SelectionRequestEvent{&event}
|
||||
xu.TimeSet(e.Time)
|
||||
runCallbacks(xu, e, SelectionRequest, e.Requestor)
|
||||
case xproto.SelectionNotifyEvent:
|
||||
e := SelectionNotifyEvent{&event}
|
||||
xu.TimeSet(e.Time)
|
||||
runCallbacks(xu, e, SelectionNotify, e.Requestor)
|
||||
case xproto.ColormapNotifyEvent:
|
||||
e := ColormapNotifyEvent{&event}
|
||||
runCallbacks(xu, e, ColormapNotify, e.Window)
|
||||
case xproto.ClientMessageEvent:
|
||||
e := ClientMessageEvent{&event}
|
||||
runCallbacks(xu, e, ClientMessage, e.Window)
|
||||
case xproto.MappingNotifyEvent:
|
||||
e := MappingNotifyEvent{&event}
|
||||
runCallbacks(xu, e, MappingNotify, NoWindow)
|
||||
case shape.NotifyEvent:
|
||||
e := ShapeNotifyEvent{&event}
|
||||
runCallbacks(xu, e, ShapeNotify, e.AffectedWindow)
|
||||
default:
|
||||
if event != nil {
|
||||
xgbutil.Logger.Printf("ERROR: UNSUPPORTED EVENT TYPE: %T",
|
||||
event)
|
||||
}
|
||||
}
|
||||
|
||||
END:
|
||||
|
||||
if pingBefore != nil && pingAfter != nil {
|
||||
pingAfter <- struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,336 +0,0 @@
|
||||
package xevent
|
||||
|
||||
/*
|
||||
Defines event types and their associated methods automatically.
|
||||
|
||||
This file is automatically generated using `scripts/write-events evtypes`.
|
||||
|
||||
Edit it at your peril.
|
||||
*/
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/BurntSushi/xgb/shape"
|
||||
"github.com/BurntSushi/xgb/xproto"
|
||||
)
|
||||
|
||||
type KeyPressEvent struct {
|
||||
*xproto.KeyPressEvent
|
||||
}
|
||||
|
||||
const KeyPress = xproto.KeyPress
|
||||
|
||||
func (ev KeyPressEvent) String() string {
|
||||
return fmt.Sprintf("%v", ev.KeyPressEvent)
|
||||
}
|
||||
|
||||
type KeyReleaseEvent struct {
|
||||
*xproto.KeyReleaseEvent
|
||||
}
|
||||
|
||||
const KeyRelease = xproto.KeyRelease
|
||||
|
||||
func (ev KeyReleaseEvent) String() string {
|
||||
return fmt.Sprintf("%v", ev.KeyReleaseEvent)
|
||||
}
|
||||
|
||||
type ButtonPressEvent struct {
|
||||
*xproto.ButtonPressEvent
|
||||
}
|
||||
|
||||
const ButtonPress = xproto.ButtonPress
|
||||
|
||||
func (ev ButtonPressEvent) String() string {
|
||||
return fmt.Sprintf("%v", ev.ButtonPressEvent)
|
||||
}
|
||||
|
||||
type ButtonReleaseEvent struct {
|
||||
*xproto.ButtonReleaseEvent
|
||||
}
|
||||
|
||||
const ButtonRelease = xproto.ButtonRelease
|
||||
|
||||
func (ev ButtonReleaseEvent) String() string {
|
||||
return fmt.Sprintf("%v", ev.ButtonReleaseEvent)
|
||||
}
|
||||
|
||||
type MotionNotifyEvent struct {
|
||||
*xproto.MotionNotifyEvent
|
||||
}
|
||||
|
||||
const MotionNotify = xproto.MotionNotify
|
||||
|
||||
func (ev MotionNotifyEvent) String() string {
|
||||
return fmt.Sprintf("%v", ev.MotionNotifyEvent)
|
||||
}
|
||||
|
||||
type EnterNotifyEvent struct {
|
||||
*xproto.EnterNotifyEvent
|
||||
}
|
||||
|
||||
const EnterNotify = xproto.EnterNotify
|
||||
|
||||
func (ev EnterNotifyEvent) String() string {
|
||||
return fmt.Sprintf("%v", ev.EnterNotifyEvent)
|
||||
}
|
||||
|
||||
type LeaveNotifyEvent struct {
|
||||
*xproto.LeaveNotifyEvent
|
||||
}
|
||||
|
||||
const LeaveNotify = xproto.LeaveNotify
|
||||
|
||||
func (ev LeaveNotifyEvent) String() string {
|
||||
return fmt.Sprintf("%v", ev.LeaveNotifyEvent)
|
||||
}
|
||||
|
||||
type FocusInEvent struct {
|
||||
*xproto.FocusInEvent
|
||||
}
|
||||
|
||||
const FocusIn = xproto.FocusIn
|
||||
|
||||
func (ev FocusInEvent) String() string {
|
||||
return fmt.Sprintf("%v", ev.FocusInEvent)
|
||||
}
|
||||
|
||||
type FocusOutEvent struct {
|
||||
*xproto.FocusOutEvent
|
||||
}
|
||||
|
||||
const FocusOut = xproto.FocusOut
|
||||
|
||||
func (ev FocusOutEvent) String() string {
|
||||
return fmt.Sprintf("%v", ev.FocusOutEvent)
|
||||
}
|
||||
|
||||
type KeymapNotifyEvent struct {
|
||||
*xproto.KeymapNotifyEvent
|
||||
}
|
||||
|
||||
const KeymapNotify = xproto.KeymapNotify
|
||||
|
||||
func (ev KeymapNotifyEvent) String() string {
|
||||
return fmt.Sprintf("%v", ev.KeymapNotifyEvent)
|
||||
}
|
||||
|
||||
type ExposeEvent struct {
|
||||
*xproto.ExposeEvent
|
||||
}
|
||||
|
||||
const Expose = xproto.Expose
|
||||
|
||||
func (ev ExposeEvent) String() string {
|
||||
return fmt.Sprintf("%v", ev.ExposeEvent)
|
||||
}
|
||||
|
||||
type GraphicsExposureEvent struct {
|
||||
*xproto.GraphicsExposureEvent
|
||||
}
|
||||
|
||||
const GraphicsExposure = xproto.GraphicsExposure
|
||||
|
||||
func (ev GraphicsExposureEvent) String() string {
|
||||
return fmt.Sprintf("%v", ev.GraphicsExposureEvent)
|
||||
}
|
||||
|
||||
type NoExposureEvent struct {
|
||||
*xproto.NoExposureEvent
|
||||
}
|
||||
|
||||
const NoExposure = xproto.NoExposure
|
||||
|
||||
func (ev NoExposureEvent) String() string {
|
||||
return fmt.Sprintf("%v", ev.NoExposureEvent)
|
||||
}
|
||||
|
||||
type VisibilityNotifyEvent struct {
|
||||
*xproto.VisibilityNotifyEvent
|
||||
}
|
||||
|
||||
const VisibilityNotify = xproto.VisibilityNotify
|
||||
|
||||
func (ev VisibilityNotifyEvent) String() string {
|
||||
return fmt.Sprintf("%v", ev.VisibilityNotifyEvent)
|
||||
}
|
||||
|
||||
type CreateNotifyEvent struct {
|
||||
*xproto.CreateNotifyEvent
|
||||
}
|
||||
|
||||
const CreateNotify = xproto.CreateNotify
|
||||
|
||||
func (ev CreateNotifyEvent) String() string {
|
||||
return fmt.Sprintf("%v", ev.CreateNotifyEvent)
|
||||
}
|
||||
|
||||
type DestroyNotifyEvent struct {
|
||||
*xproto.DestroyNotifyEvent
|
||||
}
|
||||
|
||||
const DestroyNotify = xproto.DestroyNotify
|
||||
|
||||
func (ev DestroyNotifyEvent) String() string {
|
||||
return fmt.Sprintf("%v", ev.DestroyNotifyEvent)
|
||||
}
|
||||
|
||||
type UnmapNotifyEvent struct {
|
||||
*xproto.UnmapNotifyEvent
|
||||
}
|
||||
|
||||
const UnmapNotify = xproto.UnmapNotify
|
||||
|
||||
func (ev UnmapNotifyEvent) String() string {
|
||||
return fmt.Sprintf("%v", ev.UnmapNotifyEvent)
|
||||
}
|
||||
|
||||
type MapNotifyEvent struct {
|
||||
*xproto.MapNotifyEvent
|
||||
}
|
||||
|
||||
const MapNotify = xproto.MapNotify
|
||||
|
||||
func (ev MapNotifyEvent) String() string {
|
||||
return fmt.Sprintf("%v", ev.MapNotifyEvent)
|
||||
}
|
||||
|
||||
type MapRequestEvent struct {
|
||||
*xproto.MapRequestEvent
|
||||
}
|
||||
|
||||
const MapRequest = xproto.MapRequest
|
||||
|
||||
func (ev MapRequestEvent) String() string {
|
||||
return fmt.Sprintf("%v", ev.MapRequestEvent)
|
||||
}
|
||||
|
||||
type ReparentNotifyEvent struct {
|
||||
*xproto.ReparentNotifyEvent
|
||||
}
|
||||
|
||||
const ReparentNotify = xproto.ReparentNotify
|
||||
|
||||
func (ev ReparentNotifyEvent) String() string {
|
||||
return fmt.Sprintf("%v", ev.ReparentNotifyEvent)
|
||||
}
|
||||
|
||||
type ConfigureRequestEvent struct {
|
||||
*xproto.ConfigureRequestEvent
|
||||
}
|
||||
|
||||
const ConfigureRequest = xproto.ConfigureRequest
|
||||
|
||||
func (ev ConfigureRequestEvent) String() string {
|
||||
return fmt.Sprintf("%v", ev.ConfigureRequestEvent)
|
||||
}
|
||||
|
||||
type GravityNotifyEvent struct {
|
||||
*xproto.GravityNotifyEvent
|
||||
}
|
||||
|
||||
const GravityNotify = xproto.GravityNotify
|
||||
|
||||
func (ev GravityNotifyEvent) String() string {
|
||||
return fmt.Sprintf("%v", ev.GravityNotifyEvent)
|
||||
}
|
||||
|
||||
type ResizeRequestEvent struct {
|
||||
*xproto.ResizeRequestEvent
|
||||
}
|
||||
|
||||
const ResizeRequest = xproto.ResizeRequest
|
||||
|
||||
func (ev ResizeRequestEvent) String() string {
|
||||
return fmt.Sprintf("%v", ev.ResizeRequestEvent)
|
||||
}
|
||||
|
||||
type CirculateNotifyEvent struct {
|
||||
*xproto.CirculateNotifyEvent
|
||||
}
|
||||
|
||||
const CirculateNotify = xproto.CirculateNotify
|
||||
|
||||
func (ev CirculateNotifyEvent) String() string {
|
||||
return fmt.Sprintf("%v", ev.CirculateNotifyEvent)
|
||||
}
|
||||
|
||||
type CirculateRequestEvent struct {
|
||||
*xproto.CirculateRequestEvent
|
||||
}
|
||||
|
||||
const CirculateRequest = xproto.CirculateRequest
|
||||
|
||||
func (ev CirculateRequestEvent) String() string {
|
||||
return fmt.Sprintf("%v", ev.CirculateRequestEvent)
|
||||
}
|
||||
|
||||
type PropertyNotifyEvent struct {
|
||||
*xproto.PropertyNotifyEvent
|
||||
}
|
||||
|
||||
const PropertyNotify = xproto.PropertyNotify
|
||||
|
||||
func (ev PropertyNotifyEvent) String() string {
|
||||
return fmt.Sprintf("%v", ev.PropertyNotifyEvent)
|
||||
}
|
||||
|
||||
type SelectionClearEvent struct {
|
||||
*xproto.SelectionClearEvent
|
||||
}
|
||||
|
||||
const SelectionClear = xproto.SelectionClear
|
||||
|
||||
func (ev SelectionClearEvent) String() string {
|
||||
return fmt.Sprintf("%v", ev.SelectionClearEvent)
|
||||
}
|
||||
|
||||
type SelectionRequestEvent struct {
|
||||
*xproto.SelectionRequestEvent
|
||||
}
|
||||
|
||||
const SelectionRequest = xproto.SelectionRequest
|
||||
|
||||
func (ev SelectionRequestEvent) String() string {
|
||||
return fmt.Sprintf("%v", ev.SelectionRequestEvent)
|
||||
}
|
||||
|
||||
type SelectionNotifyEvent struct {
|
||||
*xproto.SelectionNotifyEvent
|
||||
}
|
||||
|
||||
const SelectionNotify = xproto.SelectionNotify
|
||||
|
||||
func (ev SelectionNotifyEvent) String() string {
|
||||
return fmt.Sprintf("%v", ev.SelectionNotifyEvent)
|
||||
}
|
||||
|
||||
type ColormapNotifyEvent struct {
|
||||
*xproto.ColormapNotifyEvent
|
||||
}
|
||||
|
||||
const ColormapNotify = xproto.ColormapNotify
|
||||
|
||||
func (ev ColormapNotifyEvent) String() string {
|
||||
return fmt.Sprintf("%v", ev.ColormapNotifyEvent)
|
||||
}
|
||||
|
||||
type MappingNotifyEvent struct {
|
||||
*xproto.MappingNotifyEvent
|
||||
}
|
||||
|
||||
const MappingNotify = xproto.MappingNotify
|
||||
|
||||
func (ev MappingNotifyEvent) String() string {
|
||||
return fmt.Sprintf("%v", ev.MappingNotifyEvent)
|
||||
}
|
||||
|
||||
type ShapeNotifyEvent struct {
|
||||
*shape.NotifyEvent
|
||||
}
|
||||
|
||||
const ShapeNotify = shape.Notify
|
||||
|
||||
func (ev ShapeNotifyEvent) String() string {
|
||||
return fmt.Sprintf("%v", ev.NotifyEvent)
|
||||
}
|
@ -1,90 +0,0 @@
|
||||
package xevent
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/BurntSushi/xgb/xproto"
|
||||
)
|
||||
|
||||
// ClientMessageEvent embeds the struct by the same name from the xgb library.
|
||||
type ClientMessageEvent struct {
|
||||
*xproto.ClientMessageEvent
|
||||
}
|
||||
|
||||
const ClientMessage = xproto.ClientMessage
|
||||
|
||||
// NewClientMessage takes all arguments required to build a ClientMessageEvent
|
||||
// struct and hides the messy details.
|
||||
// The variadic parameters coincide with the "data" part of a client message.
|
||||
// The type of the variadic parameters depends upon the value of Format.
|
||||
// If Format is 8, 'data' should have type byte.
|
||||
// If Format is 16, 'data' should have type int16.
|
||||
// If Format is 32, 'data' should have type int.
|
||||
// Any other value of Format returns an error.
|
||||
func NewClientMessage(Format byte, Window xproto.Window, Type xproto.Atom,
|
||||
data ...interface{}) (*ClientMessageEvent, error) {
|
||||
|
||||
// Create the client data list first
|
||||
var clientData xproto.ClientMessageDataUnion
|
||||
|
||||
// Don't support formats 8 or 16 yet. They aren't used in EWMH anyway.
|
||||
switch Format {
|
||||
case 8:
|
||||
buf := make([]byte, 20)
|
||||
for i := 0; i < 20; i++ {
|
||||
if i >= len(data) {
|
||||
break
|
||||
}
|
||||
buf[i] = data[i].(byte)
|
||||
}
|
||||
clientData = xproto.ClientMessageDataUnionData8New(buf)
|
||||
case 16:
|
||||
buf := make([]uint16, 10)
|
||||
for i := 0; i < 10; i++ {
|
||||
if i >= len(data) {
|
||||
break
|
||||
}
|
||||
buf[i] = uint16(data[i].(int16))
|
||||
}
|
||||
clientData = xproto.ClientMessageDataUnionData16New(buf)
|
||||
case 32:
|
||||
buf := make([]uint32, 5)
|
||||
for i := 0; i < 5; i++ {
|
||||
if i >= len(data) {
|
||||
break
|
||||
}
|
||||
buf[i] = uint32(data[i].(int))
|
||||
}
|
||||
clientData = xproto.ClientMessageDataUnionData32New(buf)
|
||||
default:
|
||||
return nil, fmt.Errorf("NewClientMessage: Unsupported format '%d'.",
|
||||
Format)
|
||||
}
|
||||
|
||||
return &ClientMessageEvent{&xproto.ClientMessageEvent{
|
||||
Format: Format,
|
||||
Window: Window,
|
||||
Type: Type,
|
||||
Data: clientData,
|
||||
}}, nil
|
||||
}
|
||||
|
||||
// ConfigureNotifyEvent embeds the struct by the same name in XGB.
|
||||
type ConfigureNotifyEvent struct {
|
||||
*xproto.ConfigureNotifyEvent
|
||||
}
|
||||
|
||||
const ConfigureNotify = xproto.ConfigureNotify
|
||||
|
||||
// NewConfigureNotify takes all arguments required to build a
|
||||
// ConfigureNotifyEvent struct and returns a ConfigureNotifyEvent value.
|
||||
func NewConfigureNotify(Event, Window, AboveSibling xproto.Window,
|
||||
X, Y, Width, Height int, BorderWidth uint16,
|
||||
OverrideRedirect bool) *ConfigureNotifyEvent {
|
||||
|
||||
return &ConfigureNotifyEvent{&xproto.ConfigureNotifyEvent{
|
||||
Event: Event, Window: Window, AboveSibling: AboveSibling,
|
||||
X: int16(X), Y: int16(Y), Width: uint16(Width), Height: uint16(Height),
|
||||
BorderWidth: BorderWidth, OverrideRedirect: OverrideRedirect,
|
||||
}}
|
||||
}
|
@ -1,237 +0,0 @@
|
||||
package xevent
|
||||
|
||||
import (
|
||||
"github.com/BurntSushi/xgb"
|
||||
"github.com/BurntSushi/xgb/xproto"
|
||||
|
||||
"github.com/BurntSushi/xgbutil"
|
||||
)
|
||||
|
||||
// Sometimes we need to specify NO WINDOW when a window is typically
|
||||
// expected. (Like connecting to MappingNotify or KeymapNotify events.)
|
||||
// Use this value to do that.
|
||||
var NoWindow xproto.Window = 0
|
||||
|
||||
// IgnoreMods is a list of X modifiers that we don't want interfering
|
||||
// with our mouse or key bindings. In particular, for each mouse or key binding
|
||||
// issued, there is a seperate mouse or key binding made for each of the
|
||||
// modifiers specified.
|
||||
//
|
||||
// You may modify this slice to add (or remove) modifiers, but it should be
|
||||
// done before *any* key or mouse bindings are attached with the keybind and
|
||||
// mousebind packages. It should not be modified afterwards.
|
||||
//
|
||||
// TODO: We're assuming numlock is in the 'mod2' modifier, which is a pretty
|
||||
// common setup, but by no means guaranteed. This should be modified to actually
|
||||
// inspect the modifiers table and look for the special Num_Lock keysym.
|
||||
var IgnoreMods []uint16 = []uint16{
|
||||
0,
|
||||
xproto.ModMaskLock, // Caps lock
|
||||
xproto.ModMask2, // Num lock
|
||||
xproto.ModMaskLock | xproto.ModMask2, // Caps and Num lock
|
||||
}
|
||||
|
||||
// Enqueue queues up an event read from X.
|
||||
// Note that an event read may return an error, in which case, this queue
|
||||
// entry will be an error and not an event.
|
||||
//
|
||||
// ev, err := XUtilValue.Conn().WaitForEvent()
|
||||
// xevent.Enqueue(XUtilValue, ev, err)
|
||||
//
|
||||
// You probably shouldn't have to enqueue events yourself. This is done
|
||||
// automatically if you're using xevent.Main{Ping} and/or xevent.Read.
|
||||
func Enqueue(xu *xgbutil.XUtil, ev xgb.Event, err xgb.Error) {
|
||||
xu.EvqueueLck.Lock()
|
||||
defer xu.EvqueueLck.Unlock()
|
||||
|
||||
xu.Evqueue = append(xu.Evqueue, xgbutil.EventOrError{
|
||||
Event: ev,
|
||||
Err: err,
|
||||
})
|
||||
}
|
||||
|
||||
// Dequeue pops an event/error from the queue and returns it.
|
||||
// The queue item is unwrapped and returned as multiple return values.
|
||||
// Only one of the return values can be nil.
|
||||
func Dequeue(xu *xgbutil.XUtil) (xgb.Event, xgb.Error) {
|
||||
xu.EvqueueLck.Lock()
|
||||
defer xu.EvqueueLck.Unlock()
|
||||
|
||||
everr := xu.Evqueue[0]
|
||||
xu.Evqueue = xu.Evqueue[1:]
|
||||
return everr.Event, everr.Err
|
||||
}
|
||||
|
||||
// DequeueAt removes a particular item from the queue.
|
||||
// This is primarily useful when attempting to compress events.
|
||||
func DequeueAt(xu *xgbutil.XUtil, i int) {
|
||||
xu.EvqueueLck.Lock()
|
||||
defer xu.EvqueueLck.Unlock()
|
||||
|
||||
xu.Evqueue = append(xu.Evqueue[:i], xu.Evqueue[i+1:]...)
|
||||
}
|
||||
|
||||
// Empty returns whether the event queue is empty or not.
|
||||
func Empty(xu *xgbutil.XUtil) bool {
|
||||
xu.EvqueueLck.RLock()
|
||||
defer xu.EvqueueLck.RUnlock()
|
||||
|
||||
return len(xu.Evqueue) == 0
|
||||
}
|
||||
|
||||
// Peek returns a *copy* of the current queue so we can examine it.
|
||||
// This can be useful when trying to determine if a particular kind of
|
||||
// event will be processed in the future.
|
||||
func Peek(xu *xgbutil.XUtil) []xgbutil.EventOrError {
|
||||
xu.EvqueueLck.RLock()
|
||||
defer xu.EvqueueLck.RUnlock()
|
||||
|
||||
cpy := make([]xgbutil.EventOrError, len(xu.Evqueue))
|
||||
copy(cpy, xu.Evqueue)
|
||||
return cpy
|
||||
}
|
||||
|
||||
// ErrorHandlerSet sets the default error handler for errors that come
|
||||
// into the main event loop. (This may be removed in the future in favor
|
||||
// of a particular callback interface like events, but these sorts of errors
|
||||
// aren't handled often in practice, so maybe not.)
|
||||
// This is only called for errors returned from unchecked (asynchronous error
|
||||
// handling) requests.
|
||||
// The default error handler just emits them to stderr.
|
||||
func ErrorHandlerSet(xu *xgbutil.XUtil, fun xgbutil.ErrorHandlerFun) {
|
||||
xu.ErrorHandler = fun
|
||||
}
|
||||
|
||||
// ErrorHandlerGet retrieves the default error handler.
|
||||
func ErrorHandlerGet(xu *xgbutil.XUtil) xgbutil.ErrorHandlerFun {
|
||||
return xu.ErrorHandler
|
||||
}
|
||||
|
||||
type HookFun func(xu *xgbutil.XUtil, event interface{}) bool
|
||||
|
||||
func (callback HookFun) Connect(xu *xgbutil.XUtil) {
|
||||
xu.HooksLck.Lock()
|
||||
defer xu.HooksLck.Unlock()
|
||||
|
||||
// COW
|
||||
newHooks := make([]xgbutil.CallbackHook, len(xu.Hooks))
|
||||
copy(newHooks, xu.Hooks)
|
||||
newHooks = append(newHooks, callback)
|
||||
|
||||
xu.Hooks = newHooks
|
||||
}
|
||||
|
||||
func (callback HookFun) Run(xu *xgbutil.XUtil, event interface{}) bool {
|
||||
return callback(xu, event)
|
||||
}
|
||||
|
||||
func getHooks(xu *xgbutil.XUtil) []xgbutil.CallbackHook {
|
||||
xu.HooksLck.RLock()
|
||||
defer xu.HooksLck.RUnlock()
|
||||
|
||||
return xu.Hooks
|
||||
}
|
||||
|
||||
// RedirectKeyEvents, when set to a window id (greater than 0), will force
|
||||
// *all* Key{Press,Release} to callbacks attached to the specified window.
|
||||
// This is close to emulating a Keyboard grab without the racing.
|
||||
// To stop redirecting key events, use window identifier '0'.
|
||||
func RedirectKeyEvents(xu *xgbutil.XUtil, wid xproto.Window) {
|
||||
xu.KeyRedirect = wid
|
||||
}
|
||||
|
||||
// RedirectKeyGet gets the window that key events are being redirected to.
|
||||
// If 0, then no redirection occurs.
|
||||
func RedirectKeyGet(xu *xgbutil.XUtil) xproto.Window {
|
||||
return xu.KeyRedirect
|
||||
}
|
||||
|
||||
// Quit elegantly exits out of the main event loop.
|
||||
// "Elegantly" in this case means that it finishes processing the current
|
||||
// event, and breaks out of the loop afterwards.
|
||||
// There is no particular reason to use this instead of something like os.Exit
|
||||
// other than you might have code to run after the main event loop exits to
|
||||
// "clean up."
|
||||
func Quit(xu *xgbutil.XUtil) {
|
||||
xu.Quit = true
|
||||
}
|
||||
|
||||
// Quitting returns whether it's time to quit.
|
||||
// This is only used in the main event loop in xevent.
|
||||
func Quitting(xu *xgbutil.XUtil) bool {
|
||||
return xu.Quit
|
||||
}
|
||||
|
||||
// attachCallback associates a (event, window) tuple with an event.
|
||||
// Use copy on write since we run callbacks *a lot* more than attaching them.
|
||||
// (The copy on write only applies to the slice of callbacks rather than
|
||||
// the map itself, since the initial allocation is guaranteed to come before
|
||||
// any use of it.)
|
||||
func attachCallback(xu *xgbutil.XUtil, evtype int, win xproto.Window,
|
||||
fun xgbutil.Callback) {
|
||||
|
||||
xu.CallbacksLck.Lock()
|
||||
defer xu.CallbacksLck.Unlock()
|
||||
|
||||
if _, ok := xu.Callbacks[evtype]; !ok {
|
||||
xu.Callbacks[evtype] = make(map[xproto.Window][]xgbutil.Callback, 20)
|
||||
}
|
||||
if _, ok := xu.Callbacks[evtype][win]; !ok {
|
||||
xu.Callbacks[evtype][win] = make([]xgbutil.Callback, 0)
|
||||
}
|
||||
|
||||
// COW
|
||||
newCallbacks := make([]xgbutil.Callback, len(xu.Callbacks[evtype][win]))
|
||||
copy(newCallbacks, xu.Callbacks[evtype][win])
|
||||
newCallbacks = append(newCallbacks, fun)
|
||||
xu.Callbacks[evtype][win] = newCallbacks
|
||||
}
|
||||
|
||||
// runCallbacks executes every callback corresponding to a
|
||||
// particular event/window tuple.
|
||||
func runCallbacks(xu *xgbutil.XUtil, event interface{}, evtype int,
|
||||
win xproto.Window) {
|
||||
|
||||
// The callback slice for a particular (event type, window) tuple uses
|
||||
// copy on write. So just take a pointer to whatever is there and use that.
|
||||
// We can be sure that the slice won't change from underneathe us.
|
||||
xu.CallbacksLck.RLock()
|
||||
cbs := xu.Callbacks[evtype][win]
|
||||
xu.CallbacksLck.RUnlock()
|
||||
|
||||
for _, cb := range cbs {
|
||||
cb.Run(xu, event)
|
||||
}
|
||||
}
|
||||
|
||||
// Detach removes all callbacks associated with a particular window.
|
||||
// Note that if you're also using the keybind and mousebind packages, a complete
|
||||
// detachment should look like:
|
||||
//
|
||||
// keybind.Detach(XUtilValue, window-id)
|
||||
// mousebind.Detach(XUtilValue, window-id)
|
||||
// xevent.Detach(XUtilValue, window-id)
|
||||
//
|
||||
// If a window is no longer receiving events, these methods should be called.
|
||||
// Otherwise, the memory used to store the handler info for that window will
|
||||
// never be released.
|
||||
func Detach(xu *xgbutil.XUtil, win xproto.Window) {
|
||||
xu.CallbacksLck.Lock()
|
||||
defer xu.CallbacksLck.Unlock()
|
||||
|
||||
for evtype, _ := range xu.Callbacks {
|
||||
delete(xu.Callbacks[evtype], win)
|
||||
}
|
||||
}
|
||||
|
||||
// SendRootEvent takes a type implementing the xgb.Event interface, converts it
|
||||
// to raw X bytes, and sends it to the root window using the SendEvent request.
|
||||
func SendRootEvent(xu *xgbutil.XUtil, ev xgb.Event, evMask uint32) error {
|
||||
return xproto.SendEventChecked(xu.Conn(), false, xu.RootWin(), evMask,
|
||||
string(ev.Bytes())).Check()
|
||||
}
|
||||
|
||||
// ReplayPointer is a quick alias to AllowEvents with 'ReplayPointer' mode.
|
||||
func ReplayPointer(xu *xgbutil.XUtil) {
|
||||
xproto.AllowEvents(xu.Conn(), xproto.AllowReplayPointer, 0)
|
||||
}
|
@ -1,348 +0,0 @@
|
||||
package xgbutil
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
"github.com/BurntSushi/xgb"
|
||||
"github.com/BurntSushi/xgb/xinerama"
|
||||
"github.com/BurntSushi/xgb/xproto"
|
||||
)
|
||||
|
||||
// Logger is used through xgbutil when messages need to be emitted to stderr.
|
||||
var Logger = log.New(os.Stderr, "[xgbutil] ", log.Lshortfile)
|
||||
|
||||
// The current maximum request size. I think we can expand this with
|
||||
// BigReq, but it probably isn't worth it at the moment.
|
||||
const MaxReqSize = (1 << 16) * 4
|
||||
|
||||
// An XUtil represents the state of xgbutil. It keeps track of the current
|
||||
// X connection, the root window, event callbacks, key/mouse bindings, etc.
|
||||
// Regrettably, many of the members are exported, even though they should not
|
||||
// be used directly by the user. They are exported for use in sub-packages.
|
||||
// (Namely, xevent, keybind and mousebind.) In fact, there should *never*
|
||||
// be a reason to access any members of an XUtil value directly. Any
|
||||
// interaction with an XUtil value should be through its methods.
|
||||
type XUtil struct {
|
||||
// conn is the XGB connection object used to issue protocol requests.
|
||||
conn *xgb.Conn
|
||||
|
||||
// Quit can be set to true, and the main event loop will finish processing
|
||||
// the current event, and gracefully quit afterwards.
|
||||
// This is exported for use in the xevent package. Please us xevent.Quit
|
||||
// to set this value.
|
||||
Quit bool // when true, the main event loop will stop gracefully
|
||||
|
||||
// setup contains all the setup information retrieved at connection time.
|
||||
setup *xproto.SetupInfo
|
||||
|
||||
// screen is a simple alias to the default screen info.
|
||||
screen *xproto.ScreenInfo
|
||||
|
||||
// root is an alias to the default root window.
|
||||
root xproto.Window
|
||||
|
||||
// Atoms is a cache of atom names to resource identifiers. This minimizes
|
||||
// round trips to the X server, since atom identifiers never change.
|
||||
// It is exported for use in the xprop package. It should not be used.
|
||||
Atoms map[string]xproto.Atom
|
||||
AtomsLck *sync.RWMutex
|
||||
|
||||
// AtomNames is a cache just like 'atoms', but in the reverse direction.
|
||||
// It is exported for use in the xprop package. It should not be used.
|
||||
AtomNames map[xproto.Atom]string
|
||||
AtomNamesLck *sync.RWMutex
|
||||
|
||||
// Evqueue is the queue that stores the results of xgb.WaitForEvent.
|
||||
// Namely, each value is either an Event *or* an Error.
|
||||
// It is exported for use in the xevent package. Do not use it.
|
||||
// If you need to interact with the event queue, please use the functions
|
||||
// available in the xevent package: Dequeue, DequeueAt, QueueEmpty
|
||||
// and QueuePeek.
|
||||
Evqueue []EventOrError
|
||||
EvqueueLck *sync.RWMutex
|
||||
|
||||
// Callbacks is a map of event numbers to a map of window identifiers
|
||||
// to callback functions.
|
||||
// This is the data structure that stores all callback functions, where
|
||||
// a callback function is always attached to a (event, window) tuple.
|
||||
// It is exported for use in the xevent package. Do not use it.
|
||||
Callbacks map[int]map[xproto.Window][]Callback
|
||||
CallbacksLck *sync.RWMutex
|
||||
|
||||
// Hooks are called by the XEvent main loop before processing the event
|
||||
// itself. These are meant for instances when it's not possible / easy
|
||||
// to use the normal Hook system. You should not modify this yourself.
|
||||
Hooks []CallbackHook
|
||||
HooksLck *sync.RWMutex
|
||||
|
||||
// eventTime is the last time recorded by an event. It is automatically
|
||||
// updated if xgbutil's main event loop is used.
|
||||
eventTime xproto.Timestamp
|
||||
|
||||
// Keymap corresponds to xgbutil's current conception of the keyboard
|
||||
// mapping. It is automatically kept up-to-date if xgbutil's event loop
|
||||
// is used.
|
||||
// It is exported for use in the keybind package. It should not be
|
||||
// accessed directly. Instead, use keybind.KeyMapGet.
|
||||
Keymap *KeyboardMapping
|
||||
|
||||
// Modmap corresponds to xgbutil's current conception of the modifier key
|
||||
// mapping. It is automatically kept up-to-date if xgbutil's event loop
|
||||
// is used.
|
||||
// It is exported for use in the keybind package. It should not be
|
||||
// accessed directly. Instead, use keybind.ModMapGet.
|
||||
Modmap *ModifierMapping
|
||||
|
||||
// KeyRedirect corresponds to a window identifier that, when set,
|
||||
// automatically receives *all* keyboard events. This is a sort-of
|
||||
// synthetic grab and is helpful in avoiding race conditions.
|
||||
// It is exported for use in the xevent and keybind packages. Do not use
|
||||
// it directly. To redirect key events, please use xevent.RedirectKeyEvents.
|
||||
KeyRedirect xproto.Window
|
||||
|
||||
// Keybinds is the data structure storing all callbacks for key bindings.
|
||||
// This is extremely similar to the general notion of event callbacks,
|
||||
// but adds extra support to make handling key bindings easier. (Like
|
||||
// specifying human readable key sequences to bind to.)
|
||||
// KeyBindKey is a struct representing the 4-tuple
|
||||
// (event-type, window-id, modifiers, keycode).
|
||||
// It is exported for use in the keybind package. Do not access it directly.
|
||||
Keybinds map[KeyKey][]CallbackKey
|
||||
KeybindsLck *sync.RWMutex
|
||||
|
||||
// Keygrabs is a frequency count of the number of callbacks associated
|
||||
// with a particular KeyBindKey. This is necessary because we can only
|
||||
// grab a particular key *once*, but we may want to attach several callbacks
|
||||
// to a single keypress.
|
||||
// It is exported for use in the keybind package. Do not access it directly.
|
||||
Keygrabs map[KeyKey]int
|
||||
|
||||
// Keystrings is a list of all key strings used to connect keybindings.
|
||||
// They are used to rebuild key grabs when the keyboard mapping is updated.
|
||||
// It is exported for use in the keybind package. Do not access it directly.
|
||||
Keystrings []KeyString
|
||||
|
||||
// Mousebinds is the data structure storing all callbacks for mouse
|
||||
// bindings.This is extremely similar to the general notion of event
|
||||
// callbacks,but adds extra support to make handling mouse bindings easier.
|
||||
// (Like specifying human readable mouse sequences to bind to.)
|
||||
// MouseBindKey is a struct representing the 4-tuple
|
||||
// (event-type, window-id, modifiers, button).
|
||||
// It is exported for use in the mousebind package. Do not use it.
|
||||
Mousebinds map[MouseKey][]CallbackMouse
|
||||
MousebindsLck *sync.RWMutex
|
||||
|
||||
// Mousegrabs is a frequency count of the number of callbacks associated
|
||||
// with a particular MouseBindKey. This is necessary because we can only
|
||||
// grab a particular mouse button *once*, but we may want to attach
|
||||
// several callbacks to a single button press.
|
||||
// It is exported for use in the mousebind package. Do not use it.
|
||||
Mousegrabs map[MouseKey]int
|
||||
|
||||
// InMouseDrag is true if a drag is currently in progress.
|
||||
// It is exported for use in the mousebind package. Do not use it.
|
||||
InMouseDrag bool
|
||||
|
||||
// MouseDragStep is the function executed for each step (i.e., pointer
|
||||
// movement) in the current mouse drag. Note that this is nil when a drag
|
||||
// is not in progress.
|
||||
// It is exported for use in the mousebind package. Do not use it.
|
||||
MouseDragStepFun MouseDragFun
|
||||
|
||||
// MouseDragEnd is the function executed at the end of the current
|
||||
// mouse drag. This is nil when a drag is not in progress.
|
||||
// It is exported for use in the mousebind package. Do not use it.
|
||||
MouseDragEndFun MouseDragFun
|
||||
|
||||
// gc is a general purpose graphics context; used to paint images.
|
||||
// Since we don't do any real X drawing, we don't really care about the
|
||||
// particulars of our graphics context.
|
||||
gc xproto.Gcontext
|
||||
|
||||
// dummy is a dummy window used for mouse/key GRABs.
|
||||
// Basically, whenever a grab is instituted, mouse and key events are
|
||||
// redirected to the dummy the window.
|
||||
dummy xproto.Window
|
||||
|
||||
// ErrorHandler is the function that handles errors *in the event loop*.
|
||||
// By default, it simply emits them to stderr.
|
||||
// It is exported for use in the xevent package. To set the default error
|
||||
// handler, please use xevent.ErrorHandlerSet.
|
||||
ErrorHandler ErrorHandlerFun
|
||||
}
|
||||
|
||||
// NewConn connects to the X server using the DISPLAY environment variable
|
||||
// and creates a new XUtil. Most environments have the DISPLAY environment
|
||||
// variable set, so this is probably what you want to use to connect to X.
|
||||
func NewConn() (*XUtil, error) {
|
||||
return NewConnDisplay("")
|
||||
}
|
||||
|
||||
// NewConnDisplay connects to the X server and creates a new XUtil.
|
||||
// If 'display' is empty, the DISPLAY environment variable is used. Otherwise
|
||||
// there are several different display formats supported:
|
||||
//
|
||||
// NewConn(":1") -> net.Dial("unix", "", "/tmp/.X11-unix/X1")
|
||||
// NewConn("/tmp/launch-12/:0") -> net.Dial("unix", "", "/tmp/launch-12/:0")
|
||||
// NewConn("hostname:2.1") -> net.Dial("tcp", "", "hostname:6002")
|
||||
// NewConn("tcp/hostname:1.0") -> net.Dial("tcp", "", "hostname:6001")
|
||||
func NewConnDisplay(display string) (*XUtil, error) {
|
||||
c, err := xgb.NewConnDisplay(display)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewConnXgb(c)
|
||||
|
||||
}
|
||||
|
||||
// NewConnXgb use the specific xgb.Conn to create a new XUtil.
|
||||
//
|
||||
// NewConn, NewConnDisplay are wrapper of this function.
|
||||
func NewConnXgb(c *xgb.Conn) (*XUtil, error) {
|
||||
setup := xproto.Setup(c)
|
||||
screen := setup.DefaultScreen(c)
|
||||
|
||||
// Initialize our central struct that stores everything.
|
||||
xu := &XUtil{
|
||||
conn: c,
|
||||
Quit: false,
|
||||
Evqueue: make([]EventOrError, 0, 1000),
|
||||
EvqueueLck: &sync.RWMutex{},
|
||||
setup: setup,
|
||||
screen: screen,
|
||||
root: screen.Root,
|
||||
eventTime: xproto.Timestamp(0), // last event time
|
||||
Atoms: make(map[string]xproto.Atom, 50),
|
||||
AtomsLck: &sync.RWMutex{},
|
||||
AtomNames: make(map[xproto.Atom]string, 50),
|
||||
AtomNamesLck: &sync.RWMutex{},
|
||||
Callbacks: make(map[int]map[xproto.Window][]Callback, 33),
|
||||
CallbacksLck: &sync.RWMutex{},
|
||||
Hooks: make([]CallbackHook, 0),
|
||||
HooksLck: &sync.RWMutex{},
|
||||
Keymap: nil, // we don't have anything yet
|
||||
Modmap: nil,
|
||||
KeyRedirect: 0,
|
||||
Keybinds: make(map[KeyKey][]CallbackKey, 10),
|
||||
KeybindsLck: &sync.RWMutex{},
|
||||
Keygrabs: make(map[KeyKey]int, 10),
|
||||
Keystrings: make([]KeyString, 0, 10),
|
||||
Mousebinds: make(map[MouseKey][]CallbackMouse, 10),
|
||||
MousebindsLck: &sync.RWMutex{},
|
||||
Mousegrabs: make(map[MouseKey]int, 10),
|
||||
InMouseDrag: false,
|
||||
MouseDragStepFun: nil,
|
||||
MouseDragEndFun: nil,
|
||||
ErrorHandler: func(err xgb.Error) { Logger.Println(err) },
|
||||
}
|
||||
|
||||
var err error = nil
|
||||
// Create a general purpose graphics context
|
||||
xu.gc, err = xproto.NewGcontextId(xu.conn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
xproto.CreateGC(xu.conn, xu.gc, xproto.Drawable(xu.root),
|
||||
xproto.GcForeground, []uint32{xu.screen.WhitePixel})
|
||||
|
||||
// Create a dummy window
|
||||
xu.dummy, err = xproto.NewWindowId(xu.conn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
xproto.CreateWindow(xu.conn, xu.Screen().RootDepth, xu.dummy, xu.RootWin(),
|
||||
-1000, -1000, 1, 1, 0,
|
||||
xproto.WindowClassInputOutput, xu.Screen().RootVisual,
|
||||
xproto.CwEventMask|xproto.CwOverrideRedirect,
|
||||
[]uint32{1, xproto.EventMaskPropertyChange})
|
||||
xproto.MapWindow(xu.conn, xu.dummy)
|
||||
|
||||
// Register the Xinerama extension... because it doesn't cost much.
|
||||
err = xinerama.Init(xu.conn)
|
||||
|
||||
// If we can't register Xinerama, that's okay. Output something
|
||||
// and move on.
|
||||
if err != nil {
|
||||
Logger.Printf("WARNING: %s\n", err)
|
||||
Logger.Printf("MESSAGE: The 'xinerama' package cannot be used " +
|
||||
"because the XINERAMA extension could not be loaded.")
|
||||
}
|
||||
|
||||
return xu, nil
|
||||
}
|
||||
|
||||
// Conn returns the xgb connection object.
|
||||
func (xu *XUtil) Conn() *xgb.Conn {
|
||||
return xu.conn
|
||||
}
|
||||
|
||||
// ExtInitialized returns true if an extension has been initialized.
|
||||
// This is useful for determining whether an extension is available or not.
|
||||
func (xu *XUtil) ExtInitialized(extName string) bool {
|
||||
_, ok := xu.Conn().Extensions[extName]
|
||||
return ok
|
||||
}
|
||||
|
||||
// Sync forces XGB to catch up with all events/requests and synchronize.
|
||||
// This is done by issuing a benign round trip request to X.
|
||||
func (xu *XUtil) Sync() {
|
||||
xproto.GetInputFocus(xu.Conn()).Reply()
|
||||
}
|
||||
|
||||
// Setup returns the setup information retrieved during connection time.
|
||||
func (xu *XUtil) Setup() *xproto.SetupInfo {
|
||||
return xu.setup
|
||||
}
|
||||
|
||||
// Screen returns the default screen
|
||||
func (xu *XUtil) Screen() *xproto.ScreenInfo {
|
||||
return xu.screen
|
||||
}
|
||||
|
||||
// RootWin returns the current root window.
|
||||
func (xu *XUtil) RootWin() xproto.Window {
|
||||
return xu.root
|
||||
}
|
||||
|
||||
// RootWinSet will change the current root window to the one provided.
|
||||
// N.B. This probably shouldn't be used unless you're desperately trying
|
||||
// to support multiple X screens. (This is *not* the same as Xinerama/RandR or
|
||||
// TwinView. All of those have a single root window.)
|
||||
func (xu *XUtil) RootWinSet(root xproto.Window) {
|
||||
xu.root = root
|
||||
}
|
||||
|
||||
// TimeGet gets the most recent time seen by an event.
|
||||
func (xu *XUtil) TimeGet() xproto.Timestamp {
|
||||
return xu.eventTime
|
||||
}
|
||||
|
||||
// TimeSet sets the most recent time seen by an event.
|
||||
func (xu *XUtil) TimeSet(t xproto.Timestamp) {
|
||||
xu.eventTime = t
|
||||
}
|
||||
|
||||
// GC gets a general purpose graphics context that is typically used to simply
|
||||
// paint images.
|
||||
func (xu *XUtil) GC() xproto.Gcontext {
|
||||
return xu.gc
|
||||
}
|
||||
|
||||
// Dummy gets the id of the dummy window.
|
||||
func (xu *XUtil) Dummy() xproto.Window {
|
||||
return xu.dummy
|
||||
}
|
||||
|
||||
// Grabs the server. Everything becomes synchronous.
|
||||
func (xu *XUtil) Grab() {
|
||||
xproto.GrabServer(xu.Conn())
|
||||
}
|
||||
|
||||
// Ungrabs the server.
|
||||
func (xu *XUtil) Ungrab() {
|
||||
xproto.UngrabServer(xu.Conn())
|
||||
}
|
@ -1,103 +0,0 @@
|
||||
package xprop
|
||||
|
||||
/*
|
||||
xprop/atom.go contains functions related to interning atoms and retrieving
|
||||
atom names from an atom identifier.
|
||||
|
||||
It also manages an atom cache so that once an atom is interned from the X
|
||||
server, all future atom interns use that value. (So that one and only one
|
||||
request is sent for interning each atom.)
|
||||
*/
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/BurntSushi/xgb/xproto"
|
||||
|
||||
"github.com/BurntSushi/xgbutil"
|
||||
)
|
||||
|
||||
// Atm is a short alias for Atom in the common case of interning an atom.
|
||||
// Namely, interning the atom always succeeds. (If the atom does not already
|
||||
// exist, a new one is created.)
|
||||
func Atm(xu *xgbutil.XUtil, name string) (xproto.Atom, error) {
|
||||
aid, err := Atom(xu, name, false)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if aid == 0 {
|
||||
return 0, fmt.Errorf("Atm: '%s' returned an identifier of 0.", name)
|
||||
}
|
||||
|
||||
return aid, err
|
||||
}
|
||||
|
||||
// Atom interns an atom and panics if there is any error.
|
||||
func Atom(xu *xgbutil.XUtil, name string,
|
||||
onlyIfExists bool) (xproto.Atom, error) {
|
||||
|
||||
// Check the cache first
|
||||
if aid, ok := atomGet(xu, name); ok {
|
||||
return aid, nil
|
||||
}
|
||||
|
||||
reply, err := xproto.InternAtom(xu.Conn(), onlyIfExists,
|
||||
uint16(len(name)), name).Reply()
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("Atom: Error interning atom '%s': %s", name, err)
|
||||
}
|
||||
|
||||
// If we're here, it means we didn't have this atom cached. So cache it!
|
||||
cacheAtom(xu, name, reply.Atom)
|
||||
|
||||
return reply.Atom, nil
|
||||
}
|
||||
|
||||
// AtomName fetches a string representation of an ATOM given its integer id.
|
||||
func AtomName(xu *xgbutil.XUtil, aid xproto.Atom) (string, error) {
|
||||
// Check the cache first
|
||||
if atomName, ok := atomNameGet(xu, aid); ok {
|
||||
return string(atomName), nil
|
||||
}
|
||||
|
||||
reply, err := xproto.GetAtomName(xu.Conn(), aid).Reply()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("AtomName: Error fetching name for ATOM "+
|
||||
"id '%d': %s", aid, err)
|
||||
}
|
||||
|
||||
// If we're here, it means we didn't have ths ATOM id cached. So cache it.
|
||||
atomName := string(reply.Name)
|
||||
cacheAtom(xu, atomName, aid)
|
||||
|
||||
return atomName, nil
|
||||
}
|
||||
|
||||
// atomGet retrieves an atom identifier from a cache if it exists.
|
||||
func atomGet(xu *xgbutil.XUtil, name string) (xproto.Atom, bool) {
|
||||
xu.AtomsLck.RLock()
|
||||
defer xu.AtomsLck.RUnlock()
|
||||
|
||||
aid, ok := xu.Atoms[name]
|
||||
return aid, ok
|
||||
}
|
||||
|
||||
// atomNameGet retrieves an atom name from a cache if it exists.
|
||||
func atomNameGet(xu *xgbutil.XUtil, aid xproto.Atom) (string, bool) {
|
||||
xu.AtomNamesLck.RLock()
|
||||
defer xu.AtomNamesLck.RUnlock()
|
||||
|
||||
name, ok := xu.AtomNames[aid]
|
||||
return name, ok
|
||||
}
|
||||
|
||||
// cacheAtom puts an atom into the cache.
|
||||
func cacheAtom(xu *xgbutil.XUtil, name string, aid xproto.Atom) {
|
||||
xu.AtomsLck.Lock()
|
||||
xu.AtomNamesLck.Lock()
|
||||
defer xu.AtomsLck.Unlock()
|
||||
defer xu.AtomNamesLck.Unlock()
|
||||
|
||||
xu.Atoms[name] = aid
|
||||
xu.AtomNames[aid] = name
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
/*
|
||||
Package xprop provides a cache for interning atoms and helper functions for
|
||||
dealing with GetProperty and ChangeProperty X requests.
|
||||
|
||||
Atoms
|
||||
|
||||
Atoms in X are interned, meaning that strings are assigned unique integer
|
||||
identifiers. This minimizes the amount of data transmitted over an X connection.
|
||||
|
||||
Once atoms have been interned, they are never changed while the X server is
|
||||
running. xgbutil takes advantage of this invariant and will only issue an
|
||||
intern atom request once and cache the result.
|
||||
|
||||
To use the xprop package to intern an atom, use Atom:
|
||||
|
||||
atom, err := xprop.Atom(XUtilValue, "THE_ATOM_NAME", false)
|
||||
if err == nil {
|
||||
println("The atom number: ", atom.Atom)
|
||||
}
|
||||
|
||||
The 'false' parameter corresponds to the 'only_if_exists' parameter of the
|
||||
X InternAtom request. When it's false, the atom being interned always returns
|
||||
a non-zero atom number---even if the string being interned hasn't been interned
|
||||
before. If 'only_if_exists' is true, the atom being interned will return a 0
|
||||
atom number if it hasn't already been interned.
|
||||
|
||||
The typical case is to set 'only_if_exists' to false. To this end, xprop.Atm is
|
||||
an alias that always sets this value to false.
|
||||
|
||||
The reverse can also be done: getting an atom string if you have an atom
|
||||
number. This can be done with the xprop.AtomName function.
|
||||
|
||||
Properties
|
||||
|
||||
The other facility of xprop is to help with the use of GetProperty and
|
||||
ChangeProperty. Please see the source code of the ewmh package for plenty of
|
||||
examples.
|
||||
|
||||
*/
|
||||
package xprop
|
@ -1,270 +0,0 @@
|
||||
package xprop
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/BurntSushi/xgb"
|
||||
"github.com/BurntSushi/xgb/xproto"
|
||||
|
||||
"github.com/BurntSushi/xgbutil"
|
||||
)
|
||||
|
||||
// GetProperty abstracts the messiness of calling xgb.GetProperty.
|
||||
func GetProperty(xu *xgbutil.XUtil, win xproto.Window, atom string) (
|
||||
*xproto.GetPropertyReply, error) {
|
||||
|
||||
atomId, err := Atm(xu, atom)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
reply, err := xproto.GetProperty(xu.Conn(), false, win, atomId,
|
||||
xproto.GetPropertyTypeAny, 0, (1<<32)-1).Reply()
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("GetProperty: Error retrieving property '%s' "+
|
||||
"on window %x: %s", atom, win, err)
|
||||
}
|
||||
|
||||
if reply.Format == 0 {
|
||||
return nil, fmt.Errorf("GetProperty: No such property '%s' on "+
|
||||
"window %x.", atom, win)
|
||||
}
|
||||
|
||||
return reply, nil
|
||||
}
|
||||
|
||||
// ChangeProperty abstracts the semi-nastiness of xgb.ChangeProperty.
|
||||
func ChangeProp(xu *xgbutil.XUtil, win xproto.Window, format byte, prop string,
|
||||
typ string, data []byte) error {
|
||||
|
||||
propAtom, err := Atm(xu, prop)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
typAtom, err := Atm(xu, typ)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return xproto.ChangePropertyChecked(xu.Conn(), xproto.PropModeReplace, win,
|
||||
propAtom, typAtom, format,
|
||||
uint32(len(data)/(int(format)/8)), data).Check()
|
||||
}
|
||||
|
||||
// ChangeProperty32 makes changing 32 bit formatted properties easier
|
||||
// by constructing the raw X data for you.
|
||||
func ChangeProp32(xu *xgbutil.XUtil, win xproto.Window, prop string, typ string,
|
||||
data ...uint) error {
|
||||
|
||||
buf := make([]byte, len(data)*4)
|
||||
for i, datum := range data {
|
||||
xgb.Put32(buf[(i*4):], uint32(datum))
|
||||
}
|
||||
|
||||
return ChangeProp(xu, win, 32, prop, typ, buf)
|
||||
}
|
||||
|
||||
// WindowToUint is a covenience function for converting []xproto.Window
|
||||
// to []uint.
|
||||
func WindowToInt(ids []xproto.Window) []uint {
|
||||
ids32 := make([]uint, len(ids))
|
||||
for i, v := range ids {
|
||||
ids32[i] = uint(v)
|
||||
}
|
||||
return ids32
|
||||
}
|
||||
|
||||
// AtomToInt is a covenience function for converting []xproto.Atom
|
||||
// to []uint.
|
||||
func AtomToUint(ids []xproto.Atom) []uint {
|
||||
ids32 := make([]uint, len(ids))
|
||||
for i, v := range ids {
|
||||
ids32[i] = uint(v)
|
||||
}
|
||||
return ids32
|
||||
}
|
||||
|
||||
// StrToAtoms is a convenience function for converting
|
||||
// []string to []uint32 atoms.
|
||||
// NOTE: If an atom name in the list doesn't exist, it will be created.
|
||||
func StrToAtoms(xu *xgbutil.XUtil, atomNames []string) ([]uint, error) {
|
||||
var err error
|
||||
atoms := make([]uint, len(atomNames))
|
||||
for i, atomName := range atomNames {
|
||||
a, err := Atom(xu, atomName, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
atoms[i] = uint(a)
|
||||
}
|
||||
return atoms, err
|
||||
}
|
||||
|
||||
// PropValAtom transforms a GetPropertyReply struct into an ATOM name.
|
||||
// The property reply must be in 32 bit format.
|
||||
func PropValAtom(xu *xgbutil.XUtil, reply *xproto.GetPropertyReply,
|
||||
err error) (string, error) {
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if reply.Format != 32 {
|
||||
return "", fmt.Errorf("PropValAtom: Expected format 32 but got %d",
|
||||
reply.Format)
|
||||
}
|
||||
|
||||
return AtomName(xu, xproto.Atom(xgb.Get32(reply.Value)))
|
||||
}
|
||||
|
||||
// PropValAtoms is the same as PropValAtom, except that it returns a slice
|
||||
// of atom names. Also must be 32 bit format.
|
||||
// This is a method of an XUtil struct, unlike the other 'PropVal...' functions.
|
||||
func PropValAtoms(xu *xgbutil.XUtil, reply *xproto.GetPropertyReply,
|
||||
err error) ([]string, error) {
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if reply.Format != 32 {
|
||||
return nil, fmt.Errorf("PropValAtoms: Expected format 32 but got %d",
|
||||
reply.Format)
|
||||
}
|
||||
|
||||
ids := make([]string, reply.ValueLen)
|
||||
vals := reply.Value
|
||||
for i := 0; len(vals) >= 4; i++ {
|
||||
ids[i], err = AtomName(xu, xproto.Atom(xgb.Get32(vals)))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
vals = vals[4:]
|
||||
}
|
||||
return ids, nil
|
||||
}
|
||||
|
||||
// PropValWindow transforms a GetPropertyReply struct into an X resource
|
||||
// window identifier.
|
||||
// The property reply must be in 32 bit format.
|
||||
func PropValWindow(reply *xproto.GetPropertyReply,
|
||||
err error) (xproto.Window, error) {
|
||||
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if reply.Format != 32 {
|
||||
return 0, fmt.Errorf("PropValId: Expected format 32 but got %d",
|
||||
reply.Format)
|
||||
}
|
||||
return xproto.Window(xgb.Get32(reply.Value)), nil
|
||||
}
|
||||
|
||||
// PropValWindows is the same as PropValWindow, except that it returns a slice
|
||||
// of identifiers. Also must be 32 bit format.
|
||||
func PropValWindows(reply *xproto.GetPropertyReply,
|
||||
err error) ([]xproto.Window, error) {
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if reply.Format != 32 {
|
||||
return nil, fmt.Errorf("PropValIds: Expected format 32 but got %d",
|
||||
reply.Format)
|
||||
}
|
||||
|
||||
ids := make([]xproto.Window, reply.ValueLen)
|
||||
vals := reply.Value
|
||||
for i := 0; len(vals) >= 4; i++ {
|
||||
ids[i] = xproto.Window(xgb.Get32(vals))
|
||||
vals = vals[4:]
|
||||
}
|
||||
return ids, nil
|
||||
}
|
||||
|
||||
// PropValNum transforms a GetPropertyReply struct into an unsigned
|
||||
// integer. Useful when the property value is a single integer.
|
||||
func PropValNum(reply *xproto.GetPropertyReply, err error) (uint, error) {
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if reply.Format != 32 {
|
||||
return 0, fmt.Errorf("PropValNum: Expected format 32 but got %d",
|
||||
reply.Format)
|
||||
}
|
||||
return uint(xgb.Get32(reply.Value)), nil
|
||||
}
|
||||
|
||||
// PropValNums is the same as PropValNum, except that it returns a slice
|
||||
// of integers. Also must be 32 bit format.
|
||||
func PropValNums(reply *xproto.GetPropertyReply, err error) ([]uint, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if reply.Format != 32 {
|
||||
return nil, fmt.Errorf("PropValIds: Expected format 32 but got %d",
|
||||
reply.Format)
|
||||
}
|
||||
|
||||
nums := make([]uint, reply.ValueLen)
|
||||
vals := reply.Value
|
||||
for i := 0; len(vals) >= 4; i++ {
|
||||
nums[i] = uint(xgb.Get32(vals))
|
||||
vals = vals[4:]
|
||||
}
|
||||
return nums, nil
|
||||
}
|
||||
|
||||
// PropValNum64 transforms a GetPropertyReply struct into a 64 bit
|
||||
// integer. Useful when the property value is a single integer.
|
||||
func PropValNum64(reply *xproto.GetPropertyReply, err error) (int64, error) {
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if reply.Format != 32 {
|
||||
return 0, fmt.Errorf("PropValNum: Expected format 32 but got %d",
|
||||
reply.Format)
|
||||
}
|
||||
return int64(xgb.Get32(reply.Value)), nil
|
||||
}
|
||||
|
||||
// PropValStr transforms a GetPropertyReply struct into a string.
|
||||
// Useful when the property value is a null terminated string represented
|
||||
// by integers. Also must be 8 bit format.
|
||||
func PropValStr(reply *xproto.GetPropertyReply, err error) (string, error) {
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if reply.Format != 8 {
|
||||
return "", fmt.Errorf("PropValStr: Expected format 8 but got %d",
|
||||
reply.Format)
|
||||
}
|
||||
return string(reply.Value), nil
|
||||
}
|
||||
|
||||
// PropValStrs is the same as PropValStr, except that it returns a slice
|
||||
// of strings. The raw byte string is a sequence of null terminated strings,
|
||||
// which is translated into a slice of strings.
|
||||
func PropValStrs(reply *xproto.GetPropertyReply, err error) ([]string, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if reply.Format != 8 {
|
||||
return nil, fmt.Errorf("PropValStrs: Expected format 8 but got %d",
|
||||
reply.Format)
|
||||
}
|
||||
|
||||
var strs []string
|
||||
sstart := 0
|
||||
for i, c := range reply.Value {
|
||||
if c == 0 {
|
||||
strs = append(strs, string(reply.Value[sstart:i]))
|
||||
sstart = i + 1
|
||||
}
|
||||
}
|
||||
if sstart < int(reply.ValueLen) {
|
||||
strs = append(strs, string(reply.Value[sstart:]))
|
||||
}
|
||||
return strs, nil
|
||||
}
|
@ -1,50 +0,0 @@
|
||||
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…
Reference in New Issue