Skip to content

Instantly share code, notes, and snippets.

@todbot
Last active November 8, 2024 16:20
Show Gist options
  • Save todbot/bb4ec9c509f8c301e4787e5cb26ec870 to your computer and use it in GitHub Desktop.
Save todbot/bb4ec9c509f8c301e4787e5cb26ec870 to your computer and use it in GitHub Desktop.
Trinkey MIDI Theremin with Pitch! (will also work on any other CircuitPython devices with `touchio`)
# trinkey_theremin_pitch_code.py -- Trinkey MIDI Theremin! with pitch control!
# 2021 @todbot
# left antenna: pitch (by multiple MIDI notes, put your synth in legato mode)
# right antenna: filter cutoff
# video demo in comment below
#
import time
import board
import neopixel
import touchio
import usb_midi
import adafruit_midi
from adafruit_midi.note_on import NoteOn
from adafruit_midi.control_change import ControlChange
midi = adafruit_midi.MIDI(midi_out=usb_midi.ports[1], out_channel=0)
leds = neopixel.NeoPixel(board.NEOPIXEL, 4, brightness=0.2, auto_write=True)
# announce we're alive and wait for things to stabilize
# before creating touch pins so their raw_value baseline are okay
leds.fill(0xff00ff)
time.sleep(1.0)
leds.fill(0)
touch1_pin = touchio.TouchIn(board.TOUCH1)
touch2_pin = touchio.TouchIn(board.TOUCH2)
# a litlte helper class for noise filtering
class RunningAverage:
def __init__(self,count):
self.count = count
self.i = 0
self.buf = [0] * self.count
def add_value(self,val):
self.buf[self.i] = val
self.i = (self.i + 1) % self.count
def average(self):
return sum(self.buf)/self.count
base1 = int(touch1_pin.raw_value * 1.02)
base2 = int(touch2_pin.raw_value * 1.02)
avg1 = RunningAverage(8)
avg2 = RunningAverage(8)
note_on = False
note_base = 48 # C3 in MIDI land
note = note_base
velocity = 64 # half
while True:
avg1.add_value( touch1_pin.raw_value )
avg2.add_value( touch2_pin.raw_value )
r = 0
g = min(max(avg1.average()-base1, 0),255)
b = min(max(avg2.average()-base2, 0),255)
leds.fill( (r,g,b) )
if g > 10: # hand present
note_new = note_base + int(g/4)
if note_new != note:
print(time.monotonic(),"******* ON! ",note_new,note)
midi.send(NoteOn(note_new, velocity)) # turn on new note
midi.send(NoteOn(note,0)) # 0=off # turn off old note
note = note_new # save new note
else: # no hand, turn off note
midi.send(NoteOn(note,0)) # 0=off
if b > 10: # hand present
midi.send(ControlChange( 74, 16+int(b/2.5))) # 74 = filter cutoff
time.sleep(0.001)
@todbot
Copy link
Author

todbot commented Aug 16, 2023

Video demo of above code:

trinkey_midi_theramin.mp4

@todbot
Copy link
Author

todbot commented Aug 16, 2023

yes "theramin" is spelled wrong :)

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