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

Frequency domain sweep synthesis #199

Merged
merged 49 commits into from
Apr 25, 2024
Merged
Changes from 4 commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
7a35a41
added docstring for discussion
f-brinkmann Jun 2, 2021
3eb99d3
flake8 fix
f-brinkmann Jun 2, 2021
4a9f22a
Merge branch 'deprecate/sweep_synthesis' into feature/spectral_sweep_…
f-brinkmann Jun 17, 2021
b39f15a
[skip ci] add function definitions for all new sweep synthesis methods
f-brinkmann Jun 17, 2021
7a4647e
[skip ci] suggestion from mberz
f-brinkmann Jun 18, 2021
8fdaf40
Merge branch 'deprecate/sweep_synthesis' into feature/spectral_sweep_…
f-brinkmann Jun 18, 2021
b048bb8
unique naming
f-brinkmann Jun 18, 2021
89573c6
[skip ci] changed docstring
f-brinkmann Jun 28, 2021
e719946
[skip ci] started to port implementation from AKtools, not ready
al-de Jan 21, 2022
61dcac9
Merge branch 'develop' into feature/spectral_sweep_synthesis
mberz Apr 28, 2022
299acc6
[skip ci] add current status of _sweep_synthesis_freq, not working co…
al-de Jun 10, 2022
c0d6fba
Merge branch 'feature/spectral_sweep_synthesis' of https://github.com…
al-de Jun 10, 2022
9e49d03
[skip ci] update frequency domain sweep
f-brinkmann Jun 17, 2022
0fd92bd
Merge branch 'develop' into feature/spectral_sweep_synthesis
f-brinkmann Oct 18, 2023
e51f2b2
remove defaults from private function to be more explicit
f-brinkmann Oct 18, 2023
8685649
progress
f-brinkmann Oct 18, 2023
382cf14
first working version
f-brinkmann Oct 18, 2023
3eec2c0
[skip ci] flake8 fix
f-brinkmann Oct 19, 2023
f2b9b6d
further progress
f-brinkmann Oct 19, 2023
7d7a73e
return group delay as pyfar Audio object
f-brinkmann Oct 19, 2023
be43e4e
more flake fixes
f-brinkmann Oct 19, 2023
c6cd558
add fade in and out
f-brinkmann Oct 19, 2023
24bcd74
renaming for consistency
f-brinkmann Oct 20, 2023
f239137
first working version
f-brinkmann Oct 20, 2023
717cafd
fix flake8 error
f-brinkmann Oct 20, 2023
e5d7c2e
bug fix
f-brinkmann Oct 26, 2023
d7a5fb6
improve windowing
f-brinkmann Oct 26, 2023
f0fb9e9
[skip ci] initial version of docstrings
f-brinkmann Oct 26, 2023
67e6403
[skip ci] improve docstring
f-brinkmann Oct 26, 2023
7880500
add plot examples
f-brinkmann Oct 27, 2023
ab5bcf8
Merge branch 'develop' into feature/spectral_sweep_synthesis
f-brinkmann Oct 27, 2023
1f4f974
add tests for linear perfect sweep
f-brinkmann Oct 28, 2023
b46bd7d
add testing for public functions
f-brinkmann Oct 28, 2023
edf1da8
add more tests
f-brinkmann Oct 28, 2023
4f2e289
finalize tests and functions
f-brinkmann Oct 28, 2023
89e4bbb
Fix docstring
f-brinkmann Oct 29, 2023
0f3693c
Changes suggested by pingelit
f-brinkmann Dec 2, 2023
8276679
Apply suggestion from pingelit
f-brinkmann Dec 4, 2023
876ceb1
apply comment from ahms5
f-brinkmann Dec 12, 2023
6cc1af2
Account for comments by ahms5
f-brinkmann Dec 13, 2023
1b90fc1
update accroding to pingelit
f-brinkmann Dec 18, 2023
c954e59
correct typos
f-brinkmann Dec 21, 2023
e1d3bff
account for review by
f-brinkmann Apr 4, 2024
c07e9d4
Merge branch 'develop' into feature/spectral_sweep_synthesis
f-brinkmann Apr 4, 2024
9916ec8
update according to review
f-brinkmann Apr 5, 2024
68669ca
cleaner return structure
f-brinkmann Apr 5, 2024
be1b624
make group delay optional return parameter and improve docstrings
f-brinkmann Apr 5, 2024
13f12b7
update according to review from mberz
f-brinkmann Apr 24, 2024
fb4d817
rename fade parameters
f-brinkmann Apr 25, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
176 changes: 176 additions & 0 deletions pyfar/signals/deterministic.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,14 @@ def linear_sweep_time(n_samples, frequency_range, n_fade_out=90, amplitude=1,
seconds, and the frequency limits :math:`f_\\mathrm{low}` and
:math:`f_\\mathrm{high}`.

.. note::
The linear sweep can also be generated in the frequency domain
(see :py:func:`~general_sweep_synthesis`). Time domain synthesis
exhibits a constant temporal envelope in trade of slight ripples in the
magnitude response. Frequency domain synthesis exhibits smooth
magnitude spectra and in trade of a slightly irregular temporal
envelope.

Parameters
----------
n_samples : int
Expand Down Expand Up @@ -196,6 +204,17 @@ def linear_sweep_time(n_samples, frequency_range, n_fade_out=90, amplitude=1,
return signal


def linear_sweep_freq(
n_samples, start_margin=None, stop_margin=None, frequency_range=None,
butterworth_order=8, double=True, sampling_rate=44100):

signal, group_delay = _sweep_synthesis_freq(
n_samples, "linear", start_margin, stop_margin,
frequency_range, butterworth_order, double, sampling_rate)

return signal, group_delay


def exponential_sweep(n_samples, frequency_range, n_fade_out=90,
amplitude=1, sweep_rate=None, sampling_rate=44100):
"""
Expand Down Expand Up @@ -229,6 +248,14 @@ def exponential_sweep_time(n_samples, frequency_range, n_fade_out=90,
seconds, and the frequency limits :math:`f_\\mathrm{low}` and
:math:`f_\\mathrm{high}`.

.. note::
The exponential sweep can also be generated in the frequency domain
(see :py:func:`~general_sweep_synthesis`). Time domain synthesis
exhibits a constant temporal envelope in trade of slight ripples in the
magnitude response. Frequency domain synthesis exhibits smooth
magnitude spectra and in trade of a slightly irregular temporal
envelope.

Parameters
----------
n_samples : int
Expand Down Expand Up @@ -271,6 +298,155 @@ def exponential_sweep_time(n_samples, frequency_range, n_fade_out=90,
return signal


def exponential_sweep_freq(
n_samples, start_margin=None, stop_margin=None, frequency_range=None,
butterworth_order=8, double=True, sampling_rate=44100):

signal, group_delay = _sweep_synthesis_freq(
n_samples, "exponential", start_margin, stop_margin,
frequency_range, butterworth_order, double, sampling_rate)

return signal, group_delay


def magnitude_weighted_sweep(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The more I think about this, I'd prefer magnitude_spectrum_weighted_sweep:

Suggested change
def magnitude_weighted_sweep(
def magnitude_spectrum_weighted_sweep(

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes - changed :)

n_samples, magnitude, start_margin=None, stop_margin=None,
double=True, sampling_rate=44100):

signal, group_delay = _sweep_synthesis_freq(
n_samples, magnitude, start_margin, stop_margin,
None, None, double, sampling_rate)

return signal, group_delay


def perfect_sweep(
f-brinkmann marked this conversation as resolved.
Show resolved Hide resolved
n_samples, sampling_rate=44100):

signal, group_delay = _sweep_synthesis_freq(
n_samples, "perfect", None, None, None, None, False, sampling_rate)

return signal, group_delay


def _sweep_synthesis_freq(
n_samples, magnitude, start_margin=None, stop_margin=None,
frequency_range=None, buterworth_order=8, double=True,
sampling_rate=44100):
"""
Frequency domain sweep synthesis with arbitrary magnitude response.

TODO Link to fractional octave smoothing in Notes

TODO Implement calculation of group delay of impulse responses

TODO Examples

Sweep sweep synthesis according to [#]_

.. note::
The linear and exponential sweep can also be generated in the time
domain (see :py:func:`~linear_sweep`, :py:func:`~exponential_sweep`).
Frequency domain synthesis exhibits smooth magnitude spectra and in
trade of a slightly irregular temporal envelope. Time domain synthesis
exhibits a constant temporal envelope in trade of slight ripples in the
magnitude response.

Parameters
----------
n_samples : int
The length of the sweep in samples.
magnitude : Signal, string
Specify the magnitude response of the sweep.

signal
The magnitude response as :py:class:`~pyfar.classes.audio.Signal`
object. If ``signal.n_samples`` is smaller than `n_samples`, zeros
are padded to the end of `signal`. Note that `frequency_range` is
not required in this case.
``'linear'``
Design a sweep with linearly increasing frequency and a constant
magnitude spectrum.
``'exponential'``
Design a sweep with exponentially increasing frequency. The
magnitude decreases by 3 dB per frequency doubling and has constant
energy in fiters of relative constant bandwidth (e.g. octaves).
``'perfect'``
Perfect sweep according to [#]_. Note that the parameters
`start_margin`, `stop_margin`, `frequency_range` and `double` are
not required in this case.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Calling this magnitude is a bit misleading. The effect on the magnitude is only a secondary effect.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed it to sweep_type. But it should not be critical any more since it was moved to a private function


start_margin : int, optional
The time in samples, at which the sweep starts. The start margin is
required because the frequency domain sweep synthesis has pre-ringing
in the time domain. Not required if `spectrum` is ``'perfect'``.
stop_margin : int, optional
Time in samples, at which the sweep stops. This is relative to
`n_samples`, e.g., a stop margin of 100 samples means that the sweep
ends at sample ``n_samples-10``. This is required, because the
frequency domain sweep synthesis has post-ringing in the time domain.
Not required if `spectrum` is ``'perfect'``.
frequency_range : array like, optional
Frequency range of the sweep given by the lower and upper cut-off
frequency in Hz. The restriction of the frequency range is realized
by appling a Butterworth band-pass with the specified frequencies.
Not required if `spectrum` is ``'perfect'`` or `signal`.
butterworth_order : int, optional
The order of the Butterworth filters that are applied to limit the
frequency range. The default is ``8``.
double : bool, optional
Double `n_samples` during the sweep calculation (recommended). The
default is ``True``. Not required if `spectrum` is ``'perfect'``.
sampling_rate : int, optional
The sampling rate in Hz. The default is ``44100``.

Returns
-------
sweep : Signal
The sweep signal. The Signal is in the time domain and has the ``none``
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

none or rms? all other docstrings say rms.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RMS :)

FFT normalization (see :py:func:`~pyfar.dsp.fft.normalization`). The
sweep parameters are written to `comment`.
group_delay_sweep : FrequencyData
The group delay of the sweep as a single sided spectrum between 0 Hz
and ``sampling_rate/2``.

TODO add this after implementation is complete:

This can be used to calculate the group delay of the impulse responses
of linear and harmonic distortion products after deconvoloution (see
:py:func:`~pyfar.dsp...`).

Notes
-----
The envelope of the sweep time signal should be constant, appart from
slight overshoots at the beginning and end. If this is not the case, try to
provide a smoother spectrum (if `spectrum` is `signal`) or increase
`n_samples`.

References
----------
.. [#] S. Müller, P. Massarani. 'Transfer Function Measurement with Sweeps.
Directors Cut Including Previously Unreleased Material and some
Corrections. J. Audio Eng. Soc. 2001, 49 (6), 443–471.
.. [#] C. Antweiler, A. Telle, P. Vary, G. Enzner. 'Perfect-Sweep NLMS for
Time-Variant Acoustic System Identification,' IEEE Int. Conf.
Acoustics, Speech and Signal Processing (ICASSP),
Prague, Czech Republic, 2011. doi: 10.1109/ICASSP.2012.6287930.

Examples
--------
TODO Example with spectrum=singal
(e.g., Bass emphasis by means of low shelve filter)

TODO Examples with spectrum="linear"

TODO Examples with spectrum="exponential"

TODO Examples with spectrum="perfect"
"""
pass


def _time_domain_sweep(n_samples, frequency_range, n_fade_out, amplitude,
sampling_rate, sweep_type, sweep_rate=None):

Expand Down