Compare commits
2 commits
7442d68a0c
...
f86f6d213a
Author | SHA1 | Date | |
---|---|---|---|
DataHoarder | f86f6d213a | ||
DataHoarder | cfe987a51d |
30
CHANGES
30
CHANGES
|
@ -1,4 +1,34 @@
|
|||
|
||||
2.0
|
||||
|
||||
Add new analyzer methods bandpass_band_ff() and band_q().
|
||||
|
||||
Add new analyzer methods band_analysis_support() and
|
||||
band_synthesis_support().
|
||||
|
||||
Add support for adjusting the amount of overlap between bands.
|
||||
|
||||
Add support for selecting a local or global phase convention, and make
|
||||
the local phase convention the default in the 2.0 API.
|
||||
|
||||
Extend the supported ff_min range to 0.0001...0.5.
|
||||
|
||||
Add support for linear and mel frequency scales.
|
||||
|
||||
Analyzers no longer share data via static variables as this could
|
||||
cause race conditions in multithreaded applications.
|
||||
|
||||
Disable the copy constructor and assignment operator of the analyzer
|
||||
class.
|
||||
|
||||
The number of bands per octave is no longer restricted to integer
|
||||
values.
|
||||
|
||||
Further improve the performance of analyzing short signal blocks.
|
||||
|
||||
Document bands_begin() and bands_end(), and expand the documentation
|
||||
of band numbering in general.
|
||||
|
||||
1.7
|
||||
|
||||
Miscellaneous bug fixes.
|
||||
|
|
2
LICENSE
2
LICENSE
|
@ -1,5 +1,5 @@
|
|||
|
||||
The Gaborator library is Copyright (C) 1992-2019 Andreas Gustafsson.
|
||||
The Gaborator library is Copyright (C) 1992-2023 Andreas Gustafsson.
|
||||
|
||||
License to distribute and modify the code is hereby granted under the
|
||||
terms of the GNU Affero General Public License, version 3 (henceforth,
|
||||
|
|
BIN
doc/._filter-response.png
Normal file
BIN
doc/._filter-response.png
Normal file
Binary file not shown.
|
@ -1,6 +1,6 @@
|
|||
<!DOCTYPE html>
|
||||
<!--
|
||||
Copyright (C) 2017-2020 Andreas Gustafsson. This file is part of
|
||||
Copyright (C) 2017-2023 Andreas Gustafsson. This file is part of
|
||||
the Gaborator library source distribution. See the file LICENSE at
|
||||
the top level of the distribution for license information.
|
||||
-->
|
||||
|
@ -77,14 +77,17 @@ that in <a href="render.html">Example 1</a>:</p>
|
|||
|
||||
<h2>Spectrum Analysis Parameters</h2>
|
||||
|
||||
<p>The spectrum analysis works much the same as in Example 1,
|
||||
but uses slightly different parameters.
|
||||
We use a larger number of frequency bands per octave (100)
|
||||
to minimize ripple in the frequency response, and the
|
||||
reference frequency argument is omitted as we don't care about the
|
||||
exact alignment of the bands with respect to a musical scale.</p>
|
||||
<p>The spectrum analysis works much the same as in Example 1, but uses
|
||||
slightly different parameters. The number of frequency bands per
|
||||
octave determines how steep the transitions between filter bands can
|
||||
be made, and since this particular filter has no steep transitions, a
|
||||
low value like 10 is sufficient. The reference frequency argument is
|
||||
omitted as we don't care about the exact alignment of the bands with
|
||||
respect to a musical scale.</p>
|
||||
|
||||
<pre>
|
||||
gaborator::parameters params(100, 20.0 / fs);
|
||||
gaborator::log_fq_scale scale(10, 20.0 / fs);
|
||||
gaborator::parameters params(scale);
|
||||
gaborator::analyzer<float> analyzer(params);
|
||||
</pre>
|
||||
|
||||
|
@ -107,7 +110,7 @@ proportional to the square root of the inverse of the frequency.
|
|||
To get the frequency of each band, we call the
|
||||
<code>analyzer</code> method <code>band_ff()</code>, which
|
||||
returns the center frequency of the band in units of the
|
||||
sampling frequency. The gain is normalized to unity at 20 Hz.
|
||||
sample rate. The gain is normalized to unity at 20 Hz.
|
||||
</p>
|
||||
<pre>
|
||||
for (int band = analyzer.bandpass_bands_begin(); band < analyzer.bandpass_bands_end(); band++) {
|
||||
|
@ -201,7 +204,7 @@ Note that we use <code>SFC_SET_CLIPPING</code>
|
|||
to make sure that any samples too loud for the file format
|
||||
will saturate; by default, <i>libsndfile</i> makes them
|
||||
wrap around, which sounds really bad.</p>
|
||||
<a name="writing_audio_code">
|
||||
<a id="writing_audio_code">
|
||||
<pre>
|
||||
SNDFILE *sf_out = sf_open(argv[2], SFM_WRITE, &sfinfo);
|
||||
if (! sf_out) {
|
||||
|
@ -233,16 +236,16 @@ complete program:
|
|||
<p>Like <a href="render.html#compiling">Example 1</a>, this example
|
||||
can be built using a one-line build command:
|
||||
</p>
|
||||
<pre class="build Darwin Linux NetBSD FreeBSD">
|
||||
c++ -std=c++11 -I.. -O3 -ffast-math `pkg-config --cflags sndfile` filter.cc `pkg-config --libs sndfile` -o filter
|
||||
<pre class="build Darwin Linux NetBSD FreeBSD OpenBSD">
|
||||
c++ -std=c++11 -I.. -O3 -ffast-math $(pkg-config --cflags sndfile) filter.cc $(pkg-config --libs sndfile) -o filter
|
||||
</pre>
|
||||
<p>Or using the vDSP FFT on macOS:</p>
|
||||
<pre class="build Darwin">
|
||||
c++ -std=c++11 -I.. -O3 -ffast-math -DGABORATOR_USE_VDSP `pkg-config --cflags sndfile` filter.cc `pkg-config --libs sndfile` -framework Accelerate -o filter
|
||||
c++ -std=c++11 -I.. -O3 -ffast-math -DGABORATOR_USE_VDSP $(pkg-config --cflags sndfile) filter.cc $(pkg-config --libs sndfile) -framework Accelerate -o filter
|
||||
</pre>
|
||||
<p>Or using PFFFT (see <a href="render.html#compiling">Example 1</a> for how to download and build PFFFT):</p>
|
||||
<pre class="build">
|
||||
c++ -std=c++11 -I.. -Ipffft -O3 -ffast-math -DGABORATOR_USE_PFFFT `pkg-config --cflags sndfile` filter.cc pffft/pffft.o pffft/fftpack.o `pkg-config --libs sndfile` -o filter
|
||||
c++ -std=c++11 -I.. -Ipffft -O3 -ffast-math -DGABORATOR_USE_PFFFT $(pkg-config --cflags sndfile) filter.cc pffft/pffft.o pffft/fftpack.o $(pkg-config --libs sndfile) -o filter
|
||||
</pre>
|
||||
|
||||
<h2>Running</h2>
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 122 KiB After Width: | Height: | Size: 122 KiB |
Binary file not shown.
Before Width: | Height: | Size: 107 KiB After Width: | Height: | Size: 121 KiB |
Binary file not shown.
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
@ -1,6 +1,6 @@
|
|||
<!DOCTYPE html>
|
||||
<!--
|
||||
Copyright (C) 2018-2021 Andreas Gustafsson. This file is part of
|
||||
Copyright (C) 2018-2023 Andreas Gustafsson. This file is part of
|
||||
the Gaborator library source distribution. See the file LICENSE at
|
||||
the top level of the distribution for license information.
|
||||
-->
|
||||
|
@ -12,23 +12,16 @@ the top level of the distribution for license information.
|
|||
</head>
|
||||
<body>
|
||||
<h1>The Gaborator</h1>
|
||||
<p>The Gaborator is a library that generates constant-Q spectrograms
|
||||
for visualization and analysis of audio signals. It also supports a
|
||||
fast and accurate inverse transformation of the spectrogram coefficients
|
||||
back into audio for spectral effects and editing.</p>
|
||||
|
||||
<p>The Gaborator implements the invertible constant-Q transform of
|
||||
Velasco, Holighaus, Dörfler, and Grill, described in the papers
|
||||
<i><a href="http://www.univie.ac.at/nonstatgab/pdf_files/dohogrve11_amsart.pdf">
|
||||
Constructing an invertible constant-Q transform with nonstationary Gabor frames, 2011</a></i>
|
||||
and <i><a href="http://www.univie.ac.at/nonstatgab/pdf_files/dogrhove12_amsart.pdf">
|
||||
A Framework for invertible, real-time constant-Q transforms, 2012</a></i>,
|
||||
using Gaussian bandpass filters and an efficient multi-rate architecture.
|
||||
</p>
|
||||
<p>The Gaborator is a C++ library that generates spectrograms for the
|
||||
visualization and analysis of audio signals. It supports constant-Q,
|
||||
constant-bandwidth and mel scale spectrograms, as well as a fast and
|
||||
accurate inverse transformation of the spectrogram coefficients back
|
||||
into audio for spectral effects and editing.</p>
|
||||
|
||||
<p>The Gaborator is written in C++11 and compatible with C++14 and C++17.
|
||||
It has been tested on macOS, Linux, NetBSD, FreeBSD, and iOS, on Intel
|
||||
x86_64 and ARM processors.</p>
|
||||
<p>The Gaborator is compatible with C++ versions from C++11 through
|
||||
C++20 and beyond. It has been tested on macOS, iOS, Linux, NetBSD,
|
||||
FreeBSD, and OpenBSD, on Intel x86_64 and ARM processors.</p>
|
||||
|
||||
<p>The Gaborator is open source under the GNU Affero General Public
|
||||
License, version 3, and is also available for commercial licensing.
|
||||
|
@ -38,7 +31,7 @@ See the file <a href="../LICENSE">LICENSE</a> for details.</p>
|
|||
|
||||
<p>The following examples demonstrate the use of the library in
|
||||
various scenarios. They are presented in a "literate
|
||||
programming" style, with the code embedded in the commentary
|
||||
programming" style, where the code is embedded in the commentary
|
||||
rather than the other way around.
|
||||
Concatenating the code fragments in each example yields a complete C++
|
||||
program, which can also be found as a <code>.cc</code> file in
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<!DOCTYPE html>
|
||||
<!--
|
||||
Copyright (C) 2018-2021 Andreas Gustafsson. This file is part of
|
||||
Copyright (C) 2018-2023 Andreas Gustafsson. This file is part of
|
||||
the Gaborator library source distribution. See the file LICENSE at
|
||||
the top level of the distribution for license information.
|
||||
-->
|
||||
|
@ -33,20 +33,21 @@ of these functions.</p>
|
|||
an <i>analysis filter bank</i>, to split it into a number of
|
||||
overlapping frequency <i>bands</i>.</p>
|
||||
|
||||
<p>The filter bank consists of a number of logarithmically spaced
|
||||
Gaussian bandpass filters and a single lowpass filter. Each bandpass
|
||||
filter has a bandwidth proportional to its center frequency, which
|
||||
means they all have the same quality factor Q and form
|
||||
a <i>constant-Q</i> filter bank. The highest-frequency bandpass
|
||||
filter will have a center frequency close to half the sample rate; in
|
||||
the graphs below, this is simple labeled 0.5 because all frequencies
|
||||
in the Gaborator are in units of the sample rate. The
|
||||
lowest-frequency bandpass filter should be centered at, or slightly
|
||||
below, the lowest frequency of interest to the application at hand.
|
||||
For example, when analyzing audio, this is often the lower limit of
|
||||
human hearing; at a sample rate of 44100 Hz, this means 20 Hz / 44100
|
||||
Hz ≈ 0.00045. This lower frequency limit is referred to as
|
||||
the <i>minimum frequency</i> or f<sub>min</sub>.
|
||||
<p>When using a logarithmic frequency scale,
|
||||
the filter bank consists of a number of
|
||||
logarithmically spaced Gaussian bandpass filters and a single lowpass
|
||||
filter. Each bandpass filter has a bandwidth proportional to its
|
||||
center frequency, which means they all have the same quality factor Q
|
||||
and form a <i>constant-Q</i> filter bank. The highest-frequency
|
||||
bandpass filter will have a center frequency close to half the sample
|
||||
rate. In the graphs below, this is labeled 0.5 because
|
||||
frequencies in the Gaborator are generally given in units of the
|
||||
sample rate. The lowest-frequency bandpass filter should be centered
|
||||
at, or slightly below, the lowest frequency of interest to the
|
||||
application at hand. For example, when analyzing audio, this is often
|
||||
the lower limit of human hearing; at a sample rate of 44100 Hz, this
|
||||
means 20 Hz / 44100 Hz ≈ 0.00045. This lower frequency limit is
|
||||
referred to as the <i>minimum frequency</i> or f<sub>min</sub>.
|
||||
</p>
|
||||
|
||||
<p>Although frequencies below f<sub>min</sub> are assumed to not be of
|
||||
|
@ -55,12 +56,27 @@ reconstruction, and that is what the lowpass filter is for. Together,
|
|||
the lowpass filter and the bandpass filters overlap to cover the full
|
||||
frequency range from 0 to 0.5.</P>
|
||||
|
||||
<p>The spacing of the bandpass filters is specified by the user as an
|
||||
integer number of filters (or, equivalently, bands) per octave. For
|
||||
<p>The spacing of the bandpass filters is specified by the user as
|
||||
a number of filters (or, equivalently, bands) per octave. For
|
||||
example, when analyzing music, this is often 12 bands per octave (one
|
||||
band per semitone in the equal-tempered scale), or if a finer
|
||||
frequency resolution is needed, some multiple of 12.</p>
|
||||
|
||||
<p id="overlap">The bandwidth of each individual bandpass filter is
|
||||
chosen to achieve a reasonable amount of overlap with the adjacent
|
||||
filters. If the bandwidth is too narrow, there will be too little
|
||||
overlap, causing deep gaps between the bands. If it is too wide,
|
||||
there will be a great deal of overlap, resulting in a blurred
|
||||
spectrogram with poor frequency selectivity and highly redundant
|
||||
coefficients.</p>
|
||||
|
||||
<p>Since the Gaborator uses Gaussian bandpass filters, it defines the
|
||||
width of each filter in terms of its standard deviation. The overlap
|
||||
is defined as the ratio of this standard deviation to the spacing between
|
||||
adjacent bands. The default value for the overlap is 0.7, meaning the
|
||||
standard deviation of each Gaussian filter is 0.7 times the local
|
||||
spacing between adjacent filters.</p>
|
||||
|
||||
<p>The following plot shows the frequency responses of the analysis
|
||||
filters at 12 bands per octave and f<sub>min</sub> = 0.03. A more
|
||||
typical f<sub>min</sub> for audio work would be 0.00045, but
|
||||
|
@ -69,21 +85,25 @@ and the lowest-frequency bandpass filters would be extremely narrow.</p>
|
|||
|
||||
<img src="gen/allkernels_v1_bpo12_ffmin0.03_ffref0.5_anl_wob.png" alt="Analysis filters">
|
||||
|
||||
<p>The output of each bandpass filter is shifted down in frequency to
|
||||
a complex quadrature baseband. The baseband signal is then resampled
|
||||
at a reduced sample rate, lower than that of the orignal signal but
|
||||
high enough that there is negligible aliasing given the bandwidth of
|
||||
the filter in case. The Gaborator uses sample rates related to the
|
||||
original signal sample rate by powers of two. This means some of
|
||||
frequency bands are sampled a bit more often than strictly
|
||||
necessary, but has the advantage that the sampling can be synchronized
|
||||
to make the samples of many frequency bands coincide in time, which
|
||||
can be convenient in later analysis or spectrogram rendering. The
|
||||
complex samples resulting from this process are the spectrogram
|
||||
coefficients.</p>
|
||||
<p>The bandpass filters produce a complex-valued output representing
|
||||
the amplitude and the phase of the signal within each band, and sampling
|
||||
this output produces the final spectrogram coefficients.
|
||||
Since the bandwidth of an individual band is smaller than that of
|
||||
the input signal as a whole, it can be sampled at a reduced sample rate.
|
||||
</p>
|
||||
|
||||
<p>The center frequencies of the analysis filters and the points in
|
||||
time at which they are sampled form a two-dimensional,
|
||||
<p>To minimize the amount of coefficient data, each band should in
|
||||
principle be sampled at a different sample rate, but dealing with a
|
||||
large number of different sample rates would be cumbersome. Instead,
|
||||
all bands are sampled at rates that are the input sample rate divided by
|
||||
some power two, oversampling the coefficients at the next higher such
|
||||
rate as needed. This also has the advantage that the sampling can be
|
||||
synchronized to make the samples of many frequency bands coincide in
|
||||
time, which can be convenient in later analysis or spectrogram
|
||||
rendering.</p>
|
||||
|
||||
<p>The center frequencies of the bands and the sample points in
|
||||
time together form a two-dimensional,
|
||||
multi-resolution <i>time-frequency grid</i>, where high frequencies
|
||||
are sampled sparsely in frequency but densely in time, and low
|
||||
frequencies are sampled densely in frequency but sparsely in time.</p>
|
||||
|
@ -97,21 +117,35 @@ positive and the negative direction.</p>
|
|||
|
||||
<img src="gen/grid_v1_bpo12_ffmin0.03_ffref0.5_wob.png" alt="Sampling grid">
|
||||
|
||||
<p>When using a linear or mel frequency scale, no special lowpass band
|
||||
is needed because frequency scale extends to zero.
|
||||
In the case of a linear frequency scale, no multirate processing is
|
||||
needed, either, and the sampling grid is uniformly spaced in both the
|
||||
time and frequency dimensions.</p>
|
||||
|
||||
<h2>Resynthesis</h2>
|
||||
|
||||
<p>Resynthesizing a signal from the coefficients is more or less the
|
||||
reverse of the analysis process. The coefficients are frequency
|
||||
shifted from the complex baseband back to their original center
|
||||
frequencies and run through a <i>reconstruction filter bank</i>
|
||||
that is a <i>dual</i> of the analysis filter bank. The following
|
||||
reverse of the analysis process. The coefficients are upsampled
|
||||
to the original signal sample rate and run through a <i>reconstruction filter bank</i>
|
||||
that is a <i>dual</i> of the analysis filter bank. The construction
|
||||
of the dual filters is based on the methods described by
|
||||
Velasco, Holighaus, Dörfler, and Grill in the papers
|
||||
<i><a href="http://www.univie.ac.at/nonstatgab/pdf_files/dohogrve11_amsart.pdf">
|
||||
Constructing an invertible constant-Q transform with nonstationary Gabor frames, 2011</a></i>
|
||||
and <i><a href="http://www.univie.ac.at/nonstatgab/pdf_files/dogrhove12_amsart.pdf">
|
||||
A Framework for invertible, real-time constant-Q transforms, 2012</a></i>.
|
||||
</p>
|
||||
|
||||
<p>The following
|
||||
plot shows the frequency responses of the reconstruction filters
|
||||
corresponding to the analysis filters shown earlier.</p>
|
||||
|
||||
<img src="gen/allkernels_v1_bpo12_ffmin0.03_ffref0.5_syn_wob.png" alt="Reconstruction filters">
|
||||
|
||||
<p>Although the bandpass filters may look similar to the Gaussian
|
||||
filters of the analysis filter bank, their shapes are actually subtly
|
||||
different.</p>
|
||||
<p>Although the bandpass filters look superficially similar to the
|
||||
Gaussian filters of the analysis filter bank, their shapes are
|
||||
actually subtly different.</p>
|
||||
|
||||
<h2>Spectrogram Rendering</h2>
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<!DOCTYPE html>
|
||||
<!--
|
||||
Copyright (C) 2018-2020 Andreas Gustafsson. This file is part of
|
||||
Copyright (C) 2018-2021, 2023 Andreas Gustafsson. This file is part of
|
||||
the Gaborator library source distribution. See the file LICENSE at
|
||||
the top level of the distribution for license information.
|
||||
-->
|
||||
|
@ -98,7 +98,7 @@ For example, as of version 1.4, analyzing a signal in blocks of
|
|||
2<sup>10</sup> samples takes roughly five times as much CPU as
|
||||
analyzing it in blocks of 2<sup>20</sup> samples.</p>
|
||||
|
||||
<p>For sufficiently small blocks, the processing time will exceed the
|
||||
<p>For very small blocks, the processing time will exceed the
|
||||
duration of the signal, at which point the system can no longer be
|
||||
considered real-time. For example, analyzing a 48 kHz audio
|
||||
stream on a 2.5 GHz Intel Core i5 CPU, this happens at block sizes
|
||||
|
@ -111,9 +111,15 @@ blocks.</p>
|
|||
|
||||
<h2>Can it process a signal stream of any length?</h2>
|
||||
|
||||
<p>Not in practice — the length is limited by floating point
|
||||
precision. At typical audio sample rates, roundoff errors start to
|
||||
become significant after some hours.</p>
|
||||
<p>As of version 2, the length of a stream is only limited by the
|
||||
64-bit signed integers used to represent sample times, allowing for
|
||||
more than a million years of 192 kHz audio both before and after t=0.
|
||||
This assumes the local phase convention is used (which is the default
|
||||
in version 2). When using the global phase convention (as in version
|
||||
1), the floating point precision of phase values used in internal
|
||||
calculations will decrease as the distance from t=0 increases, causing
|
||||
a noticeable decrease in the signal to noise ratio after some
|
||||
hours.</p>
|
||||
|
||||
<h2>Does it avoid dynamic memory allocation in the audio processing path?</h2>
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<!DOCTYPE html>
|
||||
<!--
|
||||
Copyright (C) 2018-2021 Andreas Gustafsson. This file is part of
|
||||
Copyright (C) 2018-2023 Andreas Gustafsson. This file is part of
|
||||
the Gaborator library source distribution. See the file LICENSE at
|
||||
the top level of the distribution for license information.
|
||||
-->
|
||||
|
@ -12,34 +12,76 @@ the top level of the distribution for license information.
|
|||
<body>
|
||||
<h1>Gaborator reference: <code>gaborator.h</code></h1>
|
||||
|
||||
<h2>Spectrum Analysis Parameters</h2>
|
||||
<pre class="forward_decl">
|
||||
template<class T> class analyzer;
|
||||
</pre>
|
||||
|
||||
<p>A <code>parameters</code> object holds a set of parameters that
|
||||
determine the frequency range and resolution of the spectrum
|
||||
analysis.</p>
|
||||
<h2>Frequency scales</h2>
|
||||
|
||||
<p>The Gaborator supports three types of frequency scales:
|
||||
logarithmic, linear, and mel scales. Each is represented by a class
|
||||
deriving from the abstract base class <code>fq_scale</code>.</p>
|
||||
|
||||
<pre>
|
||||
class parameters {
|
||||
class fq_scale {
|
||||
</pre>
|
||||
|
||||
<div class="class_def">
|
||||
<h3>Constructor</h3>
|
||||
|
||||
<h3>Comparison</h3>
|
||||
<p>
|
||||
Comparison operators are provided for compatibility with
|
||||
standard container classes.
|
||||
</p>
|
||||
<pre>
|
||||
parameters(unsigned int bands_per_octave,
|
||||
double ff_min,
|
||||
double ff_ref = 1.0);
|
||||
bool operator<(const fq_scale &rhs) const;
|
||||
bool operator==(const fq_scale &rhs) const;
|
||||
</pre>
|
||||
|
||||
</div>
|
||||
|
||||
<pre>
|
||||
};
|
||||
</pre>
|
||||
|
||||
<h3>Logarithmic frequency scale</h3>
|
||||
|
||||
<p>The <code>log_fq_scale</code> class represents a logarithmic
|
||||
frequency scale, corresponding to a constant-Q transform or
|
||||
constant-Q spectrogram.</p>
|
||||
|
||||
<pre>
|
||||
class log_fq_scale: public fq_scale {
|
||||
</pre>
|
||||
|
||||
<div class="class_def">
|
||||
|
||||
<h4>Constructor</h4>
|
||||
|
||||
<pre>
|
||||
log_fq_scale(double bands_per_octave,
|
||||
double ff_min,
|
||||
double ff_ref = 0.5);
|
||||
</pre>
|
||||
|
||||
<dl>
|
||||
<dt><code>bands_per_octave</code></dt>
|
||||
<dd>The number of frequency bands per octave.
|
||||
Values from 4 to 384 (inclusive) are supported.
|
||||
Integer values yield the best performance, but
|
||||
non-integer values are also supported.
|
||||
The minimum of 4 applies when the default overlap of 0.7 is used;
|
||||
if the overlap is increased from the default, the minimum should be
|
||||
increased proportionally.
|
||||
Values greater than 384 probably work but are not regularly tested.
|
||||
</dd>
|
||||
<dt><code>ff_min</code></dt>
|
||||
<dd>The lower limit of the analysis frequency range, in units of the
|
||||
sample rate. The analysis filter bank will extend low enough in
|
||||
frequency that <code>ff_min</code> falls between the two lowest
|
||||
frequency bandpass filters.
|
||||
Values from 0.001 to 0.13 are supported.</dd>
|
||||
frequency that the lowest bandpass filter center frequency falls at
|
||||
or below <code>ff_min</code>.
|
||||
Values from 0.0001 up to but not including 0.5 are supported.
|
||||
Values below 0.0001 probably work but are not regularly tested.</dd>
|
||||
<dt><code>ff_ref</code></dt>
|
||||
<dd>The reference frequency, in units of the sample rate.
|
||||
This allows fine-tuning of the analysis and synthesis filter
|
||||
|
@ -50,12 +92,204 @@ parameters(unsigned int bands_per_octave,
|
|||
<code>ff_ref</code>. Must be positive. A typical value
|
||||
when analyzing music is <code>440.0 / fs</code>, where
|
||||
<code>fs</code> is the sample rate in Hz.
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
</div>
|
||||
|
||||
<pre>
|
||||
};
|
||||
</pre>
|
||||
|
||||
<h3>Linear frequency scale</h3>
|
||||
|
||||
<p>The <code>lin_fq_scale</code> class represents a linear
|
||||
frequency scale, corresponding to a short-term Fourier transform
|
||||
(STFT) or constant-bandwidth spectrogram.
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
class lin_fq_scale: public fq_scale {
|
||||
</pre>
|
||||
|
||||
<div class="class_def">
|
||||
|
||||
<h4>Constructor</h4>
|
||||
|
||||
<pre>
|
||||
lin_fq_scale(double size);
|
||||
</pre>
|
||||
|
||||
<dl>
|
||||
<dt><code>size</code></dt>
|
||||
|
||||
<dd><p style="margin-top: 0pt">The size of the transform. This
|
||||
corresponds to the "FFT size" parameter that is typically used with
|
||||
FFT based spectrum analyzers: the frequency spacing between bands is
|
||||
the sample rate divided by <i>size</i>, and the number of frequency
|
||||
bands produced is roughly half of <i>size</i> because negative
|
||||
frequencies are omitted. However, in the Gaborator, the <i>size</i>
|
||||
parameter is not limited to powers of two, or even to integer values,
|
||||
so any arbitrary band spacing of <i>x</i> Hz can be achieved by
|
||||
specifying a <i>size</i> of <code>fs / x</code> where
|
||||
<code>fs</code> is the sample rate in Hz.</p>
|
||||
|
||||
<p>Values from 2 to 10000 are supported. Larger values probably work but
|
||||
are not regularly tested.</p>
|
||||
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
</div>
|
||||
|
||||
<pre>
|
||||
};
|
||||
</pre>
|
||||
|
||||
<h3>Mel frequency scale</h3>
|
||||
|
||||
<p>The <code>mel_fq_scale</code> class represents a mel
|
||||
frequency scale, a scale designed to mimic human pitch perception
|
||||
such that one mel is close to the smallest percepible difference
|
||||
in frequency.
|
||||
It is defined by the formula <i>mel = 2595
|
||||
log<sub>10</sub>(1 + f / 700)</i>, where <i>f</i> 
|
||||
is the frequency in Hz.</p>
|
||||
|
||||
<p>The mel scale is approximately logarithmic
|
||||
at high frequencies, and approximately linear at low frequencies,
|
||||
with a smooth transition between the two.</p>
|
||||
|
||||
<pre>
|
||||
class mel_fq_scale: public fq_scale {
|
||||
</pre>
|
||||
|
||||
<div class="class_def">
|
||||
<h4>Constructor</h4>
|
||||
|
||||
<pre>
|
||||
mel_fq_scale(double bands_per_mel,
|
||||
double fs);
|
||||
</pre>
|
||||
|
||||
<dl>
|
||||
<dt><code>bands_per_mel</code></dt>
|
||||
|
||||
<dd>The number of frequency bands per mel. Supported values range
|
||||
from 0.003 (yielding a total of 12 frequency bands between 0 and
|
||||
22.5 kHz at a 44.1 kHz sample rate) to 1.0 (yielding some
|
||||
3924 bands). The minimum of 0.003 applies when the default overlap of
|
||||
0.7 is used; if the overlap is increased from the default, the minimum
|
||||
should be increased proportionally. Values larger than 1.0 probably
|
||||
work but are not regularly tested.</dd>
|
||||
|
||||
<dt><code>fs</code></dt>
|
||||
|
||||
<dd>The sample rate in Hz. Because the mel scale is defined in terms
|
||||
of absolute frequencies in Hz, the sample rate needs to be specified
|
||||
explicitly; this is an exception to the sample rate agnostic design
|
||||
of the rest of the Gaborator API. Values from 8000 to 96000 are
|
||||
supported. Values outside this range probably work but are not
|
||||
regularly tested.
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
</div>
|
||||
|
||||
<pre>
|
||||
};
|
||||
</pre>
|
||||
|
||||
<h2 id="phase_conventions">Phase conventions</h2>
|
||||
|
||||
<pre>
|
||||
enum class coef_phase { global, local };
|
||||
</pre>
|
||||
|
||||
<p>The spectrogram cofficients produced by the Gaborator are complex
|
||||
numbers, and as such they have both a magnitude and a phase. The
|
||||
phase can be defined in several different ways, using different
|
||||
reference points for what is considered a phase of zero.</p>
|
||||
|
||||
<p>Under one possible definition, an input signal of a real and
|
||||
positive impulse at some time <i>t</i>  will cause all
|
||||
spectrogram coefficients at that time <i>t</i>  to also be real
|
||||
and positive, or in other words, to have a phase of zero, for any
|
||||
frequency. We call this the <i>local</i> phase convention, and it is
|
||||
the default beginning with version 2 of the Gaborator.<p>
|
||||
|
||||
<p>Under an alternative definition, an input signal of a cosine wave
|
||||
at some frequency <i>f</i>  will cause all spectrogram coefficients at
|
||||
that frequency <i>f</i>  to have a phase of zero, at any point in time.
|
||||
We call this the <i>global</i> phase convention, and it was the
|
||||
default in version 1 of the Gaborator.</p>
|
||||
|
||||
<p>The local phase convention is preferred because it guarantees that
|
||||
the coefficients for a given point in time only depend on the local
|
||||
signal behavior around that point in time, and not on how far that
|
||||
point in time is from the reference point of t=0. This avoids a
|
||||
loss of floating point precision for signals far from t=0.</p>
|
||||
|
||||
<h2>Spectrum Analysis Parameters</h2>
|
||||
|
||||
<p>A <code>parameters</code> object holds a set of parameters for
|
||||
the spectrum analysis and resynthesis.</p>
|
||||
|
||||
<pre>
|
||||
class parameters {
|
||||
</pre>
|
||||
|
||||
<div class="class_def">
|
||||
<h3>Constructor</h3>
|
||||
|
||||
<p>The only required parameter is the frequency scale, which is passed
|
||||
as an argument to the <code>parameters</code> constructor. Optional
|
||||
parameters may be specified by assigning to data members of
|
||||
the <code>parameters</code> object after construction.</p>
|
||||
|
||||
<pre>
|
||||
parameters(const fq_scale &scale);
|
||||
</pre>
|
||||
<h3>Legacy Constructor</h3>
|
||||
<p>For backwards compatibilty with version 1 of the Gaborator,
|
||||
the following constrctor is also supported:</p>
|
||||
<pre>
|
||||
parameters(double bands_per_octave,
|
||||
double ff_min,
|
||||
double ff_ref = 1.0);
|
||||
</pre>
|
||||
<p>This constructs a set of parameters with a logarithmic frequency scale, like
|
||||
<code>parameters(log_fq_scale(bands_per_octave, ff_min, ff_ref))</code>
|
||||
except that the <code>phase</code> member is initialized to the version 1 default
|
||||
of <code>coef_phase::global</code> rather than the verison 2 default of
|
||||
<code>coef_phase::local</code>.
|
||||
</p>
|
||||
|
||||
<h3>Phase convention</h3>
|
||||
|
||||
<p>The <a href="#phase_conventions">phase convention</a>
|
||||
to be used for the analysis and resynthesis
|
||||
can be set or examined using the public data member <code>phase</code>.
|
||||
|
||||
<pre>
|
||||
coef_phase phase;
|
||||
</pre>
|
||||
|
||||
<h3>Overlap</h3>
|
||||
|
||||
<p>The amount of <a href="../overview.html#overlap">overlap</a> between
|
||||
adjacent bandpass bands can be set or examined using the public data
|
||||
member <code>overlap</code>. The default is 0.7, meaning each band
|
||||
will have a standard deviation of 0.7 times the band spacing.</p>
|
||||
|
||||
<pre>
|
||||
double overlap;
|
||||
</pre>
|
||||
|
||||
<h3>Comparison</h3>
|
||||
<p>
|
||||
Comparison operators are provided for compatibility with
|
||||
standard container classes. The ordering is arbitrary but consistent.
|
||||
standard container classes.
|
||||
</p>
|
||||
<pre>
|
||||
bool operator<(const parameters &rhs) const;
|
||||
|
@ -69,10 +303,6 @@ bool operator==(const parameters &rhs) const;
|
|||
|
||||
<h2>Spectrogram Coefficients</h2>
|
||||
|
||||
<pre class="forward_decl">
|
||||
template<class T> class analyzer;
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
A <code>coefs</code> object stores a set of spectrogram coefficients.
|
||||
It is a dynamic data structure and will be automatically grown to
|
||||
|
@ -85,7 +315,7 @@ it will default to <code>std::complex<T></code>.
|
|||
</p>
|
||||
|
||||
<pre>
|
||||
template<class T, class C = std::complex<T>>
|
||||
template <class T, class C = std::complex<T>>
|
||||
class coefs {
|
||||
</pre>
|
||||
<div class="class_def">
|
||||
|
@ -113,7 +343,7 @@ the floating-point type to use for the calculations. This is typically <code>fl
|
|||
alternatively, <code>double</code> can be used for increased accuracy at the
|
||||
expense of speed and memory consumption.</p>
|
||||
|
||||
<pre>template<class T>
|
||||
<pre>template <class T>
|
||||
class analyzer {</pre>
|
||||
<div class="class_def">
|
||||
|
||||
|
@ -175,12 +405,12 @@ etc.
|
|||
<pre>
|
||||
void
|
||||
synthesize(const coefs<T> &coefs,
|
||||
uint64_t t0,
|
||||
uint64_t t1,
|
||||
int64_t t0,
|
||||
int64_t t1,
|
||||
T *signal) const;
|
||||
</pre>
|
||||
<p>Synthesize signal samples from the coefficients <code>coef</code> and store
|
||||
them at <code>*signal</code>.
|
||||
<p>Synthesize signal samples from the coefficients <code>coefs</code>
|
||||
and store them at <code>*signal</code>.
|
||||
</p>
|
||||
<dl>
|
||||
<dt><code>coefs</code></dt><dd>The coefficients to synthesize the signal from.</dd>
|
||||
|
@ -207,73 +437,155 @@ be multiple of a large power of two.<p>
|
|||
<h3>Frequency Band Numbering</h3>
|
||||
|
||||
<p>The frequency bands of the analysis filter bank are numbered by
|
||||
nonnegative integers that increase towards lower (sic) frequencies.
|
||||
There is a number of <i>bandpass bands</i> corresponding to the
|
||||
non-negative integers that increase towards lower (sic) frequencies.
|
||||
Although this numbering may seem backwards, it makes a certain
|
||||
amount of sense for constant-Q analysis where the
|
||||
frequency range has a hard upper limit (the Nyquist frequency) but no
|
||||
hard lower limit. Numbering the bands from high to low frequency
|
||||
allows the frequency range to be extended into arbitrarily low
|
||||
frequencies without using negative band numbers or having to renumber
|
||||
existing bands. It also maps logically to computer graphics
|
||||
coordinate systems where the Y coordinate increases downwards.</p>
|
||||
|
||||
<p>When using a logarithmic frequency scale,
|
||||
there is a number of <i>bandpass bands</i> corresponding to the
|
||||
logarithmically spaced bandpass analysis filters, from near 0.5
|
||||
(half the sample rate) to
|
||||
(the Nyquist frequency, or half the sample rate) to
|
||||
near f<sub>min</sub>, and a single <i>lowpass band</i> containing the
|
||||
residual signal from frequencies below f<sub>min</sub>.
|
||||
The numbering can be examined using the following methods:
|
||||
Note that there is no special highpass band; frequencies at or
|
||||
close to the Nyquist frequency are included in the highest-frequency
|
||||
bandpass band(s).
|
||||
</p>
|
||||
|
||||
<p>When using a linear or mel frequency scale, all the bands are
|
||||
considered <i>bandpass bands</i> and there is no special <i>lowpass band</i>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
The band numbering can be examined using the following methods:
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
int bands_begin() const;
|
||||
</pre>
|
||||
<p>
|
||||
Return the lowest valid band number. This is always 0.
|
||||
</p>
|
||||
<pre>
|
||||
int bands_end() const;
|
||||
</pre>
|
||||
<p>
|
||||
Return the highest valid band number plus one. This is also the
|
||||
total number of bands.
|
||||
</p>
|
||||
<pre>
|
||||
int bandpass_bands_begin() const;
|
||||
</pre>
|
||||
<p>
|
||||
Return the smallest valid bandpass band number, corresponding to the
|
||||
highest-frequency bandpass filter.</p>
|
||||
Return the lowest valid bandpass band number, corresponding to the
|
||||
highest-frequency bandpass band. This is currently 0, but should
|
||||
a future version of the Gaborator support a highpass band, that will
|
||||
become band 0 and <code>bandpass_bands_begin()</code> will return 1.
|
||||
</p>
|
||||
<pre>
|
||||
int bandpass_bands_end() const;
|
||||
</pre>
|
||||
<p>
|
||||
Return the bandpass band number one past the highest valid bandpass
|
||||
band number, corresponding to one past the lowest-frequency bandpass
|
||||
filter.
|
||||
Return the highest valid bandpass band number plus one, corresponding
|
||||
to one past the lowest-frequency bandpass band.
|
||||
</p>
|
||||
<pre>
|
||||
int band_lowpass() const;
|
||||
</pre>
|
||||
<p>
|
||||
Return the band number of the lowpass band.
|
||||
For an anzlyer using a logarithmic frequency scale only,
|
||||
return the band number of the lowpass band. This returns the same
|
||||
number as <code>bandpass_bands_end()</code>, but is preferred for
|
||||
clarity when referring to the lowpass band rather than
|
||||
the excluded upper bound of the range of bandpass bands.
|
||||
</p>
|
||||
<pre>
|
||||
int band_ref() const;
|
||||
</pre>
|
||||
<p>
|
||||
Return the band number corresponding to the reference frequency
|
||||
For an anzlyer using a logarithmic frequency scale only,
|
||||
return the band number corresponding to the reference frequency
|
||||
<code>ff_ref</code>. If <code>ff_ref</code> falls within
|
||||
the frequency range of the bandpass filter bank, this will
|
||||
be a valid bandpass band number, otherwise it will not.
|
||||
the frequency range of the bandpass filter bank, this is
|
||||
a valid bandpass band number, otherwise it is not.
|
||||
</p>
|
||||
|
||||
<h3>Band properties</h3>
|
||||
|
||||
<pre>
|
||||
double band_ff(int band) const;
|
||||
</pre>
|
||||
<p>
|
||||
Return the center frequency of band number <i>band</i>, in units of the
|
||||
sampling frequency.
|
||||
sample rate. The center frequency of the lowpass band (if present) is 0.
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
double bandpass_band_ff(double band) const;
|
||||
</pre>
|
||||
<p>
|
||||
Return the center frequency of bandpass band number <i>band</i>, in
|
||||
units of the sample rate. Unlike <code>band_ff</code>, this takes a
|
||||
floating point argument and supports interpolating between bands by
|
||||
giving a non-integer band number, and/or extrapolating outside the valid
|
||||
range of bandpass band numbers.
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
double band_q(double band) const;
|
||||
</pre>
|
||||
<p>
|
||||
Return the Q (quality factor) of the analysis filter of bandpass
|
||||
band <i>band</i>. Q is defined as the -3 dB bandwidth divided by
|
||||
the center frequency. When using a logarithmic frequency scale, the
|
||||
bands form a constant-Q filter bank and Q is the same for all bands,
|
||||
and when using a linear or mel frequency scale, Q will vary from band
|
||||
to band.
|
||||
</p>
|
||||
|
||||
<h3>Support</h3>
|
||||
|
||||
<pre>
|
||||
double analysis_support() const;
|
||||
double band_analysis_support(int band) const;
|
||||
</pre>
|
||||
<p>Returns the one-sided worst-case time domain <i>support</i> of any of the
|
||||
analysis filters. When calling <code>analyze()</code> with a sample at time <i>t</i>,
|
||||
only spectrogram coefficients within the time range <i>t ± support</i>
|
||||
will be significantly changed. Coefficients outside the range may change,
|
||||
<p>Return the one-sided time domain <i>support</i> of any of the analysis
|
||||
filter for band <i>band</i>.
|
||||
When calling <code>analyze()</code> with a sample at time <i>t</i>,
|
||||
the spectrogram coefficients of band <i>band</i> will change
|
||||
significantly only within the time range <i>t ± support</i>.
|
||||
Coefficients outside the range may change slightly,
|
||||
but the changes will sufficiently small that they may be ignored without
|
||||
significantly reducing accuracy.</p>
|
||||
|
||||
<pre>
|
||||
double analysis_support() const;
|
||||
</pre>
|
||||
<p>Return the largest <code>band_analysis_support(band)</code> of any band.
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
double band_synthesis_support(int band) const;
|
||||
</pre>
|
||||
<p>Returns the one-sided time domain <i>support</i> of the
|
||||
reconstruction filter for band <i>band</i>. When
|
||||
calling <code>synthesize()</code> to synthesize a sample at
|
||||
time <i>t</i>, the sample will only be significantly affected by
|
||||
spectrogram coefficients of band <i>band</i> in the time range <i>t
|
||||
± support</i>. Coefficients outside the range may be used in
|
||||
the synthesis, but substituting zeroes for the actual coefficient
|
||||
values will not significantly reduce accuracy.</p>
|
||||
|
||||
<pre>
|
||||
double synthesis_support() const;
|
||||
</pre>
|
||||
<p>Returns the one-sided worst-case time domain <i>support</i> of any of the
|
||||
reconstruction filters. When calling <code>synthesize()</code> to
|
||||
synthesize a sample at time <i>t</i>, the sample will only be
|
||||
significantly affected by spectrogram coefficients in the time
|
||||
range <i>t ± support</i>. Coefficients outside the range may
|
||||
be used in the synthesis, but substituting zeroes for the actual
|
||||
coefficient values will not significantly reduce accuracy.</p>
|
||||
<p>Return the largest <code>band_synthesis_support(band)</code> of any band.
|
||||
</p>
|
||||
|
||||
</div>
|
||||
<pre>
|
||||
|
@ -312,7 +624,7 @@ and <code>t1</code>, respectively.
|
|||
<p>The function <code>f</code> should have the call signature</p>
|
||||
<dd>
|
||||
<pre>
|
||||
template<class T>
|
||||
template <class T>
|
||||
void f(int b, int64_t t, std::complex<T> &c0, std::complex<T> &ci...);
|
||||
</pre>
|
||||
<p>where</p>
|
||||
|
@ -344,13 +656,19 @@ coefficient <i>is</i> iterated over and is missing from one of the additional
|
|||
coefficient sets <code>ci...</code>, it will be automatically created
|
||||
and initialized to zero in that additional coefficient set.</p>
|
||||
|
||||
<p>For example, in the common case where the processing takes one input
|
||||
and produces one output, <code>c0</code> should be the input
|
||||
and <code>c1</code> should be the output. This ensures that the
|
||||
entire input is iterated over, and that the output coefficients get
|
||||
created as needed.</p>
|
||||
|
||||
<p><i>Note: The template parameters <code>C0</code>
|
||||
and <code>CI</code>... exist to support the processing of coefficient
|
||||
sets containing data of types other
|
||||
than <code>std::complex<T></code>, which is not currently part of the
|
||||
documented API. In typical use, there is no need to specify them when
|
||||
calling <code>apply()</code> because the template parameter list
|
||||
can be deduced, but if they are expicitly specified, they should all
|
||||
documented API. In typical use, there is no need to specify them
|
||||
because the template parameter list
|
||||
can be deduced, but if they are explicitly specified, they should all
|
||||
be <code>std::complex<T></code>.
|
||||
</i></p>
|
||||
|
||||
|
@ -401,8 +719,8 @@ bounded.</p>
|
|||
|
||||
<h3>Legacy API For Iterating Over Existing Coefficients</h3>
|
||||
|
||||
<p>Prior to version 1.5, the only way to iterate over
|
||||
coefficients was the <code>apply()</code> function.
|
||||
<p>Prior to version 1.5, iteration over the coefficients was done
|
||||
using the <code>apply()</code> function.
|
||||
It is similar to <code>process()</code>, except that it
|
||||
</p>
|
||||
<ul>
|
||||
|
@ -439,7 +757,7 @@ is applied to every coefficient.
|
|||
<dd>A function to apply to each coefficient in <code>c</code>,
|
||||
with the call signature
|
||||
<pre>
|
||||
template<class T>
|
||||
template <class T>
|
||||
void f(std::complex<T> &coef, int band, int64_t t);
|
||||
</pre>
|
||||
<dl>
|
||||
|
@ -450,10 +768,10 @@ void f(std::complex<T> &coef, int band, int64_t t);
|
|||
This may be either a bandpass band or the lowpass band.</dd>
|
||||
<dt><code>t</code></dt>
|
||||
<dd>The point in time the coefficient <code>c0</code> pertains to, in samples</dd>
|
||||
<dt><code>t0</code></dt><dd>When not <code>INT64_MIN</code>, only apply <code>f</code> to the coefficients for time ≥ <code>t0</code></dd>
|
||||
<dt><code>t1</code></dt><dd>When not <code>INT64_MAX</code>, only apply <code>f</code> to the coefficients for time < <code>t1</code></dd>
|
||||
</dl>
|
||||
</dd>
|
||||
<dt><code>t0</code></dt><dd>When not <code>INT64_MIN</code>, only apply <code>f</code> to the coefficients for time ≥ <code>t0</code></dd>
|
||||
<dt><code>t1</code></dt><dd>When not <code>INT64_MAX</code>, only apply <code>f</code> to the coefficients for time < <code>t1</code></dd>
|
||||
</dl>
|
||||
|
||||
<div class="nav"><span class="prev"><a href="intro.html">Previous: API Introduction</a></span><span class="next"><a href="render_h.html">Next: Spectrogram rendering: <code>render.h</code></a></span></div>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<!DOCTYPE html>
|
||||
<!--
|
||||
Copyright (C) 2018-2020 Andreas Gustafsson. This file is part of
|
||||
Copyright (C) 2018-2023 Andreas Gustafsson. This file is part of
|
||||
the Gaborator library source distribution. See the file LICENSE at
|
||||
the top level of the distribution for license information.
|
||||
-->
|
||||
|
@ -15,17 +15,17 @@ the top level of the distribution for license information.
|
|||
<p>The public API of the Gaborator library is defined in the HTML
|
||||
documentation in the form of annotated C++ declarations. These are
|
||||
similar to the actual declarations in the respective header files, but
|
||||
simplified for clarity and omitting implementation details.</p>
|
||||
simplified for clarity, omitting implementation details.</p>
|
||||
|
||||
<p>The actual implementation in the header file may be different in a
|
||||
number of ways but nonetheless compatible with the documented API.
|
||||
number of ways, but nonetheless compatible with the documented API.
|
||||
For example, classes may be declared using the keyword <code>struct</code>
|
||||
rather than <code>class</code>, function parameter names may be
|
||||
different, types may be declared using different but equivalent
|
||||
typedefs, and functions or templates in the header file may have
|
||||
additional arguments with default values. Any classes, functions, and
|
||||
other definitions not mentioned in the documentation should be
|
||||
considered private and are subject to change or deletion without
|
||||
considered private and subject to change or deletion without
|
||||
notice.
|
||||
</p>
|
||||
|
||||
|
@ -35,6 +35,22 @@ with <code>gaborator::</code>, or use <code>using namespace
|
|||
gaborator;</code>.
|
||||
</p>
|
||||
|
||||
<h2>Frequency and Time Units</h2>
|
||||
|
||||
<p>The Gaborator is generally <i>sample rate agnostic</i>: it does not
|
||||
know or care what the actual sample rate of the signal is, and will
|
||||
work equally well for signals measured in milliherz or gigaherz.
|
||||
Frequencies are specified as dimensionless numbers, in units of the
|
||||
sample rate. Since the highest frequency that can be represented by a
|
||||
sampled signal is half the sample rate, frequencies in the Gaborator
|
||||
range from 0 to 0.5. In the API, arguments that represent frequencies
|
||||
in this way have names beginning with <code>ff_</code> (short
|
||||
for <i>fractional frequency</i>) as a reminder that the values are
|
||||
fractions of the sample rate rather than having a unit such as Hz.</p>
|
||||
|
||||
<p>Similarly, time is measured not in an absolute unit such as
|
||||
seconds, but the dimensionless unit of samples.</p>
|
||||
|
||||
<div class="nav"><span class="prev"><a href="../synth.html">Previous: Example 5: Synthesis from Scratch</a></span><span class="next"><a href="gaborator_h.html">Next: Spectrum analysis and synthesis: <code>gaborator.h</code></a></span></div>
|
||||
|
||||
</body>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<!DOCTYPE html>
|
||||
<!--
|
||||
Copyright (C) 2018-2020 Andreas Gustafsson. This file is part of
|
||||
Copyright (C) 2018-2023 Andreas Gustafsson. This file is part of
|
||||
the Gaborator library source distribution. See the file LICENSE at
|
||||
the top level of the distribution for license information.
|
||||
-->
|
||||
|
@ -32,9 +32,15 @@ down by powers of two.
|
|||
<dt><code>c</code></dt>
|
||||
<dd>A set of spectrogram coefficients to render</dd>
|
||||
<dt><code>xorigin</code></dt>
|
||||
<dd>The point in time corresponding to pixel X coordinate 0, in samples</dd>
|
||||
<dd>The point in time corresponding to pixel X coordinate 0, in samples.
|
||||
Due to limitations of the implementation, this must currently be 0.
|
||||
Instead, horizontal translation must be done by means of converting the
|
||||
origin to pixel units and adding it to both <code>xi0</code> and <code>yi0</code>.</dd>
|
||||
<dt><code>yorigin</code></dt>
|
||||
<dd>The band number of the frequency band corresponding to pixel Y coordinate 0</dd>
|
||||
<dd>The band number of the frequency band corresponding to pixel Y coordinate 0.
|
||||
Typically 0, or the band number of a reference frequency such as 440 Hz,
|
||||
to make that frequency always coincide exactly with a pixel regardless of the
|
||||
scale factor.</dd>
|
||||
<dt><code>xi0</code></dt>
|
||||
<dd>The X coordinate of the first pixel to render</dd>
|
||||
<dt><code>xi1</code></dt>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<!DOCTYPE html>
|
||||
<!--
|
||||
Copyright (C) 2017-2020 Andreas Gustafsson. This file is part of
|
||||
Copyright (C) 2017-2023 Andreas Gustafsson. This file is part of
|
||||
the Gaborator library source distribution. See the file LICENSE at
|
||||
the top level of the distribution for license information.
|
||||
-->
|
||||
|
@ -91,11 +91,13 @@ mix down the channels to mono, into a new <code>std::vector<float></code>:
|
|||
}
|
||||
</pre>
|
||||
|
||||
<h2>The Spectrum Analysis Parameters</h2>
|
||||
<h2>The Frequency Scale</h2>
|
||||
|
||||
<p>Next, we need to choose some parameters for the spectrum analysis:
|
||||
the frequency resolution, the frequency range, and optionally a
|
||||
reference frequency.</p>
|
||||
<p>To make a constant-Q spectrogram, we will use a logarithmic
|
||||
frequency scale, represented by a <code>gaborator::log_fq_scale</code> object.
|
||||
To create a <code>log_fq_scale</code>, we must choose some
|
||||
parameters: the frequency resolution, the frequency range, and
|
||||
optionally a reference frequency.</p>
|
||||
|
||||
<p>The frequency resolution is specified as a number of frequency
|
||||
bands per octave. A typical number for analyzing music signals is 48
|
||||
|
@ -123,10 +125,22 @@ typically 440 Hz, the standard tuning of the note <i>A<sub>4</sub></i>.
|
|||
Like the minimum frequency, it is given in
|
||||
units of the sample rate, so we pass <code>440.0 / fs</code>.</p>
|
||||
|
||||
<p>The parameters are held in an object of type
|
||||
<code>gaborator::parameters</code>:
|
||||
<p>Let's construct a <code>log_fq_scale</code>:</p>
|
||||
|
||||
<pre>
|
||||
gaborator::parameters params(48, 20.0 / fs, 440.0 / fs);
|
||||
gaborator::log_fq_scale scale(48, 20.0 / fs, 440.0 / fs);
|
||||
</pre>
|
||||
|
||||
<h2>The Spectrum Analysis Parameters</h2>
|
||||
|
||||
<p>The frequency scale forms a part of the spectrum analysis parameters,
|
||||
which are stored in an object of type <code>gaborator::parameters</code>.
|
||||
In this example, there is no need to specify parameters other than the
|
||||
frequency scale, so the parameters can be constructed simply by calling
|
||||
the <code>gaborator::parameters</code> constructor with the frequency scale
|
||||
as an argument:</p>
|
||||
<pre>
|
||||
gaborator::parameters params(scale);
|
||||
</pre>
|
||||
|
||||
<h2>The Spectrum Analyzer</h2>
|
||||
|
@ -170,7 +184,7 @@ array, in units of samples. Since we are passing the whole file at
|
|||
once, the beginning of the range is sample number zero, and the end is
|
||||
sample number <code>mono.size()</code>. The fourth argument is a
|
||||
reference to the set of coefficients that the results of the spectrum
|
||||
analysis will be stored in.
|
||||
analysis will be stored in (or to be precise, added to).
|
||||
</p>
|
||||
<pre>
|
||||
analyzer.analyze(mono.data(), 0, mono.size(), coefs);
|
||||
|
@ -323,16 +337,16 @@ we just need to finish <code>main()</code>:
|
|||
}
|
||||
</pre>
|
||||
|
||||
<a name="compiling"><h2>Compiling</h2></a>
|
||||
<a id="compiling"><h2>Compiling</h2></a>
|
||||
<p>
|
||||
If you are using macOS, Linux, NetBSD, or a similar system, you can build
|
||||
the example by running the following command in the <code>examples</code>
|
||||
subdirectory.
|
||||
You need to have <i>libsndfile</i> is installed and supported by
|
||||
You need to have <i>libsndfile</i> installed and supported by
|
||||
<code>pkg-config</code>.
|
||||
</p>
|
||||
<pre class="build Darwin Linux NetBSD FreeBSD">
|
||||
c++ -std=c++11 -I.. -O3 -ffast-math `pkg-config --cflags sndfile` render.cc `pkg-config --libs sndfile` -o render
|
||||
<pre class="build Darwin Linux NetBSD FreeBSD OpenBSD">
|
||||
c++ -std=c++11 -I.. -O3 -ffast-math $(pkg-config --cflags sndfile) render.cc $(pkg-config --libs sndfile) -o render
|
||||
</pre>
|
||||
|
||||
<h2>Compiling for Speed</h2>
|
||||
|
@ -344,34 +358,34 @@ can use the FFT from Apple's vDSP library by defining
|
|||
framework:
|
||||
</p>
|
||||
<pre class="build Darwin">
|
||||
c++ -std=c++11 -I.. -O3 -ffast-math -DGABORATOR_USE_VDSP `pkg-config --cflags sndfile` render.cc `pkg-config --libs sndfile` -framework Accelerate -o render
|
||||
c++ -std=c++11 -I.. -O3 -ffast-math -DGABORATOR_USE_VDSP $(pkg-config --cflags sndfile) render.cc $(pkg-config --libs sndfile) -framework Accelerate -o render
|
||||
</pre>
|
||||
|
||||
<p>On Linux and NetBSD, you can use the PFFFT (Pretty Fast FFT) library.
|
||||
You can get the latest version from
|
||||
<a href="https://bitbucket.org/jpommier/pffft">https://bitbucket.org/jpommier/pffft</a>,
|
||||
or the exact version that was used for testing from gaborator.com:
|
||||
or the exact version the example code was tested with from gaborator.com:
|
||||
</p>
|
||||
<!-- ftp https://bitbucket.org/jpommier/pffft/get/29e4f76ac53b.zip -->
|
||||
<pre class="build Linux NetBSD FreeBSD">
|
||||
<pre class="build Linux NetBSD FreeBSD OpenBSD">
|
||||
wget http://download.gaborator.com/mirror/pffft/29e4f76ac53b.zip
|
||||
unzip 29e4f76ac53b.zip
|
||||
mv jpommier-pffft-29e4f76ac53b pffft
|
||||
</pre>
|
||||
<p>Then, compile it:</p>
|
||||
<pre class="build Linux NetBSD FreeBSD">
|
||||
<pre class="build Linux NetBSD FreeBSD OpenBSD">
|
||||
cc -c -O3 -ffast-math pffft/pffft.c -o pffft/pffft.o
|
||||
</pre>
|
||||
<p>(If you are building for ARM, you will need to add <code>-mfpu=neon</code> to
|
||||
both the above compilation command and the ones below.)</p>
|
||||
<p>PFFFT is single precision only, but it comes with a copy of FFTPACK which can
|
||||
be used for double-precision FFTs. Let's compile that, too:</p>
|
||||
<pre class="build Linux NetBSD FreeBSD">
|
||||
<pre class="build Linux NetBSD FreeBSD OpenBSD">
|
||||
cc -c -O3 -ffast-math -DFFTPACK_DOUBLE_PRECISION pffft/fftpack.c -o pffft/fftpack.o
|
||||
</pre>
|
||||
<p>Then build the example and link it with both PFFFT and FFTPACK:</p>
|
||||
<pre class="build Linux NetBSD FreeBSD">
|
||||
c++ -std=c++11 -I.. -Ipffft -O3 -ffast-math -DGABORATOR_USE_PFFFT `pkg-config --cflags sndfile` render.cc pffft/pffft.o pffft/fftpack.o `pkg-config --libs sndfile` -o render
|
||||
<pre class="build Linux NetBSD FreeBSD OpenBSD">
|
||||
c++ -std=c++11 -I.. -Ipffft -O3 -ffast-math -DGABORATOR_USE_PFFFT $(pkg-config --cflags sndfile) render.cc pffft/pffft.o pffft/fftpack.o $(pkg-config --libs sndfile) -o render
|
||||
</pre>
|
||||
|
||||
<h2>Running</h2>
|
||||
|
|
22
doc/snr.html
22
doc/snr.html
|
@ -1,6 +1,6 @@
|
|||
<!DOCTYPE html>
|
||||
<!--
|
||||
Copyright (C) 2019-2020 Andreas Gustafsson. This file is part of
|
||||
Copyright (C) 2019-2023 Andreas Gustafsson. This file is part of
|
||||
the Gaborator library source distribution. See the file LICENSE at
|
||||
the top level of the distribution for license information.
|
||||
-->
|
||||
|
@ -20,7 +20,7 @@ and comparing the resynthesis result to the original.
|
|||
</p>
|
||||
|
||||
<p>Since it does not involve any audio file I/O, this example
|
||||
does not require the sndfile library, making it the shortest
|
||||
does not require the libsndfile library, making it the shortest
|
||||
and simplest one by far.</p>
|
||||
|
||||
<h2>Preamble</h2>
|
||||
|
@ -34,8 +34,8 @@ and simplest one by far.</p>
|
|||
|
||||
<h2>Amplitude Measurement</h2>
|
||||
<p>To calculate the signal-to-noise ratio, we need to measure the
|
||||
amplitude of the orignal signal and the error residue. We will use
|
||||
the root-mean-square amplitude, which is calculcated by the
|
||||
amplitude of the original signal and the error residue. We will use
|
||||
the root-mean-square amplitude, which is calculated by the
|
||||
function <code>rms()</code>.
|
||||
</p>
|
||||
<pre>
|
||||
|
@ -64,10 +64,10 @@ int main(int argc, char **argv) {
|
|||
<p>Then we create a spectrum analyzer with 48 bands per octave
|
||||
and a frequency range of 3 decades (0.0005 to 0.5 times the sample rate):</p>
|
||||
<pre>
|
||||
gaborator::parameters params(48, 5e-4);
|
||||
gaborator::parameters params(gaborator::log_fq_scale(48, 5e-4));
|
||||
gaborator::analyzer<float> analyzer(params);
|
||||
</pre>
|
||||
<p>...and run the spectrum analyzis:</p>
|
||||
<p>...and run the spectrum analysis:</p>
|
||||
<pre>
|
||||
gaborator::coefs<float> coefs(analyzer);
|
||||
analyzer.analyze(signal_in.data(), 0, len, coefs);
|
||||
|
@ -96,16 +96,16 @@ and a frequency range of 3 decades (0.0005 to 0.5 times the sample rate):</p>
|
|||
<p>Like <a href="render.html#compiling">Example 1</a>, this example
|
||||
can be built using a one-line build command:
|
||||
</p>
|
||||
<pre class="build Darwin Linux NetBSD FreeBSD">
|
||||
c++ -std=c++11 -I.. -O3 -ffast-math `pkg-config --cflags sndfile` snr.cc `pkg-config --libs sndfile` -o snr
|
||||
<pre class="build Darwin Linux NetBSD FreeBSD OpenBSD">
|
||||
c++ -std=c++11 -I.. -O3 -ffast-math $(pkg-config --cflags sndfile) snr.cc $(pkg-config --libs sndfile) -o snr
|
||||
</pre>
|
||||
<p>Or using the vDSP FFT on macOS:</p>
|
||||
<pre class="build Darwin">
|
||||
c++ -std=c++11 -I.. -O3 -ffast-math -DGABORATOR_USE_VDSP `pkg-config --cflags sndfile` snr.cc `pkg-config --libs sndfile` -framework Accelerate -o snr
|
||||
c++ -std=c++11 -I.. -O3 -ffast-math -DGABORATOR_USE_VDSP $(pkg-config --cflags sndfile) snr.cc $(pkg-config --libs sndfile) -framework Accelerate -o snr
|
||||
</pre>
|
||||
<p>Or using PFFFT (see <a href="render.html#compiling">Example 1</a> for how to download and build PFFFT):</p>
|
||||
<pre class="build">
|
||||
c++ -std=c++11 -I.. -Ipffft -O3 -ffast-math -DGABORATOR_USE_PFFFT `pkg-config --cflags sndfile` snr.cc pffft/pffft.o pffft/fftpack.o `pkg-config --libs sndfile` -o snr
|
||||
c++ -std=c++11 -I.. -Ipffft -O3 -ffast-math -DGABORATOR_USE_PFFFT $(pkg-config --cflags sndfile) snr.cc pffft/pffft.o pffft/fftpack.o $(pkg-config --libs sndfile) -o snr
|
||||
</pre>
|
||||
|
||||
<h2>Running</h2>
|
||||
|
@ -113,7 +113,7 @@ c++ -std=c++11 -I.. -Ipffft -O3 -ffast-math -DGABORATOR_USE_PFFFT `pkg-config --
|
|||
<pre class="run">
|
||||
./snr
|
||||
</pre>
|
||||
<p>This will print the SNR which should be more than 100 dB if the library is working correctly.</p>
|
||||
<p>This will print the SNR which should be more than 100 dB when the library is working correctly.</p>
|
||||
|
||||
<div class="nav"><span class="prev"><a href="stream.html">Previous: Example 3: Streaming</a></span><span class="next"><a href="synth.html">Next: Example 5: Synthesis from Scratch</a></span></div>
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<!DOCTYPE html>
|
||||
<!--
|
||||
Copyright (C) 2018-2020 Andreas Gustafsson. This file is part of
|
||||
Copyright (C) 2018-2023 Andreas Gustafsson. This file is part of
|
||||
the Gaborator library source distribution. See the file LICENSE at
|
||||
the top level of the distribution for license information.
|
||||
-->
|
||||
|
@ -18,7 +18,7 @@ the top level of the distribution for license information.
|
|||
rather than operating on a complete recording at once as in the previous
|
||||
examples.</p>
|
||||
|
||||
<p>This program doesn't do anything particulary useful — it just
|
||||
<p>This program doesn't do anything particularly useful — it just
|
||||
inverts the phase of the signal, but not using the obvious method of
|
||||
changing the sign of each sample but by changing the sign of each
|
||||
spectrogram coefficient. Consider the expression <code>coef =
|
||||
|
@ -87,7 +87,8 @@ to reduce the latency, the number of frequency bands per octave is reduced
|
|||
from 48 to 12 (one per semitone), and the lower frequency limit of
|
||||
the bandpass filter bank is raised from 20 Hz to 200 Hz.</p>
|
||||
<pre>
|
||||
gaborator::parameters params(12, 200.0 / fs, 440.0 / fs);
|
||||
gaborator::log_fq_scale scale(12, 200.0 / fs, 440.0 / fs);
|
||||
gaborator::parameters params(scale);
|
||||
gaborator::analyzer<float> analyzer(params);
|
||||
</pre>
|
||||
|
||||
|
@ -185,7 +186,7 @@ the last time and will not be affected by future input blocks.
|
|||
Therefore, it is now safe to examine and/or modify these
|
||||
coefficients as required by your application. Here, by way
|
||||
of example, we simply change their signs to invert the phase of the signal.
|
||||
Note that unlike the earlier filter example where <code>prorcess()</code>
|
||||
Note that unlike the earlier filter example where <code>process()</code>
|
||||
applied a function to all the coefficients, here it is applied only to
|
||||
the coefficients within a limited time range.
|
||||
</p>
|
||||
|
@ -225,7 +226,7 @@ blocksize</code>.</p>
|
|||
</pre>
|
||||
<p>Coefficients older than <code>t_out + blocksize - synthesis_support</code>
|
||||
will no longer be needed to synthesize the next block of output signal, so
|
||||
it's now OK to forget them and free the memory they used:
|
||||
we can now forget them to free up the memory they used:
|
||||
</p>
|
||||
<pre>
|
||||
forget_before(analyzer, coefs, t_out + blocksize - synthesis_support);
|
||||
|
@ -247,16 +248,16 @@ it's now OK to forget them and free the memory they used:
|
|||
<h2>Compiling</h2>
|
||||
<p>Like the previous ones, this example can also be built using a one-line build command:
|
||||
</p>
|
||||
<pre class="build Darwin Linux NetBSD FreeBSD">
|
||||
c++ -std=c++11 -I.. -O3 -ffast-math `pkg-config --cflags sndfile` stream.cc `pkg-config --libs sndfile` -o stream
|
||||
<pre class="build Darwin Linux NetBSD FreeBSD OpenBSD">
|
||||
c++ -std=c++11 -I.. -O3 -ffast-math $(pkg-config --cflags sndfile) stream.cc $(pkg-config --libs sndfile) -o stream
|
||||
</pre>
|
||||
<p>Or using the vDSP FFT on macOS:</p>
|
||||
<pre class="build Darwin">
|
||||
c++ -std=c++11 -I.. -O3 -ffast-math -DGABORATOR_USE_VDSP `pkg-config --cflags sndfile` stream.cc `pkg-config --libs sndfile` -framework Accelerate -o stream
|
||||
c++ -std=c++11 -I.. -O3 -ffast-math -DGABORATOR_USE_VDSP $(pkg-config --cflags sndfile) stream.cc $(pkg-config --libs sndfile) -framework Accelerate -o stream
|
||||
</pre>
|
||||
<p>Or using PFFFT (see <a href="render.html#compiling">Example 1</a> for how to download and build PFFFT):</p>
|
||||
<pre class="build">
|
||||
c++ -std=c++11 -I.. -Ipffft -O3 -ffast-math -DGABORATOR_USE_PFFFT `pkg-config --cflags sndfile` stream.cc pffft/pffft.o pffft/fftpack.o `pkg-config --libs sndfile` -o stream
|
||||
c++ -std=c++11 -I.. -Ipffft -O3 -ffast-math -DGABORATOR_USE_PFFFT $(pkg-config --cflags sndfile) stream.cc pffft/pffft.o pffft/fftpack.o $(pkg-config --libs sndfile) -o stream
|
||||
</pre>
|
||||
|
||||
<h2>Running</h2>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<!DOCTYPE html>
|
||||
<!--
|
||||
Copyright (C) 2020 Andreas Gustafsson. This file is part of
|
||||
Copyright (C) 2020-2023 Andreas Gustafsson. This file is part of
|
||||
the Gaborator library source distribution. See the file LICENSE at
|
||||
the top level of the distribution for license information.
|
||||
-->
|
||||
|
@ -43,7 +43,7 @@ int main(int argc, char **argv) {
|
|||
<p>Although this example does not perform any analysis, we nonetheless
|
||||
need to create an <code>analyzer</code> object, as it is used for both
|
||||
analysis and synthesis purposes. To generate the frequencies of the
|
||||
12-note equal-tempered scale, we need 12 bands per octave; a multiple
|
||||
12-note equal-tempered scale, we need 12 bands per octave. A multiple
|
||||
of 12 would also work, but here we don't need the added frequency
|
||||
resolution that would bring, and the time resolution would be
|
||||
worse.</p>
|
||||
|
@ -55,7 +55,22 @@ bandpass filter bank, but that doesn't matter.</p>
|
|||
|
||||
<pre>
|
||||
double fs = 44100;
|
||||
gaborator::parameters params(12, 20.0 / fs, 8.18 / fs);
|
||||
gaborator::log_fq_scale scale(12, 20.0 / fs, 8.18 / fs);
|
||||
gaborator::parameters params(scale);
|
||||
</pre>
|
||||
|
||||
<p>As we create new complex coefficients from scratch, we need to be
|
||||
careful about their phases. For any given frequency, the portions of
|
||||
the output signal contributed by the coefficients at different points
|
||||
in time need to be in phase so that they combine constructively into a
|
||||
single (co)sine wave rather than interfering destructively. The
|
||||
easiest way to achieve this is to select the global phase convention
|
||||
in the synthesis parameters. With this setting, the coefficient phases
|
||||
will be interpreted relative to the common reference point of t=0
|
||||
rather than the time of each individual coefficient:</p>
|
||||
|
||||
<pre>
|
||||
params.phase = gaborator::coef_phase::global;
|
||||
gaborator::analyzer<float> analyzer(params);
|
||||
</pre>
|
||||
|
||||
|
@ -136,9 +151,9 @@ in front of <code>midi_note</code>.
|
|||
|
||||
<p>We can now synthesize audio from the coefficients by
|
||||
calling <code>synthesize()</code>. Audio will be generated
|
||||
starting half a second before the first note to allow for the pre-ringing
|
||||
of the synthesis filter, and ending a few seconds after the
|
||||
last note to allow for its decay.
|
||||
starting half a second before the first note to allow for pre-ringing
|
||||
of the synthesis filters, and ending a few seconds after the
|
||||
last note to give the note time to decay.
|
||||
</p>
|
||||
<pre>
|
||||
double audio_start_time = -0.5;
|
||||
|
@ -188,16 +203,16 @@ we need to choose a file format for the output file by filling in the
|
|||
<p>Like <a href="render.html#compiling">Example 1</a>, this example
|
||||
can be built using a one-line build command:
|
||||
</p>
|
||||
<pre class="build Darwin Linux NetBSD FreeBSD">
|
||||
c++ -std=c++11 -I.. -O3 -ffast-math `pkg-config --cflags sndfile` synth.cc `pkg-config --libs sndfile` -o synth
|
||||
<pre class="build Darwin Linux NetBSD FreeBSD OpenBSD">
|
||||
c++ -std=c++11 -I.. -O3 -ffast-math $(pkg-config --cflags sndfile) synth.cc $(pkg-config --libs sndfile) -o synth
|
||||
</pre>
|
||||
<p>Or using the vDSP FFT on macOS:</p>
|
||||
<pre class="build Darwin">
|
||||
c++ -std=c++11 -I.. -O3 -ffast-math -DGABORATOR_USE_VDSP `pkg-config --cflags sndfile` synth.cc `pkg-config --libs sndfile` -framework Accelerate -o synth
|
||||
c++ -std=c++11 -I.. -O3 -ffast-math -DGABORATOR_USE_VDSP $(pkg-config --cflags sndfile) synth.cc $(pkg-config --libs sndfile) -framework Accelerate -o synth
|
||||
</pre>
|
||||
<p>Or using PFFFT (see <a href="render.html">Example 1</a> for how to download and build PFFFT):</p>
|
||||
<pre class="build">
|
||||
c++ -std=c++11 -I.. -Ipffft -O3 -ffast-math -DGABORATOR_USE_PFFFT `pkg-config --cflags sndfile` synth.cc pffft/pffft.o pffft/fftpack.o `pkg-config --libs sndfile` -o synth
|
||||
c++ -std=c++11 -I.. -Ipffft -O3 -ffast-math -DGABORATOR_USE_PFFFT $(pkg-config --cflags sndfile) synth.cc pffft/pffft.o pffft/fftpack.o $(pkg-config --libs sndfile) -o synth
|
||||
</pre>
|
||||
|
||||
<h2>Running</h2>
|
||||
|
|
|
@ -28,7 +28,8 @@ int main(int argc, char **argv) {
|
|||
exit(1);
|
||||
}
|
||||
sf_close(sf_in);
|
||||
gaborator::parameters params(100, 20.0 / fs);
|
||||
gaborator::log_fq_scale scale(10, 20.0 / fs);
|
||||
gaborator::parameters params(scale);
|
||||
gaborator::analyzer<float> analyzer(params);
|
||||
std::vector<float> band_gains(analyzer.bands_end());
|
||||
for (int band = analyzer.bandpass_bands_begin(); band < analyzer.bandpass_bands_end(); band++) {
|
||||
|
|
|
@ -36,7 +36,8 @@ int main(int argc, char **argv) {
|
|||
v += audio[i * sfinfo.channels + c];
|
||||
mono[i] = v;
|
||||
}
|
||||
gaborator::parameters params(48, 20.0 / fs, 440.0 / fs);
|
||||
gaborator::log_fq_scale scale(48, 20.0 / fs, 440.0 / fs);
|
||||
gaborator::parameters params(scale);
|
||||
gaborator::analyzer<float> analyzer(params);
|
||||
gaborator::coefs<float> coefs(analyzer);
|
||||
analyzer.analyze(mono.data(), 0, mono.size(), coefs);
|
||||
|
|
|
@ -18,7 +18,7 @@ int main(int argc, char **argv) {
|
|||
std::uniform_real_distribution<> uniform(-1.0, 1.0);
|
||||
for (size_t i = 0; i < len; i++)
|
||||
signal_in[i] = uniform(rand);
|
||||
gaborator::parameters params(48, 5e-4);
|
||||
gaborator::parameters params(gaborator::log_fq_scale(48, 5e-4));
|
||||
gaborator::analyzer<float> analyzer(params);
|
||||
gaborator::coefs<float> coefs(analyzer);
|
||||
analyzer.analyze(signal_in.data(), 0, len, coefs);
|
||||
|
|
|
@ -32,7 +32,8 @@ int main(int argc, char **argv) {
|
|||
}
|
||||
sf_command(sf_in, SFC_SET_NORM_FLOAT, NULL, SF_FALSE);
|
||||
sf_command(sf_out, SFC_SET_NORM_FLOAT, NULL, SF_FALSE);
|
||||
gaborator::parameters params(12, 200.0 / fs, 440.0 / fs);
|
||||
gaborator::log_fq_scale scale(12, 200.0 / fs, 440.0 / fs);
|
||||
gaborator::parameters params(scale);
|
||||
gaborator::analyzer<float> analyzer(params);
|
||||
size_t analysis_support = ceil(analyzer.analysis_support());
|
||||
size_t synthesis_support = ceil(analyzer.synthesis_support());
|
||||
|
|
|
@ -11,7 +11,9 @@ int main(int argc, char **argv) {
|
|||
exit(1);
|
||||
}
|
||||
double fs = 44100;
|
||||
gaborator::parameters params(12, 20.0 / fs, 8.18 / fs);
|
||||
gaborator::log_fq_scale scale(12, 20.0 / fs, 8.18 / fs);
|
||||
gaborator::parameters params(scale);
|
||||
params.phase = gaborator::coef_phase::global;
|
||||
gaborator::analyzer<float> analyzer(params);
|
||||
static int pentatonic[] = { 57, 60, 62, 64, 67 };
|
||||
int n_notes = 64;
|
||||
|
|
|
@ -23,7 +23,7 @@ struct affine_transform {
|
|||
double a, b;
|
||||
};
|
||||
|
||||
// Composition
|
||||
// Composition: (a * b)(x) = a(b(x))
|
||||
|
||||
static inline affine_transform
|
||||
operator *(const affine_transform &a, const affine_transform &b) {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
//
|
||||
// Fast Fourier transform, naive reference implementations
|
||||
//
|
||||
// Copyright (C) 1992-2021 Andreas Gustafsson. This file is part of
|
||||
// Copyright (C) 1992-2022 Andreas Gustafsson. This file is part of
|
||||
// the Gaborator library source distribution. See the file LICENSE at
|
||||
// the top level of the distribution for license information.
|
||||
//
|
||||
|
@ -21,6 +21,14 @@
|
|||
|
||||
namespace gaborator {
|
||||
|
||||
// The template argument I is the iterator type used for accessing the
|
||||
// input and output data. Typically this is "std::complex<float> *" or
|
||||
// "std::complex<double> *", but other types such as stride iterators
|
||||
// can also be used.
|
||||
//
|
||||
// This is not fully const correct as the same iterator type is used
|
||||
// for both read-only and read-write access.
|
||||
|
||||
template <class I>
|
||||
struct fft {
|
||||
typedef typename std::iterator_traits<I>::value_type C; // complex
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,7 +1,7 @@
|
|||
//
|
||||
// The Gaussian and related functions
|
||||
//
|
||||
// Copyright (C) 2015-2021 Andreas Gustafsson. This file is part of
|
||||
// Copyright (C) 2015-2023 Andreas Gustafsson. This file is part of
|
||||
// the Gaborator library source distribution. See the file LICENSE at
|
||||
// the top level of the distribution for license information.
|
||||
//
|
||||
|
@ -17,13 +17,13 @@ namespace gaborator {
|
|||
// error function. This is good for arguments in the range 1e-8 to 1,
|
||||
// to within a few percent.
|
||||
|
||||
inline float erfc_inv(float x) {
|
||||
return sqrtf(-logf(x)) - 0.3f;
|
||||
static inline double erfc_inv(double x) {
|
||||
return sqrt(-log(x)) - 0.25;
|
||||
}
|
||||
|
||||
// Gaussian with peak = 1
|
||||
|
||||
inline double norm_gaussian(double sd, double x) {
|
||||
static inline double norm_gaussian(double sd, double x) {
|
||||
return exp(-(x * x) / (2 * sd * sd));
|
||||
}
|
||||
|
||||
|
@ -100,22 +100,22 @@ double gaussian_value_support_inv(double support, double max_error) {
|
|||
static inline
|
||||
double gaussian_support(double support, double max_error) {
|
||||
return gaussian_area_support(support, max_error);
|
||||
};
|
||||
}
|
||||
|
||||
static inline
|
||||
double gaussian_support_inv(double support, double max_error) {
|
||||
return gaussian_area_support_inv(support, max_error);
|
||||
};
|
||||
}
|
||||
#else
|
||||
static inline
|
||||
double gaussian_support(double support, double max_error) {
|
||||
return gaussian_value_support(support, max_error);
|
||||
};
|
||||
}
|
||||
|
||||
static inline
|
||||
double gaussian_support_inv(double support, double max_error) {
|
||||
return gaussian_value_support_inv(support, max_error);
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
//
|
||||
// A vector class without default-initialization, for "plain old data"
|
||||
//
|
||||
// Copyright (C) 2016-2021 Andreas Gustafsson. This file is part of
|
||||
// Copyright (C) 2016-2022 Andreas Gustafsson. This file is part of
|
||||
// the Gaborator library source distribution. See the file LICENSE at
|
||||
// the top level of the distribution for license information.
|
||||
//
|
||||
|
@ -28,9 +28,7 @@ struct pod_vector {
|
|||
b = e = 0;
|
||||
}
|
||||
explicit pod_vector(size_t size_) {
|
||||
// Allocate raw uninitialized memory
|
||||
b = static_cast<T *>(::operator new(size_ * sizeof(T)));
|
||||
e = b + size_;
|
||||
allocate(size_);
|
||||
}
|
||||
~pod_vector()
|
||||
#if __cplusplus >= 201103L
|
||||
|
@ -60,11 +58,8 @@ struct pod_vector {
|
|||
b = n;
|
||||
e = n + new_size;
|
||||
}
|
||||
pod_vector(const pod_vector &a)
|
||||
|
||||
{
|
||||
b = new T[a.size()];
|
||||
e = b + a.size();
|
||||
pod_vector(const pod_vector &a) {
|
||||
allocate(a.size());
|
||||
std::copy(a.b, a.e, b);
|
||||
//if (size()) fprintf(stderr, "pod_vector cc %d\n", (int)size());
|
||||
}
|
||||
|
@ -102,6 +97,11 @@ struct pod_vector {
|
|||
}
|
||||
#endif
|
||||
private:
|
||||
void allocate(size_t n) {
|
||||
// Allocate raw uninitialized memory
|
||||
b = static_cast<T *>(::operator new(n * sizeof(T)));
|
||||
e = b + n;
|
||||
}
|
||||
void _free() {
|
||||
// Free as raw uninitialized memory
|
||||
::operator delete(b);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
//
|
||||
// Pool of shared objects
|
||||
//
|
||||
// Copyright (C) 2015-2018 Andreas Gustafsson. This file is part of
|
||||
// Copyright (C) 2015-2022 Andreas Gustafsson. This file is part of
|
||||
// the Gaborator library source distribution. See the file LICENSE at
|
||||
// the top level of the distribution for license information.
|
||||
//
|
||||
|
@ -15,8 +15,8 @@ namespace gaborator {
|
|||
|
||||
// The "pool" class is for sharing FFT objects so that we don't
|
||||
// create multiple FFTs of the same size. It could also be used
|
||||
// to share objects of some other class T where we don't want to
|
||||
// create multiple Ts with the same K.
|
||||
// to share objects of some other class T where we wish to avoid
|
||||
// creating multiple Ts with the same K.
|
||||
|
||||
template <class T, class K>
|
||||
struct pool {
|
||||
|
@ -27,7 +27,8 @@ struct pool {
|
|||
}
|
||||
}
|
||||
T *get(const K &k) {
|
||||
std::pair<typename m_t::iterator, bool> r = m.insert(std::make_pair(k, (T *)0));
|
||||
std::pair<typename m_t::iterator, bool> r =
|
||||
m.insert(std::make_pair(k, (T *)0));
|
||||
if (r.second) {
|
||||
// New element was inserted
|
||||
assert((*(r.first)).second == 0);
|
||||
|
@ -36,12 +37,8 @@ struct pool {
|
|||
return (*r.first).second;
|
||||
}
|
||||
m_t m;
|
||||
static pool shared;
|
||||
};
|
||||
|
||||
template <class T, class K>
|
||||
pool<T, K> pool<T, K>::shared;
|
||||
|
||||
} // namespace
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
//
|
||||
// Intrusive reference counting smart pointer
|
||||
//
|
||||
// Copyright (C) 2016-2018 Andreas Gustafsson. This file is part of
|
||||
// Copyright (C) 2016-2022 Andreas Gustafsson. This file is part of
|
||||
// the Gaborator library source distribution. See the file LICENSE at
|
||||
// the top level of the distribution for license information.
|
||||
//
|
||||
|
@ -24,19 +24,20 @@ struct refcounted {
|
|||
// object type and invoke operator delete on the base class.
|
||||
|
||||
template <class T>
|
||||
void incref(T &r) {
|
||||
r.refcount++;
|
||||
void incref(T *r) {
|
||||
r->refcount++;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void decref(T &r) {
|
||||
r.refcount--;
|
||||
if (r.refcount == 0)
|
||||
delete &r;
|
||||
void decref(T *r) {
|
||||
r->refcount--;
|
||||
if (r->refcount == 0)
|
||||
delete r;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
struct ref {
|
||||
typedef T element_type;
|
||||
ref(): p(0) { }
|
||||
ref(T *p_): p(p_) {
|
||||
_incref();
|
||||
|
@ -44,7 +45,15 @@ struct ref {
|
|||
ref(const ref &o): p(o.p) {
|
||||
_incref();
|
||||
}
|
||||
ref &operator=(const ref &o) { reset(o.p); return *this; }
|
||||
// Move constructor
|
||||
ref(ref &&o) {
|
||||
p = o.p;
|
||||
o.p = 0;
|
||||
}
|
||||
ref &operator=(const ref &o) {
|
||||
reset(o.p);
|
||||
return *this;
|
||||
}
|
||||
~ref() { reset(); }
|
||||
void reset() {
|
||||
_decref();
|
||||
|
@ -65,12 +74,12 @@ private:
|
|||
void _incref() {
|
||||
if (! p)
|
||||
return;
|
||||
incref(*p);
|
||||
incref(p);
|
||||
}
|
||||
void _decref() {
|
||||
if (! p)
|
||||
return;
|
||||
decref(*p);
|
||||
decref(p);
|
||||
}
|
||||
T *p;
|
||||
};
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
//
|
||||
// Rendering of spectrogram images
|
||||
//
|
||||
// Copyright (C) 2015-2021 Andreas Gustafsson. This file is part of
|
||||
// Copyright (C) 2015-2023 Andreas Gustafsson. This file is part of
|
||||
// the Gaborator library source distribution. See the file LICENSE at
|
||||
// the top level of the distribution for license information.
|
||||
//
|
||||
|
@ -27,7 +27,7 @@ unsigned int float2pixel_8bit(T val) {
|
|||
// of a negative number; those can arise from bicubic
|
||||
// interpolation. While we're at it, let's also skip the gamma
|
||||
// correction for small numbers that will round to zero anyway,
|
||||
// and especially denormals which could rigger GCC bug target/83240.
|
||||
// and especially denormals which could trigger GCC bug target/83240.
|
||||
static const T almost_zero = 1.0 / 65536;
|
||||
if (val < almost_zero)
|
||||
val = 0;
|
||||
|
@ -43,7 +43,7 @@ unsigned int float2pixel_8bit(T val) {
|
|||
|
||||
// Magnitude
|
||||
|
||||
template<class T>
|
||||
template <class T>
|
||||
struct complex_abs_fob {
|
||||
T operator()(const complex<T> &c) {
|
||||
return complex_abs(c);
|
||||
|
@ -54,9 +54,16 @@ struct complex_abs_fob {
|
|||
|
||||
// T -> f() -> OI::value_type
|
||||
|
||||
|
||||
template <class F, class OI, class T>
|
||||
struct transform_output_iterator: public std::iterator<std::output_iterator_tag, T> {
|
||||
struct transform_output_iterator {
|
||||
// Iterator traits
|
||||
typedef std::output_iterator_tag iterator_category;
|
||||
typedef T value_type;
|
||||
typedef std::ptrdiff_t difference_type;
|
||||
typedef T* pointer;
|
||||
typedef T& reference;
|
||||
|
||||
transform_output_iterator(F f_, OI output_): f(f_), output(output_) { }
|
||||
transform_output_iterator<F, OI, T>& operator=(T v) {
|
||||
*output++ = f(v);
|
||||
|
@ -147,7 +154,7 @@ OI render_line(const analyzer<T> &anl,
|
|||
|
||||
int oct;
|
||||
unsigned int obno; // Band number within octave
|
||||
bool clip = ! bno_split(*msc.meta, gbno, oct, obno, false);
|
||||
bool clip = ! bno_split(*msc.cmeta, gbno, oct, obno, false);
|
||||
if (clip) {
|
||||
for (sample_index_t i = i0; i < i1; i++)
|
||||
*output++ = (T)0;
|
||||
|
@ -157,8 +164,8 @@ OI render_line(const analyzer<T> &anl,
|
|||
abs_rowsource(msc, oct, obno, normf);
|
||||
|
||||
// Scale by the downsampling factor of the band
|
||||
int scale_exp = band_scale_exp(*msc.octaves[oct].meta, oct, obno);
|
||||
RESAMPLER x_resampler(zoom_p2(xf, -scale_exp));
|
||||
int scale_exp = band_step_log2(*msc.cmeta, gbno);
|
||||
RESAMPLER x_resampler(zoom_p2_src(xf, -scale_exp));
|
||||
output = x_resampler(abs_rowsource, i0, i1, output);
|
||||
return output;
|
||||
}
|
||||
|
@ -228,10 +235,15 @@ struct transverse_source {
|
|||
};
|
||||
|
||||
template <class I>
|
||||
struct stride_iterator: public std::iterator
|
||||
<std::forward_iterator_tag, typename std::iterator_traits<I>::value_type>
|
||||
{
|
||||
struct stride_iterator {
|
||||
typedef typename std::iterator_traits<I>::value_type T;
|
||||
// Iterator traits
|
||||
typedef std::forward_iterator_tag iterator_category;
|
||||
typedef T value_type;
|
||||
typedef std::ptrdiff_t difference_type;
|
||||
typedef T* pointer;
|
||||
typedef T& reference;
|
||||
|
||||
stride_iterator(I it_, size_t stride_): it(it_), stride(stride_) { }
|
||||
T& operator*() { return *it; }
|
||||
stride_iterator<I>& operator++() {
|
||||
|
@ -271,7 +283,7 @@ struct updated_nop {
|
|||
// are updated, the time range being from inc_i0 (inclusive) to inc_i1
|
||||
// (exclusive). The updated parts of the image consist of zero or
|
||||
// more non-overlapping rectangles; to find out what those are, pass a
|
||||
// function "update" which will be called will each rectangle in turn
|
||||
// function "update" which will be called with each rectangle in turn
|
||||
// after it has been updated.
|
||||
|
||||
// For non-incremental rendering, pass inc_i0 = INT64_MIN and inc_i1 =
|
||||
|
@ -299,7 +311,7 @@ void render_incremental(
|
|||
assert(yi1 >= yi0);
|
||||
assert(inc_i1 >= inc_i0);
|
||||
|
||||
// The data type to reasmple
|
||||
// The data type to resample
|
||||
typedef typename NORMF::return_type RST;
|
||||
|
||||
// Vertical resampler
|
||||
|
@ -328,12 +340,12 @@ void render_incremental(
|
|||
y_resampler.support(y, y, ysi0, ysi1);
|
||||
int64_t band = ysi1;
|
||||
// Clamp the band to the valid range
|
||||
band = std::max(band, (int64_t)anl.bandpass_bands_begin());
|
||||
band = std::min(band, (int64_t)anl.bandpass_bands_end() - 1);
|
||||
set_max(band, (int64_t)anl.bandpass_bands_begin());
|
||||
set_min(band, (int64_t)anl.bandpass_bands_end() - 1);
|
||||
|
||||
// Find the analysis support in the time (x) dimension,
|
||||
// in signal samples
|
||||
double support = anl.analysis_support(band);
|
||||
double support = anl.band_analysis_support(band);
|
||||
// Convert from signal samples to coefficient samples
|
||||
int scale_exp = anl.band_scale_exp((int)band);
|
||||
|
||||
|
@ -341,7 +353,7 @@ void render_incremental(
|
|||
// support, and map it back to pixel space to find the
|
||||
// affected pixel range, taking the resampler support
|
||||
// into account.
|
||||
RESAMPLER x_resampler(zoom_p2(x_xf, -scale_exp));
|
||||
RESAMPLER x_resampler(zoom_p2_src(x_xf, -scale_exp));
|
||||
int64_t ceil_support = ceil(support);
|
||||
|
||||
// inv_support() calculates both sides of the support at once,
|
||||
|
@ -360,17 +372,17 @@ void render_incremental(
|
|||
if (inc_i0 == INT64_MIN) {
|
||||
adj_x0 = xi0;
|
||||
} else {
|
||||
adj_x0 = std::max(xi0, adj_x0);
|
||||
set_max(adj_x0, xi0);
|
||||
// Don't let width go negative
|
||||
adj_x0 = std::min(adj_x0, xi1);
|
||||
set_min(adj_x0, xi1);
|
||||
}
|
||||
|
||||
if (inc_i1 == INT64_MAX) {
|
||||
adj_x1 = xi1;
|
||||
} else {
|
||||
adj_x1 = std::min(xi1, adj_x1);
|
||||
set_min(adj_x1, xi1);
|
||||
// Don't let width go negative
|
||||
adj_x1 = std::max(adj_x1, adj_x0);
|
||||
set_max(adj_x1, adj_x0);
|
||||
}
|
||||
|
||||
assert(adj_x0 <= adj_x1);
|
||||
|
@ -423,7 +435,7 @@ void render_incremental(
|
|||
// the support also varies; use the largest resampling factor of
|
||||
// any band to get the worst-case support.
|
||||
int worstcase_band = anl.bandpass_bands_end() - 1;
|
||||
RESAMPLER x_resampler(zoom_p2(x_xf, -anl.band_scale_exp(worstcase_band)));
|
||||
RESAMPLER x_resampler(zoom_p2_src(x_xf, -anl.band_scale_exp(worstcase_band)));
|
||||
int64_t xsi0, xsi1;
|
||||
x_resampler.support(xi0, xi1, xsi0, xsi1);
|
||||
|
||||
|
|
|
@ -55,7 +55,10 @@ namespace gaborator {
|
|||
|
||||
struct p2_transform {
|
||||
p2_transform(int e_, int64_t origin_): e(e_), origin(origin_) { }
|
||||
// Convert a linear transform into a p2_transform
|
||||
// Convert an affine transform into a p2_transform.
|
||||
// The affine transform must be representable as a
|
||||
// p2_transform, meaning xf.a must be a power of two,
|
||||
// and the origin must be an integer.
|
||||
p2_transform(affine_transform xf) {
|
||||
int exp;
|
||||
double m = frexp(xf.a, &exp);
|
||||
|
@ -68,18 +71,22 @@ struct p2_transform {
|
|||
int64_t origin;
|
||||
};
|
||||
|
||||
// Scale a transform by a power of two
|
||||
|
||||
static inline p2_transform
|
||||
zoom_p2(p2_transform xf, int e) {
|
||||
return p2_transform(xf.e + e, xf.origin);
|
||||
}
|
||||
// Scale a transform by a power of two, around the origin
|
||||
// of the destination space
|
||||
|
||||
static inline affine_transform
|
||||
zoom_p2(affine_transform xf, int e) {
|
||||
zoom_p2_dst(affine_transform xf, int e) {
|
||||
return affine_transform(ldexp(xf.a, e), xf.b);
|
||||
}
|
||||
|
||||
// Scale a transform by a power of two, around the origin
|
||||
// of the source space
|
||||
|
||||
static inline affine_transform
|
||||
zoom_p2_src(affine_transform xf, int e) {
|
||||
return affine_transform(ldexp(xf.a, e), ldexp(xf.b, e));
|
||||
}
|
||||
|
||||
// Resample data from "source", generating a view between indices
|
||||
// i0 and i1 of the scale determined by exponent e, and storing
|
||||
// i1 - i0 samples starting at *out.
|
||||
|
@ -211,11 +218,11 @@ OI resample2_p2xf(const S &source, p2_transform xf,
|
|||
// As above, but taking an affine_transform.
|
||||
|
||||
template <class S, class OI>
|
||||
OI resample2(const S &source, affine_transform lxf,
|
||||
OI resample2(const S &source, affine_transform axf,
|
||||
int64_t i0, int64_t i1,
|
||||
bool interpolate, OI out)
|
||||
{
|
||||
p2_transform xf(lxf);
|
||||
p2_transform xf(axf);
|
||||
typedef typename std::iterator_traits<OI>::value_type T;
|
||||
gaborator::pod_vector<T> data(i1 - i0);
|
||||
T *p = data.data();
|
||||
|
@ -231,22 +238,22 @@ OI resample2(const S &source, affine_transform lxf,
|
|||
// return an unnecessarily large support when interpolation is off
|
||||
|
||||
inline void
|
||||
resample2_support(affine_transform lxf, int64_t i0, int64_t i1,
|
||||
resample2_support(affine_transform axf, int64_t i0, int64_t i1,
|
||||
int64_t &si0_ret, int64_t &si1_ret)
|
||||
{
|
||||
p2_transform xf(lxf);
|
||||
p2_transform xf(axf);
|
||||
int margin = 2;
|
||||
if (xf.e > 0) {
|
||||
// Note code duplication wrt resample2_p2xf().
|
||||
// Also note tail recursion.
|
||||
int64_t si0 = i0 * 2 - margin + 1;
|
||||
int64_t si1 = i1 * 2 + margin;
|
||||
resample2_support(zoom_p2(lxf, -1),
|
||||
resample2_support(zoom_p2_dst(axf, -1),
|
||||
si0, si1, si0_ret, si1_ret);
|
||||
} else if (xf.e < 0) {
|
||||
int64_t si0 = (i0 >> 1) - margin;
|
||||
int64_t si1 = ((i1 - 1) >> 1) + margin + 1;
|
||||
resample2_support(zoom_p2(lxf, +1),
|
||||
resample2_support(zoom_p2_dst(axf, +1),
|
||||
si0, si1, si0_ret, si1_ret);
|
||||
} else {
|
||||
si0_ret = xf.origin + i0;
|
||||
|
@ -258,21 +265,21 @@ resample2_support(affine_transform lxf, int64_t i0, int64_t i1,
|
|||
// destination indices that depend on a given range of source indices.
|
||||
|
||||
inline void
|
||||
resample2_inv_support(affine_transform lxf, int64_t si0, int64_t si1,
|
||||
resample2_inv_support(affine_transform axf, int64_t si0, int64_t si1,
|
||||
int64_t &i0_ret, int64_t &i1_ret)
|
||||
{
|
||||
p2_transform xf(lxf);
|
||||
p2_transform xf(axf);
|
||||
// Conservative
|
||||
int margin = 2;
|
||||
if (xf.e > 0) {
|
||||
int64_t di0, di1;
|
||||
resample2_inv_support(zoom_p2(lxf, -1),
|
||||
resample2_inv_support(zoom_p2_dst(axf, -1),
|
||||
si0, si1, di0, di1);
|
||||
i0_ret = di0 >> 1;
|
||||
i1_ret = (di1 >> 1) + 1;
|
||||
} else if (xf.e < 0) {
|
||||
int64_t di0, di1;
|
||||
resample2_inv_support(zoom_p2(lxf, +1),
|
||||
resample2_inv_support(zoom_p2_dst(axf, +1),
|
||||
si0, si1, di0, di1);
|
||||
i0_ret = di0 * 2 - margin + 1;
|
||||
i1_ret = di1 * 2 + margin;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
//
|
||||
// Vector math operations
|
||||
//
|
||||
// Copyright (C) 2016-2018 Andreas Gustafsson. This file is part of
|
||||
// Copyright (C) 2016-2021 Andreas Gustafsson. This file is part of
|
||||
// the Gaborator library source distribution. See the file LICENSE at
|
||||
// the top level of the distribution for license information.
|
||||
//
|
||||
|
@ -17,6 +17,13 @@
|
|||
|
||||
#include <complex>
|
||||
|
||||
// Use __restrict__ where supported
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
#define GABORATOR_RESTRICT __restrict__
|
||||
#else
|
||||
#define GABORATOR_RESTRICT
|
||||
#endif
|
||||
|
||||
namespace gaborator {
|
||||
|
||||
// The _naive versions are used when SSE is not available, and as
|
||||
|
@ -40,12 +47,12 @@ static inline
|
|||
std::complex<double> complex_mul(std::complex<double> a_,
|
||||
std::complex<double> b_)
|
||||
{
|
||||
__v2df a = _mm_setr_pd(a_.real(), a_.imag());
|
||||
__v2df b = _mm_setr_pd(b_.real(), b_.imag());
|
||||
__v2df as = (__v2df) _mm_shuffle_pd(a, a, 0x1);
|
||||
__v2df t0 = _mm_mul_pd(a, _mm_shuffle_pd(b, b, 0x0));
|
||||
__v2df t1 = _mm_mul_pd(as, _mm_shuffle_pd(b, b, 0x3));
|
||||
__v2df c = __builtin_ia32_addsubpd(t0, t1); // SSE3
|
||||
__m128d a = _mm_setr_pd(a_.real(), a_.imag());
|
||||
__m128d b = _mm_setr_pd(b_.real(), b_.imag());
|
||||
__m128d as = (__m128d) _mm_shuffle_pd(a, a, 0x1);
|
||||
__m128d t0 = _mm_mul_pd(a, _mm_shuffle_pd(b, b, 0x0));
|
||||
__m128d t1 = _mm_mul_pd(as, _mm_shuffle_pd(b, b, 0x3));
|
||||
__m128d c = _mm_addsub_pd(t0, t1); // SSE3
|
||||
return std::complex<double>(c[0], c[1]);
|
||||
}
|
||||
#else
|
||||
|
@ -66,9 +73,9 @@ std::complex<float> complex_mul(std::complex<float> a_,
|
|||
|
||||
template <class T, class U, class V>
|
||||
static inline void
|
||||
elementwise_product_naive(T *r,
|
||||
U *a,
|
||||
V *b,
|
||||
elementwise_product_naive(T *GABORATOR_RESTRICT r,
|
||||
U *GABORATOR_RESTRICT a,
|
||||
V *GABORATOR_RESTRICT b,
|
||||
size_t n)
|
||||
{
|
||||
for (size_t i = 0; i < n; i++)
|
||||
|
@ -77,9 +84,9 @@ elementwise_product_naive(T *r,
|
|||
|
||||
template <class T, class U, class V, class S>
|
||||
static inline void
|
||||
elementwise_product_times_scalar_naive(T *r,
|
||||
U *a,
|
||||
V *b,
|
||||
elementwise_product_times_scalar_naive(T *GABORATOR_RESTRICT r,
|
||||
U *GABORATOR_RESTRICT a,
|
||||
V *GABORATOR_RESTRICT b,
|
||||
S s,
|
||||
size_t n)
|
||||
{
|
||||
|
@ -90,12 +97,13 @@ elementwise_product_times_scalar_naive(T *r,
|
|||
// I is the input complex data type, O is the output data type
|
||||
template <class I, class O>
|
||||
static inline void
|
||||
complex_magnitude_naive(I *inv,
|
||||
O *outv,
|
||||
complex_magnitude_naive(I *GABORATOR_RESTRICT inv,
|
||||
O *GABORATOR_RESTRICT outv,
|
||||
size_t n)
|
||||
{
|
||||
for (size_t i = 0; i < n; i++)
|
||||
outv[i] = std::sqrt(inv[i].real() * inv[i].real() + inv[i].imag() * inv[i].imag());
|
||||
outv[i] = std::sqrt(inv[i].real() * inv[i].real() +
|
||||
inv[i].imag() * inv[i].imag());
|
||||
}
|
||||
|
||||
#if GABORATOR_USE_SSE3_INTRINSICS
|
||||
|
@ -105,11 +113,11 @@ complex_magnitude_naive(I *inv,
|
|||
// Perform two complex float multiplies in parallel
|
||||
|
||||
static inline
|
||||
__v4sf complex_mul_vec2(__v4sf aa, __v4sf bb) {
|
||||
__v4sf aas =_mm_shuffle_ps(aa, aa, 0xb1);
|
||||
__v4sf t0 = _mm_mul_ps(aa, _mm_moveldup_ps(bb));
|
||||
__v4sf t1 = _mm_mul_ps(aas, _mm_movehdup_ps(bb));
|
||||
return __builtin_ia32_addsubps(t0, t1); // SSE3
|
||||
__m128 complex_mul_vec2(__m128 aa, __m128 bb) {
|
||||
__m128 aas =_mm_shuffle_ps(aa, aa, 0xb1);
|
||||
__m128 t0 = _mm_mul_ps(aa, _mm_moveldup_ps(bb));
|
||||
__m128 t1 = _mm_mul_ps(aas, _mm_movehdup_ps(bb));
|
||||
return _mm_addsub_ps(t0, t1); // SSE3
|
||||
}
|
||||
|
||||
// Calculate the elementwise product of a complex float vector
|
||||
|
@ -123,12 +131,12 @@ elementwise_product(std::complex<float> *cv,
|
|||
{
|
||||
assert((n & 1) == 0);
|
||||
n >>= 1;
|
||||
__v4sf *c = (__v4sf *) cv;
|
||||
const __v4sf *a = (const __v4sf *) av;
|
||||
const __v4sf *b = (const __v4sf *) bv;
|
||||
__m128 *c = (__m128 *) cv;
|
||||
const __m128 *a = (const __m128 *) av;
|
||||
const __m128 *b = (const __m128 *) bv;
|
||||
while (n--) {
|
||||
__v4sf aa = *a++;
|
||||
__v4sf bb = *b++;
|
||||
__m128 aa = *a++;
|
||||
__m128 bb = *b++;
|
||||
*c++ = complex_mul_vec2(aa, bb);
|
||||
}
|
||||
}
|
||||
|
@ -146,13 +154,13 @@ elementwise_product(std::complex<float> *cv,
|
|||
{
|
||||
assert((n & 3) == 0);
|
||||
n >>= 2;
|
||||
__v4sf *c = (__v4sf *) cv;
|
||||
const __v4sf *a = (const __v4sf *) av;
|
||||
const __v4sf *b = (const __v4sf *) bv;
|
||||
__m128 *c = (__m128 *) cv;
|
||||
const __m128 *a = (const __m128 *) av;
|
||||
const __m128 *b = (const __m128 *) bv;
|
||||
while (n--) {
|
||||
__v4sf a0 = (__v4sf) _mm_loadu_si128((const __m128i *) a++);
|
||||
__v4sf a1 = (__v4sf) _mm_loadu_si128((const __m128i *) a++);
|
||||
__v4sf bb = *b++;
|
||||
__m128 a0 = (__m128) _mm_loadu_si128((const __m128i *) a++);
|
||||
__m128 a1 = (__m128) _mm_loadu_si128((const __m128i *) a++);
|
||||
__m128 bb = *b++;
|
||||
*c++ = _mm_mul_ps(a0, _mm_unpacklo_ps(bb, bb));
|
||||
*c++ = _mm_mul_ps(a1, _mm_unpackhi_ps(bb, bb));
|
||||
}
|
||||
|
@ -167,13 +175,13 @@ elementwise_product_times_scalar(std::complex<float> *cv,
|
|||
{
|
||||
assert((n & 1) == 0);
|
||||
n >>= 1;
|
||||
const __v4sf *a = (const __v4sf *) av;
|
||||
const __v4sf *b = (const __v4sf *) bv;
|
||||
const __v4sf dd = (__v4sf) { d.real(), d.imag(), d.real(), d.imag() };
|
||||
__v4sf *c = (__v4sf *) cv;
|
||||
const __m128 *a = (const __m128 *) av;
|
||||
const __m128 *b = (const __m128 *) bv;
|
||||
const __m128 dd = (__m128) { d.real(), d.imag(), d.real(), d.imag() };
|
||||
__m128 *c = (__m128 *) cv;
|
||||
while (n--) {
|
||||
__v4sf aa = *a++;
|
||||
__v4sf bb = *b++;
|
||||
__m128 aa = *a++;
|
||||
__m128 bb = *b++;
|
||||
*c++ = complex_mul_vec2(complex_mul_vec2(aa, bb), dd);
|
||||
}
|
||||
}
|
||||
|
@ -192,19 +200,19 @@ complex_magnitude(std::complex<float> *inv,
|
|||
*outv++ = std::sqrt(v.real() * v.real() + v.imag() * v.imag());
|
||||
n--;
|
||||
}
|
||||
const __v4sf *in = (const __v4sf *) inv;
|
||||
__v4sf *out = (__v4sf *) outv;
|
||||
const __m128 *in = (const __m128 *) inv;
|
||||
__m128 *out = (__m128 *) outv;
|
||||
while (n >= 4) {
|
||||
__v4sf aa = *in++; // c0re c0im c1re c1im
|
||||
__v4sf aa2 = _mm_mul_ps(aa, aa); // c0re^2 c0im^2 c1re^2 c1im^2
|
||||
__v4sf bb = *in++; // c2re c2im c3re c3im
|
||||
__v4sf bb2 = _mm_mul_ps(bb, bb); // etc
|
||||
__m128 aa = *in++; // c0re c0im c1re c1im
|
||||
__m128 aa2 = _mm_mul_ps(aa, aa); // c0re^2 c0im^2 c1re^2 c1im^2
|
||||
__m128 bb = *in++; // c2re c2im c3re c3im
|
||||
__m128 bb2 = _mm_mul_ps(bb, bb); // etc
|
||||
// Gather the real parts: x0 x2 y0 y2
|
||||
// 10 00 10 00 = 0x88
|
||||
__v4sf re2 =_mm_shuffle_ps(aa2, bb2, 0x88);
|
||||
__v4sf im2 =_mm_shuffle_ps(aa2, bb2, 0xdd);
|
||||
__v4sf mag2 = _mm_add_ps(re2, im2);
|
||||
__v4sf mag = __builtin_ia32_sqrtps(mag2);
|
||||
__m128 re2 =_mm_shuffle_ps(aa2, bb2, 0x88);
|
||||
__m128 im2 =_mm_shuffle_ps(aa2, bb2, 0xdd);
|
||||
__m128 mag2 = _mm_add_ps(re2, im2);
|
||||
__m128 mag = _mm_sqrt_ps(mag2);
|
||||
// Unaligned store
|
||||
_mm_storeu_si128((__m128i *)out, (__m128i)mag);
|
||||
out++;
|
||||
|
@ -298,4 +306,6 @@ complex_magnitude(I *inv,
|
|||
|
||||
} // namespace
|
||||
|
||||
#undef GABORATOR_RESTRICT
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
//
|
||||
// Version number
|
||||
//
|
||||
// Copyright (C) 2015-2021 Andreas Gustafsson. This file is part of
|
||||
// Copyright (C) 2015-2023 Andreas Gustafsson. This file is part of
|
||||
// the Gaborator library source distribution. See the file LICENSE at
|
||||
// the top level of the distribution for license information.
|
||||
//
|
||||
|
@ -9,7 +9,7 @@
|
|||
#ifndef _GABORATOR_VERSION_H
|
||||
#define _GABORATOR_VERSION_H
|
||||
|
||||
#define GABORATOR_VERSION_MAJOR 1
|
||||
#define GABORATOR_VERSION_MINOR 7
|
||||
#define GABORATOR_VERSION_MAJOR 2
|
||||
#define GABORATOR_VERSION_MINOR 0
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in a new issue