# rp_synthy_drone.py for HackerBox 0110
# Adapted from two_pot_drone_synth.py by @todbot / Tod Kurt
# https://gist.github.com/todbot/532e069845c2cc4c1bc39c9162a34bfe
#
# Tod's Original Header:
# 25 Nov 2024 - @todbot / Tod Kurt
# video demo: https://www.youtube.com/watch?v=xEmhk-dVXqQ
# original simpler version:
# https://gist.github.com/todbot/53bfa7333ac7c1fcff8eaf0f547556ff

import time, random
import board, rotaryio, keypad
import audiobusio, audiocore, audiomixer, synthio
import ulab.numpy as np

sample_rate=44100

# set up input devices
keys = keypad.Keys( (board.GP18, board.GP7), value_when_pressed=False, pull=True)
right_encoder = rotaryio.IncrementalEncoder(board.GP15, board.GP14)
left_encoder = rotaryio.IncrementalEncoder(board.GP17, board.GP16)

i2s_bclk, i2s_lclk, i2s_data = board.GP3, board.GP4, board.GP5
audio = audiobusio.I2SOut(bit_clock=i2s_bclk, word_select=i2s_lclk, data=i2s_data)
mixer = audiomixer.Mixer(voice_count=1, channel_count=1, sample_rate=sample_rate, buffer_size=2048)
audio.play(mixer)
synth = synthio.Synthesizer(channel_count=1, sample_rate=sample_rate)

# try to load up echo effect, if not available no worries
try:
    import audiodelays
    effect = audiodelays.Echo( max_delay_ms=600, delay_ms=600,
                               decay=.07, mix=0.4,  freq_shift=False,
                               buffer_size=2048, sample_rate=sample_rate )
    mixer.voice[0].play(effect)
    effect.play(synth)
except:
    mixer.voice[0].play(synth)  # if we don't have delay

# configure our synthesizer and set it going
lfo = synthio.LFO(rate=5)
amp_env = synthio.Envelope(attack_time=0.5, release_time=0.5)
# amp_env = synthio.Envelope(attack_time=0.02, attack_level=1, sustain_level=1, release_time=1)
wave_saw = np.linspace(30000, -30000, num=512, dtype=np.int16)  # max is +/-32k but gives us headroom
# need to lower amplitude or get clipping when delays are active
note1 = synthio.Note(frequency=100, waveform=wave_saw, amplitude=0.5, envelope=amp_env)
note2 = synthio.Note(frequency=100, waveform=wave_saw, amplitude=0.5, envelope=amp_env)
note3 = synthio.Note(frequency=100, waveform=wave_saw, amplitude=0.5, envelope=amp_env)
synth.press((note1,note2, note3))

k1, k2 = 0, 0
while True:
    # handle button presses
    if key := keys.events.get():
        if key.key_number == 0:  
            if key.pressed:
                note1.bend = lfo
            else:
                note1.bend = 0
        if key.key_number == 1:
            if key.pressed:
                synth.release_all()
            else:
                synth.press((note1,note2))
        
    k1 = 80 + right_encoder.position 
    k2 = 75 + left_encoder.position 
    if (k1 < 35) :
        k1=35
    if (k1 > 140):
        k1=140
    if (k2 < 35) :
        k2=35
    if (k2 > 140):
        k2=140
    
    # update the drone voices' pitches
    note1.frequency = synthio.midi_to_hz( k1 )
    note2.frequency = synthio.midi_to_hz( k2 )
    note3.frequency = note2.frequency*1.005  # detune for phatness