- show currently highlighted and currently playing in audio file browser

- bgm volume can be defined in config and adjusted in audio panel
- bgm previews loop
This commit is contained in:
Frank DeMarco 2020-05-10 05:29:55 -04:00
parent cf8021c480
commit 0878ad6181
2 changed files with 114 additions and 39 deletions

View File

@ -12,10 +12,12 @@ class Audio(GameChild):
UP, DOWN = .1, -.1
BASE_VOLUME = 1.0
CONFIG_SEPARATOR = ","
def __init__(self, game):
GameChild.__init__(self, game)
# self.original_volumes = {}
self.current_bgm = None
self.volume = self.BASE_VOLUME
if self.check_command_line("-mute"):
self.volume = 0
@ -30,11 +32,7 @@ class Audio(GameChild):
if mute:
self.volume = 0
elif increment:
self.volume += increment
if self.volume > 1:
self.volume = 1.0
elif self.volume < 0:
self.volume = 0
self.volume = clamp(self.volume + increment, 0, 1.0)
else:
self.volume = volume
@ -58,7 +56,7 @@ class Audio(GameChild):
#
def load_sfx(self, sfx_location=None):
for name, sfx_definition in self.get_configuration("sfx").items():
sfx_definition_members = sfx_definition.split(",")
sfx_definition_members = sfx_definition.split(self.CONFIG_SEPARATOR)
path, volume, fade_out, loops, maxtime = sfx_definition_members[0], 1.0, 0, 0, 0
for ii, member in enumerate(sfx_definition_members[1:]):
if ii == 0:
@ -96,7 +94,7 @@ class Audio(GameChild):
def load_sfx_file(self, path, name=None, replace=False, prefix="",
volume=1.0, fade_out=0, loops=0, maxtime=0):
path = self.get_resource(path)
if path and self.is_sound_file(path):
if path and self.is_loadable(path):
if name is None:
name = prefix + re.sub("\.[^.]*$", "", os.path.basename(path))
if not replace and name in self.sfx:
@ -117,8 +115,13 @@ class Audio(GameChild):
# written to config file
#
def load_bgm(self):
for name, path in self.get_configuration("bgm").items():
self.set_bgm(path, name)
for name, bgm_definition in self.get_configuration("bgm").items():
bgm_definition_members = bgm_definition.split(self.CONFIG_SEPARATOR)
path, volume = bgm_definition_members[0], 1.0
for ii, member in enumerate(bgm_definition_members[1:]):
if ii == 0:
volume = float(member)
self.set_bgm(path, name, volume=volume)
for root in self.get_configuration("audio", "bgm-project-path"):
if os.path.exists(root):
print("checking {} for music".format(root))
@ -133,7 +136,7 @@ class Audio(GameChild):
prefix = re.sub("/", "_", prefix) + "_"
self.set_bgm(os.path.join(node, leaf), prefix=prefix)
def set_bgm(self, path, name=None, prefix=""):
def set_bgm(self, path, name=None, prefix="", volume=1.0):
print("setting {} music to {}".format(name, path))
try:
pygame.mixer.music.load(path)
@ -142,12 +145,22 @@ class Audio(GameChild):
return False
if name is None:
name = os.path.basename(path).split(".")[0]
self.bgm[prefix + name] = path
self.bgm[prefix + name] = BGM(self, path, volume)
return True
def play_bgm(self, name):
pygame.mixer.music.load(self.bgm[name])
pygame.mixer.music.play(-1)
def play_bgm(self, name=None, store_as_current=True, start=0):
if name is None:
bgm = self.current_bgm
else:
bgm = self.bgm[name]
pygame.mixer.music.load(bgm.get_path())
try:
pygame.mixer.music.play(-1, start)
except pygame.error:
pygame.mixer.music.play(-1)
pygame.mixer.music.set_volume(bgm.get_volume())
if store_as_current:
self.current_bgm = bgm
def is_sound_file(self, path):
return path.split(".")[-1] in self.get_configuration("audio", "sfx-extensions")
@ -159,6 +172,13 @@ class Audio(GameChild):
return False
return True
def is_streamable(self, path):
try:
pygame.mixer.music.load(path)
except:
return False
return True
def update(self):
# for ii in range(pygame.mixer.get_num_channels()):
# channel = pygame.mixer.Channel(ii)
@ -170,6 +190,26 @@ class Audio(GameChild):
self.audio_panel.update()
class BGM(GameChild):
def __init__(self, parent, path, volume=1.0):
GameChild.__init__(self, parent)
self.path = path
self.volume = volume
def get_path(self):
return self.path
def adjust_volume(self, increment):
self.volume = clamp(self.volume + increment, 0, 1.0)
if self.parent.current_bgm == self:
pygame.mixer.music.set_volume(self.volume)
return self.volume
def get_volume(self):
return self.volume
class SoundEffect(GameChild, pygame.mixer.Sound):
def __init__(self, parent, path, volume=1.0, loops=0, fade_out_length=0,
@ -256,6 +296,7 @@ class AudioPanel(Animation):
def __init__(self, parent):
Animation.__init__(self, parent)
self.rows = []
self.bgm_elapsed = None
font_path = self.get_resource(self.get_configuration("audio", "panel-font"))
self.font_large = pygame.font.Font(font_path, 15)
self.font_medium = pygame.font.Font(font_path, 12)
@ -277,11 +318,17 @@ class AudioPanel(Animation):
def activate(self):
pygame.mouse.set_visible(True)
self.active = True
if pygame.mixer.music.get_busy():
self.bgm_elapsed = pygame.mixer.music.get_pos() / 1000
pygame.mixer.music.stop()
pygame.mixer.stop()
# self.build()
def deactivate(self):
pygame.mouse.set_visible(self.get_configuration("mouse", "visible"))
self.active = False
if self.bgm_elapsed is not None:
self.get_audio().play_bgm(start=self.bgm_elapsed)
def respond(self, event):
if self.get_delegate().compare(event, "toggle-audio-panel") and self.get_audio().sfx:
@ -315,6 +362,7 @@ class AudioPanel(Animation):
Animation.update(self)
ds = self.get_display_surface()
dsr = ds.get_rect()
ds.fill((0, 0, 0))
corner = Vector(self.MARGIN, self.MARGIN)
index = self.row_offset
for row in self.rows:
@ -331,8 +379,8 @@ class AudioPanel(Animation):
class AudioPanelRow(BlinkingSprite):
BACKGROUND = pygame.Color(255, 255, 255, 210)
FOREGROUND = pygame.Color(0, 0, 0, 210)
BACKGROUND = pygame.Color(128, 192, 255, 255)
FOREGROUND = pygame.Color(0, 0, 0, 255)
WIDTH = .5
HEIGHT = 30
INDENT = 4
@ -347,14 +395,18 @@ class AudioPanelRow(BlinkingSprite):
self.font = self.parent.font_large
self.is_bgm = is_bgm
self.build()
font_medium = self.parent.font_medium
font_small = self.parent.font_small
if self.is_bgm:
volume = self.get_bgm().volume
volume_function = self.get_bgm().adjust_volume
else:
volume = self.get_sound_effect().local_volume
volume_function = self.get_sound_effect().adjust_volume
self.volume_spinner = AudioPanelSpinner(
self, font_medium, font_small, self.SLIDER_W, self.location.h, .05,
volume, volume_function, self.FOREGROUND, self.BACKGROUND, 2, "vol")
if not self.is_bgm:
font_medium = self.parent.font_medium
font_small = self.parent.font_small
self.volume_spinner = AudioPanelSpinner(
self, font_medium, font_small, self.SLIDER_W, self.location.h, .05,
self.get_sound_effect().local_volume,
self.get_sound_effect().adjust_volume, self.FOREGROUND,
self.BACKGROUND, 2, "vol")
self.fade_out_spinner = AudioPanelSpinner(
self, font_medium, font_small, self.SLIDER_W, self.location.h, .1,
self.get_sound_effect().fade_out_length,
@ -371,7 +423,7 @@ class AudioPanelRow(BlinkingSprite):
self.get_sound_effect().adjust_maxtime, self.FOREGROUND,
self.BACKGROUND, 1, "cutoff")
if self.is_bgm:
callback, kwargs = self.get_game().get_audio().play_bgm, {"name": self.key}
callback, kwargs = self.get_game().get_audio().play_bgm, {"name": self.key, "store_as_current": False}
else:
callback, kwargs = self.get_sound_effect().play, {}
self.play_button = AudioPanelButton(self, callback, kwargs)
@ -441,7 +493,7 @@ class AudioPanelRow(BlinkingSprite):
file_sprite.display_surface = surface
file_name_sprite = Sprite(self)
if self.is_bgm:
file_name = self.get_bgm()
file_name = self.get_bgm().path
else:
file_name = self.get_sound_effect().path
file_name_text = self.font.render(file_name, True, self.FOREGROUND)
@ -465,7 +517,8 @@ class AudioPanelRow(BlinkingSprite):
if not self.get_configuration().has_section(section_name):
self.get_configuration().add_section(section_name)
if self.is_bgm:
config_value = self.get_bgm()
bgm = self.get_bgm()
config_value = "{}, {:.2f}".format(bgm.path, bgm.volume)
else:
sound_effect = self.get_sound_effect()
config_value = "{}, {:.2f}, {:.2f}, {}, {:.2f}".format(
@ -480,14 +533,14 @@ class AudioPanelRow(BlinkingSprite):
def update(self):
self.play_button.location.midleft = self.location.move(5, 0).midright
self.stop_button.location.midleft = self.play_button.location.midright
self.volume_spinner.location.midleft = self.stop_button.location.move(5, 0).midright
if not self.is_bgm:
self.volume_spinner.location.midleft = self.stop_button.location.move(5, 0).midright
self.fade_out_spinner.location.midleft = self.volume_spinner.location.midright
self.loops_spinner.location.midleft = self.fade_out_spinner.location.midright
self.maxtime_spinner.location.midleft = self.loops_spinner.location.midright
Sprite.update(self)
self.volume_spinner.update()
if not self.is_bgm:
self.volume_spinner.update()
self.fade_out_spinner.update()
self.loops_spinner.update()
self.maxtime_spinner.update()
@ -507,6 +560,7 @@ class AudioPanelFileBrowser(Sprite):
self.rows = []
self.font = self.parent.font_large
self.previewing_sound = None
self.previewing_sound_row = None
ds = self.get_display_surface()
dsr = ds.get_rect()
surface = pygame.Surface((dsr.w * self.WIDTH - 2, dsr.h * self.HEIGHT - 2), SRCALPHA)
@ -610,7 +664,7 @@ class AudioPanelFileBrowser(Sprite):
row.path = path
text = self.font.render(path, True, self.COLORS[1])
surface = pygame.Surface((self.location.w, text.get_height()), SRCALPHA)
surface.blit(text, (0, 0))
surface.blit(text, (8, 0))
surface.fill(self.COLORS[1], (0, surface.get_height() - 1, self.location.w, 1))
row.add_frame(surface)
row.display_surface = self.get_current_frame()
@ -618,7 +672,7 @@ class AudioPanelFileBrowser(Sprite):
self.rows.append(row)
full_path = os.path.join(os.path.sep.join(self.trail), path)
if self.get_audio().is_sound_file(full_path):
button = AudioPanelButton(self, self.preview, {"path": full_path}, [row, self])
button = AudioPanelButton(self, self.preview, {"path": full_path, "row": row}, [row, self])
row.set_child("button", button)
frame = pygame.Surface([text.get_height()] * 2, SRCALPHA)
w, h = frame.get_size()
@ -626,14 +680,22 @@ class AudioPanelFileBrowser(Sprite):
frame, self.COLORS[1], ((w * .25, h * .25), (w * .25, h * .75), (w * .75, h * .5)))
button.add_frame(frame)
button.display_surface = row.get_current_frame()
button.location.right = self.location.w
button.location.right = self.location.w - 10
def preview(self, path):
if self.get_audio().is_sound_file(path):
def preview(self, path, row):
is_bgm = self.parent.get_selected().is_bgm
audio = self.get_audio()
if is_bgm and audio.is_streamable(path) or not is_bgm and audio.is_loadable(path):
if self.previewing_sound is not None:
self.previewing_sound.stop()
self.previewing_sound = SoundEffect(self, path)
self.previewing_sound.play()
pygame.mixer.music.stop()
if is_bgm:
pygame.mixer.music.load(path)
pygame.mixer.music.play(-1)
else:
self.previewing_sound = SoundEffect(self, path)
self.previewing_sound.play()
self.previewing_sound_row = row
def update(self):
self.get_current_frame().blit(self.background, (0, 0))
@ -653,6 +715,14 @@ class AudioPanelFileBrowser(Sprite):
index += 1
for row in self.rows:
row.update()
for location in row.locations:
if location.collidepoint(*Vector(*pygame.mouse.get_pos()).get_moved(
-self.location.left, -self.location.top)) or \
row == self.previewing_sound_row:
self.get_current_frame().fill(self.COLORS[1], (
location.topleft, (6, location.h)))
self.get_current_frame().fill(self.COLORS[1], (
location.move(-8, 0).topright, (6, location.h)))
Sprite.update(self)
@ -741,12 +811,13 @@ class AudioPanelSpinner(Sprite):
class AudioPanelButton(Sprite):
def __init__(self, parent, callback, callback_kwargs={}, containers=[]):
def __init__(self, parent, callback, callback_kwargs={}, containers=[], pass_mods=False):
Sprite.__init__(self, parent)
self.callback = callback
self.callback_kwargs = callback_kwargs
self.containers = containers
self.clickable = True
self.pass_mods = pass_mods
self.subscribe(self.respond, pygame.MOUSEBUTTONDOWN)
def unsubscribe(self, callback=None, kind=None):
@ -761,7 +832,11 @@ class AudioPanelButton(Sprite):
for container in self.containers:
pos.move(-container.location.left, -container.location.top)
if self.collide(pos):
self.callback(**self.callback_kwargs)
if self.pass_mods:
kwargs = dict(**self.callback_kwargs, **{"mods": pygame.key.get_mods()})
else:
kwargs = self.callback_kwargs
self.callback(**kwargs)
def set_clickable(self, clickable=True):
self.clickable = clickable

View File

@ -59,7 +59,7 @@ class Game(Animation):
def frame(self):
self.time_filter.update()
self.delegate.dispatch()
if not self.interpolator.is_gui_active():
if not self.interpolator.is_gui_active() and not self.get_audio().audio_panel.active:
Animation.update(self)
if self.confirming_quit:
self.time_filter.close()
@ -67,7 +67,7 @@ class Game(Animation):
else:
self.time_filter.open()
self.update()
else:
elif self.interpolator.is_gui_active():
self.interpolator.gui.update()
self.audio.update()
if self.video_recorder.requested: