synthio fundamentals example code and wave files

This commit is contained in:
John Park 2023-09-20 11:07:06 -07:00
parent c6ddbde458
commit 8732b70333
18 changed files with 884 additions and 0 deletions

View file

@ -0,0 +1,32 @@
# SPDX-FileCopyrightText: 2023 John Park and @todbot / Tod Kurt
#
# SPDX-License-Identifier: MIT
import time
import board
import digitalio
import synthio
# for PWM audio with an RC filter
# import audiopwmio
# audio = audiopwmio.PWMAudioOut(board.GP10)
# for I2S audio with external I2S DAC board
import audiobusio
# I2S on Audio BFF or Amp BFF on QT Py:
# audio = audiobusio.I2SOut(bit_clock=board.A3, word_select=board.A2, data=board.A1)
# I2S audio on PropMaker Feather RP2040
power = digitalio.DigitalInOut(board.EXTERNAL_POWER)
power.switch_to_output(value=True)
audio = audiobusio.I2SOut(board.I2S_BIT_CLOCK, board.I2S_WORD_SELECT, board.I2S_DATA)
synth = synthio.Synthesizer(sample_rate=44100)
audio.play(synth)
while True:
synth.press(65) # midi note 65 = F4
time.sleep(0.5)
synth.release(65) # release the note we pressed
time.sleep(2)

View file

@ -0,0 +1,32 @@
# SPDX-FileCopyrightText: 2023 John Park and @todbot / Tod Kurt
#
# SPDX-License-Identifier: MIT
import time
import board
import digitalio
import synthio
# for PWM audio with an RC filter
# import audiopwmio
# audio = audiopwmio.PWMAudioOut(board.GP10)
# for I2S audio with external I2S DAC board
import audiobusio
# I2S on Audio BFF or Amp BFF on QT Py:
# audio = audiobusio.I2SOut(bit_clock=board.A3, word_select=board.A2, data=board.A1)
# I2S audio on PropMaker Feather RP2040
power = digitalio.DigitalInOut(board.EXTERNAL_POWER)
power.switch_to_output(value=True)
audio = audiobusio.I2SOut(board.I2S_BIT_CLOCK, board.I2S_WORD_SELECT, board.I2S_DATA)
synth = synthio.Synthesizer(sample_rate=44100)
audio.play(synth)
while True:
synth.press((65, 69, 72)) # midi note 65 = F4
time.sleep(1)
synth.release((65, 69, 72)) # release the note we pressed
time.sleep(2)

View file

@ -0,0 +1,38 @@
# SPDX-FileCopyrightText: 2023 John Park and @todbot / Tod Kurt
#
# SPDX-License-Identifier: MIT
import time
import board
import digitalio
import audiomixer
import synthio
# for PWM audio with an RC filter
# import audiopwmio
# audio = audiopwmio.PWMAudioOut(board.GP10)
# for I2S audio with external I2S DAC board
import audiobusio
# I2S on Audio BFF or Amp BFF on QT Py:
# audio = audiobusio.I2SOut(bit_clock=board.A3, word_select=board.A2, data=board.A1)
# I2S audio on PropMaker Feather RP2040
power = digitalio.DigitalInOut(board.EXTERNAL_POWER)
power.switch_to_output(value=True)
audio = audiobusio.I2SOut(board.I2S_BIT_CLOCK, board.I2S_WORD_SELECT, board.I2S_DATA)
mixer = audiomixer.Mixer(channel_count=1, sample_rate=22050, buffer_size=2048)
synth = synthio.Synthesizer(channel_count=1, sample_rate=22050)
audio.play(mixer)
mixer.voice[0].play(synth)
mixer.voice[0].level = 0.4
while True:
synth.press((65, 69, 72)) # midi note 65 = F4
time.sleep(0.5)
synth.release((65, 69, 72)) # release the note we pressed
time.sleep(0.5)
mixer.voice[0].level = (mixer.voice[0].level - 0.1) % 0.4 # reduce volume each pass

View file

