Skip to content

Instantly share code, notes, and snippets.

@jedgarpark
Last active December 2, 2023 01:56
Show Gist options
  • Save jedgarpark/e5df9de1c05a7ce5eb17be19d8bb11ff to your computer and use it in GitHub Desktop.
Save jedgarpark/e5df9de1c05a7ce5eb17be19d8bb11ff to your computer and use it in GitHub Desktop.
# testing Wave Shape Fader v0.1 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 = True
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 = 3 # how many voices for each note
lpf_basef = 1500 # filter lowest frequency
lpf_resonance = 1.5 # filter q
faders_pos = [None] * NUM_FADERS
last_faders_pos = [None] * 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)
voices=[] # holds our currently sounding voices ('Notes' in synthio speak)
def faders_to_wave():
for i in range(NUM_FADERS):
wave_user[i] = int(map_range(faders_pos[i], 0, 255, -32768, 32767))
def note_on(n):
# print(" note on ", n )
fo = synthio.midi_to_hz(n)
voices.clear() # delete any old voices
# wave_user = np.array([
# int(map_range(faders_pos[0], 0, 255, -32768, 32767)),
# int(map_range(faders_pos[1], 0, 255, -32768, 32767)),
# int(map_range(faders_pos[2], 0, 255, -32768, 32767)),
# int(map_range(faders_pos[3], 0, 255, -32768, 32767)),
# int(map_range(faders_pos[4], 0, 255, -32768, 32767)),
# int(map_range(faders_pos[5], 0, 255, -32768, 32767)),
# int(map_range(faders_pos[6], 0, 255, -32768, 32767)),
# int(map_range(faders_pos[7], 0, 255, -32768, 32767)),
# int(map_range(faders_pos[8], 0, 255, -32768, 32767)),
# int(map_range(faders_pos[9], 0, 255, -32768, 32767)),
# int(map_range(faders_pos[10], 0, 255, -32768, 32767)),
# int(map_range(faders_pos[11], 0, 255, -32768, 32767)),
# int(map_range(faders_pos[12], 0, 255, -32768, 32767)),
# int(map_range(faders_pos[13], 0, 255, -32768, 32767)),
# int(map_range(faders_pos[14], 0, 255, -32768, 32767)),
# int(map_range(faders_pos[15], 0, 255, -32768, 32767)),
# ], dtype=np.int16)
for i in range(num_voices):
f = fo * (1 + i*0.007)
lpf_f = fo * 8 # a kind of key tracking
lpf = synth.low_pass_filter( lpf_f, lpf_resonance )
voices.append( synthio.Note( frequency=f, filter=lpf, envelope=amp_env, waveform=wave_user) )
synth.press(voices)
# call to turn off a note
def note_off(n):
# print(" note off", n)
synth.release(voices)
# 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()
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.1)
@todbot
Copy link

todbot commented Dec 1, 2023

Here's a slightly modified waveform section that lets you modify it in realtime:

# waveform that will be edited by "faders_to_wave()"
wave_user = np.array( [0]*NUM_FADERS, dtype=np.int16)

def faders_to_wave():
    wave_user[:] = np.array(
        [
            int(map_range(faders_pos[0], 0,255, -30000, 30000)),
            int(map_range(faders_pos[1], 0,255, -30000, 30000)),
            int(map_range(faders_pos[2], 0,255, -30000, 30000)),
            int(map_range(faders_pos[3], 0,255, -30000, 30000)),
            int(map_range(faders_pos[4], 0,255, -30000, 30000)),
            int(map_range(faders_pos[5], 0,255, -30000, 30000)),
            int(map_range(faders_pos[6], 0,255, -30000, 30000)),
            int(map_range(faders_pos[7], 0,255, -30000, 30000)),
            int(map_range(faders_pos[8], 0,255, -30000, 30000)),
            int(map_range(faders_pos[9], 0,255, -30000, 30000)),
            int(map_range(faders_pos[10], 0,255, -30000, 30000)),
            int(map_range(faders_pos[11], 0,255, -30000, 30000)),
            int(map_range(faders_pos[12], 0,255, -30000, 30000)),
            int(map_range(faders_pos[13], 0,255, -30000, 30000)),
            int(map_range(faders_pos[14], 0,255, -30000, 30000)),
            int(map_range(faders_pos[15], 0,255, -30000, 30000)),
        ], dtype=np.int16)

And then down in your main loop when you are doing a fader update, add a call to that func, e.g.:

    for i in range(len(faders)):
        faders_pos[i] = faders[i].value//256
        if faders_pos[i] is not last_faders_pos[i]:

          faders_to_wave()

          # text.text = ("Fader " + str(i+1) + " " + str(faders_pos[i]) )
          last_faders_pos[i] = faders_pos[i]
          dac.value = faders[0].value

@todbot
Copy link

todbot commented Dec 2, 2023

Sorry I lied to you, I think it should be something like:

def faders_to_wave():
    for i in range(NUM_FADERS):
        wave_user[i] = int(map_range(faders_pos[i], 0, 255, -32768, 32767))   

@jedgarpark
Copy link
Author

Are you sorry you lied, or sorry you were caught, hmmmmm "Tod"?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment