class SphericalMapMachine(object): def __init__(self, ambi_order=1, window=None, angular_res=20.0): self.angular_res = angular_res self.phi_mesh, self.nu_mesh = spherical_mesh(angular_res) self.frame_shape = self.phi_mesh.shape self.window = window mesh_p = [ Position(phi, nu, 1., 'polar') for phi, nu in zip( self.phi_mesh.reshape(-1), self.nu_mesh.reshape(-1)) ] # Setup decoder self.decoder = AmbiDecoder(mesh_p, AmbiFormat(ambi_order), method='projection') def compute(self, data): # Decode ambisonics on a grid of speakers if self.window is not None: n_windows = data.shape[0] / self.window data = data[:self.window * n_windows] decoded = self.decoder.decode(data) # Compute RMS at each speaker if self.window is not None: decoded = decoded.reshape((n_windows, self.window, -1)) rms = np.sqrt(np.mean(decoded**2, 1)) rms = rms.reshape((n_windows, ) + self.frame_shape) else: rms = np.sqrt(np.mean(decoded**2, 0)) rms = rms.reshape(self.frame_shape) return rms
class AmbisonicDecoder(object): def __init__(self, ambi_format, mic_pos, method='projection', td_order=5, **kwargs): self.source_decoder = VirtualMics(mic_pos, **kwargs) self.fmt = ambi_format self.method = method # Initialize speakers if self.method == 'pseudoinv': self.speaker_pos = list(map(lambda x: Position(x[0], x[1], x[2], 'cartesian'), get_tDesign(td_order))) # self.speaker_pos = list(map(lambda x: Position(x[0], x[1], x[2], 'cartesian'), get_tDesign(self.fmt.order))) elif self.method == 'projection': speakers_phi = (2. * np.arange(2*self.fmt.num_channels) / float(2*self.fmt.num_channels) - 1.) * np.pi self.speaker_pos = list(map(lambda x: Position(x, 0, self.fmt.radius, 'polar'), speakers_phi)) else: raise ValueError('Unknown decoding method. Options: projection and pseudoinv') self.n_speakers = len(self.speaker_pos) self.ambi_decoder = AmbiDecoder(self.speaker_pos, self.fmt, method=self.method) def binauralize(self, ambi): # Decode ambisonics into speakers speakers = self.ambi_decoder.decode(ambi) # Binauralize speaker as if they were point sources sources = list(map(lambda i: PositionalSource(speakers[i], self.speaker_pos[i], self.fmt.sample_rate), range(self.n_speakers))) stereo = self.source_decoder.binauralize(sources) return stereo
class DirectAmbisonicBinauralizer(object): def __init__(self, ambi_format, method='projection'): self.fmt = ambi_format self.method = method # Initialize ear position self.ear_pos = [Position(0, 0.1, 0, 'cartesian'), Position(0, -0.1, 0, 'cartesian')] self.ambi_decoder = AmbiDecoder(self.ear_pos, self.fmt, method=self.method) def binauralize(self, ambi): return self.ambi_decoder.decode(ambi)
class AmbiPower: def __init__(self, pos): from utils.ambisonics.decoder import AmbiDecoder from utils.ambisonics.position import Position pos = [Position(*er2polar(x, y), 'polar') for x, y in pos] self.decoder = AmbiDecoder(pos, method='projection') def compute(self, ambi): sig = self.decoder.decode(ambi) sig_pow = np.sum(sig ** 2, axis=1) return sig_pow
class SphericalAmbisonicsVisualizer(object): def __init__(self, data, rate=22050, window=0.1, angular_res=2.0): self.window = window self.angular_res = angular_res self.data = data self.phi_mesh, self.nu_mesh = spherical_mesh(angular_res) mesh_p = [ Position(phi, nu, 1., 'polar') for phi, nu in zip( self.phi_mesh.reshape(-1), self.nu_mesh.reshape(-1)) ] # Setup decoder ambi_order = np.sqrt(data.shape[0]) - 1 self.decoder = AmbiDecoder(mesh_p, AmbiFormat(ambi_order=ambi_order, sample_rate=rate), method='projection') # Compute spherical energy averaged over consecutive chunks of "window" secs self.window_frames = int(self.window * rate) self.n_frames = data.shape[1] / self.window_frames self.output_rate = float(rate) / self.window_frames self.frame_dims = self.phi_mesh.shape self.cur_frame = -1 def visualization_rate(self): return self.output_rate def mesh(self): return self.nu_mesh, self.phi_mesh def get_next_frame(self): self.cur_frame += 1 if self.cur_frame >= self.n_frames: return None # Decode ambisonics on a grid of speakers chunk_ambi = self.data[:, self.cur_frame * self.window_frames:((self.cur_frame + 1) * self.window_frames)] decoded = self.decoder.decode(chunk_ambi) # Compute RMS at each speaker rms = np.sqrt(np.mean(decoded**2, 1)).reshape(self.phi_mesh.shape) return np.flipud(rms) def loop_frames(self): while True: rms = self.get_next_frame() if rms is None: break yield rms
def ambix_power_map(ambix, audio_rate=22050, outp_rate=10, angular_res=5.0): from utils.ambisonics.distance import spherical_mesh from utils.ambisonics.decoder import AmbiDecoder, AmbiFormat from utils.ambisonics.position import Position phi_mesh, nu_mesh = spherical_mesh(angular_res) mesh_p = [Position(phi, nu, 1., 'polar') for phi, nu in zip(phi_mesh.reshape(-1), nu_mesh.reshape(-1))] ambi_order = math.sqrt(ambix.shape[0]) - 1 decoder = AmbiDecoder(mesh_p, AmbiFormat(ambi_order=int(ambi_order), sample_rate=audio_rate), method='projection') # Compute RMS at each speaker rms = [] window_size = int(audio_rate / outp_rate) for t in np.arange(0, ambix.shape[1], window_size): chunk = ambix[:, int(t):int(t) + window_size] decoded = decoder.decode(chunk) rms += [np.flipud(np.sqrt(np.mean(decoded ** 2, 1)).reshape(phi_mesh.shape))] return np.stack(rms, 0)