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

PlayExample

Shows how to load and audio buffer from a sound file and play it using the Play UGen.

This example uses open sound control to control Play's playback speed and VAMoogFilter's cutoff frequency. These can be sent from a simple touchosc patch or any other OSC controller. A touchosc patch is provided for control.

This example is able to run by pressing the "play" button in VSCode or compiling and running the whole file on the command line.

This example has a corresponding TouchOSC file.

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 import *

def main():
    mmm_audio = MMMAudio(128, graph_name="PlayExample", package_name="examples")

    mmm_audio.start_audio() # start the audio thread - or restart it where it left off
    # time.sleep(10.0) 

    # this example uses open sound control to control Play's playback speed and VAMoogFilter's cutoff frequency
    # there is a simple touchosc patch provided for control
    # it is looking for /fader1 and /fader2 on port 5005; these can be adjusted
    # Start the OSC server on its own thread
    # this is a bug, but this thread has to start after audio or audio won't start

    # Usage:
    def osc_msg_handler(key, *args):
        print(f"Received OSC message: {key} with arguments: {args}")
        if key == "/fader1":
            val = lincurve(args[0], 0.0, 1.0, -4.0, 4.0, -1)
            print(f"Mapped play_rate value: {val}")
            mmm_audio.send_float("play_rate", val)
        elif key == "/fader2":
            val = linexp(args[0], 0.0, 1.0, 100.0, 20000.0)
            mmm_audio.send_float("lpf_freq", val)

    # Start server
    osc_server = OSCServer("0.0.0.0", 5005, osc_msg_handler)
    osc_server.start()

if __name__ == "__main__":
    main()

Mojo Code

from mmm_audio import *

comptime num_chans = 2

struct BufSynth(Movable, Copyable):
    var world: World
    var buffer: SIMDBuffer[2]
    var num_chans: Int

    var play_buf: Play
    var play_rate: Float64

    var moog: VAMoogLadder[num_chans, 1] # 2 channels, os_index == 1 (2x oversampling)
    var lpf_freq: Float64
    var lpf_freq_lag: Lag[]
    var messenger: Messenger

    fn __init__(out self, world: World):
        self.world = world 
        print("world memory location:", world)

        # load the audio buffer 
        self.buffer = SIMDBuffer[2].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_rate = 1.0

        self.play_buf = Play(self.world)

        self.moog = VAMoogLadder[num_chans, 1](self.world)
        self.lpf_freq = 20000.0
        self.lpf_freq_lag = Lag(self.world, 0.1)

        self.messenger = Messenger(self.world)

    fn next(mut self) -> MFloat[num_chans]:
        self.messenger.update(self.lpf_freq, "lpf_freq")
        self.messenger.update(self.play_rate, "play_rate")

        out = self.play_buf.next[num_chans=num_chans](self.buffer, self.play_rate, True)

        freq = self.lpf_freq_lag.next(self.lpf_freq)
        out = self.moog.next(out, freq, 1.0)
        return out

struct PlayExample(Movable, Copyable):
    var world: World

    var buf_synth: BufSynth  # Instance of the BufSynth

    fn __init__(out self, world: World):
        self.world = world

        self.buf_synth = BufSynth(self.world)  

    fn next(mut self) -> MFloat[num_chans]:
        return self.buf_synth.next()  # Return the combined output sample