Skip to content

Commit aeec0d2

Browse files
committed
metronome first commit
1 parent 301e64a commit aeec0d2

File tree

1 file changed

+209
-0
lines changed

1 file changed

+209
-0
lines changed

CLUE_Metronome/clue_metronome.py

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
import time
2+
import array
3+
import math
4+
import board
5+
from audiocore import RawSample
6+
import audiopwmio
7+
import displayio
8+
import terminalio
9+
from adafruit_display_text import label
10+
from adafruit_display_shapes.rect import Rect
11+
from adafruit_display_shapes.circle import Circle
12+
from adafruit_clue import clue
13+
14+
blink_dot = False # optional blinking dot on accent
15+
blink_light = True # optional flashing backlight on accent
16+
tempo = 120 # in bpm
17+
print("BPM: {}".format(tempo))
18+
time_signature = 4 # Beats per measure
19+
BEEP_DURATION = 0.05
20+
delay = 60 / tempo
21+
22+
# constants for sine wave generation
23+
SIN_LENGTH = 100 # more is less choppy
24+
SIN_AMPLITUDE = 2 ** 15 # 0 (min) to 32768 (max)
25+
SIN_OFFSET = 32767.5 # for 16bit range, (2**16 - 1) / 2
26+
DELTA_PI = 2 * math.pi / SIN_LENGTH # happy little constant
27+
28+
sine_wave = [
29+
int(SIN_OFFSET + SIN_AMPLITUDE * math.sin(DELTA_PI * i)) for i in range(SIN_LENGTH)
30+
]
31+
tones = (
32+
RawSample(array.array("H", sine_wave), sample_rate=1200 * SIN_LENGTH),
33+
RawSample(array.array("H", sine_wave), sample_rate=1800 * SIN_LENGTH),
34+
)
35+
36+
dac = audiopwmio.PWMAudioOut(board.SPEAKER) # CLUE onboard speaker
37+
# dac = audiopwmio.PWMAudioOut(board.A2) # external amp/speaker, but can't use #0 touch pad
38+
39+
clue.display.brightness = 1.0
40+
clue.pixel.brightness = 0.2
41+
screen = displayio.Group(max_size=11)
42+
TEAL = 0x009E98
43+
LT_TEAL = 0x000F0F
44+
GRAY = 0x02403E
45+
BLACK = 0x000000
46+
WHITE = 0xFFFFFF
47+
YELLOW = 0xFFFF00
48+
49+
clue.pixel.fill(0) # Turn off the pixel
50+
51+
# Setup screen
52+
# BG
53+
color_bitmap = displayio.Bitmap(240, 240, 1)
54+
color_palette = displayio.Palette(1)
55+
color_palette[0] = TEAL
56+
bg_sprite = displayio.TileGrid(color_bitmap, x=0, y=0, pixel_shader=color_palette)
57+
screen.append(bg_sprite)
58+
59+
# Downbeat indicator graphic
60+
flash = Circle(184, 216, 16, fill=TEAL, outline=None)
61+
62+
screen.append(flash)
63+
64+
# title box
65+
title_box = Rect(0, 0, 240, 60, fill=GRAY, outline=None)
66+
screen.append(title_box)
67+
68+
# title text
69+
title_label = label.Label(
70+
terminalio.FONT, text="Metronome", scale=4, color=TEAL, max_glyphs=11
71+
)
72+
title_label.x = 14
73+
title_label.y = 26
74+
screen.append(title_label)
75+
76+
# interval text
77+
interval_label = label.Label(
78+
terminalio.FONT, text=("{} BPM".format(tempo)), scale=5, color=WHITE, max_glyphs=7
79+
)
80+
interval_label.x = 20
81+
interval_label.y = 95
82+
screen.append(interval_label)
83+
84+
# mid line
85+
mid_line = Rect(0, 134, 240, 3, fill=GRAY, outline=None)
86+
screen.append(mid_line)
87+
88+
# vert line
89+
vert_line = Rect(117, 134, 3, 56, fill=GRAY, outline=None)
90+
screen.append(vert_line)
91+
92+
# Signature text
93+
sig_label = label.Label(
94+
terminalio.FONT,
95+
text=("{}/4".format(time_signature)),
96+
scale=3,
97+
color=BLACK,
98+
max_glyphs=3,
99+
)
100+
sig_label.x = 30
101+
sig_label.y = 160
102+
screen.append(sig_label)
103+
104+
# play text
105+
play_label = label.Label(
106+
terminalio.FONT, text=(" play"), scale=3, color=BLACK, max_glyphs=5
107+
)
108+
play_label.x = 138
109+
play_label.y = 160
110+
screen.append(play_label)
111+
112+
# footer line
113+
footer_line = Rect(0, 190, 240, 3, fill=GRAY, outline=None)
114+
screen.append(footer_line)
115+
116+
# increment label
117+
increment_label = label.Label(
118+
terminalio.FONT, text=("-1 +1"), scale=3, color=GRAY, max_glyphs=8
119+
)
120+
increment_label.x = 3
121+
increment_label.y = 220
122+
screen.append(increment_label)
123+
124+
# show the screen
125+
clue.display.show(screen)
126+
127+
128+
def metronome(accent): # Play metronome sound and flash display
129+
clue.display.brightness = 0.5 # Dim the display slightly
130+
if accent == 1: # Put emphasis on downbeat
131+
if blink_dot:
132+
flash.fill = YELLOW # Flash the indicator
133+
if blink_light:
134+
clue.pixel.fill(YELLOW) # Flash the pixel
135+
dac.play(tones[1], loop=True)
136+
else: # All the other beats in the measure
137+
if blink_light:
138+
clue.pixel.fill(LT_TEAL) # Flash the pixel
139+
dac.play(tones[0], loop=True)
140+
time.sleep(BEEP_DURATION) # Play the sound for a while
141+
dac.stop() # Then stop the sound
142+
if blink_dot:
143+
flash.fill = TEAL # Turn off the downbeat indicator
144+
if blink_light:
145+
clue.pixel.fill(0) # Turn off the pixel
146+
clue.display.brightness = 1.0 # Restore display to normal brightness
147+
148+
149+
time.sleep(0.2)
150+
tempo_increment = 1 # increment for tempo value setting
151+
feedback_mode = 0 # 0 is sound and visual, 1 is sound only, 2 is visual only
152+
running = False
153+
154+
t0 = time.monotonic() # set start time
155+
156+
157+
while True:
158+
159+
# play/pause
160+
if clue.button_b:
161+
if play_label.text == " play":
162+
play_label.text = "pause"
163+
else:
164+
play_label.text = " play"
165+
running = not running
166+
time.sleep(0.4)
167+
beat = 1 # start with downbeat
168+
169+
# Time Signature change
170+
if clue.button_a:
171+
print("sig change")
172+
if time_signature == 4:
173+
time_signature = 3
174+
else:
175+
time_signature = 4
176+
sig_label.text = "{}/4".format(time_signature)
177+
time.sleep(0.4)
178+
beat = 1 # start with downbeat
179+
180+
if running and (time.monotonic() - t0) >= delay:
181+
t0 = time.monotonic() # reset time before click to maintain accuracy
182+
metronome(beat)
183+
beat = beat - 1
184+
if beat == 0: # if the downbeat was just played, start at top of measure
185+
beat = time_signature
186+
187+
# tempo changes
188+
if clue.touch_0:
189+
if tempo_increment is 1:
190+
tempo_increment = 10
191+
increment_label.text = "-10 +10"
192+
else:
193+
tempo_increment = 1
194+
increment_label.text = "-1 +1"
195+
time.sleep(0.2) # debounce
196+
197+
if clue.touch_1:
198+
if tempo > 40:
199+
tempo = tempo - tempo_increment
200+
delay = 60 / tempo
201+
interval_label.text = "{} BPM".format(tempo)
202+
time.sleep(0.2) # debounce
203+
204+
if clue.touch_2:
205+
if tempo < 330:
206+
tempo = tempo + tempo_increment
207+
delay = 60 / tempo
208+
interval_label.text = "{} BPM".format(tempo)
209+
time.sleep(0.2) # debounce

0 commit comments

Comments
 (0)