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

AnalysisExample

Python Code

from mmm_src.MMMAudio import MMMAudio

ma = MMMAudio(128, graph_name="AnalysisExample", package_name="examples")
ma.start_audio()

ma.send_float("freq", 240.5)
ma.send_float("which", 2.0)
ma.stop_audio()

Mojo Code

"""use this as a template for your own graphs"""

from mmm_src.MMMWorld import MMMWorld
from mmm_dsp.Analysis import SpectralCentroid, YIN, RMS
from mmm_dsp.Osc import *
from mmm_utils.Messenger import *
from mmm_dsp.BufferedProcess import *
from mmm_dsp.FFT import *
from mmm_dsp.PlayBuf import *

struct CustomAnalysis[window_size: Int = 1024](BufferedProcessable):
    var world: UnsafePointer[MMMWorld]
    var centroid: Float64
    var rms: Float64
    var pitch: Float64
    var pitch_conf: Float64
    var sr: Float64
    var yin: YIN[window_size, 50, 5000]

    fn __init__(out self, world: UnsafePointer[MMMWorld]):
        self.world = world
        self.sr = self.world[].sample_rate
        self.centroid = 0.0
        self.pitch = 0.0
        self.pitch_conf = 0.0
        self.rms = 0.0
        self.yin = YIN[window_size, 50, 5000](world)

    fn next_window(mut self, mut frame: List[Float64]):
        self.yin.next_window(frame)
        self.pitch = self.yin.pitch
        self.pitch_conf = self.yin.confidence
        self.rms = RMS.from_window(frame)
        # YIN has to do a special FFT internally no matter what, 
        # so we'll just use the "raw" mags it computes
        # for spectral centroid. It is an FFT with double the frequency resolution
        # (i.e., it's higher resolution, just "interpolated" FFT mags). But it will work just fine here.
        self.centroid = SpectralCentroid.from_mags(self.yin.fft.mags, self.world[].sample_rate)

struct AnalysisExample(Movable, Copyable):
    var world: UnsafePointer[MMMWorld]
    var osc: Osc[2]
    var buffer: Buffer
    var playBuf: PlayBuf
    var freq: Float64
    var analyzer: BufferedInput[CustomAnalysis[1024],1024,512]
    var m: Messenger
    var which: Float64

    fn __init__(out self, world: UnsafePointer[MMMWorld]):
        self.world = world
        self.osc = Osc[2](world)
        self.buffer = Buffer("resources/Shiverer.wav")
        self.playBuf = PlayBuf(self.world)
        self.analyzer = BufferedInput[CustomAnalysis[1024],1024,512](world, CustomAnalysis[1024](world))
        self.freq = 440.0
        self.m = Messenger(world)
        self.which = 0.0

    fn next(mut self) -> SIMD[DType.float64, 2]:

        self.m.update(self.freq,"freq")
        self.m.update(self.which,"which")

        oscs = self.osc.next(self.freq,0,False,[OscType.sine, OscType.saw])
        flute = self.playBuf.next(self.buffer,0,1.0,True)

        sig = select(self.which,[oscs[0], oscs[1], flute])

        # do the analysis
        self.analyzer.next(sig)

        # get the results
        (frequency, confidence) = (self.analyzer.process.pitch, self.analyzer.process.pitch_conf)
        rms = self.analyzer.process.rms
        centroid = self.analyzer.process.centroid

        # print the results
        self.world[].print("Pitch: ", frequency, " \tHz, Confidence: ", confidence, ", \tRMS: ", rms, ", \tCentroid: ", centroid)

        return sig * 0.1