@ -0,0 +1,58 @@
# SPDX-FileCopyrightText: 2023 John Park and @todbot / Tod Kurt
#
# SPDX-License-Identifier: MIT
import time
import board
import digitalio
import audiomixer
import synthio
# for PWM audio with an RC filter
#import audiopwmio
#audio = audiopwmio.PWMAudioOut(board.GP10)
# for I2S audio with external I2S DAC board
import audiobusio
# I2S on Audio BFF or Amp BFF on QT Py:
# audio = audiobusio.I2SOut(bit_clock=board.A3, word_select=board.A2, data=board.A1)
# I2S audio on PropMaker Feather RP2040
power = digitalio.DigitalInOut(board.EXTERNAL_POWER)
power.switch_to_output(value=True)
audio = audiobusio.I2SOut(board.I2S_BIT_CLOCK, board.I2S_WORD_SELECT, board.I2S_DATA)
mixer = audiomixer.Mixer(channel_count=1, sample_rate=22050, buffer_size=2048)
amp_env_slow = synthio.Envelope(
attack_time=0.2,
sustain_level=1.0,
release_time=0.8
)
amp_env_fast = synthio.Envelope(
attack_time=0.1,
sustain_level=0.5,
release_time=0.2
)
synth = synthio.Synthesizer(channel_count=1, sample_rate=22050, envelope=amp_env_slow)
audio.play(mixer)
mixer.voice[0].play(synth)
mixer.voice[0].level = 0.2
while True:
synth.envelope = amp_env_slow
synth.press(46)
time.sleep(1.25)
synth.release(46)
time.sleep(1.25)
synth.envelope = amp_env_fast
synth.press(51)
time.sleep(1.25)
synth.release(51)
time.sleep(1.25)

View file

@ -0,0 +1,76 @@
# SPDX-FileCopyrightText: 2023 John Park and @todbot / Tod Kurt
#
# SPDX-License-Identifier: MIT
import time
import board
import digitalio
import audiomixer
import synthio
# for PWM audio with an RC filter
# import audiopwmio
# audio = audiopwmio.PWMAudioOut(board.GP10)
# for I2S audio with external I2S DAC board
import audiobusio
# I2S on Audio BFF or Amp BFF on QT Py:
# audio = audiobusio.I2SOut(bit_clock=board.A3, word_select=board.A2, data=board.A1)
# I2S audio on PropMaker Feather RP2040
power = digitalio.DigitalInOut(board.EXTERNAL_POWER)
power.switch_to_output(value=True)
audio = audiobusio.I2SOut(board.I2S_BIT_CLOCK, board.I2S_WORD_SELECT, board.I2S_DATA)
mixer = audiomixer.Mixer(channel_count=1, sample_rate=22050, buffer_size=2048)
amp_env_slow = synthio.Envelope(
attack_time=0.2,
sustain_level=1.0,
release_time=0.8
)
synth = synthio.Synthesizer(channel_count=1, sample_rate=22050, envelope=amp_env_slow)
# set up filters
frequency = 2000
resonance = 1.5
lpf = synth.low_pass_filter(frequency, resonance)
hpf = synth.high_pass_filter(frequency, resonance)
bpf = synth.band_pass_filter(frequency, resonance)
note1 = synthio.Note(frequency=330, filter=None)
audio.play(mixer)
mixer.voice[0].play(synth)
mixer.voice[0].level = 0.2
while True:
# no filter
note1.filter = None
synth.press(note1)
time.sleep(1.25)
synth.release(note1)
time.sleep(1.25)
# lpf
note1.filter = lpf
synth.press(note1)
time.sleep(1.25)
synth.release(note1)
time.sleep(1.25)
# hpf
note1.filter = hpf
synth.press(note1)
time.sleep(1.25)
synth.release(note1)
time.sleep(1.25)
# bpf
note1.filter = bpf
synth.press(note1)
time.sleep(1.25)
synth.release(note1)
time.sleep(1.25)

View file

