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).
Python Code¶
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parent.parent))
from mmm_python import *
def main():
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
app = QApplication([])
# Create the main window
window = QWidget()
window.setWindowTitle("Pitch Shift Controller")
window.resize(300, 100)
# stop audio when window is closed
window.closeEvent = lambda event: (mmm_audio.exit_all(), event.accept())
# Create layout
layout = QVBoxLayout()
pitch_shift_slider = Handle("pitch_shift",ControlSpec(0.25, 4.0, 0.5), 1, callback=lambda v: mmm_audio.send_float("pitch_shift", v), run_callback_on_init=True)
layout.addWidget(pitch_shift_slider)
grain_dur_slider = Handle("grain_dur", ControlSpec(0.1, 1.0, 1), 0.4, callback=lambda v: mmm_audio.send_float("grain_dur", v))
layout.addWidget(grain_dur_slider)
pitch_dispersion_slider = Handle("pitch_dispersion", ControlSpec(0.0, 1.0, 1), 0, callback=lambda v: mmm_audio.send_float("pitch_dispersion", v), run_callback_on_init=True)
layout.addWidget(pitch_dispersion_slider)
time_dispersion_slider = Handle("time_dispersion", ControlSpec(0.0, 1.0, 1), 0, callback=lambda v: mmm_audio.send_float("time_dispersion", v), run_callback_on_init=True)
layout.addWidget(time_dispersion_slider)
added_delay_low_slider = Handle("added_delay_low", ControlSpec(0.0, 2.0, 1), 0, callback=lambda v: mmm_audio.send_float("added_delay_low", v), run_callback_on_init=True)
layout.addWidget(added_delay_low_slider)
added_delay_high_slider = Handle("added_delay_high", ControlSpec(0.0, 2.0, 1), 0, callback=lambda v: mmm_audio.send_float("added_delay_high", v), run_callback_on_init=True)
layout.addWidget(added_delay_high_slider)
overlaps_slider = Handle("overlaps", ControlSpec(1, 16, 1), 4, callback=lambda v: mmm_audio.send_int("overlaps", int(v)), run_callback_on_init=True)
layout.addWidget(overlaps_slider)
fb_perc_slider = Handle("feedback", ControlSpec(0.0, 1.0, 1), 0, callback=lambda v: mmm_audio.send_float("fb_perc", v), run_callback_on_init=True)
layout.addWidget(fb_perc_slider)
window.setLayout(layout)
window.show()
app.exec()
if __name__ == "__main__":
main()
Mojo Code¶
from mmm_audio import *
# THE SYNTH
struct PitchShiftExample(Representable, Movable, Copyable):
var world: World
var pitch_shift: PitchShift[num_chans=2]
var messenger: Messenger
var shift: Float64
var grain_dur: Float64
var pitch_dispersion: Float64
var time_dispersion: Float64
var in_chan: Int
var which_input: Float64
var noise: WhiteNoise[]
var overlaps: Int
var added_delay_low: Float64
var added_delay_high: Float64
var fb: MFloat[2]
var fb_perc: Float64
var dc_trap: DCTrap[2]
fn __init__(out self, world: World):
self.world = world
self.pitch_shift = PitchShift[num_chans=2](self.world, 2.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()
self.overlaps = 4
self.added_delay_low = 0.0
self.added_delay_high = 0.0
self.fb = MFloat[2](0.0, 0.0)
self.fb_perc = 0.0
self.dc_trap = DCTrap[2](world)
@always_inline
fn next(mut self) -> MFloat[2]:
self.messenger.update(self.in_chan, "in_chan")
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")
self.messenger.update(self.overlaps,"overlaps")
self.messenger.update(self.added_delay_low,"added_delay_low")
self.messenger.update(self.added_delay_high,"added_delay_high")
self.messenger.update(self.fb_perc,"fb_perc")
temp = self.world[].sound_in[self.in_chan]
input_sig = MFloat[2](temp, temp) + (self.fb * self.fb_perc)
out = self.pitch_shift.next(input_sig, self.grain_dur, self.overlaps, self.shift, self.pitch_dispersion, self.time_dispersion, self.added_delay_low, self.added_delay_high)
self.fb = self.dc_trap.next(out)
return out
fn __repr__(self) -> String:
return String("PitchShift")