test with new gui code
This commit is contained in:
569
app.py
569
app.py
@@ -1,451 +1,164 @@
|
||||
import pyray as pr
|
||||
import math
|
||||
from ctypes import c_float
|
||||
from gapless_player import GaplessPlayer, Song, song_data_to_Song
|
||||
from scrolling_text import ScrollingText
|
||||
import numpy as np
|
||||
import threading
|
||||
# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
# Copyright (c) 2017 Adafruit Industries
|
||||
# Author: James DeVito
|
||||
# Ported to RGB Display by Melissa LeBlanc-Williams
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
# This example is for use on (Linux) computers that are using CPython with
|
||||
# Adafruit Blinka to support CircuitPython libraries. CircuitPython does
|
||||
# not support PIL/pillow (python imaging library)!
|
||||
"""
|
||||
This example is for use on (Linux) computers that are using CPython with
|
||||
Adafruit Blinka to support CircuitPython libraries. CircuitPython does
|
||||
not support PIL/pillow (python imaging library)!
|
||||
"""
|
||||
|
||||
import random
|
||||
import time
|
||||
import os
|
||||
from jelly import server, client
|
||||
from colorsys import hsv_to_rgb
|
||||
|
||||
# # --- Configuration Constants ---
|
||||
# INITIAL_SCREEN_WIDTH = 240
|
||||
# INITIAL_SCREEN_HEIGHT = 240
|
||||
# TARGET_FPS =60
|
||||
import board
|
||||
from digitalio import DigitalInOut, Direction
|
||||
from PIL import Image, ImageDraw, ImageFont
|
||||
|
||||
# # --- State Variables ---
|
||||
# state = {
|
||||
# "screen_width": INITIAL_SCREEN_WIDTH,
|
||||
# "screen_height": INITIAL_SCREEN_HEIGHT,
|
||||
# }
|
||||
from adafruit_rgb_display import st7789
|
||||
import old_app
|
||||
|
||||
# # --- Utility Functions ---
|
||||
# Create the display
|
||||
cs_pin = DigitalInOut(board.CE0)
|
||||
dc_pin = DigitalInOut(board.D25)
|
||||
reset_pin = DigitalInOut(board.D24)
|
||||
BAUDRATE = 24000000
|
||||
|
||||
|
||||
# def format_time_mm_ss(seconds):
|
||||
# """Converts a time in seconds to an 'MM:SS' string format."""
|
||||
# seconds = int(seconds)
|
||||
# minutes = seconds // 60
|
||||
# seconds_remainder = seconds % 60
|
||||
# return f"{minutes:02d}:{seconds_remainder:02d}"
|
||||
|
||||
|
||||
# def get_progress_bar_rect(screen_width, screen_height):
|
||||
# width = screen_width
|
||||
# height = screen_height*0.021
|
||||
# x = (screen_width - width) / 2
|
||||
# y = screen_height - height
|
||||
# return pr.Rectangle(x, y, width, height)
|
||||
|
||||
|
||||
# def draw_progress_bar(rect, current_time, total_time):
|
||||
# if total_time > 0:
|
||||
# progress_ratio = current_time / total_time
|
||||
# else:
|
||||
# progress_ratio = 0.0
|
||||
|
||||
# pr.draw_rectangle_rec(rect, pr.Color(100, 100, 100, 255))
|
||||
# progress_width = rect.width * progress_ratio
|
||||
# pr.draw_rectangle(
|
||||
# int(rect.x),
|
||||
# int(rect.y)+1,
|
||||
# int(progress_width),
|
||||
# int(rect.height),
|
||||
# pr.Color(200, 50, 50, 255),
|
||||
# )
|
||||
# # pr.draw_rectangle_lines_ex(rect, 2, pr.Color(50, 50, 50, 255))
|
||||
|
||||
# time_text = f"{format_time_mm_ss(current_time)} / {format_time_mm_ss(total_time)}"
|
||||
# text_width = pr.measure_text(time_text, int(rect.height * 0.7))
|
||||
# # pr.draw_text(
|
||||
# # time_text,
|
||||
# # int(rect.x + rect.width / 2 - text_width / 2),
|
||||
# # int(rect.y + rect.height * 0.15),
|
||||
# # int(rect.height * 0.7),
|
||||
# # pr.WHITE,
|
||||
# # )
|
||||
|
||||
# pr.set_config_flags(pr.ConfigFlags.FLAG_WINDOW_RESIZABLE)
|
||||
# # pr.set_config_flags(pr.FLAG_MSAA_4X_HINT)
|
||||
# #pr.set_config_flags(pr.FLAG_FULLSCREEN_MODE)
|
||||
# pr.init_window(state["screen_width"], state["screen_height"], "UgPod")
|
||||
# pr.set_target_fps(TARGET_FPS)
|
||||
|
||||
player = GaplessPlayer()
|
||||
|
||||
print("add queue")
|
||||
|
||||
player.add_to_queue(
|
||||
Song(
|
||||
"bruhh",
|
||||
"music/pink floyd/dark side of the moon/06 Money.flac",
|
||||
"Money",
|
||||
1,
|
||||
"The Dark Side Of The Moon",
|
||||
"",
|
||||
"Pink Floyd",
|
||||
)
|
||||
spi = board.SPI()
|
||||
disp = st7789.ST7789(
|
||||
spi,
|
||||
height=240,
|
||||
y_offset=80,
|
||||
rotation=180,
|
||||
cs=cs_pin,
|
||||
dc=dc_pin,
|
||||
rst=reset_pin,
|
||||
baudrate=BAUDRATE,
|
||||
)
|
||||
|
||||
player.add_to_queue(
|
||||
Song(
|
||||
"bruhh",
|
||||
"music/pink floyd/dark side of the moon/07 Us and Them.flac",
|
||||
"Us and Them",
|
||||
1,
|
||||
"The Dark Side Of The Moon",
|
||||
"",
|
||||
"Pink Floyd",
|
||||
)
|
||||
)
|
||||
# Input pins:
|
||||
button_A = DigitalInOut(board.D5)
|
||||
button_A.direction = Direction.INPUT
|
||||
|
||||
# albums = client.jellyfin.user_items(
|
||||
# params={
|
||||
# "IncludeItemTypes": "MusicAlbum",
|
||||
# "SearchTerm": "Dawn FM", # album name
|
||||
# "Recursive": True,
|
||||
# },
|
||||
# )
|
||||
button_B = DigitalInOut(board.D6)
|
||||
button_B.direction = Direction.INPUT
|
||||
|
||||
# album = albums["Items"][0] # pick the album you want
|
||||
# album_id = album["Id"]
|
||||
button_L = DigitalInOut(board.D27)
|
||||
button_L.direction = Direction.INPUT
|
||||
|
||||
button_R = DigitalInOut(board.D23)
|
||||
button_R.direction = Direction.INPUT
|
||||
|
||||
# tracks = client.jellyfin.user_items(
|
||||
# params={
|
||||
# "ParentId": album_id,
|
||||
# "IncludeItemTypes": "Audio",
|
||||
# "SortBy": "IndexNumber",
|
||||
# "SortOrder": "Ascending",
|
||||
# },
|
||||
# )
|
||||
button_U = DigitalInOut(board.D17)
|
||||
button_U.direction = Direction.INPUT
|
||||
|
||||
# for track in tracks["Items"]:
|
||||
# player.add_to_queue(
|
||||
# song_data_to_Song(
|
||||
# track, server
|
||||
# )
|
||||
# )
|
||||
button_D = DigitalInOut(board.D22)
|
||||
button_D.direction = Direction.INPUT
|
||||
|
||||
# print("add queue done")
|
||||
# player.load_state("data/player.json")
|
||||
# close_event = threading.Event()
|
||||
# def save_state_loop():
|
||||
# while not close_event.wait(10):
|
||||
# player.save_state("data/player.lock.json")
|
||||
# os.rename("data/player.lock.json", "data/player.json")
|
||||
# # save_state_thread = threading.Thread(target=save_state_loop)
|
||||
# # save_state_thread.start()
|
||||
button_C = DigitalInOut(board.D4)
|
||||
button_C.direction = Direction.INPUT
|
||||
|
||||
# current_path = None
|
||||
# texture = None
|
||||
# Turn on the Backlight
|
||||
backlight = DigitalInOut(board.D26)
|
||||
backlight.switch_to_output()
|
||||
backlight.value = True
|
||||
|
||||
# Create blank image for drawing.
|
||||
# Make sure to create image with mode 'RGB' for color.
|
||||
width = disp.width
|
||||
height = disp.height
|
||||
image = Image.new("RGB", (width, height))
|
||||
|
||||
# def load_texture(path):
|
||||
# global texture, current_path
|
||||
# Get drawing object to draw on image.
|
||||
draw = ImageDraw.Draw(image)
|
||||
|
||||
# if not path:
|
||||
# return
|
||||
# Clear display.
|
||||
draw.rectangle((0, 0, width, height), outline=0, fill=(255, 0, 0))
|
||||
disp.image(image)
|
||||
|
||||
# if path == current_path:
|
||||
# return
|
||||
# Get drawing object to draw on image.
|
||||
draw = ImageDraw.Draw(image)
|
||||
|
||||
# if texture is not None:
|
||||
# pr.unload_texture(texture)
|
||||
# Draw a black filled box to clear the image.
|
||||
draw.rectangle((0, 0, width, height), outline=0, fill=0)
|
||||
|
||||
# texture = pr.load_texture(path)
|
||||
# current_path = path
|
||||
udlr_fill = "#00FF00"
|
||||
udlr_outline = "#00FFFF"
|
||||
button_fill = "#FF00FF"
|
||||
button_outline = "#FFFFFF"
|
||||
|
||||
fnt = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", 30)
|
||||
|
||||
# def draw_play_pause_button(pos: pr.Vector2, size: pr.Vector2, is_playing: bool) -> bool:
|
||||
# clicked = False
|
||||
|
||||
# rect = pr.Rectangle(pos.x, pos.y, size.x, size.y)
|
||||
|
||||
# # Optional hover background
|
||||
# if pr.check_collision_point_rec(pr.get_mouse_position(), rect):
|
||||
# pr.draw_rectangle_rec(rect, pr.fade(pr.BLACK, 0.4))
|
||||
# if pr.is_mouse_button_pressed(pr.MOUSE_LEFT_BUTTON):
|
||||
# clicked = True
|
||||
# cx = pos.x + size.x / 2
|
||||
# cy = pos.y + size.y / 2
|
||||
|
||||
# icon_padding = size.x * 0.25
|
||||
# icon_size = size.x - icon_padding * 2
|
||||
|
||||
# if is_playing:
|
||||
# # PAUSE (two bars centered, same visual weight as play)
|
||||
# bar_width = icon_size * 0.25
|
||||
# bar_height = icon_size
|
||||
|
||||
# left_x = cx - bar_width - bar_width * 0.4
|
||||
# right_x = cx + bar_width * 0.4
|
||||
# top_y = 1+cy - bar_height / 2
|
||||
|
||||
# pr.draw_rectangle(
|
||||
# int(left_x),
|
||||
# int(top_y),
|
||||
# int(bar_width),
|
||||
# int(bar_height),
|
||||
# pr.WHITE,
|
||||
# )
|
||||
# pr.draw_rectangle(
|
||||
# int(right_x),
|
||||
# int(top_y),
|
||||
# int(bar_width),
|
||||
# int(bar_height),
|
||||
# pr.WHITE,
|
||||
# )
|
||||
# else:
|
||||
# # PLAY (centered triangle)
|
||||
# p1 = pr.Vector2(cx - icon_size / 2, cy - icon_size / 2)
|
||||
# p2 = pr.Vector2(cx - icon_size / 2, cy + icon_size / 2)
|
||||
# p3 = pr.Vector2(cx + icon_size / 2, cy)
|
||||
|
||||
# pr.draw_triangle(p1, p2, p3, pr.WHITE)
|
||||
|
||||
# return clicked
|
||||
|
||||
|
||||
# title = ScrollingText(
|
||||
# "",
|
||||
# 15
|
||||
# )
|
||||
|
||||
# # --- Main Game Loop ---
|
||||
# while not pr.window_should_close():
|
||||
# # 1. Update
|
||||
# current_width = pr.get_screen_width()
|
||||
# current_height = pr.get_screen_height()
|
||||
|
||||
# if pr.is_key_pressed(pr.KEY_F11):
|
||||
# pr.toggle_fullscreen()
|
||||
# if pr.is_key_pressed(pr.KeyboardKey.KEY_SPACE):
|
||||
# if player.playing:
|
||||
# player.pause()
|
||||
# else:
|
||||
# player.play()
|
||||
# if pr.is_key_pressed(pr.KeyboardKey.KEY_LEFT):
|
||||
# player.seek(player.position - 5)
|
||||
# if pr.is_key_pressed(pr.KeyboardKey.KEY_RIGHT):
|
||||
# player.seek(player.position + 5)
|
||||
|
||||
# pr.begin_drawing()
|
||||
# pr.clear_background(pr.Color(40, 40, 40, 255))
|
||||
# dt = pr.get_frame_time()
|
||||
|
||||
# progress_rect = get_progress_bar_rect(current_width, current_height)
|
||||
|
||||
# # pr.draw_text(
|
||||
# # "UgPod",
|
||||
# # int(current_width * 0.05),
|
||||
# # int(current_height * 0.05),
|
||||
# # int(current_height * 0.05),
|
||||
# # pr.SKYBLUE,
|
||||
# # )
|
||||
|
||||
# current_song = player.get_current_song()
|
||||
|
||||
|
||||
# draw_progress_bar(
|
||||
# progress_rect,
|
||||
# player.position,
|
||||
# (current_song and current_song.duration) or 0.0,
|
||||
# )
|
||||
# if current_song:
|
||||
# load_texture(current_song.album_cover_path)
|
||||
# title_font_size = int(current_height*0.05)
|
||||
# album_cover_size = int(min(current_width, current_height*0.7))
|
||||
# title.speed = title_font_size*2.5
|
||||
# title_size = pr.Vector2(current_width-int(current_height * 0.01)*2, title_font_size)
|
||||
# title.update(dt,title_size)
|
||||
# title.set_text(f"{current_song.name} - {current_song.artist_name}", title_font_size)
|
||||
# title.draw(pr.Vector2(int(current_height * 0.01),int(current_height * 0.8)),title_size)
|
||||
# # pr.draw_text(
|
||||
# # ,
|
||||
# # ,
|
||||
# # int(current_height * 0.03),
|
||||
# # pr.WHITE,
|
||||
# # )
|
||||
# points = player.oscilloscope_data_points
|
||||
# if texture is not None:
|
||||
# scale = min(album_cover_size / texture.width, album_cover_size / texture.height)
|
||||
|
||||
# dest_rect = pr.Rectangle(
|
||||
# current_width//2 - album_cover_size//2,
|
||||
# (current_height*0.8)//2 - album_cover_size//2,
|
||||
# texture.width * scale,
|
||||
# texture.height * scale,
|
||||
# )
|
||||
|
||||
# src_rect = pr.Rectangle(0, 0, texture.width, texture.height)
|
||||
|
||||
# pr.draw_texture_pro(
|
||||
# texture, src_rect, dest_rect, pr.Vector2(0, 0), 0.0, pr.WHITE
|
||||
# )
|
||||
# else:
|
||||
# clip = pr.Rectangle(int(current_width//2 - album_cover_size//2),
|
||||
# int((current_height*0.8)//2 - album_cover_size//2),
|
||||
# int(album_cover_size),
|
||||
# int(album_cover_size))
|
||||
# pr.begin_scissor_mode(
|
||||
# int(clip.x),
|
||||
# int(clip.y),
|
||||
# int(clip.width),
|
||||
# int(clip.height),
|
||||
# )
|
||||
# pr.draw_rectangle(
|
||||
# int(clip.x),
|
||||
# int(clip.y),
|
||||
# int(clip.width),
|
||||
# int(clip.height), pr.BLACK)
|
||||
|
||||
# # cx = current_width * 0.5+1
|
||||
# # cy = current_height * 0.4+1
|
||||
|
||||
# # MAX_LEN = album_cover_size * 0.25 # tune this
|
||||
# # MIN_ALPHA = 10
|
||||
# # MAX_ALPHA = 255
|
||||
|
||||
# # for i in range(len(points) - 1):
|
||||
# # x1 = cx + points[i][0] * album_cover_size * 0.5
|
||||
# # y1 = cy + -points[i][1] * album_cover_size * 0.5
|
||||
# # x2 = cx + points[i+1][0] * album_cover_size * 0.5
|
||||
# # y2 = cy + -points[i+1][1] * album_cover_size * 0.5
|
||||
|
||||
# # dx = x2 - x1
|
||||
# # dy = y2 - y1
|
||||
# # length = (dx * dx + dy * dy) ** 0.5
|
||||
|
||||
# # # 1.0 = short line, 0.0 = long line
|
||||
# # t = max(0.0, min(1.0, 1.0 - (length / MAX_LEN)))*math.pow(i/len(points), 2)
|
||||
|
||||
# # alpha = int(MIN_ALPHA + t * (MAX_ALPHA - MIN_ALPHA))
|
||||
|
||||
# # color = pr.Color(255, 255, 255, alpha)
|
||||
|
||||
# # pr.draw_line(int(x1), int(y1), int(x2), int(y2), color)
|
||||
# # draw background square
|
||||
# if len(points) >= 2:
|
||||
# samples = np.fromiter(
|
||||
# ((p[0] + p[1]) * 0.5 for p in points),
|
||||
# dtype=np.float32
|
||||
# )
|
||||
|
||||
# # Guard: FFT must have meaningful size
|
||||
# if samples.size > 128:
|
||||
|
||||
|
||||
# rect_x = int(current_width // 2 - album_cover_size // 2)
|
||||
# rect_y = int((current_height * 0.8) // 2 - album_cover_size // 2)
|
||||
|
||||
# # ---- FFT ----
|
||||
|
||||
# FFT_SIZE = min(samples.size, 2048)
|
||||
# window = np.hanning(FFT_SIZE)
|
||||
|
||||
# fft = np.fft.rfft(samples[:FFT_SIZE] * window)
|
||||
# magnitudes = np.abs(fft)
|
||||
|
||||
# # remove DC component (important for visuals)
|
||||
# magnitudes[0] = 0.0
|
||||
|
||||
# # ---- LOG BINNING ----
|
||||
|
||||
# num_bars = album_cover_size//10
|
||||
# num_bins = magnitudes.size
|
||||
|
||||
# # logarithmic bin edges (low end stretched)
|
||||
# log_min = 1
|
||||
# log_max = math.log10(num_bins)
|
||||
|
||||
# log_edges = np.logspace(
|
||||
# math.log10(log_min),
|
||||
# log_max,
|
||||
# num_bars + 1
|
||||
# ).astype(int)
|
||||
|
||||
# bar_values = np.zeros(num_bars, dtype=np.float32)
|
||||
|
||||
# for i in range(num_bars):
|
||||
# start = log_edges[i]
|
||||
# end = log_edges[i + 1]
|
||||
|
||||
# if end <= start:
|
||||
# continue
|
||||
|
||||
# bar_values[i] = np.mean(magnitudes[start:end])
|
||||
|
||||
# # ---- STATIC SCALING ----
|
||||
|
||||
# # Instead of normalizing to the max of the frame, we scale by the FFT size.
|
||||
# # For a Hanning windowed FFT, dividing by (FFT_SIZE / 4) maps
|
||||
# # maximum possible volume roughly to 1.0.
|
||||
# bar_values = bar_values / (FFT_SIZE / 4.0)
|
||||
|
||||
# # ---- DRAW ----
|
||||
|
||||
# def map_to_screen(val):
|
||||
# return rect_x + (math.log10(max(1, val)) / log_max) * album_cover_size
|
||||
|
||||
# spacing = 0
|
||||
|
||||
# for i in range(num_bars):
|
||||
# # 1. Calculate integer pixel boundaries first
|
||||
# # This ensures the right edge of one bar is exactly the left edge of the next
|
||||
# x_start_int = int(map_to_screen(log_edges[i]))
|
||||
# x_end_int = int(map_to_screen(log_edges[i+1]))
|
||||
|
||||
# # 2. Width is the difference between these fixed integer points
|
||||
# w = (x_end_int - x_start_int) - spacing
|
||||
|
||||
# value = bar_values[i]
|
||||
# h = int(min(1.0, value) * album_cover_size)
|
||||
|
||||
# # 3. Anchor to bottom
|
||||
# y = (rect_y + album_cover_size) - h
|
||||
|
||||
# alpha = min(1.0, ((value+1)**2)-1)
|
||||
# r = 255
|
||||
# g = 0
|
||||
# b = 0
|
||||
|
||||
# # Keep alpha at 255 (fully opaque)
|
||||
# color = pr.Color(r, g, b, int(255 * alpha))
|
||||
|
||||
# # 4. Draw the bar
|
||||
# # Use max(1, w) to ensure high-frequency bars don't disappear on small screens
|
||||
# pr.draw_rectangle(
|
||||
# x_start_int,
|
||||
# int(y),
|
||||
# max(1, int(w)),
|
||||
# h,
|
||||
# color
|
||||
# )
|
||||
# pr.end_scissor_mode()
|
||||
|
||||
# pos = pr.Vector2(current_width * 0.5 - current_height * 0.05, current_height * 0.9-progress_rect.height)
|
||||
# size = pr.Vector2(current_height * 0.1, current_height * 0.1)
|
||||
|
||||
# if draw_play_pause_button(pos, size, player.playing):
|
||||
# if player.playing:
|
||||
# player.pause()
|
||||
# else:
|
||||
# player.play()
|
||||
# pr.end_drawing()
|
||||
|
||||
# # Cleanup
|
||||
# if texture is not None:
|
||||
# pr.unload_texture(texture)
|
||||
|
||||
# pr.close_window()
|
||||
# close_event.set()
|
||||
# # save_state_thread.join()
|
||||
|
||||
|
||||
|
||||
|
||||
player.play()
|
||||
while True:
|
||||
time.sleep(1)
|
||||
up_fill = 0
|
||||
if not button_U.value: # up pressed
|
||||
up_fill = udlr_fill
|
||||
draw.polygon([(40, 40), (60, 4), (80, 40)], outline=udlr_outline, fill=up_fill) # Up
|
||||
|
||||
down_fill = 0
|
||||
if not button_D.value: # down pressed
|
||||
down_fill = udlr_fill
|
||||
draw.polygon([(60, 120), (80, 84), (40, 84)], outline=udlr_outline, fill=down_fill) # down
|
||||
|
||||
left_fill = 0
|
||||
if not button_L.value: # left pressed
|
||||
left_fill = udlr_fill
|
||||
draw.polygon([(0, 60), (36, 42), (36, 81)], outline=udlr_outline, fill=left_fill) # left
|
||||
|
||||
right_fill = 0
|
||||
if not button_R.value: # right pressed
|
||||
right_fill = udlr_fill
|
||||
draw.polygon([(120, 60), (84, 42), (84, 82)], outline=udlr_outline, fill=right_fill) # right
|
||||
|
||||
center_fill = 0
|
||||
if not button_C.value: # center pressed
|
||||
center_fill = button_fill
|
||||
draw.rectangle((40, 44, 80, 80), outline=button_outline, fill=center_fill) # center
|
||||
|
||||
A_fill = 0
|
||||
if not button_A.value: # left pressed
|
||||
A_fill = button_fill
|
||||
draw.ellipse((140, 80, 180, 120), outline=button_outline, fill=A_fill) # A button
|
||||
|
||||
B_fill = 0
|
||||
if not button_B.value: # left pressed
|
||||
B_fill = button_fill
|
||||
draw.ellipse((190, 40, 230, 80), outline=button_outline, fill=B_fill) # B button
|
||||
|
||||
# make a random color and print text
|
||||
rcolor = tuple(int(x * 255) for x in hsv_to_rgb(random.random(), 1, 1))
|
||||
draw.text((20, 150), "Hello World", font=fnt, fill=rcolor)
|
||||
rcolor = tuple(int(x * 255) for x in hsv_to_rgb(random.random(), 1, 1))
|
||||
draw.text((20, 180), "Hello World", font=fnt, fill=rcolor)
|
||||
rcolor = tuple(int(x * 255) for x in hsv_to_rgb(random.random(), 1, 1))
|
||||
draw.text((20, 210), "Hello World", font=fnt, fill=rcolor)
|
||||
|
||||
# Display the Image
|
||||
disp.image(image)
|
||||
|
||||
time.sleep(0.1)
|
||||
451
old_app.py
Normal file
451
old_app.py
Normal file
@@ -0,0 +1,451 @@
|
||||
import pyray as pr
|
||||
import math
|
||||
from ctypes import c_float
|
||||
from gapless_player import GaplessPlayer, Song, song_data_to_Song
|
||||
from scrolling_text import ScrollingText
|
||||
import numpy as np
|
||||
import threading
|
||||
import time
|
||||
import os
|
||||
from jelly import server, client
|
||||
|
||||
# # --- Configuration Constants ---
|
||||
# INITIAL_SCREEN_WIDTH = 240
|
||||
# INITIAL_SCREEN_HEIGHT = 240
|
||||
# TARGET_FPS =60
|
||||
|
||||
# # --- State Variables ---
|
||||
# state = {
|
||||
# "screen_width": INITIAL_SCREEN_WIDTH,
|
||||
# "screen_height": INITIAL_SCREEN_HEIGHT,
|
||||
# }
|
||||
|
||||
# # --- Utility Functions ---
|
||||
|
||||
|
||||
# def format_time_mm_ss(seconds):
|
||||
# """Converts a time in seconds to an 'MM:SS' string format."""
|
||||
# seconds = int(seconds)
|
||||
# minutes = seconds // 60
|
||||
# seconds_remainder = seconds % 60
|
||||
# return f"{minutes:02d}:{seconds_remainder:02d}"
|
||||
|
||||
|
||||
# def get_progress_bar_rect(screen_width, screen_height):
|
||||
# width = screen_width
|
||||
# height = screen_height*0.021
|
||||
# x = (screen_width - width) / 2
|
||||
# y = screen_height - height
|
||||
# return pr.Rectangle(x, y, width, height)
|
||||
|
||||
|
||||
# def draw_progress_bar(rect, current_time, total_time):
|
||||
# if total_time > 0:
|
||||
# progress_ratio = current_time / total_time
|
||||
# else:
|
||||
# progress_ratio = 0.0
|
||||
|
||||
# pr.draw_rectangle_rec(rect, pr.Color(100, 100, 100, 255))
|
||||
# progress_width = rect.width * progress_ratio
|
||||
# pr.draw_rectangle(
|
||||
# int(rect.x),
|
||||
# int(rect.y)+1,
|
||||
# int(progress_width),
|
||||
# int(rect.height),
|
||||
# pr.Color(200, 50, 50, 255),
|
||||
# )
|
||||
# # pr.draw_rectangle_lines_ex(rect, 2, pr.Color(50, 50, 50, 255))
|
||||
|
||||
# time_text = f"{format_time_mm_ss(current_time)} / {format_time_mm_ss(total_time)}"
|
||||
# text_width = pr.measure_text(time_text, int(rect.height * 0.7))
|
||||
# # pr.draw_text(
|
||||
# # time_text,
|
||||
# # int(rect.x + rect.width / 2 - text_width / 2),
|
||||
# # int(rect.y + rect.height * 0.15),
|
||||
# # int(rect.height * 0.7),
|
||||
# # pr.WHITE,
|
||||
# # )
|
||||
|
||||
# pr.set_config_flags(pr.ConfigFlags.FLAG_WINDOW_RESIZABLE)
|
||||
# # pr.set_config_flags(pr.FLAG_MSAA_4X_HINT)
|
||||
# #pr.set_config_flags(pr.FLAG_FULLSCREEN_MODE)
|
||||
# pr.init_window(state["screen_width"], state["screen_height"], "UgPod")
|
||||
# pr.set_target_fps(TARGET_FPS)
|
||||
|
||||
player = GaplessPlayer()
|
||||
|
||||
print("add queue")
|
||||
|
||||
player.add_to_queue(
|
||||
Song(
|
||||
"bruhh",
|
||||
"music/pink floyd/dark side of the moon/06 Money.flac",
|
||||
"Money",
|
||||
1,
|
||||
"The Dark Side Of The Moon",
|
||||
"",
|
||||
"Pink Floyd",
|
||||
)
|
||||
)
|
||||
|
||||
player.add_to_queue(
|
||||
Song(
|
||||
"bruhh",
|
||||
"music/pink floyd/dark side of the moon/07 Us and Them.flac",
|
||||
"Us and Them",
|
||||
1,
|
||||
"The Dark Side Of The Moon",
|
||||
"",
|
||||
"Pink Floyd",
|
||||
)
|
||||
)
|
||||
|
||||
# albums = client.jellyfin.user_items(
|
||||
# params={
|
||||
# "IncludeItemTypes": "MusicAlbum",
|
||||
# "SearchTerm": "Dawn FM", # album name
|
||||
# "Recursive": True,
|
||||
# },
|
||||
# )
|
||||
|
||||
# album = albums["Items"][0] # pick the album you want
|
||||
# album_id = album["Id"]
|
||||
|
||||
|
||||
# tracks = client.jellyfin.user_items(
|
||||
# params={
|
||||
# "ParentId": album_id,
|
||||
# "IncludeItemTypes": "Audio",
|
||||
# "SortBy": "IndexNumber",
|
||||
# "SortOrder": "Ascending",
|
||||
# },
|
||||
# )
|
||||
|
||||
# for track in tracks["Items"]:
|
||||
# player.add_to_queue(
|
||||
# song_data_to_Song(
|
||||
# track, server
|
||||
# )
|
||||
# )
|
||||
|
||||
# print("add queue done")
|
||||
# player.load_state("data/player.json")
|
||||
# close_event = threading.Event()
|
||||
# def save_state_loop():
|
||||
# while not close_event.wait(10):
|
||||
# player.save_state("data/player.lock.json")
|
||||
# os.rename("data/player.lock.json", "data/player.json")
|
||||
# # save_state_thread = threading.Thread(target=save_state_loop)
|
||||
# # save_state_thread.start()
|
||||
|
||||
# current_path = None
|
||||
# texture = None
|
||||
|
||||
|
||||
# def load_texture(path):
|
||||
# global texture, current_path
|
||||
|
||||
# if not path:
|
||||
# return
|
||||
|
||||
# if path == current_path:
|
||||
# return
|
||||
|
||||
# if texture is not None:
|
||||
# pr.unload_texture(texture)
|
||||
|
||||
# texture = pr.load_texture(path)
|
||||
# current_path = path
|
||||
|
||||
|
||||
# def draw_play_pause_button(pos: pr.Vector2, size: pr.Vector2, is_playing: bool) -> bool:
|
||||
# clicked = False
|
||||
|
||||
# rect = pr.Rectangle(pos.x, pos.y, size.x, size.y)
|
||||
|
||||
# # Optional hover background
|
||||
# if pr.check_collision_point_rec(pr.get_mouse_position(), rect):
|
||||
# pr.draw_rectangle_rec(rect, pr.fade(pr.BLACK, 0.4))
|
||||
# if pr.is_mouse_button_pressed(pr.MOUSE_LEFT_BUTTON):
|
||||
# clicked = True
|
||||
# cx = pos.x + size.x / 2
|
||||
# cy = pos.y + size.y / 2
|
||||
|
||||
# icon_padding = size.x * 0.25
|
||||
# icon_size = size.x - icon_padding * 2
|
||||
|
||||
# if is_playing:
|
||||
# # PAUSE (two bars centered, same visual weight as play)
|
||||
# bar_width = icon_size * 0.25
|
||||
# bar_height = icon_size
|
||||
|
||||
# left_x = cx - bar_width - bar_width * 0.4
|
||||
# right_x = cx + bar_width * 0.4
|
||||
# top_y = 1+cy - bar_height / 2
|
||||
|
||||
# pr.draw_rectangle(
|
||||
# int(left_x),
|
||||
# int(top_y),
|
||||
# int(bar_width),
|
||||
# int(bar_height),
|
||||
# pr.WHITE,
|
||||
# )
|
||||
# pr.draw_rectangle(
|
||||
# int(right_x),
|
||||
# int(top_y),
|
||||
# int(bar_width),
|
||||
# int(bar_height),
|
||||
# pr.WHITE,
|
||||
# )
|
||||
# else:
|
||||
# # PLAY (centered triangle)
|
||||
# p1 = pr.Vector2(cx - icon_size / 2, cy - icon_size / 2)
|
||||
# p2 = pr.Vector2(cx - icon_size / 2, cy + icon_size / 2)
|
||||
# p3 = pr.Vector2(cx + icon_size / 2, cy)
|
||||
|
||||
# pr.draw_triangle(p1, p2, p3, pr.WHITE)
|
||||
|
||||
# return clicked
|
||||
|
||||
|
||||
# title = ScrollingText(
|
||||
# "",
|
||||
# 15
|
||||
# )
|
||||
|
||||
# # --- Main Game Loop ---
|
||||
# while not pr.window_should_close():
|
||||
# # 1. Update
|
||||
# current_width = pr.get_screen_width()
|
||||
# current_height = pr.get_screen_height()
|
||||
|
||||
# if pr.is_key_pressed(pr.KEY_F11):
|
||||
# pr.toggle_fullscreen()
|
||||
# if pr.is_key_pressed(pr.KeyboardKey.KEY_SPACE):
|
||||
# if player.playing:
|
||||
# player.pause()
|
||||
# else:
|
||||
# player.play()
|
||||
# if pr.is_key_pressed(pr.KeyboardKey.KEY_LEFT):
|
||||
# player.seek(player.position - 5)
|
||||
# if pr.is_key_pressed(pr.KeyboardKey.KEY_RIGHT):
|
||||
# player.seek(player.position + 5)
|
||||
|
||||
# pr.begin_drawing()
|
||||
# pr.clear_background(pr.Color(40, 40, 40, 255))
|
||||
# dt = pr.get_frame_time()
|
||||
|
||||
# progress_rect = get_progress_bar_rect(current_width, current_height)
|
||||
|
||||
# # pr.draw_text(
|
||||
# # "UgPod",
|
||||
# # int(current_width * 0.05),
|
||||
# # int(current_height * 0.05),
|
||||
# # int(current_height * 0.05),
|
||||
# # pr.SKYBLUE,
|
||||
# # )
|
||||
|
||||
# current_song = player.get_current_song()
|
||||
|
||||
|
||||
# draw_progress_bar(
|
||||
# progress_rect,
|
||||
# player.position,
|
||||
# (current_song and current_song.duration) or 0.0,
|
||||
# )
|
||||
# if current_song:
|
||||
# load_texture(current_song.album_cover_path)
|
||||
# title_font_size = int(current_height*0.05)
|
||||
# album_cover_size = int(min(current_width, current_height*0.7))
|
||||
# title.speed = title_font_size*2.5
|
||||
# title_size = pr.Vector2(current_width-int(current_height * 0.01)*2, title_font_size)
|
||||
# title.update(dt,title_size)
|
||||
# title.set_text(f"{current_song.name} - {current_song.artist_name}", title_font_size)
|
||||
# title.draw(pr.Vector2(int(current_height * 0.01),int(current_height * 0.8)),title_size)
|
||||
# # pr.draw_text(
|
||||
# # ,
|
||||
# # ,
|
||||
# # int(current_height * 0.03),
|
||||
# # pr.WHITE,
|
||||
# # )
|
||||
# points = player.oscilloscope_data_points
|
||||
# if texture is not None:
|
||||
# scale = min(album_cover_size / texture.width, album_cover_size / texture.height)
|
||||
|
||||
# dest_rect = pr.Rectangle(
|
||||
# current_width//2 - album_cover_size//2,
|
||||
# (current_height*0.8)//2 - album_cover_size//2,
|
||||
# texture.width * scale,
|
||||
# texture.height * scale,
|
||||
# )
|
||||
|
||||
# src_rect = pr.Rectangle(0, 0, texture.width, texture.height)
|
||||
|
||||
# pr.draw_texture_pro(
|
||||
# texture, src_rect, dest_rect, pr.Vector2(0, 0), 0.0, pr.WHITE
|
||||
# )
|
||||
# else:
|
||||
# clip = pr.Rectangle(int(current_width//2 - album_cover_size//2),
|
||||
# int((current_height*0.8)//2 - album_cover_size//2),
|
||||
# int(album_cover_size),
|
||||
# int(album_cover_size))
|
||||
# pr.begin_scissor_mode(
|
||||
# int(clip.x),
|
||||
# int(clip.y),
|
||||
# int(clip.width),
|
||||
# int(clip.height),
|
||||
# )
|
||||
# pr.draw_rectangle(
|
||||
# int(clip.x),
|
||||
# int(clip.y),
|
||||
# int(clip.width),
|
||||
# int(clip.height), pr.BLACK)
|
||||
|
||||
# # cx = current_width * 0.5+1
|
||||
# # cy = current_height * 0.4+1
|
||||
|
||||
# # MAX_LEN = album_cover_size * 0.25 # tune this
|
||||
# # MIN_ALPHA = 10
|
||||
# # MAX_ALPHA = 255
|
||||
|
||||
# # for i in range(len(points) - 1):
|
||||
# # x1 = cx + points[i][0] * album_cover_size * 0.5
|
||||
# # y1 = cy + -points[i][1] * album_cover_size * 0.5
|
||||
# # x2 = cx + points[i+1][0] * album_cover_size * 0.5
|
||||
# # y2 = cy + -points[i+1][1] * album_cover_size * 0.5
|
||||
|
||||
# # dx = x2 - x1
|
||||
# # dy = y2 - y1
|
||||
# # length = (dx * dx + dy * dy) ** 0.5
|
||||
|
||||
# # # 1.0 = short line, 0.0 = long line
|
||||
# # t = max(0.0, min(1.0, 1.0 - (length / MAX_LEN)))*math.pow(i/len(points), 2)
|
||||
|
||||
# # alpha = int(MIN_ALPHA + t * (MAX_ALPHA - MIN_ALPHA))
|
||||
|
||||
# # color = pr.Color(255, 255, 255, alpha)
|
||||
|
||||
# # pr.draw_line(int(x1), int(y1), int(x2), int(y2), color)
|
||||
# # draw background square
|
||||
# if len(points) >= 2:
|
||||
# samples = np.fromiter(
|
||||
# ((p[0] + p[1]) * 0.5 for p in points),
|
||||
# dtype=np.float32
|
||||
# )
|
||||
|
||||
# # Guard: FFT must have meaningful size
|
||||
# if samples.size > 128:
|
||||
|
||||
|
||||
# rect_x = int(current_width // 2 - album_cover_size // 2)
|
||||
# rect_y = int((current_height * 0.8) // 2 - album_cover_size // 2)
|
||||
|
||||
# # ---- FFT ----
|
||||
|
||||
# FFT_SIZE = min(samples.size, 2048)
|
||||
# window = np.hanning(FFT_SIZE)
|
||||
|
||||
# fft = np.fft.rfft(samples[:FFT_SIZE] * window)
|
||||
# magnitudes = np.abs(fft)
|
||||
|
||||
# # remove DC component (important for visuals)
|
||||
# magnitudes[0] = 0.0
|
||||
|
||||
# # ---- LOG BINNING ----
|
||||
|
||||
# num_bars = album_cover_size//10
|
||||
# num_bins = magnitudes.size
|
||||
|
||||
# # logarithmic bin edges (low end stretched)
|
||||
# log_min = 1
|
||||
# log_max = math.log10(num_bins)
|
||||
|
||||
# log_edges = np.logspace(
|
||||
# math.log10(log_min),
|
||||
# log_max,
|
||||
# num_bars + 1
|
||||
# ).astype(int)
|
||||
|
||||
# bar_values = np.zeros(num_bars, dtype=np.float32)
|
||||
|
||||
# for i in range(num_bars):
|
||||
# start = log_edges[i]
|
||||
# end = log_edges[i + 1]
|
||||
|
||||
# if end <= start:
|
||||
# continue
|
||||
|
||||
# bar_values[i] = np.mean(magnitudes[start:end])
|
||||
|
||||
# # ---- STATIC SCALING ----
|
||||
|
||||
# # Instead of normalizing to the max of the frame, we scale by the FFT size.
|
||||
# # For a Hanning windowed FFT, dividing by (FFT_SIZE / 4) maps
|
||||
# # maximum possible volume roughly to 1.0.
|
||||
# bar_values = bar_values / (FFT_SIZE / 4.0)
|
||||
|
||||
# # ---- DRAW ----
|
||||
|
||||
# def map_to_screen(val):
|
||||
# return rect_x + (math.log10(max(1, val)) / log_max) * album_cover_size
|
||||
|
||||
# spacing = 0
|
||||
|
||||
# for i in range(num_bars):
|
||||
# # 1. Calculate integer pixel boundaries first
|
||||
# # This ensures the right edge of one bar is exactly the left edge of the next
|
||||
# x_start_int = int(map_to_screen(log_edges[i]))
|
||||
# x_end_int = int(map_to_screen(log_edges[i+1]))
|
||||
|
||||
# # 2. Width is the difference between these fixed integer points
|
||||
# w = (x_end_int - x_start_int) - spacing
|
||||
|
||||
# value = bar_values[i]
|
||||
# h = int(min(1.0, value) * album_cover_size)
|
||||
|
||||
# # 3. Anchor to bottom
|
||||
# y = (rect_y + album_cover_size) - h
|
||||
|
||||
# alpha = min(1.0, ((value+1)**2)-1)
|
||||
# r = 255
|
||||
# g = 0
|
||||
# b = 0
|
||||
|
||||
# # Keep alpha at 255 (fully opaque)
|
||||
# color = pr.Color(r, g, b, int(255 * alpha))
|
||||
|
||||
# # 4. Draw the bar
|
||||
# # Use max(1, w) to ensure high-frequency bars don't disappear on small screens
|
||||
# pr.draw_rectangle(
|
||||
# x_start_int,
|
||||
# int(y),
|
||||
# max(1, int(w)),
|
||||
# h,
|
||||
# color
|
||||
# )
|
||||
# pr.end_scissor_mode()
|
||||
|
||||
# pos = pr.Vector2(current_width * 0.5 - current_height * 0.05, current_height * 0.9-progress_rect.height)
|
||||
# size = pr.Vector2(current_height * 0.1, current_height * 0.1)
|
||||
|
||||
# if draw_play_pause_button(pos, size, player.playing):
|
||||
# if player.playing:
|
||||
# player.pause()
|
||||
# else:
|
||||
# player.play()
|
||||
# pr.end_drawing()
|
||||
|
||||
# # Cleanup
|
||||
# if texture is not None:
|
||||
# pr.unload_texture(texture)
|
||||
|
||||
# pr.close_window()
|
||||
# close_event.set()
|
||||
# # save_state_thread.join()
|
||||
|
||||
|
||||
|
||||
|
||||
player.play()
|
||||
# while True:
|
||||
# time.sleep(1)
|
||||
Reference in New Issue
Block a user