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

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