For more information about the examples, such as how the Python and Mojo files interact with each other, see the Examples Overview
ChowningFM¶
Chowning FM synthesis example.
These examples are adapted from Chowning's original paper on FM synthesis and can also be found in "Computer Music" by Dodge and Jerse. pg. 123-127.
Python Code¶
from mmm_python.MMMAudio import MMMAudio
mmm_audio = MMMAudio(128, graph_name="ChowningFM", package_name="examples")
mmm_audio.start_audio()
# bell
mmm_audio.send_float("m_freq", 280.0)
mmm_audio.send_float("c_freq", 200.0)
mmm_audio.send_floats("amp_vals",[0.0,1.0,0.2,0.0])
mmm_audio.send_floats("amp_times",[0.001,1.8,1.7])
mmm_audio.send_floats("amp_curves",[1,1,1])
mmm_audio.send_floats("index_vals",[10.0,2.0,0.0])
mmm_audio.send_floats("index_times",[1.8,1.7])
mmm_audio.send_floats("index_curves",[1,1])
mmm_audio.send_trig("trigger")
# woodblock
mmm_audio.send_float("m_freq", 55.0)
mmm_audio.send_float("c_freq", 80.0)
mmm_audio.send_floats("amp_vals",[0.75, 1.0, 0.6, 0.2, 0.0])
mmm_audio.send_floats("amp_times",[0.02, 0.02, 0.06, 0.1])
mmm_audio.send_floats("amp_curves",[1, 1, 1, 1])
mmm_audio.send_floats("index_vals",[25.0, 0.0])
mmm_audio.send_floats("index_times",[0.012])
mmm_audio.send_floats("index_curves",[1])
mmm_audio.send_trig("trigger")
# brass
mmm_audio.send_float("m_freq", 440.0)
mmm_audio.send_float("c_freq", 440.0)
mmm_audio.send_floats("amp_vals",[0, 1, 0.7, 0.7, 0])
mmm_audio.send_floats("amp_times",[0.075, 0.050, 0.4, 0.06])
mmm_audio.send_floats("amp_curves",[1, 1, 1, 1])
mmm_audio.send_floats("index_vals",[0, 5, 3.5, 3.5, 0])
mmm_audio.send_floats("index_times",[0.075, 0.050, 0.4, 0.06])
mmm_audio.send_floats("index_curves",[1,1,1,1])
mmm_audio.send_trig("trigger")
# brass (less bright)
mmm_audio.send_float("m_freq", 440.0)
mmm_audio.send_float("c_freq", 440.0)
mmm_audio.send_floats("amp_vals",[0, 1, 0.7, 0.7, 0])
mmm_audio.send_floats("amp_times",[0.075, 0.050, 0.4, 0.06])
mmm_audio.send_floats("amp_curves",[1, 1, 1, 1])
mmm_audio.send_floats("index_vals",[0, 3, 2.1, 2.1, 0])
mmm_audio.send_floats("index_times",[0.075, 0.050, 0.4, 0.06])
mmm_audio.send_floats("index_curves",[1,1,1,1])
mmm_audio.send_trig("trigger")
# clarinet
mmm_audio.send_float("m_freq", 600.0)
mmm_audio.send_float("c_freq", 900.0)
mmm_audio.send_floats("amp_vals",[0, 1, 1, 0])
mmm_audio.send_floats("amp_times",[0.087, 0.4, 0.087])
mmm_audio.send_floats("amp_curves",[1, 1, 1])
mmm_audio.send_floats("index_vals",[4, 2])
mmm_audio.send_floats("index_times",[0.073])
mmm_audio.send_floats("index_curves",[1])
mmm_audio.send_trig("trigger")
# stop audio
mmm_audio.stop_audio()
Mojo Code¶
from mmm_audio import *
struct ChowningFM(Representable, Movable, Copyable):
var world: UnsafePointer[MMMWorld] # pointer to the MMMWorld
var m: Messenger
var c_osc: Osc[1,1,1] # Carrier oscillator
var m_osc: Osc # Modulator oscillator
var index_env: Env
var index_env_params: EnvParams
var amp_env: Env
var amp_env_params: EnvParams
var cfreq: Float64
var mfreq: Float64
var vol: Float64
fn __init__(out self, world: UnsafePointer[MMMWorld]):
self.world = world
self.m = Messenger(self.world)
self.c_osc = Osc[1,1,1](self.world)
self.m_osc = Osc(self.world)
self.index_env = Env(self.world)
self.index_env_params = EnvParams()
self.amp_env = Env(self.world)
self.amp_env_params = EnvParams()
self.cfreq = 200.0
self.mfreq = 100.0
self.vol = -12.0
fn __repr__(self) -> String:
return String("ChowningFM")
@always_inline
fn update_envs(mut self):
self.m.update(self.index_env_params.values,"index_vals")
self.m.update(self.index_env_params.times,"index_times")
self.m.update(self.index_env_params.curves,"index_curves")
self.m.update(self.amp_env_params.values,"amp_vals")
self.m.update(self.amp_env_params.times,"amp_times")
self.m.update(self.amp_env_params.curves,"amp_curves")
@always_inline
fn next(mut self) -> SIMD[DType.float64, 2]:
self.m.update(self.cfreq,"c_freq")
self.m.update(self.mfreq,"m_freq")
self.m.update(self.vol,"vol")
trig = self.m.notify_trig("trigger")
self.update_envs()
index = self.index_env.next(self.index_env_params, trig)
msig = self.m_osc.next(self.mfreq) * self.mfreq * index
csig = self.c_osc.next(self.cfreq + msig)
csig *= self.amp_env.next(self.amp_env_params, trig)
csig *= dbamp(self.vol)
return csig