For more information about the examples, such as how the Python and Mojo files interact with each other, see the Examples Overview
VariableOsc¶
this example shows how to use the variable wavetable oscillator. it shows how the oscillator can be made using linear, quadratic, or sinc interpolation and can also be set to use oversampling. with sinc interpolation, use an oversampling index of 0 (no oversampling), 1 (2x). with linear or quadratic interpolation, use an oversampling index of 0 (no oversampling), 1 (2x), 2 (4x), 3 (8x), or 4 (16x).
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
# instantiate and load the graph
mmm_audio = MMMAudio(128, graph_name="VariableOsc", package_name="examples")
mmm_audio.start_audio()
from mmm_utils.GUI import Slider2D
from mmm_src.MMMAudio import MMMAudio
from PySide6.QtWidgets import QApplication, QWidget, QVBoxLayout, QCheckBox
app = QApplication([])
app.quitOnLastWindowClosed = True
# Create the main window
window = QWidget()
window.setWindowTitle("Variable Oscillator Controller")
window.resize(300, 300)
# stop audio when window is closed
window.closeEvent = lambda event: (mmm_audio.stop_audio(), event.accept())
# Create layout
layout = QVBoxLayout()
slider2d = Slider2D(250, 250)
def on_slider_change(x, y):
mmm_audio.send_float("x", x) # map x from 0-1 to 20-2020 Hz
mmm_audio.send_float("y", y) # map y from 0-1 to 0-1 amplitude
def slider_mouse_updown(is_down):
mmm_audio.send_bool("mouse_down", is_down) # set amplitude to 0 when mouse is released
slider2d.value_changed.connect(on_slider_change)
slider2d.mouse_updown.connect(slider_mouse_updown)
layout.addWidget(slider2d)
window.setLayout(layout)
window.show()
app.exec()
Mojo Code¶
"""use this as a template for your own graphs"""
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.Filters import Lag
from mmm_utils.Messenger import Messenger
from mmm_dsp.Env import ASREnv
struct VariableOsc(Representable, Movable, Copyable):
var world: UnsafePointer[MMMWorld]
# for efficiency we set the interpolation and oversampling in the constructor
# so here we have sinc interpolation with 2x oversampling
# var osc: Osc[1,2,1]
# var lag: Lag[1]
var osc: Osc[2,2,1]
var lag: Lag[2]
var m: Messenger
var x: Float64
var y: Float64
var is_down: Bool
var asr: ASREnv
fn __init__(out self, world: UnsafePointer[MMMWorld]):
self.world = world
# for efficiency we set the interpolation and oversampling in the constructor
self.osc = Osc[2,2,1](self.world)
self.lag = Lag[2](self.world, 0.1)
self.m = Messenger(self.world)
self.x = 0.0
self.y = 0.0
self.is_down = False
self.asr = ASREnv(self.world)
fn __repr__(self) -> String:
return String("Default")
fn next(mut self) -> SIMD[DType.float64, 2]:
self.m.update(self.x, "x")
self.m.update(self.y, "y")
self.m.update(self.is_down, "mouse_down")
env = self.asr.next(0.05, 1, 0.05, self.is_down)
# freq = self.world[].mouse_y
freq = SIMD[DType.float64, 2](1-self.y, self.y)
freq = self.lag.next(freq)
freq = linexp(freq, 0.0, 1.0, 100, 10000)
# by defualt, next_interp will interpolate between the four default waveforms - sin, tri, square, saw
# osc_frac = self.world[].mouse_x
osc_frac = SIMD[DType.float64, 2](1-self.x, self.x)
sample = self.osc.next_interp(freq, osc_frac = osc_frac)
return sample * 0.1 * env