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_python import *
def main():
    # instantiate and load the graph
    mmm_audio = MMMAudio(128, graph_name="VariableOsc", package_name="examples")
    mmm_audio.start_audio() 

    from mmm_python.GUI import Slider2D
    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()

if __name__ == "__main__":
    main()

Mojo Code

from mmm_audio import *

struct VariableOsc(Representable, Movable, Copyable):
    var world: World  
    # 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,Interp.sinc,os_index=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: World):
        self.world = world
        # for efficiency we set the interpolation and oversampling in the constructor
        self.osc = Osc[2,Interp.sinc,os_index=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) -> MFloat[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 = MFloat[2](1-self.y, self.y)
        freq = self.lag.next(freq)
        freq = linexp(freq, 0.0, 1.0, 100, 10000)

        # osc_frac = self.world[].mouse_x
        osc_frac = MFloat[2](1-self.x, self.x)
        sample = self.osc.next_basic_waveforms(freq, osc_frac = osc_frac)

        return sample * 0.1 * env