Spaces:
Sleeping
Sleeping
"""3D format conversion for coordinates and Ambisonics""" | |
from functools import singledispatch | |
from typing import List, Tuple, Union | |
import numpy as np | |
def convert_ambisonics_a_to_b( | |
front_left_up: np.ndarray, | |
front_right_down: np.ndarray, | |
back_right_up: np.ndarray, | |
back_left_down: np.ndarray, | |
) -> np.ndarray: | |
"""Converts Ambisonics A-format to B-format | |
Parameters | |
---------- | |
front_left_up : np.ndarray | |
Front Left Up signal from A-format | |
front_right_down : np.ndarray | |
Front Right Down signal from A-format | |
back_right_up : np.ndarray | |
Back Right Up signal from A-format | |
back_left_down : np.ndarray | |
Back Left Down signal from A-format | |
Returns | |
------- | |
np.ndarray | |
B-format outputs (W, X, Y, Z) | |
""" | |
front = front_left_up + front_right_down | |
back = back_left_down + back_right_up | |
left = front_left_up + back_left_down | |
right = front_right_down + back_right_up | |
up = front_left_up + back_right_up # pylint: disable=invalid-name | |
down = front_right_down + back_left_down | |
w_channel = front + back | |
x_channel = front - back | |
y_channel = left - right | |
z_channel = up - down | |
return np.array([w_channel, x_channel, y_channel, z_channel]) | |
def _(aformat_channels: List[np.ndarray]) -> np.ndarray: | |
"""Converts Ambisonics A-format to B-format. | |
Parameters | |
---------- | |
aformat_channels : List[np.ndarray] | |
A list containing the 4 channels of A-format Ambisonics in the following order: | |
1. Front Left Up | |
2. Front Right Down | |
3. Back Right Up | |
4. Back Left Down | |
Returns | |
------- | |
np.ndarray | |
B-format outputs (W, X, Y, Z) | |
""" | |
assert ( | |
len(aformat_channels) == 4 | |
), "Conversion from A-format to B-format requires 4 channels" | |
return convert_ambisonics_a_to_b.dispatch(np.ndarray)( | |
front_left_up=aformat_channels[0], | |
front_right_down=aformat_channels[1], | |
back_right_up=aformat_channels[2], | |
back_left_down=aformat_channels[3], | |
) | |
def spherical_to_cartesian( | |
radius: Union[float, np.ndarray], | |
azimuth: Union[float, np.ndarray], | |
elevation: Union[float, np.ndarray], | |
) -> Tuple[Union[float, np.ndarray]]: | |
"""Convert three 3D polar coordinates to Cartesian ones. | |
Parameters | |
radius: float | np.ndarray. The radii (or rho). | |
azimuth: float | np.ndarray. The azimuth (also called theta or alpha). | |
elevation: float | np.ndarray. The elevation (also called phi or polar). | |
Returns | |
(x, y, z): Tuple[float | np.ndarray]. The corresponding Cartesian coordinates. | |
""" | |
return ( | |
radius * np.cos(np.deg2rad(azimuth)) * np.cos(np.deg2rad(elevation)), | |
radius * np.sin(np.deg2rad(azimuth)) * np.cos(np.deg2rad(elevation)), | |
radius * np.sin(np.deg2rad(elevation)), | |
) | |
def cartesian_to_spherical(intensity_windowed: np.ndarray): | |
"""_summary_ | |
Parameters | |
---------- | |
intensity_windowed : np.ndarray | |
_description_ | |
Returns | |
------- | |
_type_ | |
_description_ | |
""" | |
# Convert to total intensity, azimuth and elevation | |
intensity = np.sqrt((intensity_windowed**2).sum(axis=0)).squeeze() | |
azimuth = np.rad2deg( | |
np.arctan2(intensity_windowed[1], intensity_windowed[0]) | |
).squeeze() | |
elevation = np.rad2deg(np.arcsin(intensity_windowed[2] / intensity)).squeeze() | |
return intensity, azimuth, elevation | |