Skip to content

Commit d1f7f64

Browse files
committed
first commit Walkmp3rson code and assets
1 parent e426cc1 commit d1f7f64

File tree

6 files changed

+227
-0
lines changed

6 files changed

+227
-0
lines changed
851 KB
Binary file not shown.
1.25 MB
Binary file not shown.
1.35 MB
Binary file not shown.
911 KB
Binary file not shown.

Walkmp3rson/code.py

Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
1+
# SPDX-FileCopyrightText: 2022 John Park and Tod Kurt for Adafruit Industries
2+
# SPDX-License-Identifier: MIT
3+
# Walkmp3rson digital cassette tape player (ok fine it's just SD cards)
4+
import time
5+
import os
6+
import board
7+
import busio
8+
import sdcardio
9+
import storage
10+
import audiomixer
11+
import audiobusio
12+
import audiomp3
13+
from adafruit_neokey.neokey1x4 import NeoKey1x4
14+
from adafruit_seesaw import seesaw, rotaryio
15+
import displayio
16+
import terminalio
17+
from adafruit_display_text import label
18+
from adafruit_st7789 import ST7789
19+
from adafruit_progressbar.progressbar import HorizontalProgressBar
20+
from adafruit_progressbar.verticalprogressbar import VerticalProgressBar
21+
22+
23+
displayio.release_displays()
24+
25+
# SPI for TFT display, and SD Card reader on TFT display
26+
spi = board.SPI()
27+
# display setup
28+
tft_cs = board.D6
29+
tft_dc = board.D9
30+
tft_reset = board.D12
31+
display_bus = displayio.FourWire(spi, command=tft_dc, chip_select=tft_cs, reset=tft_reset)
32+
display = ST7789(display_bus, width=320, height=240, rotation=90)
33+
34+
# SD Card setup
35+
sd_cs = board.D13
36+
sdcard = sdcardio.SDCard(spi, sd_cs)
37+
vfs = storage.VfsFat(sdcard)
38+
storage.mount(vfs, "/sd")
39+
40+
# I2C NeoKey setup
41+
i2c = busio.I2C(board.SCL, board.SDA)
42+
neokey = NeoKey1x4(i2c, addr=0x30)
43+
amber = 0x300800
44+
red = 0x900000
45+
green = 0x009000
46+
47+
neokey.pixels.fill(amber)
48+
keys = [
49+
(neokey, 0, green),
50+
(neokey, 1, red),
51+
(neokey, 2, green),
52+
(neokey, 3, green),
53+
]
54+
# states for key presses
55+
key_states = [False, False, False, False]
56+
57+
# STEMMA QT Rotary encoder setup
58+
rotary_seesaw = seesaw.Seesaw(i2c, addr=0x37) # default address is 0x36
59+
encoder = rotaryio.IncrementalEncoder(rotary_seesaw)
60+
last_encoder_pos = 0
61+
62+
# file system setup
63+
mp3s = []
64+
for filename in os.listdir('/sd'):
65+
if filename.lower().endswith('.mp3') and not filename.startswith('.'):
66+
mp3s.append("/sd/"+filename)
67+
68+
mp3s.sort() # sort alphanumerically for mixtape order, e.g., "1_King_of_Rock.mp3"
69+
for mp3 in mp3s:
70+
print(mp3)
71+
72+
track_number = 0
73+
mp3_filename = mp3s[track_number]
74+
mp3_bytes = os.stat(mp3_filename)[6] # size in bytes is position 6
75+
mp3_file = open(mp3_filename, "rb")
76+
mp3stream = audiomp3.MP3Decoder(mp3_file)
77+
78+
def tracktext(full_path_name, position):
79+
return full_path_name.split('_')[position].split('.')[0]
80+
81+
audio = audiobusio.I2SOut(bit_clock=board.D1, word_select=board.D10, data=board.D11)
82+
mixer = audiomixer.Mixer(voice_count=1, sample_rate=22050, channel_count=1,
83+
bits_per_sample=16, samples_signed=True)
84+
mixer.voice[0].level = 0.15
85+
audio.play(mixer)
86+
87+
# Colors
88+
blue_bright = 0x17afcf
89+
blue_mid = 0x0d6173
90+
blue_dark = 0x041f24
91+
92+
orange_bright = 0xda8c57
93+
orange_mid = 0xa46032
94+
orange_dark = 0x472a16
95+
96+
# display
97+
main_display_group = displayio.Group() # everything goes in main group
98+
display.show(main_display_group) # show main group (clears screen, too)
99+
100+
# background bitmap w OnDiskBitmap
101+
tape_bitmap = displayio.OnDiskBitmap(open("mp3_tape.bmp", "rb"))
102+
tape_tilegrid = displayio.TileGrid(tape_bitmap, pixel_shader=tape_bitmap.pixel_shader)
103+
main_display_group.append(tape_tilegrid)
104+
105+
106+
# song name label
107+
song_name_text_group = displayio.Group(scale=3, x=90, y=44) # text label goes in this Group
108+
song_name_text = tracktext(mp3_filename, 2)
109+
song_name_label = label.Label(terminalio.FONT, text=song_name_text, color=orange_bright)
110+
song_name_text_group.append(song_name_label) # add the label to the group
111+
main_display_group.append(song_name_text_group) # add to the parent group
112+
113+
# artist name label
114+
artist_name_text_group = displayio.Group(scale=2, x=92, y=186)
115+
artist_name_text = tracktext(mp3_filename, 1)
116+
artist_name_label = label.Label(terminalio.FONT, text=artist_name_text, color=orange_bright)
117+
artist_name_text_group.append(artist_name_label)
118+
main_display_group.append(artist_name_text_group)
119+
120+
# song progress bar
121+
progress_bar = HorizontalProgressBar(
122+
(72, 144),
123+
(174, 12),
124+
bar_color=blue_bright,
125+
outline_color=blue_mid,
126+
fill_color=blue_dark,
127+
)
128+
main_display_group.append(progress_bar)
129+
130+
# volume level bar
131+
volume_bar = VerticalProgressBar(
132+
(304, 40),
133+
(8, 170),
134+
bar_color=orange_bright,
135+
outline_color=orange_mid,
136+
fill_color=orange_dark,
137+
)
138+
main_display_group.append(volume_bar)
139+
volume_bar.value = mixer.voice[0].level * 100
140+
141+
def change_track(tracknum):
142+
# pylint: disable=global-statement
143+
global mp3_filename
144+
mp3_filename = mp3s[tracknum]
145+
song_name_fc = tracktext(mp3_filename, 2)
146+
artist_name_fc = tracktext(mp3_filename, 1)
147+
mp3_file_fc = open(mp3_filename, "rb")
148+
mp3stream_fc = audiomp3.MP3Decoder(mp3_file_fc)
149+
mp3_bytes_fc = os.stat(mp3_filename)[6] # size in bytes is position 6
150+
# print(mp3_bytes)
151+
# print("Now playing: '{}'".format(mp3_filename))
152+
return (mp3_file_fc, mp3stream_fc, song_name_fc, artist_name_fc, mp3_bytes_fc)
153+
154+
print("Walkmp3rson")
155+
play_state = False # so we know if we're auto advancing when mixer finishes a song
156+
last_debug_time = 0 # for timing track position
157+
reels_anim_frame = 0
158+
last_percent_done = 0.01
159+
160+
while True:
161+
encoder_pos = -encoder.position
162+
if encoder_pos != last_encoder_pos:
163+
encoder_delta = encoder_pos - last_encoder_pos
164+
volume_adjust = min(max((mixer.voice[0].level + (encoder_delta*0.005)), 0.0), 1.0)
165+
mixer.voice[0].level = volume_adjust
166+
167+
last_encoder_pos = encoder_pos
168+
volume_bar.value = mixer.voice[0].level * 100
169+
170+
if play_state is True: # if not stopped, auto play next mp3_filename
171+
if time.monotonic() - last_debug_time > 0.2: # so we can check track progress
172+
last_debug_time = time.monotonic()
173+
bytes_played = mp3_file.tell()
174+
percent_done = (bytes_played / mp3_bytes)
175+
progress_bar.value = min(max(percent_done * 100, 0), 100)
176+
177+
if not mixer.playing:
178+
print("next song")
179+
track_number = ((track_number + 1) % len(mp3s))
180+
mp3_file, mp3stream, song_name, artist_name, mp3_bytes = change_track(track_number)
181+
song_name_label.text = song_name
182+
artist_name_label.text = artist_name
183+
mixer.voice[0].play(mp3stream, loop=False)
184+
185+
# Use the NeoKeys as transport controls
186+
for k in range(len(keys)):
187+
neokey, key_number, color = keys[k]
188+
if neokey[key_number] and not key_states[key_number]:
189+
key_states[key_number] = True
190+
neokey.pixels[key_number] = color
191+
192+
if key_number == 0: # previous track
193+
track_number = ((track_number - 1) % len(mp3s) )
194+
mp3_file, mp3stream, song_name, artist_name, mp3_bytes = change_track(track_number)
195+
song_name_label.text = song_name
196+
artist_name_label.text = artist_name
197+
audio.resume()
198+
mixer.voice[0].play(mp3stream, loop=False)
199+
play_state = True
200+
201+
if key_number == 1: # Play/pause
202+
if play_state:
203+
audio.pause()
204+
play_state = False
205+
else:
206+
audio.resume()
207+
play_state = True
208+
209+
if key_number == 2: # Play track from beginning
210+
audio.resume()
211+
mixer.voice[0].play(mp3stream, loop=False)
212+
song_name_label.text = tracktext(mp3_filename, 2)
213+
artist_name_label.text = tracktext(mp3_filename, 1)
214+
play_state = True
215+
216+
if key_number == 3: # next track
217+
track_number = ((track_number + 1) % len(mp3s))
218+
mp3_file, mp3stream, song_name, artist_name, mp3_bytes = change_track(track_number)
219+
song_name_label.text = song_name
220+
artist_name_label.text = artist_name
221+
audio.resume()
222+
mixer.voice[0].play(mp3stream, loop=False)
223+
play_state = True
224+
225+
if not neokey[key_number] and key_states[key_number]:
226+
neokey.pixels[key_number] = amber
227+
key_states[key_number] = False

Walkmp3rson/mp3_tape.bmp

75.1 KB
Binary file not shown.

0 commit comments

Comments
 (0)