For more information about the examples, such as how the Python and Mojo files interact with each other, see the Examples Overview
FreeverbExample¶
A classic reverb effect using the Freeverb UGen.
This example demonstrates how to use the Freeverb graph in MMM-Audio and how to control its parameters via a simple GUI built with PySide6.
Python Code¶
import sys
from pathlib import Path
# This example is able to run by pressing the "play" button in VSCode
# that executes the whole file.
# In order to do this, it needs to add the parent directory to the path
# (the next line here) so that it can find the mmm_src and mmm_utils packages.
# If you want to run it line by line in a REPL, skip this line!
sys.path.insert(0, str(Path(__file__).parent.parent))
from mmm_python.MMMAudio import MMMAudio
from mmm_python.python_utils import *
from mmm_python.GUI import Handle, ControlSpec
from mmm_python.MMMAudio import MMMAudio
from PySide6.QtWidgets import QApplication, QWidget, QVBoxLayout, QCheckBox
mmm_audio = MMMAudio(128, graph_name="FreeverbExample", package_name="examples")
mmm_audio.start_audio() # start the audio thread - or restart it where it left off
app = QApplication([])
# Create the main window
window = QWidget()
window.setWindowTitle("Freeverb Controller")
window.resize(300, 100)
# stop audio when window is closed
window.closeEvent = lambda event: (mmm_audio.stop_audio(), event.accept())
# Create layout
layout = QVBoxLayout()
# Create a slider
roomsize_slider = Handle("room size", ControlSpec(0, 1.0, 4), 0.5, callback=lambda v: mmm_audio.send_float("room_size", v), run_callback_on_init=True)
layout.addWidget(roomsize_slider)
lpf = Handle("lpf",ControlSpec(100.0, 20000.0, 0.5), 2000, callback=lambda v: mmm_audio.send_float("lpf_comb", v), run_callback_on_init=True)
layout.addWidget(lpf)
added_space = Handle("added space",ControlSpec(0.2, 1.0), 0.0, callback=lambda v: mmm_audio.send_float("added_space", v), run_callback_on_init=True)
layout.addWidget(added_space)
mix_slider = Handle("mix",ControlSpec(0.1, 1.0, 0.5), 0.2, callback=lambda v: mmm_audio.send_float("mix", v), run_callback_on_init=True)
layout.addWidget(mix_slider)
# Set the layout for the main window
window.setLayout(layout)
# Show the window
window.show()
# Start the application's event loop
app.exec()
Mojo Code¶
from mmm_audio import *
struct FreeverbSynth(Copyable, Movable):
var world: UnsafePointer[MMMWorld]
var buffer: Buffer
var num_chans: Int64
var play_buf: Play
var freeverb: Freeverb[2]
var m: Messenger
var room_size: Float64
var lpf_comb: Float64
var added_space: Float64
var mix: Float64
fn __init__(out self, world: UnsafePointer[MMMWorld]):
self.world = world
# load the audio buffer
self.buffer = Buffer.load("resources/Shiverer.wav")
self.num_chans = self.buffer.num_chans
# without printing this, the compiler wants to free the buffer for some reason
print("Loaded buffer with", self.buffer.num_chans, "channels and", self.buffer.num_frames, "frames.")
self.play_buf = Play(self.world)
self.freeverb = Freeverb[2](self.world)
self.room_size = 0.9
self.lpf_comb = 1000.0
self.added_space = 0.5
self.mix = 0.2
self.m = Messenger(self.world)
@always_inline
fn next(mut self) -> SIMD[DType.float64, 2]:
self.m.update(self.room_size,"room_size")
self.m.update(self.lpf_comb,"lpf_comb")
self.m.update(self.added_space,"added_space")
self.m.update(self.mix,"mix")
added_space_simd = SIMD[DType.float64, 2](self.added_space, self.added_space * 0.99)
out = self.play_buf.next[num_chans=2](self.buffer, 1.0, True)
out = self.freeverb.next(out, self.room_size, self.lpf_comb, added_space_simd) * 0.2 * self.mix + out * (1.0 - self.mix)
return out
struct FreeverbExample(Representable, Movable, Copyable):
var world: UnsafePointer[MMMWorld]
var freeverb_synth: FreeverbSynth # Instance of the FreeverbSynth
fn __init__(out self, world: UnsafePointer[MMMWorld]):
self.world = world
self.freeverb_synth = FreeverbSynth(self.world)
fn __repr__(self) -> String:
return String("Freeverb_Graph")
fn next(mut self) -> SIMD[DType.float64, 2]:
#return SIMD[DType.float64, 2](0.0)
return self.freeverb_synth.next() # Return the combined output sample