@ -0,0 +1,65 @@
# SPDX-FileCopyrightText: 2023 John Park and @todbot / Tod Kurt
#
# SPDX-License-Identifier: MIT
import board
import digitalio
import audiomixer
import synthio
import usb_midi
import adafruit_midi
from adafruit_midi.note_on import NoteOn
from adafruit_midi.note_off import NoteOff
# note for ESP32-S2 boards, due to not enough available endpoints,
# to enable USB MIDI, create a "boot.py" with following in it, then power cycle board:
# import usb_hid
# import usb_midi
# usb_hid.disable()
# usb_midi.enable()
# print("enabled USB MIDI, disabled USB HID")
# for PWM audio with an RC filter
# import audiopwmio
# audio = audiopwmio.PWMAudioOut(board.GP10)
# for I2S audio with external I2S DAC board
import audiobusio
# I2S on Audio BFF or Amp BFF on QT Py:
# audio = audiobusio.I2SOut(bit_clock=board.A3, word_select=board.A2, data=board.A1)
# I2S audio on PropMaker Feather RP2040
power = digitalio.DigitalInOut(board.EXTERNAL_POWER)
power.switch_to_output(value=True)
audio = audiobusio.I2SOut(board.I2S_BIT_CLOCK, board.I2S_WORD_SELECT, board.I2S_DATA)
mixer = audiomixer.Mixer(channel_count=1, sample_rate=22050, buffer_size=2048)
midi = adafruit_midi.MIDI(midi_in=usb_midi.ports[0], in_channel=0)
amp_env_med = synthio.Envelope(
attack_time=0.05,
sustain_level=0.8,
release_time=0.2
)
synth = synthio.Synthesizer(channel_count=1, sample_rate=22050, envelope=amp_env_med)
note1 = synthio.Note(frequency=330, filter=None)
audio.play(mixer)
mixer.voice[0].play(synth)
mixer.voice[0].level = 0.2
while True:
msg = midi.receive()
if isinstance(msg, NoteOn) and msg.velocity != 0:
print("noteOn: ", msg.note, "vel:", msg.velocity)
note1.frequency = synthio.midi_to_hz(msg.note)
synth.press(note1)
elif isinstance(msg, NoteOff) or isinstance(msg, NoteOn) and msg.velocity == 0:
print("noteOff:", msg.note, "vel:", msg.velocity)
note1.frequency = synthio.midi_to_hz(msg.note)
synth.release(note1)

View file

@ -0,0 +1,94 @@
# SPDX-FileCopyrightText: 2023 John Park and @todbot / Tod Kurt
#
# SPDX-License-Identifier: MIT
import board
import busio
import digitalio
import audiomixer
import synthio
import usb_midi
import adafruit_midi
from adafruit_midi.note_on import NoteOn
from adafruit_midi.note_off import NoteOff
from adafruit_midi.control_change import ControlChange
# note for ESP32-S2 boards, due to not enough available endpoints,
# to enable USB MIDI, create a "boot.py" with following in it, then power cycle board:
# import usb_hid
# import usb_midi
# usb_hid.disable()
# usb_midi.enable()
# print("enabled USB MIDI, disabled USB HID")
# for PWM audio with an RC filter
# import audiopwmio
# audio = audiopwmio.PWMAudioOut(board.GP10)
# for I2S audio with external I2S DAC board
import audiobusio
# I2S on Audio BFF or Amp BFF on QT Py:
# audio = audiobusio.I2SOut(bit_clock=board.A3, word_select=board.A2, data=board.A1)
# I2S audio on PropMaker Feather RP2040
power = digitalio.DigitalInOut(board.EXTERNAL_POWER)
power.switch_to_output(value=True)
audio = audiobusio.I2SOut(board.I2S_BIT_CLOCK, board.I2S_WORD_SELECT, board.I2S_DATA)
mixer = audiomixer.Mixer(channel_count=1, sample_rate=22050, buffer_size=2048)
midi_channel = 1
uart = busio.UART(tx=board.TX, rx=board.RX, baudrate=31250, timeout=0.001)
midi_usb = adafruit_midi.MIDI(midi_in=usb_midi.ports[0], in_channel=0)
midi_uart = adafruit_midi.MIDI(midi_in=uart, in_channel=midi_channel-1)
amp_env_med = synthio.Envelope(
attack_time=0.05,
sustain_level=0.8,
release_time=0.2
)
synth = synthio.Synthesizer(channel_count=1, sample_rate=22050, envelope=amp_env_med)
# set up filters
filter_freq = 4000
filter_res = 0.5
filter_freq_lo = 100 # filter lowest freq
filter_freq_hi = 4500 # filter highest freq
filter_res_lo = 0.5 # filter q lowest value
filter_res_hi = 2.0 # filter q highest value
lpf = synth.low_pass_filter(filter_freq, filter_res)
note1 = synthio.Note(frequency=330, filter=lpf)
audio.play(mixer)
mixer.voice[0].play(synth)
mixer.voice[0].level = 0.2
def map_range(s, a1, a2, b1, b2):
return b1 + ((s - a1) * (b2 - b1) / (a2 - a1))
while True:
note1.filter = synth.low_pass_filter(filter_freq, filter_res)
msg = midi_uart.receive() or midi_usb.receive()
if isinstance(msg, NoteOn) and msg.velocity != 0:
print("noteOn: ", msg.note, "vel:", msg.velocity)
note1.frequency = synthio.midi_to_hz(msg.note)
synth.press(note1)
elif isinstance(msg, NoteOff) or isinstance(msg, NoteOn) and msg.velocity == 0:
print("noteOff:", msg.note, "vel:", msg.velocity)
note1.frequency = synthio.midi_to_hz(msg.note)
synth.release(note1)
elif isinstance(msg, ControlChange):
print("CC", msg.control, "=", msg.value)
if msg.control == 21: # filter cutoff
filter_freq = map_range(msg.value, 0, 127, filter_freq_lo, filter_freq_hi)
elif msg.control == 22: # filter Q
filter_res = map_range(msg.value, 0, 127, filter_res_lo, filter_res_hi)
elif msg.control == 7: # volume
mixer.voice[0].level = map_range(msg.value, 0, 127, 0.0, 1.0)

