Created
December 5, 2023 21:52
-
-
Save jedgarpark/20442b0c367665884d9df0f634caab02 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Faderwave v0.2 PCB | |
# john park 2023 | |
import time | |
import board | |
import busio | |
import ulab.numpy as np | |
import rotaryio | |
import neopixel | |
from digitalio import DigitalInOut, Pull | |
import displayio | |
from adafruit_display_shapes.rect import Rect | |
from adafruit_display_text import label | |
import terminalio | |
import synthio | |
import audiomixer | |
from adafruit_debouncer import Debouncer | |
import adafruit_ads7830.ads7830 as ADC | |
from adafruit_ads7830.analog_in import AnalogIn | |
import adafruit_displayio_ssd1306 | |
import adafruit_ad569x | |
import usb_midi | |
import adafruit_midi | |
from adafruit_midi.note_on import NoteOn | |
from adafruit_midi.note_off import NoteOff | |
displayio.release_displays() | |
midi = adafruit_midi.MIDI(midi_in=usb_midi.ports[0], in_channel=0) | |
debug_notes = False | |
ITSY_TYPE = 0 # 0=M4, 1=RP2040 | |
# neopixel setup | |
if ITSY_TYPE is 1: | |
pixel = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.3) | |
pixel.fill(0x004444) | |
i2c = busio.I2C(board.SCL, board.SDA, frequency=1_000_000) | |
NUM_FADERS = 16 | |
num_voices = 2 # how many voices for each note | |
lpf_basef = 500 # filter lowest frequency | |
lpf_resonance = 0.1 # filter q | |
faders_pos = [0] * NUM_FADERS | |
last_faders_pos = [0] * NUM_FADERS | |
# Initialize ADS7830 | |
adc_a = ADC.ADS7830(i2c, address=0x48) # default address 0x48 | |
adc_b = ADC.ADS7830(i2c, address=0x4A) # A0 jumper 0x49, A1 0x4A | |
faders = [] # list for fader objects on first ADC | |
for f in range(8): # add first group to list | |
faders.append(AnalogIn(adc_a, f)) | |
for f in range(8): # add second group | |
faders.append(AnalogIn(adc_b, f)) | |
# Initialize AD5693R for CV out | |
dac = adafruit_ad569x.Adafruit_AD569x(i2c) | |
dac.gain = True | |
dac.value = faders[0].value # set dac out to the slider level | |
# Rotary encoder setup | |
ENC_A = board.D9 | |
ENC_B = board.D10 | |
ENC_SW = board.D7 | |
button_in = DigitalInOut(ENC_SW) # defaults to input | |
button_in.pull = Pull.UP # turn on internal pull-up resistor | |
button = Debouncer(button_in) | |
encoder = rotaryio.IncrementalEncoder(ENC_A, ENC_B) | |
encoder_pos = encoder.position | |
last_encoder_pos = encoder.position | |
# display setup | |
OLED_RST = board.D13 | |
OLED_DC = board.D12 | |
OLED_CS = board.D11 | |
spi = board.SPI() | |
display_bus = displayio.FourWire(spi, command=OLED_DC, chip_select=OLED_CS, | |
reset=OLED_RST, baudrate=1000000) | |
display = adafruit_displayio_ssd1306.SSD1306(display_bus, width=128, height=64) | |
# Create display group | |
group = displayio.Group() | |
# Create background rectangle | |
bg_rect = Rect(0, 0, display.width, display.height, fill=0x0) | |
group.append(bg_rect) | |
# Set the font for the text label | |
font = terminalio.FONT | |
# Create text label to display the number | |
text = label.Label(font, text=str(faders[0].value//256), color=0xffffff) | |
text.x = display.width // 12 | |
text.y = display.height // 2 | |
text.anchor_point = (0.5, 0.5) | |
text.scale = 2 | |
group.append(text) | |
# Show the display group | |
display.show(group) | |
# Synthio setup | |
if ITSY_TYPE is 0: | |
import audioio | |
audio = audioio.AudioOut(left_channel=board.A0, right_channel=board.A1) # M4 built-in DAC | |
if ITSY_TYPE is 1: | |
import audiopwmio | |
audio = audiopwmio.PWMAudioOut(board.A1) | |
# if using I2S amp: | |
# audio = audiobusio.I2SOut(bit_clock=board.MOSI, word_select=board.MISO, data=board.SCK) | |
mixer = audiomixer.Mixer(channel_count=2, sample_rate=44100, buffer_size=4096) | |
synth = synthio.Synthesizer(channel_count=2, sample_rate=44100) | |
audio.play(mixer) | |
mixer.voice[0].play(synth) | |
mixer.voice[0].level = 0.75 | |
wave_user = np.array([0]*NUM_FADERS, dtype=np.int16) | |
amp_env = synthio.Envelope(attack_time=0.3, attack_level=1, sustain_level=0.65, release_time=0.3) | |
def faders_to_wave(): | |
for i in range(NUM_FADERS): | |
wave_user[i] = int(map_range(faders_pos[i], 0, 255, -32768, 32767)) | |
notes_pressed = {} # which notes being pressed. key=midi note, val=note object | |
def note_on(n): | |
voices = [] # holds our currently sounding voices ('Notes' in synthio speak) | |
fo = synthio.midi_to_hz(n) | |
lpf_f = fo * 8 # a kind of key tracking | |
lpf = synth.low_pass_filter(lpf_f, lpf_resonance) | |
voices.clear() # delete any old voices | |
for i in range(num_voices): | |
f = fo * (1 + i*0.007) | |
voices.append(synthio.Note(frequency=f, filter=lpf, envelope=amp_env, waveform=wave_user)) | |
synth.press(voices) | |
notes_pressed[n] = voices | |
def note_off(n): | |
# print(" note off", n) | |
note = notes_pressed.get(n, None) | |
if note: | |
synth.release(note) | |
# simple range mapper, like Arduino map() | |
def map_range(s, a1, a2, b1, b2): return b1 + ((s - a1) * (b2 - b1) / (a2 - a1)) | |
while True: | |
msg = midi.receive() | |
if isinstance(msg, NoteOn) and msg.velocity != 0: | |
# print("noteOn: ", msg.note) | |
# text.text=(str(msg.note)) | |
note_on(msg.note) | |
elif isinstance(msg, NoteOff) or isinstance(msg, NoteOn) and msg.velocity == 0: | |
# print("noteOff:", msg.note) | |
note_off(msg.note) | |
button.update() | |
if button.fell: | |
note_on(45) | |
if button.rose: | |
note_off(45) | |
encoder_pos = encoder.position | |
if encoder_pos is not last_encoder_pos: | |
# print("encoder:", encoder.position) # starts at zero, goes neg or pos | |
last_encoder_pos = encoder.position | |
# | |
for i in range(len(faders)): | |
# print(faders[0].value) | |
faders_pos[i] = faders[i].value//256 | |
if faders_pos[i] is not last_faders_pos[i]: | |
faders_to_wave() | |
# text.text=(str(faders[i].value)) | |
last_faders_pos[i] = faders_pos[i] | |
# if you want to send out a DAC value as a test: | |
# dac.value = faders[0].value | |
time.sleep(0.001) |
To guard against the same note being played twice without a noteoff, you could do something like this in your note_on()
:
def note_on(n):
# ...
note_off(n)
notes_pressed[n] = voices
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I forget precedence on
and
vsor
in Python, so to be explicit instead of doingdo this:
I think I've made this mistake in my own code.