Skip to content

Commit 082f9c0

Browse files
committed
adding pyportal winamp player and skin converter
1 parent 83398ee commit 082f9c0

7 files changed

Lines changed: 654 additions & 0 deletions

File tree

PyPortal_Winamp_Player/PyPortal_Winamp_Skin_Converter/.circuitpython-skip

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