View file

@ -0,0 +1,39 @@
# SPDX-FileCopyrightText: 2023 John Park and @todbot / Tod Kurt
#
# SPDX-License-Identifier: MIT
import board
import digitalio
import audiomixer
import synthio
# for PWM audio with an RC filter
# import audiopwmio
# audio = audiopwmio.PWMAudioOut(board.GP10)
# for I2S audio with external I2S DAC board
import audiobusio
# I2S on Audio BFF or Amp BFF on QT Py:
# audio = audiobusio.I2SOut(bit_clock=board.A3, word_select=board.A2, data=board.A1)
# I2S audio on PropMaker Feather RP2040
power = digitalio.DigitalInOut(board.EXTERNAL_POWER)
power.switch_to_output(value=True)
audio = audiobusio.I2SOut(board.I2S_BIT_CLOCK, board.I2S_WORD_SELECT, board.I2S_DATA)
mixer = audiomixer.Mixer(channel_count=1, sample_rate=44100, buffer_size=4096)
synth = synthio.Synthesizer(channel_count=1, sample_rate=44100)
audio.play(mixer)
mixer.voice[0].play(synth)
mixer.voice[0].level = 0.1
lfo = synthio.LFO(rate=0.6, scale=0.05) # 1 Hz lfo at 0.25%
midi_note = 52
note = synthio.Note(synthio.midi_to_hz(midi_note), bend=lfo)
synth.press(note)
while True:
pass

View file

@ -0,0 +1,43 @@
# SPDX-FileCopyrightText: 2023 John Park and @todbot / Tod Kurt
#
# SPDX-License-Identifier: MIT
import time
import board
import digitalio
import audiomixer
import synthio
# for PWM audio with an RC filter
# import audiopwmio
# audio = audiopwmio.PWMAudioOut(board.GP10)
# for I2S audio with external I2S DAC board
import audiobusio
# I2S on Audio BFF or Amp BFF on QT Py:
# audio = audiobusio.I2SOut(bit_clock=board.A3, word_select=board.A2, data=board.A1)
# I2S audio on PropMaker Feather RP2040
power = digitalio.DigitalInOut(board.EXTERNAL_POWER)
power.switch_to_output(value=True)
audio = audiobusio.I2SOut(board.I2S_BIT_CLOCK, board.I2S_WORD_SELECT, board.I2S_DATA)
mixer = audiomixer.Mixer(channel_count=1, sample_rate=44100, buffer_size=4096)
synth = synthio.Synthesizer(channel_count=1, sample_rate=44100)
audio.play(mixer)
mixer.voice[0].play(synth)
mixer.voice[0].level = 0.2
lfo_tremolo = synthio.LFO(rate=0.8, scale=0.3, offset=0.7)
midi_note = 50
note1 = synthio.Note(synthio.midi_to_hz(midi_note), amplitude=lfo_tremolo)
synth.press(note1)
while True:
print(lfo_tremolo.value)
time.sleep(0.05)

View file

