mirror of
https://github.com/Yonokid/PyTaiko.git
synced 2026-02-04 03:30:13 +01:00
upgrade to uv, many fxes
This commit is contained in:
@@ -29,7 +29,9 @@ class Animation:
|
||||
self.duration,
|
||||
self.params['total_distance'],
|
||||
self.params['start_position'],
|
||||
delay=self.params.get('delay', 0.0))
|
||||
delay=self.params.get('delay', 0.0),
|
||||
ease_in=self.params.get('ease_in', None),
|
||||
ease_out=self.params.get('ease_out', None))
|
||||
elif self.type == 'texture_change':
|
||||
self.texture_change(current_ms,
|
||||
self.duration,
|
||||
@@ -50,25 +52,26 @@ class Animation:
|
||||
initial_size=self.params.get('final_size', 1.0),
|
||||
delay=self.params.get('delay', 0.0) + self.duration)
|
||||
|
||||
def _ease_out_progress(self, progress: float, ease: str | None) -> float:
|
||||
if ease == 'quadratic':
|
||||
return progress * (2 - progress)
|
||||
elif ease == 'cubic':
|
||||
return 1 - pow(1 - progress, 3)
|
||||
elif ease == 'exponential':
|
||||
return 1 - pow(2, -10 * progress)
|
||||
else:
|
||||
return progress
|
||||
def _ease_in_progress(self, progress: float, ease: str | None) -> float:
|
||||
if ease == 'quadratic':
|
||||
return progress * progress
|
||||
elif ease == 'cubic':
|
||||
return progress * progress * progress
|
||||
elif ease == 'exponential':
|
||||
return pow(2, 10 * (progress - 1))
|
||||
else:
|
||||
return progress
|
||||
|
||||
def fade(self, current_ms: float, duration: float, initial_opacity: float, final_opacity: float, delay: float, ease_in: str | None, ease_out: str | None) -> None:
|
||||
def _ease_out_progress(progress: float, ease: str | None) -> float:
|
||||
if ease == 'quadratic':
|
||||
return progress * (2 - progress)
|
||||
elif ease == 'cubic':
|
||||
return 1 - pow(1 - progress, 3)
|
||||
elif ease == 'exponential':
|
||||
return 1 - pow(2, -10 * progress)
|
||||
else:
|
||||
return progress
|
||||
def _ease_in_progress(progress: float, ease: str | None) -> float:
|
||||
if ease == 'quadratic':
|
||||
return progress * progress
|
||||
elif ease == 'cubic':
|
||||
return progress * progress * progress
|
||||
elif ease == 'exponential':
|
||||
return pow(2, 10 * (progress - 1))
|
||||
else:
|
||||
return progress
|
||||
elapsed_time = current_ms - self.start_ms
|
||||
if elapsed_time < delay:
|
||||
self.attribute = initial_opacity
|
||||
@@ -79,27 +82,32 @@ class Animation:
|
||||
self.is_finished = True
|
||||
|
||||
if ease_in is not None:
|
||||
progress = _ease_in_progress(elapsed_time / duration, ease_in)
|
||||
progress = self._ease_in_progress(elapsed_time / duration, ease_in)
|
||||
elif ease_out is not None:
|
||||
progress = _ease_out_progress(elapsed_time / duration, ease_out)
|
||||
progress = self._ease_out_progress(elapsed_time / duration, ease_out)
|
||||
else:
|
||||
progress = elapsed_time / duration
|
||||
|
||||
current_opacity = initial_opacity + (final_opacity - initial_opacity) * progress
|
||||
self.attribute = current_opacity
|
||||
def move(self, current_ms: float, duration: float, total_distance: float, start_position: float, delay: float) -> None:
|
||||
def move(self, current_ms: float, duration: float, total_distance: float, start_position: float, delay: float, ease_in: str | None, ease_out: str | None) -> None:
|
||||
elapsed_time = current_ms - self.start_ms
|
||||
if elapsed_time < delay:
|
||||
self.attribute = start_position
|
||||
|
||||
elapsed_time -= delay
|
||||
if elapsed_time <= duration:
|
||||
progress = elapsed_time / duration
|
||||
if ease_in is not None:
|
||||
progress = self._ease_in_progress(elapsed_time / duration, ease_in)
|
||||
elif ease_out is not None:
|
||||
progress = self._ease_out_progress(elapsed_time / duration, ease_out)
|
||||
else:
|
||||
progress = elapsed_time / duration
|
||||
self.attribute = start_position + (total_distance * progress)
|
||||
else:
|
||||
self.attribute = start_position + total_distance
|
||||
self.is_finished = True
|
||||
def texture_change(self, current_ms: float, duration: float, textures: list[tuple[float, float, float]]) -> None:
|
||||
def texture_change(self, current_ms: float, duration: float, textures: list[tuple[float, float, int]]) -> None:
|
||||
elapsed_time = current_ms - self.start_ms
|
||||
if elapsed_time <= duration:
|
||||
for start, end, index in textures:
|
||||
|
||||
@@ -741,3 +741,5 @@ class AudioEngineWrapper:
|
||||
raise AttributeError(f"'{type(self).__name__}' object has no attribute '{name}' and '{type(self._module).__name__}' has no attribute '{name}'")
|
||||
|
||||
audio = AudioEngineWrapper(get_config()["audio"]["device_type"])
|
||||
if get_config()["audio"]["device_type"] == 'ASIO':
|
||||
audio.set_master_volume(0.75)
|
||||
|
||||
@@ -371,6 +371,7 @@ class TJAParser:
|
||||
raise Exception("Balloon note found, but no count was specified")
|
||||
note = Balloon(note)
|
||||
note.count = int(balloon[balloon_index])
|
||||
balloon_index += 1
|
||||
self.current_ms += increment
|
||||
play_note_list.append(note)
|
||||
self.get_moji(play_note_list, ms_per_measure)
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import os
|
||||
import tempfile
|
||||
import time
|
||||
import tomllib
|
||||
import zipfile
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
import pyray as ray
|
||||
import tomllib
|
||||
|
||||
#TJA Format creator is unknown. I did not create the format, but I did write the parser though.
|
||||
|
||||
@@ -95,16 +95,33 @@ def get_config() -> dict[str, Any]:
|
||||
config_file = tomllib.load(f)
|
||||
return config_file
|
||||
|
||||
def draw_scaled_texture(texture, x: int, y: int, scale: float, color: ray.Color) -> None:
|
||||
width = texture.width
|
||||
height = texture.height
|
||||
src_rect = ray.Rectangle(0, 0, width, height)
|
||||
dst_rect = ray.Rectangle(x, y, width*scale, height*scale)
|
||||
ray.draw_texture_pro(texture, src_rect, dst_rect, ray.Vector2(0, 0), 0, color)
|
||||
|
||||
@dataclass
|
||||
class SessionData:
|
||||
selected_song: str = '' #Path
|
||||
selected_difficulty: int = 0
|
||||
song_title: str = ''
|
||||
result_score: int = 0
|
||||
result_good: int = 0
|
||||
result_ok: int = 0
|
||||
result_bad: int = 0
|
||||
result_max_combo: int = 0
|
||||
result_total_drumroll: int = 0
|
||||
result_gauge_length: int = 0
|
||||
|
||||
session_data = SessionData()
|
||||
|
||||
def reset_session():
|
||||
return SessionData()
|
||||
|
||||
@dataclass
|
||||
class GlobalData:
|
||||
videos_cleared = False
|
||||
selected_song: str = '' #Path
|
||||
selected_difficulty: int = -1
|
||||
song_title: str = ''
|
||||
result_good: int = -1
|
||||
result_ok: int = -1
|
||||
result_bad: int = -1
|
||||
result_score: int = -1
|
||||
songs_played: int = 0
|
||||
|
||||
global_data = GlobalData()
|
||||
@@ -168,3 +185,49 @@ class OutlinedText:
|
||||
|
||||
def unload(self):
|
||||
ray.unload_texture(self.texture)
|
||||
|
||||
'''
|
||||
class RenderTextureStack:
|
||||
def __init__(self):
|
||||
"""Initialize an empty stack for render textures."""
|
||||
self.texture_stack = []
|
||||
|
||||
def load_render_texture(self, width, height):
|
||||
"""Create and return a render texture with the specified dimensions."""
|
||||
return ray.load_render_texture(width, height)
|
||||
|
||||
def begin_texture_mode(self, target):
|
||||
"""Begin drawing to the render texture and add it to the stack."""
|
||||
ray.begin_texture_mode(target)
|
||||
self.texture_stack.append(target)
|
||||
return target
|
||||
|
||||
def end_texture_mode(self, pop_count=1):
|
||||
"""End the texture mode for the specified number of textures in the stack."""
|
||||
if not self.texture_stack:
|
||||
raise IndexError("Cannot end texture mode: texture stack is empty")
|
||||
|
||||
# Ensure pop_count is within valid range
|
||||
pop_count = min(pop_count, len(self.texture_stack))
|
||||
|
||||
# End the texture modes and pop from stack
|
||||
for _ in range(pop_count):
|
||||
ray.end_texture_mode()
|
||||
self.texture_stack.pop()
|
||||
|
||||
def get_texture(self, target):
|
||||
"""Get the texture from the render texture."""
|
||||
return ray.get_texture_default(target)
|
||||
|
||||
def draw_texture(self, texture, pos_x, pos_y, tint=ray.WHITE):
|
||||
"""Draw a texture at the specified position with the given tint."""
|
||||
ray.draw_texture(texture, pos_x, pos_y, tint)
|
||||
|
||||
def get_current_target(self):
|
||||
"""Get the current active render target from the stack."""
|
||||
if not self.texture_stack:
|
||||
return None
|
||||
return self.texture_stack[-1]
|
||||
|
||||
render_stack = RenderTextureStack()
|
||||
'''
|
||||
|
||||
@@ -9,63 +9,68 @@ class VideoPlayer:
|
||||
def __init__(self, path: str):
|
||||
self.video_path = path
|
||||
self.start_ms = None
|
||||
|
||||
self.current_frame = None
|
||||
self.last_frame = self.current_frame
|
||||
self.frame_index = 0
|
||||
self.frames = []
|
||||
self.cap = cv2.VideoCapture(self.video_path)
|
||||
self.fps = self.cap.get(cv2.CAP_PROP_FPS)
|
||||
|
||||
self.is_finished = [False, False]
|
||||
self.is_finished_list = [False, False, False] # Added third flag for frame conversion
|
||||
self.all_frames_converted = False
|
||||
audio_path = path[:-4] + '.ogg'
|
||||
self.audio = audio.load_music_stream(audio_path)
|
||||
|
||||
def _convert_frames_background(self):
|
||||
if not self.cap.isOpened():
|
||||
raise ValueError("Error: Could not open video file.")
|
||||
|
||||
total_frames = int(self.cap.get(cv2.CAP_PROP_FRAME_COUNT))
|
||||
if len(self.frames) == total_frames:
|
||||
return 0
|
||||
self.cap.set(cv2.CAP_PROP_POS_FRAMES, self.frame_index)
|
||||
|
||||
success, frame = self.cap.read()
|
||||
|
||||
timestamp = (self.frame_index / self.fps * 1000)
|
||||
frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
|
||||
|
||||
new_frame = ray.Image(frame_rgb.tobytes(), frame_rgb.shape[1], frame_rgb.shape[0], 1, ray.PixelFormat.PIXELFORMAT_UNCOMPRESSED_R8G8B8)
|
||||
|
||||
self.frames.append((timestamp, new_frame))
|
||||
self.frame_index += 1
|
||||
def is_finished(self) -> bool:
|
||||
return all(self.is_finished_list)
|
||||
|
||||
def _convert_frames(self):
|
||||
"""Legacy method that converts all frames at once"""
|
||||
if not self.cap.isOpened():
|
||||
raise ValueError("Error: Could not open video file.")
|
||||
|
||||
frame_count = 0
|
||||
success, frame = self.cap.read()
|
||||
|
||||
while success:
|
||||
timestamp = (frame_count / self.fps * 1000)
|
||||
frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
|
||||
|
||||
frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
|
||||
new_frame = ray.Image(frame_rgb.tobytes(), frame_rgb.shape[1], frame_rgb.shape[0], 1, ray.PixelFormat.PIXELFORMAT_UNCOMPRESSED_R8G8B8)
|
||||
|
||||
self.frames.append((timestamp, new_frame))
|
||||
|
||||
success, frame = self.cap.read()
|
||||
frame_count += 1
|
||||
|
||||
self.cap.release()
|
||||
print(f"Extracted {len(self.frames)} frames.")
|
||||
self.start_ms = get_current_ms()
|
||||
self.all_frames_converted = True
|
||||
self.is_finished_list[2] = True
|
||||
|
||||
def convert_frames_background(self):
|
||||
"""Converts a single frame each time it's called"""
|
||||
if self.all_frames_converted:
|
||||
return
|
||||
|
||||
if not self.cap.isOpened():
|
||||
self.cap = cv2.VideoCapture(self.video_path)
|
||||
if not self.cap.isOpened():
|
||||
raise ValueError("Error: Could not open video file.")
|
||||
|
||||
# Process one frame
|
||||
success, frame = self.cap.read()
|
||||
if success:
|
||||
timestamp = (len(self.frames) / self.fps * 1000)
|
||||
frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
|
||||
new_frame = ray.Image(frame_rgb.tobytes(), frame_rgb.shape[1], frame_rgb.shape[0], 1, ray.PixelFormat.PIXELFORMAT_UNCOMPRESSED_R8G8B8)
|
||||
self.frames.append((timestamp, new_frame))
|
||||
else:
|
||||
# No more frames to convert
|
||||
self.cap.release()
|
||||
print(f"Extracted {len(self.frames)} frames.")
|
||||
self.all_frames_converted = True
|
||||
self.is_finished_list[2] = True
|
||||
|
||||
def _check_for_start(self):
|
||||
if self.frames == []:
|
||||
self._convert_frames()
|
||||
if not audio.is_music_stream_playing(self.audio):
|
||||
# Start audio once we have at least one frame
|
||||
if len(self.frames) > 0 and not audio.is_music_stream_playing(self.audio):
|
||||
audio.play_music_stream(self.audio)
|
||||
|
||||
def _audio_manager(self):
|
||||
@@ -73,36 +78,42 @@ class VideoPlayer:
|
||||
time_played = audio.get_music_time_played(self.audio) / audio.get_music_time_length(self.audio)
|
||||
ending_lenience = 0.95
|
||||
if time_played > ending_lenience:
|
||||
self.is_finished[1] = True
|
||||
self.is_finished_list[1] = True
|
||||
|
||||
def update(self):
|
||||
self._check_for_start()
|
||||
self._audio_manager()
|
||||
|
||||
if self.frame_index == len(self.frames)-1:
|
||||
self.is_finished[0] = True
|
||||
# Check if we've reached the end of available frames
|
||||
if self.frame_index == len(self.frames) - 1 and self.all_frames_converted:
|
||||
self.is_finished_list[0] = True
|
||||
return
|
||||
|
||||
if self.start_ms is None:
|
||||
return
|
||||
|
||||
timestamp, frame = self.frames[self.frame_index][0], self.frames[self.frame_index][1]
|
||||
elapsed_time = get_current_ms() - self.start_ms
|
||||
if elapsed_time >= timestamp:
|
||||
self.current_frame = ray.load_texture_from_image(frame)
|
||||
if self.last_frame != self.current_frame and self.last_frame is not None:
|
||||
ray.unload_texture(self.last_frame)
|
||||
self.frame_index += 1
|
||||
self.last_frame = self.current_frame
|
||||
# Only proceed if we have frames to display
|
||||
if self.frame_index < len(self.frames):
|
||||
timestamp, frame = self.frames[self.frame_index][0], self.frames[self.frame_index][1]
|
||||
elapsed_time = get_current_ms() - self.start_ms
|
||||
|
||||
if elapsed_time >= timestamp:
|
||||
self.current_frame = ray.load_texture_from_image(frame)
|
||||
if self.last_frame != self.current_frame and self.last_frame is not None:
|
||||
ray.unload_texture(self.last_frame)
|
||||
self.frame_index += 1
|
||||
self.last_frame = self.current_frame
|
||||
|
||||
def draw(self):
|
||||
if self.current_frame is not None:
|
||||
ray.draw_texture(self.current_frame, 0, 0, ray.WHITE)
|
||||
|
||||
def __del__(self):
|
||||
def stop(self):
|
||||
if hasattr(self, 'current_frame') and self.current_frame:
|
||||
ray.unload_texture(self.current_frame)
|
||||
if hasattr(self, 'last_frame') and self.last_frame:
|
||||
ray.unload_texture(self.last_frame)
|
||||
if audio.is_music_stream_playing(self.audio):
|
||||
audio.stop_music_stream(self.audio)
|
||||
if hasattr(self, 'cap') and self.cap.isOpened():
|
||||
self.cap.release()
|
||||
|
||||
Reference in New Issue
Block a user