-
Couldn't load subscription status.
- Fork 4
Add SphericalHarmonicsAudio classes #166
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
base: develop
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the effort. It generally looks good to me! I suggested one structural change and adding the docs but otherwise its fine to me. I think the testing should be fine. complete functionality is tested for SH signal, initialization tested for each class separately.
spharpy/classes/audio.py
Outdated
| # check dimensions | ||
| if len(data.shape) < 3: | ||
| raise ValueError("Invalid number of dimensions. Data should have " | ||
| "at least 3 dimensions.") | ||
|
|
||
| # set n_max | ||
| n_max = np.sqrt(data.shape[-2])-1 | ||
| if n_max - int(n_max) != 0: | ||
| raise ValueError("Invalid number of SH channels: " | ||
| f"{data.shape[-2]}. It must match (n_max + 1)^2.") | ||
| self._n_max = int(n_max) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems a little redundant to me to have this in all three classes. Would the following work:
- First call the init of the Audio class (FrequencyData in this case)
- Second call the init of the SHAudio class and move this code there
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point, yes that works.
spharpy/classes/audio.py
Outdated
|
|
||
|
|
||
| class SphericalHarmonicTimeData(SphericalHarmonicsAudio, TimeData): | ||
| """_summary_ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing docs - probably intended to get a review before the effort.
spharpy/classes/audio.py
Outdated
|
|
||
|
|
||
| class SphericalHarmonicFrequencyData(SphericalHarmonicsAudio, FrequencyData): | ||
| """_summary_ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing docs - probably intended to get a review before the effort.
add docstrings move n_max and dimensions check to base class
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me. I would suggest to wait with the merge until we finalized discussions on the SH conventions that we want to support. This might cause changes here as well.
| comment : str | ||
| A comment related to `data`. The default is ``None``. | ||
| """ | ||
| def __init__(self, data, frequencies, basis_type, normalization, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we want to have default values for the SH parameters as in the SH class, or are there not defaults by intention to make sure people think about this when inputting data from sources other than our internal SHT functions?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for taking care!
just minor comments
spharpy/classes/audio.py
Outdated
| class SphericalHarmonicSignal(Signal): | ||
| """Create audio object with spherical harmonics coefficients in time or | ||
| frequency domain. | ||
| class SphericalHarmonicAudio(_Audio): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| class SphericalHarmonicAudio(_Audio): | |
| class _SphericalHarmonicAudio(_Audio): |
shouldnt this be private, too?
spharpy/classes/audio.py
Outdated
| comment=comment, is_complex=is_complex) | ||
| SphericalHarmonicAudio.__init__( | ||
| self, basis_type, normalization, channel_convention, | ||
| condon_shortley, domain="time", comment=comment) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| condon_shortley, domain="time", comment=comment) | |
| condon_shortley, domain=domain, comment=comment) |
domain="time"would overwrite the given domain. But i think we can remove it here anyway
spharpy/classes/audio.py
Outdated
| def __init__(self, basis_type, normalization, channel_convention, | ||
| condon_shortley, domain, comment=""): | ||
| _Audio.__init__(self, domain=domain, comment=comment) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| def __init__(self, basis_type, normalization, channel_convention, | |
| condon_shortley, domain, comment=""): | |
| _Audio.__init__(self, domain=domain, comment=comment) | |
| def __init__(self, basis_type, normalization, channel_convention, | |
| condon_shortley): |
i wounder if we really need it here, because it would be set twice, by the audio class and then overwritten by this class. I think the _Audio.init is not setting anything else then this to entries, so i guess we can remove it.
spharpy/classes/audio.py
Outdated
| SphericalHarmonicAudio.__init__( | ||
| self, basis_type, normalization, channel_convention, | ||
| condon_shortley, domain="time", comment=comment) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| SphericalHarmonicAudio.__init__( | |
| self, basis_type, normalization, channel_convention, | |
| condon_shortley, domain="time", comment=comment) | |
| SphericalHarmonicAudio.__init__( | |
| self, basis_type, normalization, channel_convention, | |
| condon_shortley) |
|
Thanks for implementing! I think I am missing something fundamental here. Why is it not possible that _SphericalHarmonicAudio inherits from the SphericalHarmonics class? |
Because the SphericalHarmonics class contains a number of properties which are not meaningful in the SphericalHarmonicSignal class. They however share a set of properties/methods which would make a shared base class usefull, see #173 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The import of SH TimeData and FrequencyData is missing in spharpy/__init__.py
| well as the normalizations N3D, SN3D, or MaxN, see [#]_. The definition of | ||
| the spherical harmonics basis functions is based on the scipy convention | ||
| which includes the Condon-Shortley phase, [#]_, [#]_. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would suggest to keep this plain and without references. We will have a large general documentation for SHs for this:
| well as the normalizations N3D, SN3D, or MaxN, see [#]_. The definition of | |
| the spherical harmonics basis functions is based on the scipy convention | |
| which includes the Condon-Shortley phase, [#]_, [#]_. | |
| well as the normalizations N3D, NM, SN3D, SNM, or MaxN. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This re-appears in the SphericalHarmonicSignal class. But I can't comment there because the lines were not changed in this pull.
| .. [#] F. Zotter, M. Frank, "Ambisonics A Practical 3D Audio Theory | ||
| for Recording, Studio Production, Sound Reinforcement, and | ||
| Virtual Reality", (2019), Springer-Verlag | ||
| .. [#] B. Rafely, "Fundamentals of Spherical Array Processing", (2015), | ||
| Springer-Verlag | ||
| .. [#] E.G. Williams, "Fourier Acoustics", (1999), Academic Press |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See above. But maybe wait if anyone disagrees.
| .. [#] F. Zotter, M. Frank, "Ambisonics A Practical 3D Audio Theory | |
| for Recording, Studio Production, Sound Reinforcement, and | |
| Virtual Reality", (2019), Springer-Verlag | |
| .. [#] B. Rafely, "Fundamentals of Spherical Array Processing", (2015), | |
| Springer-Verlag | |
| .. [#] E.G. Williams, "Fourier Acoustics", (1999), Academic Press |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you please merge develop into this again to make sure we have capitalized channel conventions 'ACN' and 'FuMa'?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the effort. The inheritance generally looks good, but I found two things that I think must be considered:
- I think we must overwrite the setters for
n_max,basis_type, andcondon_shortleyto raise an error when the user attempts to set these attributes. Otherwise these parameters can be changed but the change won't be refelcted in the data. We could allow for such things, but I would not consider it in this pull request:
td = SphericalHarmonicTimeData([[[1, 2, 3]]], [1, 2, 3], 'real', 'SN3D', 'acn', 'auto')
td.n_max = 1 # this changes the order, but the data stays the same.- We need to handle setting the data and getting
n_max. The following changes the data, without changingn_max. I assume we want to be able to set new data and updating the order accordingly, e.g., whan upmixing to a higher SH order:
td = sy.classes.SphericalHarmonicTimeData([[[1, 2, 3]]], [1, 2, 3], 'real', 'SN3D', 'acn', 'auto')
td.time = np.random.rand(1, 4, 100)
spharpy/classes/audio.py
Outdated
| """ | ||
| def __init__(self, basis_type, normalization, channel_convention, | ||
| condon_shortley, domain, comment=""): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
domain and time are not accessed here, I assume because they are set in the audio classes from this inherits.
| condon_shortley, domain, comment=""): | |
| condon_shortley): |
spharpy/classes/audio.py
Outdated
| harmonic coefficients and times. | ||
| Objects of this class contain time data which is not directly convertible | ||
| to frequency domain, i.e., non-equidistant samples. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| to frequency domain, i.e., non-equidistant samples. | |
| to the frequency domain, i.e., non-equidistant samples. |
spharpy/classes/audio.py
Outdated
| ``'maxN'``, ``'SN3D'`` or ``'SNM'``. (maxN is only supported up | ||
| to 3rd order) | ||
| channel_convention : str | ||
| Channel ordering convention, either ``'acn'`` or ``'fuma'``. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| Channel ordering convention, either ``'acn'`` or ``'fuma'``. | |
| Channel ordering convention, either ``'ACN'`` or ``'FuMa'``. |
| Flag to indicate if the Condon-Shortley phase term is included | ||
| (``True``) or not (``False``). | ||
| comment : str | ||
| A comment related to `data`. The default is ``None``. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| A comment related to `data`. The default is ``None``. | |
| A comment related to `data`. The default is ``""``. |
spharpy/classes/audio.py
Outdated
| coefficients with 1024 samples each. The data can be ``int`` or | ||
| ``float`` and is converted to ``float`` in any case. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To be consistent with FrequencyData
| coefficients with 1024 samples each. The data can be ``int`` or | |
| ``float`` and is converted to ``float`` in any case. | |
| coefficients with 1024 samples each. The data can be ``int``, | |
| ``float`` or ``complex``. Data of type ``int`` is converted to | |
| ``float``. |
spharpy/classes/audio.py
Outdated
| ``'maxN'``, ``'SN3D'`` or ``'SNM'``. (maxN is only supported up | ||
| to 3rd order) | ||
| channel_convention : str | ||
| Channel ordering convention, either ``'acn'`` or ``'fuma'``. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| Channel ordering convention, either ``'acn'`` or ``'fuma'``. | |
| Channel ordering convention, either ``'ACN'`` or ``'FuMa'``. |
spharpy/classes/audio.py
Outdated
| self._channel_convention = value | ||
| _SphericalHarmonicAudio.__init__( | ||
| self, basis_type, normalization, channel_convention, | ||
| condon_shortley, domain=domain, comment=comment) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
see comment above:
| condon_shortley, domain=domain, comment=comment) | |
| condon_shortley) |
| data, times, basis_type='real', normalization='SN3D', | ||
| channel_convention="acn", condon_shortley=False, | ||
| comment="") | ||
| assert isinstance(sh_time_data, SphericalHarmonicTimeData) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This would be more strict
| assert isinstance(sh_time_data, SphericalHarmonicTimeData) | |
| assert type(sh_time_data) == SphericalHarmonicTimeData |
(maybe needs is instead of ==)
| data, frequencies, basis_type='real', normalization='SN3D', | ||
| channel_convention="acn", condon_shortley=False, | ||
| comment="") | ||
| assert isinstance(sh_freq_data, SphericalHarmonicFrequencyData) No newline at end of file |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| assert isinstance(sh_freq_data, SphericalHarmonicFrequencyData) | |
| assert type(sh_freq_data) == SphericalHarmonicFrequencyData |
| match="Invalid channel convention, currently only 'acn' " | ||
| "and 'fuma' are supported"): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| match="Invalid channel convention, currently only 'acn' " | |
| "and 'fuma' are supported"): | |
| match="Invalid channel convention, currently only 'ACN' " | |
| "and 'FuMa' are supported"): |
Should also be changed below after merging develop into this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, forgot one thing. I would suggest to rename audio.py to sh_audio.py.
Please rename files in this pr. this will cause conflicts with other branches. |
I'm very sorry, I meant to write don't rename in this branch, since the file is already part of develop. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm just commenting on this now, as I feel we need to make some adjustments to the SphericalHarmonicDefinition class before continuing with this PR.
- I think it makes sense to move the functionality of saving the old properties to the parent class, as it could come in handy in other classes.
- I now remember why I first had the SphericalHarmonicDefintion in mind without the
n_maxproperty defined. I'll add an abstract base class for that which could serve as parent for the SphericalHarmonicAudio classes.
Sorry to postpone this PR once again.
| three sub-classes :py:func:`SphericalHarmonicsTimeData`, | ||
| :py:func:`SphericalHarmonicsFrequencyData`, and | ||
| :py:func:`SphericalHarmonicsSignal`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| three sub-classes :py:func:`SphericalHarmonicsTimeData`, | |
| :py:func:`SphericalHarmonicsFrequencyData`, and | |
| :py:func:`SphericalHarmonicsSignal`. | |
| three sub-classes :py:func:`SphericalHarmonicTimeData`, | |
| :py:func:`SphericalHarmonicFrequencyData`, and | |
| :py:func:`SphericalHarmonicSignal`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, reverted. I kind of had the feeling it was a typo cause it did't made sense to me :D ...
I agree that it makes sense to move the saving of the old parameters to the SHDefinitions class. However, I think it could be an option to do that in a separate PR. The functionality from outside would not change, and we could move on with other stuff, like SHtrafo etc.
Which issue(s) are closed by this pull request?
Closes #165
Changes proposed in this pull request: