For more information about the examples, such as how the Python and Mojo files interact with each other, see the Examples Overview
ChowningFM¶
These examples are adapted from Chowning's original paper on FM synthesis: https://web.eecs.umich.edu/~fessler/course/100/misc/chowning-73-tso.pdf and can also be found in "Computer Music" by Dodge and Jerse. pg. 123-127.
Python Code¶
from mmm_src.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_src.MMMWorld import MMMWorld
from mmm_utils.functions import *
from mmm_src.MMMTraits import *
from mmm_dsp.Osc import Osc
from mmm_dsp.Env import *
from collections import Dict
from mmm_utils.Messenger import *
struct ChowningFM(Representable, Movable, Copyable):
var world: UnsafePointer[MMMWorld] # pointer to the MMMWorld
var m: Messenger
var c_osc: Osc[1,0,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(world)
self.c_osc = Osc[1,0,1](world)
self.m_osc = Osc(world)
self.index_env = Env(world)
self.index_env_params = EnvParams()
self.amp_env = Env(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