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

MelBandsExample

The energy of each Mel band is visualized in the console as a series of asterisks. Also the energy of each Mel band controls the loudness of a sine tone at center frequency of its band. The result is a frequency-quantized analysis-sinusoidal-resynthesis effect.

Python Code

from mmm_python import *
ma = MMMAudio(128, graph_name="MelBandsExample", package_name="examples")
ma.start_audio()

ma.send_float("viz_mul",300.0) # 300 is the default in Mojo also
ma.send_float("sines_vol",-36.0) # db
ma.send_float("mix",1.0) 
ma.send_float("mix",0.0)
ma.send_float("mix",0.7)
ma.send_int("update_modulus",80) # higher number = slower updates

ma.stop_audio()

Mojo Code

from mmm_audio import *

comptime num_bands: Int = 100

struct MelBandsExample(Movable, Copyable):
    var world: World
    var buffer: Buffer
    var playBuf: Play
    var analyzer: FFTProcess[MelBands,ifft=False,input_window_shape=WindowType.hann]
    var m: Messenger
    var viz_mul: Float64
    var mix: Float64
    var oscs: List[SinOsc[]]
    var freqs: List[Float64]
    var lags: List[Lag[]]
    var sines_vol: Float64
    var print_counter: Int
    var update_modulus: Int

    fn __init__(out self, world: World):
        self.world = world
        self.buffer = Buffer.load("resources/Shiverer.wav")
        self.playBuf = Play(self.world)
        p = MelBands(self.world[].sample_rate, num_bands, 20.0, 20000.0)
        self.analyzer = FFTProcess[MelBands,ifft=False,input_window_shape=WindowType.hann](self.world,p^, window_size=1024, hop_size=512)
        self.m = Messenger(self.world)
        self.viz_mul = 500.0
        self.mix = 1.0
        self.lags = List[Lag[]]()
        self.sines_vol = -38.0
        self.print_counter = 0
        self.update_modulus = 50

        for _ in range(num_bands):
            self.lags.append(Lag(self.world,512.0 / self.world[].sample_rate))

        self.oscs = List[SinOsc[]]()
        for i in range(num_bands):
            self.oscs.append(SinOsc(self.world))

        self.freqs = MelBands.mel_frequencies(num_bands,20.0,20000.0)

    fn next(mut self) -> MFloat[2]:

        self.m.update(self.viz_mul,"viz_mul")
        self.m.update(self.mix,"mix")
        self.m.update(self.sines_vol,"sines_vol")
        self.m.update(self.update_modulus,"update_modulus")
        flute = self.playBuf.next(self.buffer)

        # do the analysis
        _ = self.analyzer.next(flute)

        # get the results
        if self.world[].top_of_block:
            # print the mel band energies
            if self.print_counter % self.update_modulus == 0:
                string = "\n\n\n\n\n"
                for i in range(num_bands):
                    idx = num_bands - i - 1
                    val = self.analyzer.buffered_process.process.process.bands[idx]
                    for _ in range(Int(val * self.viz_mul)):
                        string += "*"
                    string += "\n"

                print(string)
                # print the results
            self.print_counter += 1

        sines = 0.0
        for i in range(num_bands):
            amp = self.lags[i].next(self.analyzer.buffered_process.process.process.bands[i])
            sines += self.oscs[i].next(self.freqs[i]) * amp

        sines *= dbamp(self.sines_vol)

        sig = select(self.mix,flute,sines)

        return sig