@ -0,0 +1,81 @@
# SPDX-FileCopyrightText: 2023 John Park and @todbot / Tod Kurt
#
# SPDX-License-Identifier: MIT
import time
import board
import digitalio
import audiomixer
import synthio
import ulab.numpy as np
# for PWM audio with an RC filter
# import audiopwmio
# audio = audiopwmio.PWMAudioOut(board.GP10)
# for I2S audio with external I2S DAC board
import audiobusio
# I2S on Audio BFF or Amp BFF on QT Py:
# audio = audiobusio.I2SOut(bit_clock=board.A3, word_select=board.A2, data=board.A1)
# I2S audio on PropMaker Feather RP2040
power = digitalio.DigitalInOut(board.EXTERNAL_POWER)
power.switch_to_output(value=True)
audio = audiobusio.I2SOut(board.I2S_BIT_CLOCK, board.I2S_WORD_SELECT, board.I2S_DATA)
mixer = audiomixer.Mixer(channel_count=1, sample_rate=44100, buffer_size=4096)
amp_env_slow = synthio.Envelope(
attack_time=0.15,
sustain_level=1.0,
release_time=0.8
)
synth = synthio.Synthesizer(channel_count=1, sample_rate=44100, envelope=amp_env_slow)
audio.play(mixer)
mixer.voice[0].play(synth)
mixer.voice[0].level = 0.6
# create sine, tri, saw & square single-cycle waveforms to act as oscillators
SAMPLE_SIZE = 512
SAMPLE_VOLUME = 32000 # 0-32767
half_period = SAMPLE_SIZE // 2
wave_sine = np.array(np.sin(np.linspace(0, 2*np.pi, SAMPLE_SIZE, endpoint=False)) * SAMPLE_VOLUME,
dtype=np.int16)
wave_saw = np.linspace(SAMPLE_VOLUME, -SAMPLE_VOLUME, num=SAMPLE_SIZE, dtype=np.int16)
wave_tri = np.concatenate((np.linspace(-SAMPLE_VOLUME, SAMPLE_VOLUME, num=half_period,
dtype=np.int16),
np.linspace(SAMPLE_VOLUME, -SAMPLE_VOLUME, num=half_period,
dtype=np.int16)))
wave_square = np.concatenate((np.full(half_period, SAMPLE_VOLUME, dtype=np.int16),
np.full(half_period, -SAMPLE_VOLUME, dtype=np.int16)))
midi_note = 65
while True:
# create notes using those waveforms
note1 = synthio.Note(synthio.midi_to_hz(midi_note), waveform=wave_sine, amplitude=1)
synth.press(note1)
time.sleep(0.75)
synth.release(note1)
time.sleep(.75)
note1 = synthio.Note(synthio.midi_to_hz(midi_note), waveform=wave_tri, amplitude=0.7)
synth.press(note1)
time.sleep(0.75)
synth.release(note1)
time.sleep(.75)
note1 = synthio.Note(synthio.midi_to_hz(midi_note), waveform=wave_saw, amplitude=0.25)
synth.press(note1)
time.sleep(0.75)
synth.release(note1)
time.sleep(.75)
note1 = synthio.Note(synthio.midi_to_hz(midi_note), waveform=wave_square, amplitude=0.2)
synth.press(note1)
time.sleep(0.75)
synth.release(note1)
time.sleep(.75)

View file

@ -0,0 +1,77 @@
# SPDX-FileCopyrightText: 2023 John Park and @todbot / Tod Kurt
#
# SPDX-License-Identifier: MIT
import time
import board
import audiomixer
import digitalio
import synthio
import ulab.numpy as np
# for PWM audio with an RC filter
# import audiopwmio
# audio = audiopwmio.PWMAudioOut(board.GP10)
# for I2S audio with external I2S DAC board
import audiobusio
# I2S on Audio BFF or Amp BFF on QT Py:
# audio = audiobusio.I2SOut(bit_clock=board.A3, word_select=board.A2, data=board.A1)
# I2S audio on PropMaker Feather RP2040
power = digitalio.DigitalInOut(board.EXTERNAL_POWER)
power.switch_to_output(value=True)
audio = audiobusio.I2SOut(board.I2S_BIT_CLOCK, board.I2S_WORD_SELECT, board.I2S_DATA)
mixer = audiomixer.Mixer(channel_count=1, sample_rate=44100, buffer_size=4096)
amp_env_slow = synthio.Envelope(
attack_time=0.25,
sustain_level=1.0,
release_time=0.8
)
synth = synthio.Synthesizer(channel_count=1, sample_rate=44100, envelope=amp_env_slow)
audio.play(mixer)
mixer.voice[0].play(synth)
mixer.voice[0].level = 0.6
# create sine, tri, saw & square single-cycle waveforms to act as oscillators
SAMPLE_SIZE = 512
SAMPLE_VOLUME = 32000 # 0-32767
half_period = SAMPLE_SIZE // 2
wave_sine = np.array(np.sin(np.linspace(0, 2*np.pi, SAMPLE_SIZE, endpoint=False)) * SAMPLE_VOLUME,
dtype=np.int16)
wave_saw = np.linspace(SAMPLE_VOLUME, -SAMPLE_VOLUME, num=SAMPLE_SIZE, dtype=np.int16)
wave_tri = np.concatenate((np.linspace(-SAMPLE_VOLUME, SAMPLE_VOLUME, num=half_period,
dtype=np.int16),
np.linspace(SAMPLE_VOLUME, -SAMPLE_VOLUME, num=half_period,
dtype=np.int16)))
wave_square = np.concatenate((np.full(half_period, SAMPLE_VOLUME, dtype=np.int16),
np.full(half_period, -SAMPLE_VOLUME, dtype=np.int16)))
def lerp(a, b, t): # function to morph shapes w linear interpolation
return (1-t) * a + t * b
wave_empty = np.zeros(SAMPLE_SIZE, dtype=np.int16) # empty buffer we use array slice copy "[:]" on
note1 = synthio.Note(frequency=440, waveform=wave_empty, amplitude=0.6)
synth.press(note1)
pos = 0
my_wave = wave_empty
while True:
while pos <= 1.0:
print(pos)
pos += 0.01
my_wave[:] = lerp(wave_sine, wave_saw, pos)
note1.waveform = my_wave
time.sleep(0.05)
while pos >= 0.1:
print(pos)
pos -= 0.01
my_wave[:] = lerp(wave_sine, wave_saw, pos)
note1.waveform = my_wave
time.sleep(0.05)

View file

@ -0,0 +1,72 @@
# SPDX-FileCopyrightText: 2023 John Park and @todbot / Tod Kurt
#
# SPDX-License-Identifier: MIT
import time
import board
import audiomixer
import digitalio
import synthio
import ulab.numpy as np
# for PWM audio with an RC filter
# import audiopwmio
# audio = audiopwmio.PWMAudioOut(board.GP10)
# for I2S audio with external I2S DAC board
import audiobusio
# I2S on Audio BFF or Amp BFF on QT Py:
# audio = audiobusio.I2SOut(bit_clock=board.A3, word_select=board.A2, data=board.A1)
# I2S audio on PropMaker Feather RP2040
power = digitalio.DigitalInOut(board.EXTERNAL_POWER)
power.switch_to_output(value=True)
audio = audiobusio.I2SOut(board.I2S_BIT_CLOCK, board.I2S_WORD_SELECT, board.I2S_DATA)
mixer = audiomixer.Mixer(channel_count=1, sample_rate=44100, buffer_size=4096)
amp_env_slow = synthio.Envelope(
attack_time=0.65,
sustain_level=1.0,
release_time=0.8
)
synth = synthio.Synthesizer(channel_count=1, sample_rate=44100, envelope=amp_env_slow)
audio.play(mixer)
mixer.voice[0].play(synth)
mixer.voice[0].level = 0.3
# create sine, tri, saw & square single-cycle waveforms to act as oscillators
SAMPLE_SIZE = 512
SAMPLE_VOLUME = 32000 # 0-32767
half_period = SAMPLE_SIZE // 2
wave_sine = np.array(np.sin(np.linspace(0, 2*np.pi, SAMPLE_SIZE, endpoint=False)) * SAMPLE_VOLUME,
dtype=np.int16)
wave_saw = np.linspace(SAMPLE_VOLUME, -SAMPLE_VOLUME, num=SAMPLE_SIZE, dtype=np.int16)
wave_tri = np.concatenate((np.linspace(-SAMPLE_VOLUME, SAMPLE_VOLUME, num=half_period,
dtype=np.int16),
np.linspace(SAMPLE_VOLUME, -SAMPLE_VOLUME, num=half_period,
dtype=np.int16)))
wave_square = np.concatenate((np.full(half_period, SAMPLE_VOLUME, dtype=np.int16),
np.full(half_period, -SAMPLE_VOLUME, dtype=np.int16)))
# note1 = synthio.Note( frequency = 220, waveform = wave_sine, amplitude=0.3)
detune = 0.003 # how much to detune
num_oscs = 1
midi_note = 52
while True:
print("num_oscs:", num_oscs)
notes = [] # holds note objs being pressed
# simple detune, always detunes up
for i in range(num_oscs):
f = synthio.midi_to_hz(midi_note) * (1 + i*detune)
notes.append(synthio.Note(frequency=f, waveform=wave_saw))
synth.press(notes)
time.sleep(3.6)
synth.release(notes)
time.sleep(0.1)
# increment number of detuned oscillators
num_oscs = num_oscs+1 if num_oscs < 5 else 1

