Skip to content

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