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

PitchShiftExample

Demonstrates how to use the PitchShift grain-based pitch shifter with microphone input.

This example assumes you have a microphone input device set up and selected as the default input device on your system.

A couple of settings in the .py file are important:

  • num_input_channels: This can be set to any value, but it should be at least as high as the input channel you want to use.
  • in_chan: This should be set to the input channel number of your microphone input source (0-indexed).

The graph allows you to set various parameters for the pitch shifter:

  • which_input: Selects which input channel to use from the multi-channel input (0-indexed).
  • pitch_shift: Sets the pitch shift factor (e.g., 1.0 = no shift, 2.0 = one octave up, 0.5 = one octave down).
  • grain_dur: Sets the duration of the grains in seconds.
  • pitch_dispersion: Sets the amount of random variation in pitch for each grain.
  • time_dispersion: Sets the amount of random variation in timing for each grain.

Python Code

from mmm_python.MMMAudio import MMMAudio
mmm_audio = MMMAudio(128, num_input_channels = 12, graph_name="PitchShiftExample", package_name="examples")
mmm_audio.send_int("in_chan", 0) # set input channel to your input source
mmm_audio.start_audio() # start the audio thread - or restart it where it left off


mmm_audio.send_float("which_input", 2)
mmm_audio.send_float("pitch_shift", 1.5)
mmm_audio.send_float("grain_dur", 0.4)

mmm_audio.send_float("pitch_dispersion", 0.4)

mmm_audio.send_float("time_dispersion", 0.5)

mmm_audio.send_float("pitch_dispersion", 0.0)
mmm_audio.send_float("time_dispersion", 0.0)

mmm_audio.start_audio()
mmm_audio.stop_audio()  # stop the audio thread

mmm_audio.plot(44000)

Mojo Code

from mmm_audio import *

# THE SYNTH

struct PitchShiftExample(Representable, Movable, Copyable):
    var world: UnsafePointer[MMMWorld]

    var pitch_shift: PitchShift[num_chans=2, overlaps=4]
    var messenger: Messenger
    var shift: Float64
    var grain_dur: Float64
    var pitch_dispersion: Float64
    var time_dispersion: Float64
    var in_chan: Int64
    var which_input: Float64
    var noise: WhiteNoise

    fn __init__(out self, world: UnsafePointer[MMMWorld]):
        self.world = world
        self.pitch_shift = PitchShift[num_chans=2, overlaps=4](self.world, 1.0) # the duration of the buffer needs to == grain size*(max_pitch_shift-1).
        self.messenger = Messenger(self.world)
        self.shift = 1.0
        self.grain_dur = 0.2
        self.pitch_dispersion = 0.0
        self.time_dispersion = 0.0
        self.in_chan = 0
        self.which_input = 0.0
        self.noise = WhiteNoise()

    @always_inline
    fn next(mut self) -> SIMD[DType.float64, 2]:
        self.messenger.update(self.which_input, "which_input")
        self.messenger.update(self.in_chan, "in_chan")

        temp = self.world[].sound_in[self.in_chan]
        input_sig = select(self.which_input, [SIMD[DType.float64, 2](temp, temp), SIMD[DType.float64, 2](temp, 0.0), SIMD[DType.float64, 2](0.0, temp)])

        self.messenger.update(self.shift,"pitch_shift")
        self.messenger.update(self.grain_dur,"grain_dur")
        self.messenger.update(self.pitch_dispersion,"pitch_dispersion")
        self.messenger.update(self.time_dispersion,"time_dispersion")

        # shift = linexp(self.world[].mouse_y, 0.0, 1.0, 0.25, 4.0)
        # grain_dur = linexp(self.world[].mouse_x, 0.0, 1.0, 0.05, 0.3)
        out = self.pitch_shift.next(input_sig, self.grain_dur, self.shift, self.pitch_dispersion, self.time_dispersion)

        return out

    fn __repr__(self) -> String:
        return String("PitchShift")