11# pico_test_synth_hwtest_code.py -- test hardware of pico_test_synth board
22# 27 Jun 2023 - 15 Feb 2024 - @todbot / Tod Kurt
3+ # 3 May 2025 - updated for CircuitPython 10
34#
45# Functionality:
56# - touch pads to trigger synth notes (defined in 'midi_notes')
2930import touchio
3031from adafruit_debouncer import Debouncer
3132import usb_midi
32-
33- #midi_notes = (33, 45, 52, 57)
34- midi_notes = list (range (45 ,45 + 16 ))
33+ import adafruit_midi
34+ from adafruit_midi .note_on import NoteOn
35+ from adafruit_midi .note_off import NoteOff
36+ from adafruit_midi .control_change import ControlChange
37+
38+ SAMPLE_RATE = 28000
39+ BUFFER_SIZE = 4096 # need a bigger buffer when screen updated
40+ midi_notes = list (range (48 ,48 + 16 )) # which MIDI notes the touch pads send
3541filter_freq = 4000
3642filter_resonance = 1.2
3743output_volume = 1.0
7278 touchs .append (Debouncer (touchin ))
7379
7480print ("starting up..." )
75- midi_in_usb = usb_midi .ports [0 ]
76- midi_in_uart = busio .UART (rx = uart_rx_pin , tx = uart_tx_pin , baudrate = 31250 , timeout = 0.001 )
7781i2c = busio .I2C (scl = i2c_scl_pin , sda = i2c_sda_pin , frequency = 1_000_000 )
82+ uart = busio .UART (rx = uart_rx_pin , tx = uart_tx_pin , baudrate = 31250 , timeout = 0.001 )
83+ midi_uart = adafruit_midi .MIDI (midi_in = uart , midi_out = uart )
84+ midi_usb = adafruit_midi .MIDI (midi_in = usb_midi .ports [0 ], midi_out = usb_midi .ports [1 ])
7885
7986dw ,dh = 128 , 64
8087display_bus = i2cdisplaybus .I2CDisplayBus (i2c , device_address = 0x3c )
8188display = adafruit_displayio_ssd1306 .SSD1306 (display_bus , width = dw , height = dh , rotation = 180 )
8289
8390# set up the synth->audio system
8491audio = audiobusio .I2SOut (bit_clock = i2s_bclk_pin , word_select = i2s_lclk_pin , data = i2s_data_pin )
85- mixer = audiomixer .Mixer (voice_count = 1 , sample_rate = 28000 , channel_count = 1 ,
92+ mixer = audiomixer .Mixer (voice_count = 1 , sample_rate = SAMPLE_RATE , channel_count = 1 ,
8693 bits_per_sample = 16 , samples_signed = True ,
87- buffer_size = 4096 ) # buffer_size=4096) # need a big buffer when screen updated
88- synth = synthio .Synthesizer (sample_rate = 28000 )
94+ buffer_size = BUFFER_SIZE )
95+ synth = synthio .Synthesizer (sample_rate = SAMPLE_RATE )
8996audio .play (mixer )
9097mixer .voice [0 ].level = output_volume
9198mixer .voice [0 ].play (synth )
9299
93100# set up the synth
94- wave_saw = np .linspace (20000 , - 20000 , num = 512 , dtype = np .int16 ) # default squ is too clippy
101+ wave_saw = np .linspace (32000 , - 32000 , num = 256 , dtype = np .int16 ) # default squ is too clippy
95102#amp_env = synthio.Envelope(attack_level=1.0, sustain_level=1.0, release_time=0.4, attack_time=0.4, decay_time=0.5)
96103amp_env = synthio .Envelope (release_time = 0.3 , attack_time = 0.3 )
97104synth .envelope = amp_env
106113 maingroup .append (t )
107114time .sleep (1 )
108115
109- touch_notes = [ None ] * len ( midi_notes )
116+ notes_pressed = {}
110117sw_pressed = False
111118
119+ def note_on (midi_note ):
120+ print ("note_on:" , midi_note )
121+ note_off (midi_note ) # only one note per midi_note allowed
122+ f = synthio .midi_to_hz (midi_note )
123+ filter = synthio .Biquad (synthio .FilterMode .LOW_PASS , filter_freq , filter_resonance )
124+ n = synthio .Note (frequency = f , waveform = wave_saw , filter = filter , amplitude = 0.75 )
125+ synth .press ( n )
126+ notes_pressed [midi_note ] = n
127+
128+ def note_off (midi_note ):
129+ if n := notes_pressed .get (midi_note ,None ):
130+ synth .release (n )
131+
132+ # check the touch pads and trigger synthio notes
112133def check_touch ():
113134 for i in range (len (touchs )):
114135 touch = touchs [i ]
115136 touch .update ()
116137 if touch .rose :
117138 print ("touch press" ,i )
118- f = synthio .midi_to_hz (midi_notes [i ])
119- filter = synthio .Biquad (synthio .FilterMode .LOW_PASS , filter_freq , filter_resonance )
120- n = synthio .Note (frequency = f , waveform = wave_saw , filter = filter , amplitude = 0.75 )
121- synth .press ( n )
122- touch_notes [i ] = n
139+ note_on (midi_notes [i ])
123140 if touch .fell :
124141 print ("touch release" , i )
125- if touch_notes [i ]:
126- synth . release ( touch_notes [ i ] )
127-
142+ note_off ( midi_notes [i ])
143+
144+ # print to REPL current state of knobs, button, and touchpads
128145async def debug_printer ():
129146 t1_last = ""
130147 t2_last = ""
@@ -142,52 +159,44 @@ async def debug_printer():
142159 print ("T:" + '' .join (["%3d " % (t .raw_value // 16 ) for t in touchins [0 :4 ]]))
143160 await asyncio .sleep (0.3 )
144161
162+ # handle all user input: knobs, button, and touchpads
145163async def input_handler ():
146- global sw_pressed
147164 global filter_freq , filter_resonance
148165
149- note = None
166+ midi_note = None
150167
151168 while True :
152- filter_freq = knobA .value / 65535 * 8000 + 100 # range 100-8100
169+ filter_freq = knobA .value / 65535 * 8000 + 1 # range 1-8001
153170 filter_resonance = knobB .value / 65535 * 3 + 0.2 # range 0.2-3.2
154171
155- for n in touch_notes : # real-time adjustment of filter
172+ for n in notes_pressed . values () : # real-time adjustment of filter
156173 if n :
157174 n .filter .frequency = filter_freq
158175 n .filter .Q = filter_resonance
159176
160177 check_touch ()
161178
162179 if key := keys .events .get ():
163- if key .released :
164- sw_pressed = False
165- synth .release ( note )
166180 if key .pressed :
167181 sw_pressed = True
168- f = synthio .midi_to_hz (random .randint (32 ,60 ))
169- note = synthio .Note (frequency = f , waveform = wave_saw , amplitude = 0.8 )
170- synth .press (note )
182+ midi_note = random .randint (32 ,60 )
183+ note_on (midi_note )
184+ if key .released :
185+ sw_pressed = False
186+ note_off (midi_note )
171187 await asyncio .sleep (0.001 )
172188
189+
173190async def midi_handler ():
174191 while True :
175- while msg := midi_in_uart .read (3 ):
176- print ("midi in uart:" , [hex (b ) for b in msg ])
177- if msg [0 ] == 0x90 : # note on
178- synth .press ( msg [1 ] )
179- elif msg [0 ] == 0x80 or msg [0 ] == 0x90 and msg [2 ] == 0 : # note off
180- synth .release ( msg [1 ] )
181-
182- while msg := midi_in_usb .read (3 ):
183- print ("midi in usb:" , [hex (b ) for b in msg ])
184- if msg [0 ] == 0x90 : # note on
185- synth .press ( msg [1 ] )
186- elif msg [0 ] == 0x80 or msg [0 ] == 0x90 and msg [2 ] == 0 : # note off
187- synth .release ( msg [1 ] )
188-
192+ while msg := midi_usb .receive () or midi_uart .receive ():
193+ if isinstance (msg , NoteOn ) and msg .velocity != 0 :
194+ note_on (msg .note )
195+ elif isinstance (msg ,NoteOff ) or isinstance (msg ,NoteOn ) and msg .velocity == 0 :
196+ note_off (msg .note )
189197 await asyncio .sleep (0 )
190198
199+
191200# main coroutine
192201async def main (): # Don't forget the async!
193202 task1 = asyncio .create_task (debug_printer ())
0 commit comments