For more information about the examples, such as how the Python and Mojo files interact with each other, see the Examples Overview
PanAzExample¶
Shows how to use PanAz to pan audio between multiple speakers arranged in a circle. You can set the number of speakers between 2 and 8 using the "num_speakers" parameter. You can set the width of the panning using the "width" parameter. You can set the frequency of the input tone using the "freq" parameter.
PanAz outputs a SIMD array with one channel per speaker. Since SIMD arrays must be a power of 2 in size, the num_speakers parameter must be set to a value below or equal to the size of the SIMD array (8 in this case). Any unused channels will be silent.
Python Code¶
from mmm_python.MMMAudio import MMMAudio
# instantiate and load the graph
# PanAz is not quite right as of yet
mmm_audio = MMMAudio(128, graph_name="PanAzExample", package_name="examples", num_output_channels=8)
mmm_audio.start_audio()
mmm_audio.send_int("num_speakers", 2 ) # set the number of speakers to between 2 and 8
mmm_audio.send_int("num_speakers", 7 ) # set the number of speakers to between 2 and 8
mmm_audio.send_float("width", 1.0 ) # set the width to 1.0 (one speaker at a time)
mmm_audio.send_float("width", 3.0 ) # set the width to 3.0 (extra wide stereo width)
from random import random
mmm_audio.send_float("freq", random() * 500 + 100 ) # set the frequency to a random value
Mojo Code¶
from mmm_audio import *
struct PanAz_Synth(Representable, Movable, Copyable):
var world: UnsafePointer[MMMWorld]
var osc: Osc
var freq: Float64
var pan_osc: Phasor
var num_speakers: Int64
var width: Float64
var messenger: Messenger
fn __init__(out self, world: UnsafePointer[MMMWorld]):
self.world = world
self.osc = Osc(self.world)
self.freq = 440.0
self.pan_osc = Phasor(self.world)
self.num_speakers = 7 # default to 7 speakers
self.width = 2.0
self.messenger = Messenger(self.world)
fn __repr__(self) -> String:
return String("Default")
fn next(mut self) -> SIMD[DType.float64, 8]:
self.messenger.update(self.freq, "freq")
self.messenger.update(self.num_speakers, "num_speakers")
self.messenger.update(self.width, "width")
# PanAz needs to be given a SIMD size that is a power of 2, in this case [8], but the speaker size can be anything smaller than that
panned = pan_az[8](self.osc.next(self.freq, osc_type=2), self.pan_osc.next(0.1), self.num_speakers, self.width) * 0.1
if self.num_speakers == 2:
return SIMD[DType.float64, 8](panned[0], panned[1], 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
else:
return SIMD[DType.float64, 8](panned[0], panned[2], panned[1], 0.0, panned[6], panned[3], panned[5], panned[4])
# there can only be one graph in an MMMAudio instance
# a graph can have as many synths as you want
struct PanAzExample(Representable, Movable, Copyable):
var world: UnsafePointer[MMMWorld]
var synth: PanAz_Synth
fn __init__(out self, world: UnsafePointer[MMMWorld]):
self.world = world
self.synth = PanAz_Synth(self.world)
fn __repr__(self) -> String:
return String("PanAzExample")
fn next(mut self) -> SIMD[DType.float64, 8]:
sample = self.synth.next() # Get the next sample from the synth
# the output will pan to the number of channels available
# if there are fewer than 5 channels, only those channels will be output
return sample # Return the combined output samples