From cca980716f52821b4f85796d1ab0e656577bff88 Mon Sep 17 00:00:00 2001 From: Yonokid <37304577+Yonokid@users.noreply.github.com> Date: Tue, 13 May 2025 16:23:39 -0400 Subject: [PATCH] add better WASAPI --- PyTaiko.py | 2 +- libs/audio.py | 37 +++++++---------------- scenes/game.py | 68 ++++++++++++++++++++++++++++++------------- scenes/song_select.py | 2 +- 4 files changed, 59 insertions(+), 50 deletions(-) diff --git a/PyTaiko.py b/PyTaiko.py index 73fd5cc..163a9d8 100644 --- a/PyTaiko.py +++ b/PyTaiko.py @@ -101,8 +101,8 @@ def main(): ray.toggle_fullscreen() next_screen = screen.update() - screen.draw() ray.clear_background(ray.BLACK) + screen.draw() if next_screen is not None: current_screen = next_screen diff --git a/libs/audio.py b/libs/audio.py index 8f8d328..7410145 100644 --- a/libs/audio.py +++ b/libs/audio.py @@ -5,7 +5,6 @@ import time import wave from threading import Lock, Thread -import pyray as ray from numpy import ( abs as np_abs, ) @@ -413,10 +412,10 @@ class Music: except Exception: raise Exception("unable to close music stream") -class ASIOEngine: - def __init__(self): +class AudioEngine: + def __init__(self, type: str): self.target_sample_rate = 48000 - self.buffer_size = get_config()["audio"]["asio_buffer"] + self.buffer_size = get_config()["audio"]["buffer_size"] self.sounds = {} self.music_streams = {} self.stream = None @@ -431,6 +430,7 @@ class ASIOEngine: # Threading for music stream updates self.update_thread = None self.update_thread_running = False + self.type = type def _initialize_asio(self): """Set up ASIO device""" @@ -438,11 +438,12 @@ class ASIOEngine: hostapis = sd.query_hostapis() asio_api_index = -1 for i, api in enumerate(hostapis): - if isinstance(api, dict) and 'name' in api and api['name'] == 'ASIO': + if isinstance(api, dict) and 'name' in api and api['name'] == self.type: asio_api_index = i break - if asio_api_index is not None: + print(hostapis) + if isinstance(hostapis, tuple): asio_api = hostapis[asio_api_index] if isinstance(asio_api, dict) and 'default_output_device' in asio_api: default_asio_device = asio_api['default_output_device'] @@ -461,9 +462,7 @@ class ASIOEngine: self.output_channels = 2 return True else: - print("No default ASIO device found, using system default.") - else: - print("ASIO API not found, using system default device.") + print("ASIO API not found, using system default device.") # If we get here, use default system device self.device_id = None @@ -740,21 +739,5 @@ class ASIOEngine: return self.music_streams[music].get_time_played() raise ValueError(f"Music stream {music} not initialized") -class AudioEngineWrapper: - def __init__(self, host_api): - self.host_api = host_api - if host_api == 'WASAPI': - self._module = ray - elif host_api == 'ASIO': - self._module = ASIOEngine() - else: - raise Exception("Invalid host API passed to wrapper") - def __getattr__(self, name): - try: - return getattr(self._module, name) - except AttributeError: - 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) +audio = AudioEngine(get_config()["audio"]["device_type"]) +audio.set_master_volume(0.75) diff --git a/scenes/game.py b/scenes/game.py index 1a63bd1..8c8a5d1 100644 --- a/scenes/game.py +++ b/scenes/game.py @@ -160,8 +160,9 @@ class GameScreen: self.on_screen_start() self.current_ms = get_current_ms() - self.start_ms if (self.current_ms >= self.tja.offset*1000 + self.start_delay) and not self.song_started: - if not audio.is_sound_playing(self.song_music): - audio.play_sound(self.song_music) + if self.song_music is not None: + if not audio.is_sound_playing(self.song_music): + audio.play_sound(self.song_music) if self.movie is not None: self.movie.start(get_current_ms()) self.song_started = True @@ -252,6 +253,7 @@ class Player: self.gauge_hit_effect: list[GaugeHitEffect] = [] self.autoplay_hit_side = 'L' + self.last_subdivision = -1 def get_result_score(self): return self.score, self.good_count, self.ok_count, self.bad_count, self.total_drumroll, self.max_combo @@ -475,30 +477,54 @@ class Player: self.check_note(game_screen, config["note_type"]) self.input_log[game_screen.current_ms] = (hit_type, key) - def autoplay_manager(self, game_screen): + def autoplay_manager(self, game_screen: GameScreen): if not get_config()["general"]["autoplay"]: return if len(self.play_notes) == 0: return note = self.play_notes[0] - if game_screen.current_ms >= note.hit_ms and note.type != 8: - hit_type = 'DON' - if note.type == 2 or note.type == 4: - hit_type = 'KAT' - self.lane_hit_effect = LaneHitEffect(hit_type) - if self.autoplay_hit_side == 'L': - self.autoplay_hit_side = 'R' - else: - self.autoplay_hit_side = 'L' - self.draw_drum_hit_list.append(DrumHitEffect(hit_type, self.autoplay_hit_side)) - sound = game_screen.sound_don if hit_type == "DON" else game_screen.sound_kat - audio.play_sound(sound) - type = note.type - if type == 6 or type == 9: - type = 3 - elif type == 5 or type == 7: - type = 1 - self.check_note(game_screen, type) + if self.is_drumroll or self.is_balloon: + subdivision_in_ms = game_screen.current_ms // ((60000 * 4 / game_screen.tja.bpm) / 24) + if subdivision_in_ms > self.last_subdivision: + self.last_subdivision = subdivision_in_ms + hit_type = 'DON' + self.lane_hit_effect = LaneHitEffect(hit_type) + if self.autoplay_hit_side == 'L': + self.autoplay_hit_side = 'R' + else: + self.autoplay_hit_side = 'L' + self.draw_drum_hit_list.append(DrumHitEffect(hit_type, self.autoplay_hit_side)) + audio.play_sound(game_screen.sound_don) + type = note.type + if type == 6 or type == 9: + type = 3 + elif type == 5 or type == 7: + type = 1 + self.check_note(game_screen, type) + else: + while game_screen.current_ms >= note.hit_ms and note.type <= 4: + hit_type = 'DON' + if note.type == 2 or note.type == 4: + hit_type = 'KAT' + self.lane_hit_effect = LaneHitEffect(hit_type) + if self.autoplay_hit_side == 'L': + self.autoplay_hit_side = 'R' + else: + self.autoplay_hit_side = 'L' + self.draw_drum_hit_list.append(DrumHitEffect(hit_type, self.autoplay_hit_side)) + sound = game_screen.sound_don if hit_type == "DON" else game_screen.sound_kat + audio.play_sound(sound) + type = note.type + if type == 6 or type == 9: + type = 3 + elif type == 5 or type == 7: + type = 1 + self.check_note(game_screen, type) + if len(self.play_notes) > 0: + note = self.play_notes[0] + print(note) + else: + break def update(self, game_screen: GameScreen): diff --git a/scenes/song_select.py b/scenes/song_select.py index 47e2650..867ef9d 100644 --- a/scenes/song_select.py +++ b/scenes/song_select.py @@ -1,6 +1,6 @@ import os -from pathlib import Path import sqlite3 +from pathlib import Path import pyray as ray