Skip to content

Commit d64043f

Browse files
authored
Merge pull request #2008 from FoamyGuy/winamp_player
adding pyportal winamp player and skin converter
2 parents 9171175 + 605cbeb commit d64043f

7 files changed

Lines changed: 673 additions & 0 deletions

File tree

PyPortal_Winamp_Player/PyPortal_Winamp_Skin_Converter/.circuitpython-skip

Whitespace-only changes.
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
# SPDX-FileCopyrightText: 2022 Tim C, written for Adafruit Industries
2+
#
3+
# SPDX-License-Identifier: Unlicense
4+
"""
5+
PyPortal winamp skin converter.
6+
"""
7+
import sys
8+
import json
9+
from PIL import Image, ImageDraw
10+
11+
# get input filename from terminal args
12+
if len(sys.argv) >= 2:
13+
input_filename = sys.argv[1]
14+
else:
15+
input_filename = "base.png"
16+
17+
# Opens a image in RGB mode
18+
im = Image.open(input_filename)
19+
20+
newsize = (240, 320)
21+
22+
find_text_color_dict = {}
23+
24+
# scan pixels to get color counts
25+
for i in range(8):
26+
if im.getpixel((113, 26 + i)) in find_text_color_dict.keys():
27+
find_text_color_dict[im.getpixel((113, 26 + i))] += 1
28+
else:
29+
find_text_color_dict[im.getpixel((113, 26 + i))] = 1
30+
# print(find_text_color_dict)
31+
lowest_pixel_count = None
32+
text_color = None
33+
highest_pixel_count = None
34+
35+
# scan pixel for backdrop color
36+
back_drop_color = im.getpixel((120, 29))
37+
for color in find_text_color_dict:
38+
if not highest_pixel_count:
39+
highest_pixel_count = find_text_color_dict[color]
40+
text_color = color
41+
elif highest_pixel_count < find_text_color_dict[color]:
42+
highest_pixel_count = find_text_color_dict[color]
43+
text_color = color
44+
45+
# print("backdrop: {}".format(back_drop_color))
46+
time_color = text_color
47+
config_data = {"text_color": text_color, "time_color": time_color}
48+
49+
find_text_color_dict = {}
50+
for i in range(21):
51+
if im.getpixel((65, 23 + i)) in find_text_color_dict.keys():
52+
find_text_color_dict[im.getpixel((65, 23 + i))] += 1
53+
else:
54+
find_text_color_dict[im.getpixel((65, 23 + i))] = 1
55+
56+
# rectangle cutout for for current track title
57+
cur_song_shape = ((112, 25), (265, 34))
58+
img_draw = ImageDraw.Draw(im)
59+
img_draw.rectangle(cur_song_shape, fill=back_drop_color)
60+
61+
# rectangle cutouts for clock display
62+
time_shape_size = (9, 13)
63+
time_shape_x_locs = (48, 60, 78, 90)
64+
65+
for x_loc in time_shape_x_locs:
66+
_cur_time_shape_loc = (x_loc, 26)
67+
_cur_time_shape = (
68+
_cur_time_shape_loc,
69+
(
70+
_cur_time_shape_loc[0] + time_shape_size[0],
71+
_cur_time_shape_loc[1] + time_shape_size[1],
72+
),
73+
)
74+
img_draw.rectangle(_cur_time_shape, fill=back_drop_color)
75+
76+
# rectangle cutout for playlist display
77+
playlist_shape_size = (244, 48)
78+
playlist_shape_loc = (12, 257)
79+
playlist_shape = (
80+
playlist_shape_loc,
81+
(
82+
playlist_shape_loc[0] + playlist_shape_size[0],
83+
playlist_shape_loc[1] + playlist_shape_size[1],
84+
),
85+
)
86+
87+
img_draw.rectangle(playlist_shape, fill=back_drop_color)
88+
89+
# write config json file
90+
f = open(input_filename.replace(".png", "_config.json"), "w")
91+
f.write(json.dumps(config_data))
92+
f.close()
93+
94+
# resize to fit pyportal
95+
im = im.resize(newsize)
96+
97+
# convert to indexed color
98+
im = im.convert(mode="P", palette=Image.WEB)
99+
# save output BMP file
100+
im.save(input_filename.replace(".png", "_240x320.bmp"))
76.1 KB
Binary file not shown.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"text_color": [0, 226, 0, 255], "time_color": [0, 226, 0, 255]}

PyPortal_Winamp_Player/code.py

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
# SPDX-FileCopyrightText: 2022 Tim C, written for Adafruit Industries
2+
#
3+
# SPDX-License-Identifier: Unlicense
4+
"""
5+
PyPortal winamp player
6+
"""
7+
import time
8+
import sys
9+
import storage
10+
import board
11+
import busio
12+
import digitalio
13+
import adafruit_touchscreen
14+
import adafruit_sdcard
15+
from winamp_helpers import WinampApplication
16+
17+
# which playlist to play
18+
PLAYLIST_FILE = "playlist.json"
19+
20+
# which skin background to use
21+
SKIN_IMAGE = "/base_240x320.bmp"
22+
23+
# skin configuration for color values
24+
SKIN_CONFIG_FILE = "base_config.json"
25+
26+
# must wait at least this long between touch events
27+
TOUCH_COOLDOWN = 0.5 # seconds
28+
29+
# display orientation. Must be 90 or 270.
30+
ORIENTATION = 90
31+
32+
if ORIENTATION == 270:
33+
# setup touch screen
34+
ts = adafruit_touchscreen.Touchscreen(
35+
board.TOUCH_YD,
36+
board.TOUCH_YU,
37+
board.TOUCH_XR,
38+
board.TOUCH_XL,
39+
calibration=((5200, 59000), (5800, 57000)),
40+
size=(240, 320),
41+
)
42+
elif ORIENTATION == 90:
43+
# setup touch screen
44+
ts = adafruit_touchscreen.Touchscreen(
45+
board.TOUCH_YU,
46+
board.TOUCH_YD,
47+
board.TOUCH_XL,
48+
board.TOUCH_XR,
49+
calibration=((5200, 59000), (5800, 57000)),
50+
size=(240, 320),
51+
)
52+
else:
53+
raise ValueError("ORIENTATION must be 90 or 270")
54+
55+
# Initializations for SDCard
56+
spi = busio.SPI(board.SCK, board.MOSI, board.MISO)
57+
cs = digitalio.DigitalInOut(board.SD_CS)
58+
sdcard = adafruit_sdcard.SDCard(spi, cs)
59+
vfs = storage.VfsFat(sdcard)
60+
storage.mount(vfs, "/sd")
61+
sys.path.append("/sd")
62+
63+
# debugging, print files that exist on SDCard
64+
# print(os.listdir("/sd"))
65+
66+
# get reference to the display
67+
display = board.DISPLAY
68+
69+
# set rotation
70+
display.rotation = ORIENTATION
71+
72+
# Initialize WinampApplication helper class
73+
winamp_application = WinampApplication(
74+
playlist_file=PLAYLIST_FILE,
75+
skin_image=SKIN_IMAGE,
76+
skin_config_file=SKIN_CONFIG_FILE,
77+
)
78+
79+
# Add the Group to the Display
80+
display.show(winamp_application)
81+
82+
# previous iteration touch events
83+
_previous_touch = None
84+
85+
# last time a touch occured
86+
_previous_touch_time = 0
87+
88+
# main loop
89+
while True:
90+
# update winamp application
91+
winamp_application.update()
92+
93+
# check for touch events
94+
p = ts.touch_point
95+
_now = time.monotonic()
96+
# if touch cooldown time has elapsed
97+
if _now >= _previous_touch_time + TOUCH_COOLDOWN:
98+
# if there is a touch
99+
if p and not _previous_touch:
100+
# store the time to compare with next iteration
101+
_previous_touch_time = _now
102+
# if touch is on bottom half
103+
if p[1] > 320 // 2:
104+
# if touch is on right half
105+
if p[0] >= 240 // 2:
106+
winamp_application.next_track()
107+
108+
# if touch is on left half
109+
else:
110+
winamp_application.previous_track()
111+
# if touch is on top half
112+
elif p[1] <= 240 // 2:
113+
# if currently playing song
114+
if winamp_application.CURRENT_STATE == winamp_application.STATE_PLAYING:
115+
print("pausing")
116+
winamp_application.pause()
117+
118+
# if song is paused
119+
else:
120+
print("resuming")
121+
winamp_application.resume()
122+
123+
# store previous touch event t compare with next iteration
124+
_previous_touch = p
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"playlist": {
3+
"files": [
4+
5+
]
6+
}
7+
}

0 commit comments

Comments
 (0)