Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add LPC and LineSpectralFrequencies classes #47

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion src/parselmouth.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,9 @@ Thing_declare(Daata);
Thing_declare(Formant);
Thing_declare(Function);
Thing_declare(Harmonicity);
Thing_declare(Harmonicity);
Thing_declare(Intensity);
Thing_declare(LineSpectralFrequencies);
Thing_declare(LPC);
Thing_declare(Matrix);
Thing_declare(MFCC);
Thing_declare(Pitch);
Expand Down Expand Up @@ -146,6 +147,8 @@ using PraatBindings = Bindings<PraatError,
CC,
MFCC,
TextGrid,
LPC,
LineSpectralFrequencies,
PraatModule>;

} // namespace parselmouth
Expand Down
2 changes: 2 additions & 0 deletions src/parselmouth/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ target_sources(parselmouth PRIVATE
Function.cpp
Harmonicity.cpp
Intensity.cpp
LineSpectralFrequencies.cpp
LPC.cpp
Matrix.cpp
MFCC.cpp
Pitch.cpp
Expand Down
118 changes: 118 additions & 0 deletions src/parselmouth/LPC.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
/*
* Copyright (C) 2017-2021 Yannick Jadoul
*
* This file is part of Parselmouth.
*
* Parselmouth is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Parselmouth is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Parselmouth. If not, see <http://www.gnu.org/licenses/>
*/

#include "Parselmouth.h"

#include "LPC_docstrings.h"
#include "TimeClassAspects.h"
#include "utils/pybind11/NumericPredicates.h"

#include <praat/LPC/LPC.h>
// #include <praat/LPC/LPC_and_Cepstrumc.h>
// #include <praat/LPC/LPC_and_Formant.h>
// #include <praat/LPC/LPC_and_LFCC.h>
#include <praat/LPC/LPC_and_LineSpectralFrequencies.h>
// #include <praat/LPC/LPC_and_Polynomial.h>
// #include <praat/LPC/LPC_and_Tube.h>
#include <praat/LPC/LPC_to_Spectrogram.h>
#include <praat/LPC/LPC_to_Spectrum.h>
#include <praat/LPC/Sound_and_LPC_robust.h>
#include <praat/fon/Sound.h>

#include <pybind11/numpy.h>
#include <pybind11/stl.h>

namespace py = pybind11;
using namespace py::literals;

namespace parselmouth
{

PRAAT_STRUCT_BINDING(Frame, LPC_Frame)
{

doc() = FRAME_CLASS_DOCSTRING;

def_readonly("n_coefficients", &structLPC_Frame::nCoefficients,
FRAME_N_COEFFICIENTS);
def_readonly("gain", &structLPC_Frame::gain, FRAME_GAIN);
def_property_readonly(
"a",
[](LPC_Frame self) {
return py::array({self->nCoefficients}, self->a.cells);
},
py::return_value_policy::reference_internal, FRAME_A);
}

PRAAT_CLASS_BINDING(LPC)
{
addTimeFrameSampledMixin(*this);

NESTED_BINDINGS(LPC_Frame)

doc() = CLASS_DOCSTRING;

def_readonly("sampling_period", &structLPC::samplingPeriod,
SAMPLING_PERIOD_DOCSTRING);
def_readonly("max_n_coefficients", &structLPC::maxnCoefficients,
MAX_N_COEFFICIENTS_DOCSTRING);

def(
"__len__", [](LPC self) { return self->nx; }, LEN_DOCSTRING);
def(
"__getitem__",
[](LPC self, int i) {
if (i < 0) i += self->nx; // Python-style negative indexing
if (i < 0 || i >= self->nx)
throw py::index_error("LPC index out of range");
return &self->d_frames.cells[i];
},
"i"_a, py::return_value_policy::reference_internal, GETITEM_DOCSTRING);

def(
"__iter__",
[](LPC self) {
return py::make_iterator(self->d_frames.cells,
self->d_frames.cells + self->nx);
},
py::keep_alive<0, 1>(), ITER_DOCSTRING);

// FORM (NEW_LPC_to_Spectrum_slice
def("to_spectrum_slice", &LPC_to_Spectrum, "time"_a,
"minimum_frequency_resolution"_a = 20.0, "bandwidth_reduction"_a = 0.0,
"deemphasis_frequency"_a = 50.0, TO_SPECTRUM_SLICE_DOCSTRING);

// FORM (NEW_LPC_to_Spectrogram
def("to_spectrogram", &LPC_to_Spectrogram,
"minimum_frequency_resolution"_a = 20.0, "bandwidth_reduction"_a = 0.0,
"deemphasis_frequency"_a = 50.0, TO_SPECTROGRAM_DOCSTRING);

// FORM (NEW_LPC_to_LineSpectralFrequencies)
def("to_line_spectral_frequencies", &LPC_to_LineSpectralFrequencies,
"grid_size"_a = 0.0, TO_LINE_SPECTRAL_FREQUENCIES);

// FORM (NEW1_LPC_Sound_to_LPC_robust
def(
"to_lpc_robust", [](LPC self, Sound sound, Positive<double> windowLength, Positive<double> preEmphasisFrequency, Positive<double> numberOfStandardDeviations, Positive<int> maximumNumberOfIterations, double tolerance, bool locationVariable) {
return LPC_Sound_to_LPC_robust(self, sound, windowLength, preEmphasisFrequency, numberOfStandardDeviations, maximumNumberOfIterations, tolerance, locationVariable);
},
"sound"_a.none(false), "window_length"_a = 0.025, "preemphasis_frequency"_a = 50.0, "number_of_std_dev"_a = 1.5, "maximum_number_of_iterations"_a = 5, "tolerance"_a = 0.000001, "variable_location"_a = false);
}

} // namespace parselmouth
180 changes: 180 additions & 0 deletions src/parselmouth/LPC_docstrings.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
#pragma once

#include "common_docstring.h"

namespace parselmouth {

constexpr auto FRAME_CLASS_DOCSTRING = R"(Praat LPC Frame Class.

A class containing the linear predictive coding (LPC) analysis outcome over
a frame of acoustic data samples.

Note that the $a_0=1$ coefficient is omitted in a.

This class is instantiated in :obj:`~parselmouth.LPC` object and not
intended to be instantiated by user.

See also
--------
:praat:`Sound: LPC analysis`
:obj:`~parselmouth.LPC`
)";

constexpr auto FRAME_N_COEFFICIENTS = R"(int : Number of AR coefficients a used to model the frame)";
constexpr auto FRAME_GAIN = R"(float : Linear predictive model gain)";
constexpr auto FRAME_A = R"(numpy.ndarray of float : Linear predictive model coefficients.)";


constexpr auto CLASS_DOCSTRING = R"(Praat LPC Class.

A sequence object contains the outcomes of linear predictive coding (LPC)
analysis outcomes. The analysis is performed repeatedly with a sliding
window, which offsets :attr:`LPC.dt<parselmouth.LPC.dt>` seconds between
frames.

The i-th item of this object is a :func:`LPC.Frame<parselmouth.LPC.Frame>`
object representing the i-th frame.

In the LPC analysis one tries to predict xn on the basis of the p previous
samples, :math:`x_n^' = \sum_{k=1}^p {a_k x_{n-k}}` then
:math:`{a_1, a_2, \ldots, a_p}` can be chosen to minimize the prediction
power :math:`Q_p` where :math:`Q_p = E[ |x_n - x_n^'|^2 ]`.

This class is not intended to be instantiated by user, instead from a
:obj:`~parselmouth.Sound` object using one of its to_lpc_xxx()
methods.

See Also
--------
:praat:`Sound: LPC analysis`
:obj:`parselmouth.LPC.Frame`
:func:`parselmouth.Sound.to_lpc_autocorrelation`
:func:`parselmouth.Sound.to_lpc_covariance`
:func:`parselmouth.Sound.to_lpc_burg`
:func:`parselmouth.Sound.to_lpc_marple`
)";