View file

@ -0,0 +1,174 @@
# SPDX-FileCopyrightText: 2023 @todbot / Tod Kurt w mods by John Park
#
# SPDX-License-Identifier: MIT
# wavetable_midisynth_code_i2s.py -- simple wavetable synth that responds to MIDI
# 26 Jul 2023 - @todbot / Tod Kurt
# Demonstrate using wavetables to make a MIDI synth
# Needs WAV files from waveeditonline.com
# - BRAIDS01.WAV - http://waveeditonline.com/index-17.html
import time
import busio
import board
import audiomixer
import synthio
import digitalio
import audiobusio
import ulab.numpy as np
import adafruit_wave
import usb_midi
import adafruit_midi
from adafruit_midi.note_on import NoteOn
from adafruit_midi.note_off import NoteOff
from adafruit_midi.control_change import ControlChange
auto_play = False # set to true to have it play its own little song
auto_play_notes = [36, 38, 40, 41, 43, 45, 46, 48, 50, 52]
auto_play_speed = 0.9 # time in seconds between notes
midi_channel = 1
wavetable_fname = "wav/BRAIDS01.WAV" # from http://waveeditonline.com/index-17.html
wavetable_sample_size = 256 # number of samples per wave in wavetable (256 is standard)
sample_rate = 44100
wave_lfo_min = 0 # which wavetable number to start from 10
wave_lfo_max = 6 # which wavetable number to go up to 25
# for PWM audio with an RC filter
# Pins used on QTPY RP2040:
# - board.MOSI - Audio PWM output (needs RC filter output)
# import audiopwmio
# audio = audiopwmio.PWMAudioOut(board.GP10)
# for I2S audio with external I2S DAC board
# import audiobusio
# I2S on Audio BFF or Amp BFF on QT Py:
# audio = audiobusio.I2SOut(bit_clock=board.A3, word_select=board.A2, data=board.A1)
# I2S audio on PropMaker Feather RP2040
power = digitalio.DigitalInOut(board.EXTERNAL_POWER)
power.switch_to_output(value=True)
audio = audiobusio.I2SOut(board.I2S_BIT_CLOCK, board.I2S_WORD_SELECT, board.I2S_DATA)
mixer = audiomixer.Mixer(buffer_size=4096, voice_count=1, sample_rate=sample_rate, channel_count=1,
bits_per_sample=16, samples_signed=True)
audio.play(mixer) # attach mixer to audio playback
synth = synthio.Synthesizer(sample_rate=sample_rate)
mixer.voice[0].play(synth) # attach synth to mixer
mixer.voice[0].level = 1
uart = busio.UART(tx=board.TX, rx=board.RX, baudrate=31250, timeout=0.001)
midi_uart = adafruit_midi.MIDI(midi_in=uart, in_channel=midi_channel-1)
midi_usb = adafruit_midi.MIDI(midi_in=usb_midi.ports[0], in_channel=midi_channel-1)
# mix between values a and b, works with numpy arrays too, t ranges 0-1
def lerp(a, b, t):
return (1-t)*a + t*b
class Wavetable:
""" A 'waveform' for synthio.Note that uses a wavetable w/ a scannable wave position."""
def __init__(self, filepath, wave_len=256):
self.w = adafruit_wave.open(filepath)
self.wave_len = wave_len # how many samples in each wave
if self.w.getsampwidth() != 2 or self.w.getnchannels() != 1:
raise ValueError("unsupported WAV format")
self.waveform = np.zeros(wave_len, dtype=np.int16) # empty buffer we'll copy into
self.num_waves = self.w.getnframes() // self.wave_len
self.set_wave_pos(0)
def set_wave_pos(self, pos):
"""Pick where in wavetable to be, morphing between waves"""
pos = min(max(pos, 0), self.num_waves-1) # constrain
samp_pos = int(pos) * self.wave_len # get sample position
self.w.setpos(samp_pos)
waveA = np.frombuffer(self.w.readframes(self.wave_len), dtype=np.int16)
self.w.setpos(samp_pos + self.wave_len) # one wave up
waveB = np.frombuffer(self.w.readframes(self.wave_len), dtype=np.int16)
pos_frac = pos - int(pos) # fractional position between wave A & B
self.waveform[:] = lerp(waveA, waveB, pos_frac) # mix waveforms A & B
wavetable1 = Wavetable(wavetable_fname, wave_len=wavetable_sample_size)
amp_env = synthio.Envelope(attack_level=0.2, sustain_level=0.2, attack_time=0.05, release_time=0.3,
decay_time=.5)
wave_lfo = synthio.LFO(rate=0.2, waveform=np.array((0, 32767), dtype=np.int16))
lpf = synth.low_pass_filter(4000, 1) # cut some of the annoying harmonics
synth.blocks.append(wave_lfo) # attach wavelfo to global lfo runner since cannot attach to note
notes_pressed = {} # keys = midi note num, value = synthio.Note,
def note_on(notenum):
# release old note at this notenum if present
if oldnote := notes_pressed.pop(notenum, None):
synth.release(oldnote)
if not auto_play:
wave_lfo.retrigger()
f = synthio.midi_to_hz(notenum)
vibrato_lfo = synthio.LFO(rate=1, scale=0.01)
note = synthio.Note(frequency=f, waveform=wavetable1.waveform,
envelope=amp_env, filter=lpf, bend=vibrato_lfo)
synth.press(note)
notes_pressed[notenum] = note
def note_off(notenum):
if note := notes_pressed.pop(notenum, None):
synth.release(note)
def set_wave_lfo_minmax(wmin, wmax):
scale = (wmax - wmin)
wave_lfo.scale = scale
wave_lfo.offset = wmin
last_synth_update_time = 0
def update_synth():
# pylint: disable=global-statement
global last_synth_update_time
# only update 100 times a sec to lighten the load
if time.monotonic() - last_synth_update_time > 0.01:
# last_update_time = time.monotonic()
wavetable1.set_wave_pos( wave_lfo.value )
last_auto_play_time = 0
auto_play_pos = -1
def update_auto_play():
# pylint: disable=global-statement
global last_auto_play_time, auto_play_pos
if auto_play and time.monotonic() - last_auto_play_time > auto_play_speed:
last_auto_play_time = time.monotonic()
note_off( auto_play_notes[ auto_play_pos ] )
auto_play_pos = (auto_play_pos + 3) % len(auto_play_notes)
note_on( auto_play_notes[ auto_play_pos ] )
set_wave_lfo_minmax(wave_lfo_min, wave_lfo_max)
def map_range(s, a1, a2, b1, b2):
return b1 + ((s - a1) * (b2 - b1) / (a2 - a1))
print("wavetable midisynth i2s. auto_play:",auto_play)
while True:
update_synth()
update_auto_play()
msg = midi_uart.receive() or midi_usb.receive()
if isinstance(msg, NoteOn) and msg.velocity != 0:
note_on(msg.note)
elif isinstance(msg,NoteOff) or isinstance(msg,NoteOn) and msg.velocity==0:
note_off(msg.note)
elif isinstance(msg,ControlChange):
if msg.control == 21: # mod wheel
scan_low = map_range(msg.value, 0,127, 0, 64)
set_wave_lfo_minmax(scan_low, scan_low)

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,3 @@
WaveEdit is the free, open-source wavetable editor developed by Synthesis Technology for the E370 Quad Morphing VCO and E352 Cloud Terrarium VCO Eurorack format wavetable oscillators. WaveEdit wavetables are also compatible with the Piston Honda Mark III from Industrial Music Electronics and the Qu-Bit Chord v2. You can download WaveEdit at http://synthtech.com/waveedit for Mac, Windows and Linux.
This site presents a growing library of free wavetable banks shared by WaveEdit users via the WaveEdit Online tab within the software. All wavetables here are under CC0 1.0 Universal (CC0 1.0) Public Domain Dedication.