For more information about the examples, such as how the Python and Mojo files interact with each other, see the Examples Overview
VectorBasedPanning¶
An example of Vector Base Amplitude Panning in a 4 channel speaker array where speakers are placed at azimuths of -55, 55, -110, and 110 degrees.
The position of the audio source is controlled by the mouse. The corners of the screen are positioned directly on top of the speakers.
Python Code¶
from mmm_python import *
from math import pi
# from wsl_fixes import MMMAudioWSL
# instantiate and load the graph
mmm_audio = MMMAudio(128, num_output_channels=4, graph_name="VectorBasedPanning", package_name="examples")
mmm_audio.start_audio()
mmm_audio.send_float("az", 0.0 * 2 * pi)
mmm_audio.send_float("az", 0.125 * 2 * pi)
mmm_audio.send_float("az", 0.25 * 2 * pi)
mmm_audio.send_float("az", 0.375 * 2 * pi)
mmm_audio.send_float("az", 0.5 * 2 * pi)
mmm_audio.send_float("az", 0.625 * 2 * pi)
mmm_audio.send_float("az", 0.75 * 2 * pi)
mmm_audio.send_float("az", 0.875 * 2 * pi)
#Enable/disable mouse
mmm_audio.send_bool("mouse", True)
mmm_audio.send_bool("mouse", False)
# for Wayland use the fake mouse
MMMAudio.fake_mouse()
# # WSL Fake Mouse
# def handle_wsl_mouse(x, y):
# from numpy import interp
# mmm_audio.send_floats("pos", [interp(x, [0, 1], [-1, 1]), interp(y, [0, 1], [1, -1])]
# mmm_audio.send_int("wsl", 1)
# MMMAudioWSL.wsl_fake_mouse(handle_wsl_mouse)
mmm_audio.stop_audio()
mmm_audio.plot(48000)
Mojo Code¶
from mmm_audio import *
# THE SYNTH
struct VectorBasedPanning(Movable, Copyable):
var world: World
var dust: Dust[1]
var messenger: Messenger
var az: Float64
var filt: Reson[1]
var wsl: Int
var pos: List[Float64]
var mouse: Bool
def __init__(out self, world: World):
self.world = world
self.dust = Dust[1](world)
self.filt = Reson[1](world)
self.messenger = Messenger(self.world)
self.az = 0.0
self.wsl = 0
self.pos = [0.0, -1.0]
self.mouse = False
def next(mut self) -> MFloat[8]:
comptime max_simd = 8
comptime two_pi = 2 * pi
self.messenger.update("az", self.az)
self.messenger.update("pos", self.pos)
self.messenger.update("mouse", self.mouse)
# self.messenger.update("wsl", self.wsl)
# if self.wsl == 0:
# var x = linlin(self.world[].mouse_x, 0.0, 1.0, -1.0, 1.0)
# var y = linlin(self.world[].mouse_y, 0.0, 1.0, 1.0, -1.0)
# self.az = atan2(y, x)
# else:
# self.az = atan2(self.pos[1], self.pos[0])
comptime offset = deg_to_rad(90)
if self.mouse:
var x = linlin(self.world[].mouse_x(), 0.0, 1.0, -1.0, 1.0)
var y = linlin(self.world[].mouse_y(), 0.0, 1.0, 1.0, -1.0)
self.az = atan2(y, x) + offset
# 4 speaker setup
comptime speakers : InlineArray[Float64, 4] = [
deg_to_rad(-55),
deg_to_rad(55),
deg_to_rad(-110),
deg_to_rad(110)
]
sig = self.dust.next(10, 40) * 0.5
sig = self.filt.bpf(sig, 1200, 10.0, 1.0)
out = vbap2D[4, max_simd, speakers](sig, self.az)
#7 speaker setup
# comptime speakers : InlineArray[MFloat[2], 7] = [
# MFloat[2](-0.66, 1),
# MFloat[2](0.66, 1),
# MFloat[2](0, 1),
# MFloat[2](-1, 0),
# MFloat[2](1, 0),
# MFloat[2](-0.66, -1),
# MFloat[2](0.66, -1)
# ]
# comptime weights : InlineArray[Float64, 7] = [
# 1,1,1,1,1,1,1
# ]
# sig = self.dust.next(10, 40) * 0.5
# sig = self.filt.bpf(sig, 1200, 10.0, 1.0)
# out = dbap2D[7, max_simd, speakers, weights](sig, self.pos, 0.5)
return out * 0.5
def deg_to_rad(degrees: Float64) -> Float64:
"""
Converts from degrees to radians.
"""
return degrees * (pi/180)
def rad_to_deg(radians: Float64) -> Float64:
"""
Converts from radians to degrees.
"""
return radians * (180/pi)