For more information about the examples, such as how the Python and Mojo files interact with each other, see the Examples Overview
PlayBufExample¶
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_src.MMMAudio import MMMAudio
mmm_audio = MMMAudio(128, graph_name="PlayBufExample", package_name="examples")
mmm_audio.start_audio() # start the audio thread - or restart it where it left off
# this example uses open sound control to control PlayBuf'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
from mmm_utils.osc_server import OSCServer
from mmm_utils.functions import *
# 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)
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()
Mojo Code¶
from mmm_src.MMMWorld import MMMWorld
from mmm_utils.Messenger import Messenger
from mmm_src.MMMTraits import *
from mmm_utils.functions import *
from mmm_dsp.Filters import Lag
from mmm_dsp.Buffer import *
from mmm_dsp.PlayBuf import *
from mmm_dsp.Filters import VAMoogLadder
struct BufSynth(Representable, Movable, Copyable):
var world: UnsafePointer[MMMWorld]
var buffer: Buffer
var num_chans: Int64
var play_buf: PlayBuf
var play_rate: Float64
var moog: VAMoogLadder[2, 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: UnsafePointer[MMMWorld]):
self.world = world
# load the audio buffer
self.buffer = Buffer("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 = PlayBuf(self.world)
self.moog = VAMoogLadder[2, 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) -> SIMD[DType.float64, 2]:
self.messenger.update(self.lpf_freq, "lpf_freq")
self.messenger.update(self.play_rate, "play_rate")
out = self.play_buf.next[num_playback_chans=2](self.buffer, 0, self.play_rate, True)
freq = self.lpf_freq_lag.next(self.lpf_freq)
out = self.moog.next(out, freq, 1.0)
return out
fn __repr__(self) -> String:
return String("BufSynth")
struct PlayBufExample(Representable, Movable, Copyable):
var world: UnsafePointer[MMMWorld]
var buf_synth: BufSynth # Instance of the GrainSynth
fn __init__(out self, world: UnsafePointer[MMMWorld]):
self.world = world
self.buf_synth = BufSynth(world)
fn __repr__(self) -> String:
return String("PlayBufExample")
fn next(mut self) -> SIMD[DType.float64, 2]:
#return SIMD[DType.float64, 2](0.0)
return self.buf_synth.next() # Return the combined output sample