constexpr auto SAMPLING_PERIOD_DOCSTRING = R"(Sampling rate of the audio data)";
constexpr auto MAX_N_COEFFICIENTS_DOCSTRING = R"(Largest number of coefficients over all frames)";

constexpr auto TO_LINE_SPECTRAL_FREQUENCIES=R"(Convert to line spectra.

Returns :obj:`parselmouth.LineSpectralFrequencies` object with the
line frequencies found in the LPC models.

Parameter
----------
grid_size : float, default 0.0
TBD

See also
--------
:obj:`parselmouth.LineSpectralFrequencies`
)";


constexpr auto TO_SPECTRUM_SLICE_DOCSTRING=R"(Convert to spectrogram.

Returns :obj:`parselmouth.Spectrum` object with the spectral
representation of the LPC model found at specified time t.

The Spectrum at t will be calculated from the nearest
:obj:`~parselmouth.LPC.Frame`. See :praat:`LPC: To Spectrum (slice)...`
for dedailed algorithm description.

Parameters
----------
time : float
Time at which the spectrum should be calculated.

minimum_frequency_resolution : float, default 20.0
Maximum distance separation of successive frequencies in the Spectrum,
in Hz

bandwidth_reduction : float, default 0.0
Reduces the bandwidth of each zero by this factor (<=0.0 for no
reduction). Formants with small bandwidths show up very well as darker
regions in the spectrogram because the poles lie close to the contour
along which a spectrum is computed (the unit circle in the z-plane).
Peak enhancement can be realized by computing a spectrum in the z-plane
along a contour of radius:

:math:`r = exp \left(– \pi \times
\frac{bandwidthReduction}{samplingFrequency}\right)`.

deemphasis_frequency : float, default 50.0
Performs de-emphasis when value is in this interval, specified in Hz.
(0, Nyquist frequency)

See also
--------
:praat:`LPC: To Spectrum (slice)...`
)";

constexpr auto TO_SPECTROGRAM_DOCSTRING=R"(Convert to spectrogram.

Returns :obj:`parselmouth.Spectrogram` object with the spectral
representation of the LPC models.

For each LPC_Frame the corresponding Spectrum will be calculated according
to the algorithm explained in :func:`parselmouth.LPC.to_spectrum`. For each
frequency the power, i.e., the square of the complex values, will be stored
in the corresponding area in the Spectrogram.

Parameters
----------
minimum_frequency_resolution : float, default 20.0
Maximum distance separation of successive frequencies in the Spectrum,
in Hz

bandwidth_reduction : float, default 0.0
Reduces the bandwidth of each zero by this factor (<=0.0 for no
reduction). Formants with small bandwidths show up very well as darker
regions in the spectrogram because the poles lie close to the contour
along which a spectrum is computed (the unit circle in the z-plane).
Peak enhancement can be realized by computing a spectrum in the z-plane
along a contour of radius:

:math:`r = exp \left(– \pi \times
\frac{bandwidthReduction}{samplingFrequency}\right)`.

deemphasis_frequency : float, default 50.0
Performs de-emphasis when value is in this interval, specified in Hz.
(0, Nyquist frequency)

See also
--------
:praat:`LPC: To Spectrogram...`
)";

constexpr auto TO_LPC_ROBUST_DOCSTRING = R"(
Modify LPC coefficients using Huber's M-estimation approach.

Parameters
----------
sound : parselmouth.Sound
Sound object originally used to generate this LPC object

window_length : float, default 0.025
LPC frame size in seconds used to generate this LPC object

preemphasis_frequency : float, default 50.0
+6dB / octave filtering will be applied above this frequency. If you do
not want pre-emphasis, choose a frequency greater than the Nyquist
frequency.

number_of_std_dev : float, default 1.5
TBD

maximum_number_of_iterations : int, default 5
Maximum number of iterations for Newton's algorithm

tolerance : float, default 0.000001
Newton's algorithm termination criterion on change in coefficients

variable_location : bool, default False
TBD

)";

}// namespace parselmouth
69 changes: 69 additions & 0 deletions src/parselmouth/LineSpectralFrequencies.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
* Copyright (C) 2017-2021 Yannick Jadoul
*
* This file is part of Parselmouth.
*
* Parselmouth is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Parselmouth is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Parselmouth. If not, see <http://www.gnu.org/licenses/>
*/

#include "Parselmouth.h"

#include "LineSpectralFrequencies_docstrings.h"
#include "TimeClassAspects.h"

#include <praat/LPC/LPC_and_LineSpectralFrequencies.h>
#include <praat/LPC/LineSpectralFrequencies.h>

#include <pybind11/numpy.h>
#include <pybind11/stl.h>

namespace py = pybind11;
using namespace py::literals;

namespace parselmouth
{

PRAAT_CLASS_BINDING(LineSpectralFrequencies)
{
addTimeFrameSampledMixin(*this);

doc() = CLASS_DOCSTRING;

oo_DOUBLE(maximumFrequency) oo_INT(maximumNumberOfFrequencies)

def_readonly("maximum_frequency",
&structLineSpectralFrequencies::maximumFrequency,
MAXIMUM_FREQUENCY_DOCSTRING);
def_readonly("maximum_number_of_frequencies",
&structLineSpectralFrequencies::maximumNumberOfFrequencies,
MAXIMUM_NUMBER_OF_FREQUENCIES_DOCSTRING);

def(
"__len__", [](LineSpectralFrequencies self) { return self->nx; },
LEN_DOCSTRING);
def(
"__getitem__",
[](LineSpectralFrequencies self, int i) {
if (i < 0) i += self->nx; // Python-style negative indexing
if (i < 0 || i >= self->nx) throw py::index_error("Index out of range");
structLineSpectralFrequencies_Frame &item = self->d_frames.cells[i];
return py::array({item.numberOfFrequencies}, item.frequencies.cells);
},
"i"_a, py::return_value_policy::reference_internal, GETITEM_DOCSTRING);

// DIRECT (NEW_LineSpectralFrequencies_to_LPC) {
def("to_lpc", &LineSpectralFrequencies_to_LPC, TO_LPC_DOCSTRING);
}

} // namespace parselmouth
Loading